Repository: microsoft/xbox-live-api Branch: main Commit: dd61050a9503 Files: 994 Total size: 12.3 MB Directory structure: gitextract_y7zsxcyl/ ├── .gitattributes ├── .gitignore ├── .gitmodules ├── Build/ │ ├── GetXsapiAndroidBinaryDir.cmake │ ├── Microsoft.Xbox.Services.141.GDK.C/ │ │ └── Microsoft.Xbox.Services.141.GDK.C.vcxproj │ ├── Microsoft.Xbox.Services.141.GDK.C.Thunks/ │ │ ├── Microsoft.Xbox.Services.141.GDK.C.Thunks.vcxproj │ │ ├── Microsoft.Xbox.Services.141.GDK.C.Thunks.vcxproj.filters │ │ ├── dll/ │ │ │ ├── Microsoft.Xbox.Services.141.GDK.C.Thunks.def │ │ │ ├── dllmain.cpp │ │ │ ├── pch.cpp │ │ │ └── pch.h │ │ └── generator/ │ │ └── ThunksGenerator/ │ │ ├── App.config │ │ ├── Program.cs │ │ ├── Properties/ │ │ │ └── AssemblyInfo.cs │ │ └── ThunksGenerator.csproj │ ├── Microsoft.Xbox.Services.142.GDK.C/ │ │ └── Microsoft.Xbox.Services.142.GDK.C.vcxproj │ ├── Microsoft.Xbox.Services.Common/ │ │ ├── Microsoft.Xbox.Services.Common.vcxitems │ │ └── Microsoft.Xbox.Services.Common.vcxitems.filters │ ├── Microsoft.Xbox.Services.GDK/ │ │ ├── Microsoft.Xbox.Services.GDK.vcxitems │ │ └── Microsoft.Xbox.Services.GDK.vcxitems.filters │ ├── Microsoft.Xbox.Services.Win32.C.Dll.def │ ├── xsapi.gdk.bwoi.props │ ├── xsapi.gdk.props │ └── xsapi.win32.props ├── CONTRIBUTING.md ├── Custom.props ├── Directory.Packages.props ├── External/ │ └── Xal/ │ ├── README │ └── Source/ │ └── Xal/ │ └── Include/ │ └── Xal/ │ ├── xal.h │ ├── xal_android.h │ ├── xal_apple.h │ ├── xal_generic.h │ ├── xal_gsdk.h │ ├── xal_gsdk_impl.h │ ├── xal_internal_marketing.h │ ├── xal_internal_telemetry.h │ ├── xal_internal_types.h │ ├── xal_internal_web_account.h │ ├── xal_platform.h │ ├── xal_platform_types.h │ ├── xal_types.h │ ├── xal_user.h │ ├── xal_uwp.h │ ├── xal_uwp_user.h │ ├── xal_version.h │ ├── xal_win32.h │ ├── xal_xdk.h │ └── xal_xdk_ext.h ├── Include/ │ ├── cpprestinclude/ │ │ ├── cpprest/ │ │ │ ├── astreambuf.h │ │ │ ├── asyncrt_utils.h │ │ │ ├── base_uri.h │ │ │ ├── containerstream.h │ │ │ ├── details/ │ │ │ │ ├── SafeInt3.hpp │ │ │ │ ├── asyncrt_utils.hpp │ │ │ │ ├── base64.hpp │ │ │ │ ├── basic_types.h │ │ │ │ ├── cpprest_compat.h │ │ │ │ ├── http_client_msg.hpp │ │ │ │ ├── http_helpers.h │ │ │ │ ├── http_helpers.hpp │ │ │ │ ├── http_msg.hpp │ │ │ │ ├── json.hpp │ │ │ │ ├── json_parsing.hpp │ │ │ │ ├── json_serialization.hpp │ │ │ │ ├── nosal.h │ │ │ │ ├── uri.hpp │ │ │ │ ├── uri_builder.hpp │ │ │ │ ├── uri_parser.h │ │ │ │ ├── uri_parser.hpp │ │ │ │ └── web_utilities.h │ │ │ ├── http_headers.h │ │ │ ├── http_msg.h │ │ │ ├── json.h │ │ │ ├── producerconsumerstream.h │ │ │ ├── streams.h │ │ │ ├── uri.h │ │ │ └── uri_builder.h │ │ ├── cpprestsdk_impl.h │ │ └── pplx/ │ │ ├── details/ │ │ │ ├── pplx.hpp │ │ │ ├── pplxapple.hpp │ │ │ ├── pplxlinux.hpp │ │ │ ├── pplxwin.hpp │ │ │ └── threadpool.hpp │ │ ├── pplx.h │ │ ├── pplxcancellation_token.h │ │ ├── pplxinterface.h │ │ ├── pplxlinux.h │ │ ├── pplxtasks.110.h │ │ ├── pplxtasks.140.h │ │ ├── pplxtasks.h │ │ ├── pplxwin.h │ │ └── threadpool.h │ ├── xsapi-c/ │ │ ├── achievements_c.h │ │ ├── achievements_manager_c.h │ │ ├── errors_c.h │ │ ├── events_c.h │ │ ├── game_invite_c.h │ │ ├── http_call_c.h │ │ ├── leaderboard_c.h │ │ ├── matchmaking_c.h │ │ ├── multiplayer_activity_c.h │ │ ├── multiplayer_c.h │ │ ├── multiplayer_manager_c.h │ │ ├── notification_c.h │ │ ├── pal.h │ │ ├── platform_c.h │ │ ├── presence_c.h │ │ ├── privacy_c.h │ │ ├── profile_c.h │ │ ├── real_time_activity_c.h │ │ ├── services_c.h │ │ ├── social_c.h │ │ ├── social_manager_c.h │ │ ├── string_verify_c.h │ │ ├── title_managed_statistics_c.h │ │ ├── title_storage_c.h │ │ ├── types_c.h │ │ ├── user_statistics_c.h │ │ ├── xbox_live_context_c.h │ │ ├── xbox_live_context_settings_c.h │ │ └── xbox_live_global_c.h │ └── xsapi-cpp/ │ ├── achievements.h │ ├── errors.h │ ├── events.h │ ├── http_call.h │ ├── http_call_request_message.h │ ├── impl/ │ │ ├── achievements.hpp │ │ ├── errors.hpp │ │ ├── events.hpp │ │ ├── http_call.hpp │ │ ├── http_call_request_message.hpp │ │ ├── leaderboard.hpp │ │ ├── matchmaking.hpp │ │ ├── mem.hpp │ │ ├── multiplayer.hpp │ │ ├── multiplayer_manager.hpp │ │ ├── notification.hpp │ │ ├── presence.hpp │ │ ├── privacy.hpp │ │ ├── profile.hpp │ │ ├── public_utils.h │ │ ├── real_time_activity.hpp │ │ ├── service_call_logging_config.hpp │ │ ├── social.hpp │ │ ├── social_manager.hpp │ │ ├── string_verify.hpp │ │ ├── system.hpp │ │ ├── title_storage.hpp │ │ ├── user_statistics.hpp │ │ ├── xbox_live_app_config.hpp │ │ ├── xbox_live_context.hpp │ │ ├── xbox_live_context_settings.hpp │ │ └── xbox_service_call_routed_event_args.hpp │ ├── leaderboard.h │ ├── matchmaking.h │ ├── mem.h │ ├── multiplayer.h │ ├── multiplayer_manager.h │ ├── notification_helper.h │ ├── notification_service.h │ ├── presence.h │ ├── privacy.h │ ├── profile.h │ ├── real_time_activity.h │ ├── service_call_logging_config.h │ ├── services.h │ ├── social.h │ ├── social_manager.h │ ├── string_verify.h │ ├── system.h │ ├── title_callable_ui.h │ ├── title_storage.h │ ├── types.h │ ├── user_statistics.h │ ├── xbox_live_app_config.h │ ├── xbox_live_context.h │ ├── xbox_live_context_settings.h │ └── xbox_service_call_routed_event_args.h ├── LICENSE.md ├── LINKTOSOURCE.md ├── Microsoft.Xbox.Services.GDK.VS2017.sln ├── Microsoft.Xbox.Services.GDK.VS2019.sln ├── Microsoft.Xbox.Services.GDK.VS2022.sln ├── NuGet.config ├── README.md ├── SECURITY.md ├── Source/ │ ├── Services/ │ │ ├── Achievements/ │ │ │ ├── Manager/ │ │ │ │ ├── achievements_manager_api.cpp │ │ │ │ ├── achievements_manager_internal.cpp │ │ │ │ └── achievements_manager_internal.h │ │ │ ├── achievement_service_internal.cpp │ │ │ ├── achievements_api.cpp │ │ │ ├── achievements_internal.h │ │ │ ├── achievements_result.cpp │ │ │ └── achievements_subscription.cpp │ │ ├── Common/ │ │ │ ├── Cpp/ │ │ │ │ ├── pch.cpp │ │ │ │ └── pch.h │ │ │ ├── Unix/ │ │ │ │ ├── pch.cpp │ │ │ │ └── pch.h │ │ │ ├── iOS/ │ │ │ │ ├── pch.cpp │ │ │ │ └── pch.h │ │ │ ├── pch_common.h │ │ │ ├── xbox_live_context.cpp │ │ │ ├── xbox_live_context_api.cpp │ │ │ ├── xbox_live_context_internal.h │ │ │ ├── xbox_live_context_settings.cpp │ │ │ ├── xbox_live_context_settings_internal.h │ │ │ └── xbox_live_global_api.cpp │ │ ├── Events/ │ │ │ ├── Android/ │ │ │ │ └── events_service_android.cpp │ │ │ ├── Generic/ │ │ │ │ └── events_service_generic.cpp │ │ │ ├── Windows/ │ │ │ │ └── events_service_windows.cpp │ │ │ ├── event.cpp │ │ │ ├── event_queue.cpp │ │ │ ├── event_upload_payload.cpp │ │ │ ├── events_service.h │ │ │ ├── events_service_api.cpp │ │ │ ├── events_service_etw.cpp │ │ │ ├── events_service_etw.h │ │ │ ├── events_service_gdk.cpp │ │ │ ├── events_service_gdk.h │ │ │ ├── events_service_xsapi.cpp │ │ │ ├── events_service_xsapi.h │ │ │ └── iOS/ │ │ │ └── events_service_ios.mm │ │ ├── Leaderboard/ │ │ │ ├── leaderboard_column.cpp │ │ │ ├── leaderboard_internal.h │ │ │ ├── leaderboard_result.cpp │ │ │ ├── leaderboard_row.cpp │ │ │ └── leaderboard_service.cpp │ │ ├── Matchmaking/ │ │ │ ├── hopper_statistics_response.cpp │ │ │ ├── match_ticket_details_response.cpp │ │ │ ├── matchmaking_internal.h │ │ │ └── matchmaking_service.cpp │ │ ├── Multiplayer/ │ │ │ ├── Manager/ │ │ │ │ ├── multiplayer_client_manager.cpp │ │ │ │ ├── multiplayer_client_pending_reader.cpp │ │ │ │ ├── multiplayer_client_pending_request.cpp │ │ │ │ ├── multiplayer_event_args.cpp │ │ │ │ ├── multiplayer_event_queue.cpp │ │ │ │ ├── multiplayer_game_client.cpp │ │ │ │ ├── multiplayer_game_session.cpp │ │ │ │ ├── multiplayer_lobby_client.cpp │ │ │ │ ├── multiplayer_lobby_session.cpp │ │ │ │ ├── multiplayer_local_user.cpp │ │ │ │ ├── multiplayer_local_user_manager.cpp │ │ │ │ ├── multiplayer_manager.cpp │ │ │ │ ├── multiplayer_manager_api.cpp │ │ │ │ ├── multiplayer_manager_internal.h │ │ │ │ ├── multiplayer_manager_utils.cpp │ │ │ │ ├── multiplayer_match_client.cpp │ │ │ │ ├── multiplayer_member.cpp │ │ │ │ └── multiplayer_session_writer.cpp │ │ │ ├── multiplayer_activity_handle_post_request.cpp │ │ │ ├── multiplayer_activity_query_post_request.cpp │ │ │ ├── multiplayer_api.cpp │ │ │ ├── multiplayer_internal.h │ │ │ ├── multiplayer_invite_handle_post_request.cpp │ │ │ ├── multiplayer_query_search_handle_request.cpp │ │ │ ├── multiplayer_search_handle_details.cpp │ │ │ ├── multiplayer_search_handle_request.cpp │ │ │ ├── multiplayer_serializers.cpp │ │ │ ├── multiplayer_service.cpp │ │ │ ├── multiplayer_session.cpp │ │ │ ├── multiplayer_session_member.cpp │ │ │ ├── multiplayer_session_reference.cpp │ │ │ ├── multiplayer_subscription.cpp │ │ │ └── multiplayer_transfer_handle_post_request.cpp │ │ ├── MultiplayerActivity/ │ │ │ ├── multiplayer_activity_api.cpp │ │ │ ├── multiplayer_activity_info.cpp │ │ │ ├── multiplayer_activity_internal.h │ │ │ └── multiplayer_activity_service.cpp │ │ ├── Notification/ │ │ │ ├── Mobile/ │ │ │ │ └── notification_service_mobile.cpp │ │ │ ├── RTA/ │ │ │ │ ├── notification_service_rta.cpp │ │ │ │ ├── notification_subscription.cpp │ │ │ │ └── notification_subscription.h │ │ │ ├── UWP/ │ │ │ │ └── notification_service_uwp.cpp │ │ │ ├── iOS/ │ │ │ │ └── notification_helper.mm │ │ │ ├── notification_api.cpp │ │ │ ├── notification_internal.h │ │ │ └── notification_service.cpp │ │ ├── Presence/ │ │ │ ├── device_presence_change_subscription.cpp │ │ │ ├── presence_api.cpp │ │ │ ├── presence_device_record.cpp │ │ │ ├── presence_internal.h │ │ │ ├── presence_record.cpp │ │ │ ├── presence_service.cpp │ │ │ ├── presence_title_request.cpp │ │ │ ├── presence_user_batch_request.cpp │ │ │ └── title_presence_change_subscription.cpp │ │ ├── Privacy/ │ │ │ ├── permission_check_result.cpp │ │ │ ├── privacy_api.cpp │ │ │ ├── privacy_service.cpp │ │ │ └── privacy_service_internal.h │ │ ├── RealTimeActivityManager/ │ │ │ ├── real_time_activity_api.cpp │ │ │ ├── real_time_activity_connection.cpp │ │ │ ├── real_time_activity_connection.h │ │ │ ├── real_time_activity_manager.cpp │ │ │ ├── real_time_activity_manager.h │ │ │ └── real_time_activity_subscription.h │ │ ├── Social/ │ │ │ ├── Manager/ │ │ │ │ ├── peoplehub_service.cpp │ │ │ │ ├── peoplehub_service.h │ │ │ │ ├── social_graph.cpp │ │ │ │ ├── social_graph.h │ │ │ │ ├── social_manager.cpp │ │ │ │ ├── social_manager_api.cpp │ │ │ │ ├── social_manager_internal.h │ │ │ │ ├── social_manager_user_group.cpp │ │ │ │ └── social_manager_user_group.h │ │ │ ├── profile_api.cpp │ │ │ ├── profile_internal.h │ │ │ ├── profile_service.cpp │ │ │ ├── reputation_feedback_request.cpp │ │ │ ├── reputation_service.cpp │ │ │ ├── social_api.cpp │ │ │ ├── social_internal.h │ │ │ ├── social_relationship_change_subscription.cpp │ │ │ ├── social_relationship_result.cpp │ │ │ └── social_service.cpp │ │ ├── Stats/ │ │ │ ├── requested_statistics.cpp │ │ │ ├── service_configuration_statistic.cpp │ │ │ ├── statistic.cpp │ │ │ ├── statistic_change_subscription.cpp │ │ │ ├── title_managed_statistics_api.cpp │ │ │ ├── title_managed_statistics_internal.h │ │ │ ├── title_managed_statistics_service.cpp │ │ │ ├── user_statistics_api.cpp │ │ │ ├── user_statistics_internal.h │ │ │ ├── user_statistics_result.cpp │ │ │ └── user_statistics_service.cpp │ │ ├── StringVerify/ │ │ │ ├── string_service.cpp │ │ │ ├── string_service_internal.h │ │ │ └── verify_string_result.cpp │ │ ├── TCUI/ │ │ │ ├── Android/ │ │ │ │ ├── title_callable_static_glue.h │ │ │ │ ├── title_callable_ui_android.cpp │ │ │ │ ├── title_callable_ui_jni.h │ │ │ │ └── title_callable_ui_static_glue.cpp │ │ │ └── iOS/ │ │ │ └── title_callable_ui.mm │ │ └── TitleStorage/ │ │ ├── title_storage_api.cpp │ │ ├── title_storage_blob_metadata_result.cpp │ │ ├── title_storage_internal.h │ │ └── title_storage_service.cpp │ ├── Shared/ │ │ ├── HookedUri/ │ │ │ ├── asyncrt_utils.h │ │ │ ├── base_uri.h │ │ │ ├── details/ │ │ │ │ ├── asyncrt_utils.hpp │ │ │ │ ├── basic_types.h │ │ │ │ ├── cpprest_compat.h │ │ │ │ ├── nosal.h │ │ │ │ ├── uri.hpp │ │ │ │ ├── uri_builder.hpp │ │ │ │ ├── uri_parser.h │ │ │ │ └── uri_parser.hpp │ │ │ ├── uri.h │ │ │ └── uri_builder.h │ │ ├── Logger/ │ │ │ ├── log.cpp │ │ │ ├── log.h │ │ │ ├── log_entry.cpp │ │ │ ├── log_hc_output.cpp │ │ │ ├── log_hc_output.h │ │ │ └── log_output.cpp │ │ ├── a/ │ │ │ ├── android_utils.cpp │ │ │ ├── android_utils.h │ │ │ ├── guid.cpp │ │ │ ├── http_call_jni.cpp │ │ │ ├── http_call_jni.h │ │ │ ├── http_call_static_glue.cpp │ │ │ ├── http_call_static_glue.h │ │ │ ├── interop_jni.cpp │ │ │ ├── interop_jni.h │ │ │ ├── jni_utils.h │ │ │ ├── rwlock_guard.cpp │ │ │ ├── rwlock_guard.h │ │ │ ├── utils_a.cpp │ │ │ ├── utils_a.h │ │ │ ├── xbox_live_app_config_jni.cpp │ │ │ └── xbox_live_app_config_static_glue.h │ │ ├── async_helpers.cpp │ │ ├── async_helpers.h │ │ ├── build_version.h │ │ ├── enum_traits.h │ │ ├── errors.cpp │ │ ├── errors_legacy.h │ │ ├── fault_injection.cpp │ │ ├── fault_injection.h │ │ ├── global_state.cpp │ │ ├── global_state.h │ │ ├── http_call_api.cpp │ │ ├── http_call_legacy.cpp │ │ ├── http_call_legacy.h │ │ ├── http_call_request_message.cpp │ │ ├── http_call_request_message_internal.h │ │ ├── http_call_wrapper_internal.cpp │ │ ├── http_call_wrapper_internal.h │ │ ├── http_headers.h │ │ ├── http_utils.cpp │ │ ├── http_utils.h │ │ ├── i/ │ │ │ ├── guid.mm │ │ │ └── utils_locales_ios.mm │ │ ├── internal_errors.h │ │ ├── internal_mem.cpp │ │ ├── internal_mem.h │ │ ├── internal_types.h │ │ ├── mem.cpp │ │ ├── perf_tester.h │ │ ├── public_utils_legacy.cpp │ │ ├── public_utils_legacy.h │ │ ├── ref_counter.cpp │ │ ├── ref_counter.h │ │ ├── service_call_routed_handler.cpp │ │ ├── service_call_routed_handler.h │ │ ├── shared_macros.h │ │ ├── string_array.h │ │ ├── u/ │ │ │ └── xbl_guid.h │ │ ├── uri_impl.h │ │ ├── user.cpp │ │ ├── user.h │ │ ├── utils_locales.cpp │ │ ├── web_socket.cpp │ │ ├── web_socket.h │ │ ├── xbox_live_app_config.cpp │ │ ├── xbox_live_app_config_internal.h │ │ ├── xsapi_json_utils.cpp │ │ ├── xsapi_json_utils.h │ │ ├── xsapi_utils.cpp │ │ └── xsapi_utils.h │ └── System/ │ ├── Android/ │ │ └── local_storage_android.cpp │ ├── Win32/ │ │ └── local_storage_win32.cpp │ ├── a/ │ │ ├── java_interop.cpp │ │ └── java_interop.h │ ├── client_operation.h │ ├── iOS/ │ │ ├── XBLDictionaryToJSON.h │ │ ├── XBLDictionaryToJSON.mm │ │ ├── XBLKeychainStorage.h │ │ ├── XBLKeychainStorage.mm │ │ ├── XBLiOSGlobalState.h │ │ ├── XBLiOSGlobalState.mm │ │ ├── local_storage_ios.mm │ │ └── xbox_live_app_config_ios.mm │ ├── local_storage.cpp │ ├── local_storage.h │ └── platform_api.cpp ├── Tests/ │ ├── ApiExplorer/ │ │ ├── APIExplorer.Shared.vcxitems │ │ ├── APIExplorer.Shared.vcxitems.filters │ │ ├── APIs/ │ │ │ ├── api_xblc_multiplayer_activity.cpp │ │ │ ├── apis.cpp │ │ │ ├── apis.h │ │ │ ├── apis_async.cpp │ │ │ ├── apis_cpp_achievements.cpp │ │ │ ├── apis_cpp_events.cpp │ │ │ ├── apis_cpp_leaderboard.cpp │ │ │ ├── apis_cpp_multiplayer.cpp │ │ │ ├── apis_cpp_presence.cpp │ │ │ ├── apis_cpp_privacy.cpp │ │ │ ├── apis_cpp_profile.cpp │ │ │ ├── apis_cpp_real_time_activity.cpp │ │ │ ├── apis_cpp_social.cpp │ │ │ ├── apis_cpp_social_manager.cpp │ │ │ ├── apis_cpp_statistics.cpp │ │ │ ├── apis_cpp_stringverify.cpp │ │ │ ├── apis_cpp_title_storage.cpp │ │ │ ├── apis_docs.cpp │ │ │ ├── apis_grts_gameinvite.cpp │ │ │ ├── apis_libhttp.cpp │ │ │ ├── apis_xal.cpp │ │ │ ├── apis_xblc.cpp │ │ │ ├── apis_xblc_achievement_unlock_notification.cpp │ │ │ ├── apis_xblc_achievements.cpp │ │ │ ├── apis_xblc_achievements_manager.cpp │ │ │ ├── apis_xblc_achievements_progress_notification.cpp │ │ │ ├── apis_xblc_events.cpp │ │ │ ├── apis_xblc_gameinvite_notifications.cpp │ │ │ ├── apis_xblc_grts.cpp │ │ │ ├── apis_xblc_leaderboard.cpp │ │ │ ├── apis_xblc_multiplayer.cpp │ │ │ ├── apis_xblc_multiplayer_manager.cpp │ │ │ ├── apis_xblc_presence.cpp │ │ │ ├── apis_xblc_privacy.cpp │ │ │ ├── apis_xblc_profile.cpp │ │ │ ├── apis_xblc_real_time_activity.cpp │ │ │ ├── apis_xblc_social.cpp │ │ │ ├── apis_xblc_social_manager.cpp │ │ │ ├── apis_xblc_statistics.cpp │ │ │ ├── apis_xblc_stats2017.cpp │ │ │ ├── apis_xblc_stringVerify.cpp │ │ │ ├── apis_xblc_title_storage.cpp │ │ │ └── apis_xblc_xblhttp.cpp │ │ ├── Android/ │ │ │ └── pch.h │ │ ├── Include/ │ │ │ ├── api_explorer.h │ │ │ ├── multidevice.h │ │ │ └── runner.h │ │ ├── Shared/ │ │ │ ├── Win/ │ │ │ │ └── pal.cpp │ │ │ ├── commands.cpp │ │ │ ├── log.cpp │ │ │ ├── lua-include.cpp │ │ │ ├── mem_hook.cpp │ │ │ ├── mem_hook.h │ │ │ ├── multidevice.cpp │ │ │ ├── pal.h │ │ │ ├── pch_apicommon.cpp │ │ │ ├── pch_common.cpp │ │ │ ├── pch_common.h │ │ │ ├── utils.cpp │ │ │ ├── utils.h │ │ │ └── xboxservices.config │ │ ├── Tests/ │ │ │ ├── MultiplayerActivity/ │ │ │ │ ├── receive_invite_gsdk.lua │ │ │ │ ├── receive_invite_win32.lua │ │ │ │ ├── send_invite.lua │ │ │ │ ├── set_activity.lua │ │ │ │ ├── update_activity.lua │ │ │ │ └── update_recent_players.lua │ │ │ ├── _luasetup_/ │ │ │ │ ├── u-test/ │ │ │ │ │ ├── LICENSE │ │ │ │ │ ├── README.md │ │ │ │ │ └── u-test.lua │ │ │ │ ├── uwp/ │ │ │ │ │ └── setup.lua │ │ │ │ └── xal/ │ │ │ │ ├── common.lua │ │ │ │ └── setup.lua │ │ │ ├── achievements/ │ │ │ │ ├── PerformanceTestMockResponse.json │ │ │ │ ├── achievements-cpp.lua │ │ │ │ ├── achievements.lua │ │ │ │ ├── achievements_manager.lua │ │ │ │ ├── achievements_manager_performance_test.lua │ │ │ │ ├── achievements_manager_update_achievements.lua │ │ │ │ ├── achievements_progress_notification.lua │ │ │ │ ├── update-achievements-cpp.lua │ │ │ │ └── update-achievements.lua │ │ │ ├── cmds.json │ │ │ ├── events/ │ │ │ │ ├── events-cpp.lua │ │ │ │ ├── events.lua │ │ │ │ ├── offline-cpp.lua │ │ │ │ ├── offline.lua │ │ │ │ ├── offline2-cpp.lua │ │ │ │ └── offline2.lua │ │ │ ├── gdk-gameinvite/ │ │ │ │ ├── game-invite-listen.lua │ │ │ │ ├── game-invite-send.lua │ │ │ │ └── game-mpainvite-send.lua │ │ │ ├── leaderboard/ │ │ │ │ ├── leaderboard-2017.lua │ │ │ │ ├── leaderboard-bvt.lua │ │ │ │ ├── leaderboard-cpp.lua │ │ │ │ └── leaderboard.lua │ │ │ ├── libHttp/ │ │ │ │ ├── httpHandle.lua │ │ │ │ ├── httpPerform.lua │ │ │ │ ├── libHCTrace.lua │ │ │ │ ├── manualDispatchTest.lua │ │ │ │ ├── websocket_cleanup_while_connected.lua │ │ │ │ ├── websocket_cleanup_while_connecting.lua │ │ │ │ ├── websocket_closehandle_while_connected.lua │ │ │ │ ├── websocket_disconnect_while_connecting.lua │ │ │ │ └── websocket_send.lua │ │ │ ├── misc/ │ │ │ │ ├── global_state.lua │ │ │ │ ├── null_task_queue.lua │ │ │ │ ├── override_locale.lua │ │ │ │ ├── simpletest.lua │ │ │ │ └── xbox_live_context.lua │ │ │ ├── mp/ │ │ │ │ ├── MP_JoinLobbyViaActivity.lua │ │ │ │ ├── matchmaking_single_device-cpp.lua │ │ │ │ ├── matchmaking_single_device.lua │ │ │ │ ├── mp-cpp.lua │ │ │ │ ├── mp.lua │ │ │ │ ├── mp_multiple_contexts.lua │ │ │ │ ├── mp_partial_invite_flow.lua │ │ │ │ ├── mp_search-cpp.lua │ │ │ │ ├── mp_search.lua │ │ │ │ ├── mp_transfer-cpp.lua │ │ │ │ └── mp_transfer.lua │ │ │ ├── multiplayerManager/ │ │ │ │ ├── MPM_Invite.lua │ │ │ │ ├── MPM_InviteUI.lua │ │ │ │ ├── MPM_JoinFixedGameSession.lua │ │ │ │ ├── MPM_JoinLobbyViaActivity.lua │ │ │ │ ├── MPM_Match.lua │ │ │ │ ├── MPM_SingleDevice_JoinLeaveGame.lua │ │ │ │ └── MPM_SingleDevice_SyncHostWrite.lua │ │ │ ├── notification/ │ │ │ │ ├── achievement_unlock_notification.lua │ │ │ │ ├── gameinvitenotifications.lua │ │ │ │ └── multiple_notification_subscriptions.lua │ │ │ ├── presence/ │ │ │ │ ├── presence-cpp.lua │ │ │ │ ├── presence.lua │ │ │ │ └── presence_rta.lua │ │ │ ├── privacy/ │ │ │ │ ├── privacy-cpp.lua │ │ │ │ └── privacy.lua │ │ │ ├── profile/ │ │ │ │ ├── GetUserProfile-cpp.lua │ │ │ │ ├── GetUserProfile.lua │ │ │ │ ├── GetUserProfiles-cpp.lua │ │ │ │ ├── GetUserProfiles.lua │ │ │ │ ├── GetUserProfilesForSocialGroup-cpp.lua │ │ │ │ └── GetUserProfilesForSocialGroupAsync.lua │ │ │ ├── rta/ │ │ │ │ ├── RTAResync.lua │ │ │ │ ├── RTASuspendResume.lua │ │ │ │ ├── RTA_MP_SM.lua │ │ │ │ ├── RTA_activation.lua │ │ │ │ ├── simpleRTA-cpp.lua │ │ │ │ ├── simpleRTA.lua │ │ │ │ └── simpleRTA_legacy.lua │ │ │ ├── social/ │ │ │ │ ├── reputation-cpp.lua │ │ │ │ ├── reputation.lua │ │ │ │ ├── social-cpp.lua │ │ │ │ ├── social.lua │ │ │ │ ├── social_manager_1-cpp.lua │ │ │ │ ├── social_manager_1.lua │ │ │ │ ├── social_manager_2-cpp.lua │ │ │ │ ├── social_manager_2.lua │ │ │ │ ├── social_manager_filter-cpp.lua │ │ │ │ ├── social_manager_filter.lua │ │ │ │ ├── social_manager_list-cpp.lua │ │ │ │ ├── social_manager_list.lua │ │ │ │ ├── social_manager_remove_realloc-cpp.lua │ │ │ │ ├── social_manager_remove_realloc.lua │ │ │ │ ├── social_manager_wait.lua │ │ │ │ ├── social_relationship_changed.lua │ │ │ │ └── social_sub_unsub.lua │ │ │ ├── stats/ │ │ │ │ ├── stats-bvt.lua │ │ │ │ ├── stats-cpp.lua │ │ │ │ ├── stats.lua │ │ │ │ └── stats_legacy.lua │ │ │ ├── stats2017/ │ │ │ │ ├── stats2017-test429.lua │ │ │ │ └── stats2017.lua │ │ │ ├── stringVerify/ │ │ │ │ ├── stringVerify-cpp.lua │ │ │ │ └── stringVerify.lua │ │ │ ├── tests.root │ │ │ ├── titleStorage/ │ │ │ │ ├── title_storage-cpp.lua │ │ │ │ ├── title_storage-restCalls.lua │ │ │ │ └── title_storage.lua │ │ │ ├── xal/ │ │ │ │ ├── addfirst.lua │ │ │ │ └── signOut.lua │ │ │ └── xblHttp/ │ │ │ ├── XBLHttpCall.lua │ │ │ └── XBLHttpCallPerform.lua │ │ └── lua/ │ │ ├── Makefile │ │ ├── README │ │ ├── doc/ │ │ │ ├── contents.html │ │ │ ├── index.css │ │ │ ├── lua.1 │ │ │ ├── lua.css │ │ │ ├── luac.1 │ │ │ ├── manual.css │ │ │ ├── manual.html │ │ │ └── readme.html │ │ └── src/ │ │ ├── Makefile │ │ ├── lapi.c │ │ ├── lapi.h │ │ ├── lauxlib.c │ │ ├── lauxlib.h │ │ ├── lbaselib.c │ │ ├── lbitlib.c │ │ ├── lcode.c │ │ ├── lcode.h │ │ ├── lcorolib.c │ │ ├── lctype.c │ │ ├── lctype.h │ │ ├── ldblib.c │ │ ├── ldebug.c │ │ ├── ldebug.h │ │ ├── ldo.c │ │ ├── ldo.h │ │ ├── ldump.c │ │ ├── lfunc.c │ │ ├── lfunc.h │ │ ├── lgc.c │ │ ├── lgc.h │ │ ├── linit.c │ │ ├── liolib.c │ │ ├── llex.c │ │ ├── llex.h │ │ ├── llimits.h │ │ ├── lmathlib.c │ │ ├── lmem.c │ │ ├── lmem.h │ │ ├── loadlib.c │ │ ├── lobject.c │ │ ├── lobject.h │ │ ├── lopcodes.c │ │ ├── lopcodes.h │ │ ├── loslib.c │ │ ├── lparser.c │ │ ├── lparser.h │ │ ├── lprefix.h │ │ ├── lstate.c │ │ ├── lstate.h │ │ ├── lstring.c │ │ ├── lstring.h │ │ ├── lstrlib.c │ │ ├── ltable.c │ │ ├── ltable.h │ │ ├── ltablib.c │ │ ├── ltm.c │ │ ├── ltm.h │ │ ├── lua.h │ │ ├── lua.hpp │ │ ├── luaconf.h │ │ ├── lualib.h │ │ ├── lundump.c │ │ ├── lundump.h │ │ ├── lutf8lib.c │ │ ├── lvm.c │ │ ├── lvm.h │ │ ├── lzio.c │ │ └── lzio.h │ ├── GDK/ │ │ ├── APIRunner.GDK/ │ │ │ ├── .gitignore │ │ │ ├── APIRunner.143.GDK.Src.vcxproj │ │ │ ├── APIRunner.143.GDK.Src.vcxproj.filters │ │ │ ├── APIRunner.GDK.Bin.vcxproj │ │ │ ├── APIRunner.GDK.Bin.vcxproj.filters │ │ │ ├── APIRunner.GDK.Src.vcxproj │ │ │ ├── APIRunner.GDK.Src.vcxproj.filters │ │ │ ├── APIRunner.GDK.cpp │ │ │ ├── APIRunner.GDK.h │ │ │ ├── APIRunnerBin-MicrosoftGame.Config │ │ │ ├── APIRunnerSrc-MicrosoftGame.Config │ │ │ ├── APIRunnerSrc143-MicrosoftGame.Config │ │ │ ├── APIRunnerStats2017-MicrosoftGame.Config │ │ │ ├── AppxManifest.xml │ │ │ ├── Assets/ │ │ │ │ ├── ATGConsoleBlack.DDS │ │ │ │ ├── ATGSampleBackground.DDS │ │ │ │ └── SampleUI.csv │ │ │ ├── CopyLogFromConsole.cmd │ │ │ ├── DeviceResources.cpp │ │ │ ├── DeviceResources.h │ │ │ ├── Kits/ │ │ │ │ ├── ATGTK/ │ │ │ │ │ ├── ATGColors.h │ │ │ │ │ ├── CSVReader.h │ │ │ │ │ ├── ControllerFont.h │ │ │ │ │ ├── FindMedia.h │ │ │ │ │ ├── Json.h │ │ │ │ │ ├── SampleGUI.cpp │ │ │ │ │ ├── SampleGUI.h │ │ │ │ │ ├── StringUtil.cpp │ │ │ │ │ ├── StringUtil.h │ │ │ │ │ ├── TextConsole.cpp │ │ │ │ │ ├── TextConsole.h │ │ │ │ │ ├── d3dx12.h │ │ │ │ │ └── json/ │ │ │ │ │ ├── LICENSE.MIT │ │ │ │ │ ├── commit_id.txt │ │ │ │ │ ├── documentation.txt │ │ │ │ │ ├── json.hpp │ │ │ │ │ └── repo.txt │ │ │ │ ├── ATGTelemetry/ │ │ │ │ │ └── GDK/ │ │ │ │ │ ├── ATGTelemetry.cpp │ │ │ │ │ └── ATGTelemetry.h │ │ │ │ ├── DirectXTK12/ │ │ │ │ │ ├── Audio/ │ │ │ │ │ │ ├── AudioEngine.cpp │ │ │ │ │ │ ├── DynamicSoundEffectInstance.cpp │ │ │ │ │ │ ├── SoundCommon.cpp │ │ │ │ │ │ ├── SoundCommon.h │ │ │ │ │ │ ├── SoundEffect.cpp │ │ │ │ │ │ ├── SoundEffectInstance.cpp │ │ │ │ │ │ ├── SoundStreamInstance.cpp │ │ │ │ │ │ ├── WAVFileReader.cpp │ │ │ │ │ │ ├── WAVFileReader.h │ │ │ │ │ │ ├── WaveBank.cpp │ │ │ │ │ │ ├── WaveBankReader.cpp │ │ │ │ │ │ └── WaveBankReader.h │ │ │ │ │ ├── DirectXTK12_GDK_2017.vcxproj │ │ │ │ │ ├── DirectXTK12_GDK_2017.vcxproj.filters │ │ │ │ │ ├── Inc/ │ │ │ │ │ │ ├── Audio.h │ │ │ │ │ │ ├── BufferHelpers.h │ │ │ │ │ │ ├── CommonStates.h │ │ │ │ │ │ ├── DDSTextureLoader.h │ │ │ │ │ │ ├── DescriptorHeap.h │ │ │ │ │ │ ├── DirectXHelpers.h │ │ │ │ │ │ ├── EffectPipelineStateDescription.h │ │ │ │ │ │ ├── Effects.h │ │ │ │ │ │ ├── GamePad.h │ │ │ │ │ │ ├── GeometricPrimitive.h │ │ │ │ │ │ ├── GraphicsMemory.h │ │ │ │ │ │ ├── Keyboard.h │ │ │ │ │ │ ├── Model.h │ │ │ │ │ │ ├── Mouse.h │ │ │ │ │ │ ├── PostProcess.h │ │ │ │ │ │ ├── PrimitiveBatch.h │ │ │ │ │ │ ├── RenderTargetState.h │ │ │ │ │ │ ├── ResourceUploadBatch.h │ │ │ │ │ │ ├── ScreenGrab.h │ │ │ │ │ │ ├── SimpleMath.h │ │ │ │ │ │ ├── SimpleMath.inl │ │ │ │ │ │ ├── SpriteBatch.h │ │ │ │ │ │ ├── SpriteFont.h │ │ │ │ │ │ ├── VertexTypes.h │ │ │ │ │ │ ├── WICTextureLoader.h │ │ │ │ │ │ └── XboxDDSTextureLoader.h │ │ │ │ │ ├── README.md │ │ │ │ │ └── Src/ │ │ │ │ │ ├── AlignedNew.h │ │ │ │ │ ├── AlphaTestEffect.cpp │ │ │ │ │ ├── BasicEffect.cpp │ │ │ │ │ ├── BasicPostProcess.cpp │ │ │ │ │ ├── Bezier.h │ │ │ │ │ ├── BinaryReader.cpp │ │ │ │ │ ├── BinaryReader.h │ │ │ │ │ ├── BufferHelpers.cpp │ │ │ │ │ ├── CommonStates.cpp │ │ │ │ │ ├── DDS.h │ │ │ │ │ ├── DDSTextureLoader.cpp │ │ │ │ │ ├── DebugEffect.cpp │ │ │ │ │ ├── DemandCreate.h │ │ │ │ │ ├── DescriptorHeap.cpp │ │ │ │ │ ├── DirectXHelpers.cpp │ │ │ │ │ ├── DualPostProcess.cpp │ │ │ │ │ ├── DualTextureEffect.cpp │ │ │ │ │ ├── EffectCommon.cpp │ │ │ │ │ ├── EffectCommon.h │ │ │ │ │ ├── EffectFactory.cpp │ │ │ │ │ ├── EffectPipelineStateDescription.cpp │ │ │ │ │ ├── EffectTextureFactory.cpp │ │ │ │ │ ├── EnvironmentMapEffect.cpp │ │ │ │ │ ├── GamePad.cpp │ │ │ │ │ ├── GeometricPrimitive.cpp │ │ │ │ │ ├── Geometry.cpp │ │ │ │ │ ├── Geometry.h │ │ │ │ │ ├── GraphicsMemory.cpp │ │ │ │ │ ├── Keyboard.cpp │ │ │ │ │ ├── LinearAllocator.cpp │ │ │ │ │ ├── LinearAllocator.h │ │ │ │ │ ├── LoaderHelpers.h │ │ │ │ │ ├── Model.cpp │ │ │ │ │ ├── ModelLoadSDKMESH.cpp │ │ │ │ │ ├── ModelLoadVBO.cpp │ │ │ │ │ ├── Mouse.cpp │ │ │ │ │ ├── NormalMapEffect.cpp │ │ │ │ │ ├── PBREffect.cpp │ │ │ │ │ ├── PBREffectFactory.cpp │ │ │ │ │ ├── PlatformHelpers.h │ │ │ │ │ ├── PrimitiveBatch.cpp │ │ │ │ │ ├── ResourceUploadBatch.cpp │ │ │ │ │ ├── SDKMesh.h │ │ │ │ │ ├── ScreenGrab.cpp │ │ │ │ │ ├── Shaders/ │ │ │ │ │ │ ├── AlphaTestEffect.fx │ │ │ │ │ │ ├── BasicEffect.fx │ │ │ │ │ │ ├── Common.fxh │ │ │ │ │ │ ├── CompileShaders.cmd │ │ │ │ │ │ ├── DebugEffect.fx │ │ │ │ │ │ ├── DualTextureEffect.fx │ │ │ │ │ │ ├── EnvironmentMapEffect.fx │ │ │ │ │ │ ├── GenerateMips.hlsl │ │ │ │ │ │ ├── Lighting.fxh │ │ │ │ │ │ ├── NormalMapEffect.fx │ │ │ │ │ │ ├── PBRCommon.fxh │ │ │ │ │ │ ├── PBREffect.fx │ │ │ │ │ │ ├── PixelPacking_Velocity.hlsli │ │ │ │ │ │ ├── PostProcess.fx │ │ │ │ │ │ ├── RootSig.fxh │ │ │ │ │ │ ├── SkinnedEffect.fx │ │ │ │ │ │ ├── SpriteEffect.fx │ │ │ │ │ │ ├── Structures.fxh │ │ │ │ │ │ ├── ToneMap.fx │ │ │ │ │ │ └── Utilities.fxh │ │ │ │ │ ├── SharedResourcePool.h │ │ │ │ │ ├── SimpleMath.cpp │ │ │ │ │ ├── SkinnedEffect.cpp │ │ │ │ │ ├── SpriteBatch.cpp │ │ │ │ │ ├── SpriteFont.cpp │ │ │ │ │ ├── TeapotData.inc │ │ │ │ │ ├── ToneMapPostProcess.cpp │ │ │ │ │ ├── VertexTypes.cpp │ │ │ │ │ ├── WICTextureLoader.cpp │ │ │ │ │ ├── XboxDDSTextureLoader.cpp │ │ │ │ │ ├── d3dx12.h │ │ │ │ │ ├── pch.cpp │ │ │ │ │ ├── pch.h │ │ │ │ │ └── vbo.h │ │ │ │ └── LiveTK/ │ │ │ │ ├── LiveInfoHUD.cpp │ │ │ │ ├── LiveInfoHUD.h │ │ │ │ ├── LiveResources.cpp │ │ │ │ ├── LiveResources.h │ │ │ │ ├── UITwist.cpp │ │ │ │ └── UITwist.h │ │ │ ├── Main.cpp │ │ │ ├── Media/ │ │ │ │ └── Fonts/ │ │ │ │ ├── SegoeUI_18.spritefont │ │ │ │ ├── SegoeUI_18_Bold.spritefont │ │ │ │ ├── SegoeUI_18_Italic.spritefont │ │ │ │ ├── SegoeUI_22.spritefont │ │ │ │ ├── SegoeUI_22_Bold.spritefont │ │ │ │ ├── SegoeUI_22_Italic.spritefont │ │ │ │ ├── SegoeUI_36.spritefont │ │ │ │ ├── SegoeUI_36_Bold.spritefont │ │ │ │ ├── SegoeUI_36_Italic.spritefont │ │ │ │ ├── XboxOneControllerLegend.spritefont │ │ │ │ └── XboxOneControllerLegendSmall.spritefont │ │ │ ├── MicrosoftGame.Config │ │ │ ├── StepTimer.h │ │ │ ├── pch.cpp │ │ │ └── pch.h │ │ └── ManualTest.GDK/ │ │ ├── Assets/ │ │ │ └── SampleUI.csv │ │ ├── DeviceResources.cpp │ │ ├── DeviceResources.h │ │ ├── Main.cpp │ │ ├── ManualTest.cpp │ │ ├── ManualTest.h │ │ ├── ManualTest.sln │ │ ├── ManualTest.vcxproj │ │ ├── ManualTest.vcxproj.filters │ │ ├── Media/ │ │ │ ├── Fonts/ │ │ │ │ ├── Courier_16.spritefont │ │ │ │ ├── Courier_36.spritefont │ │ │ │ ├── SegoeUI_18.spritefont │ │ │ │ ├── SegoeUI_18_Bold.spritefont │ │ │ │ ├── SegoeUI_18_Italic.spritefont │ │ │ │ ├── SegoeUI_22.spritefont │ │ │ │ ├── SegoeUI_22_Bold.spritefont │ │ │ │ ├── SegoeUI_22_Italic.spritefont │ │ │ │ ├── SegoeUI_36.spritefont │ │ │ │ ├── SegoeUI_36_Bold.spritefont │ │ │ │ ├── SegoeUI_36_Italic.spritefont │ │ │ │ ├── XboxOneController.spritefont │ │ │ │ ├── XboxOneControllerLegend.spritefont │ │ │ │ ├── XboxOneControllerLegendSmall.spritefont │ │ │ │ └── XboxOneControllerSmall.spritefont │ │ │ └── Textures/ │ │ │ └── ATGSampleBackground.DDS │ │ ├── MicrosoftGameConfig.mgc │ │ ├── SampleLiveInfoHUD.cpp │ │ ├── SampleLiveInfoHUD.h │ │ ├── StepTimer.h │ │ ├── pch.cpp │ │ └── pch.h │ └── UnitTests/ │ ├── Mocks/ │ │ ├── http_mock.cpp │ │ ├── http_mock.h │ │ ├── mock_rta_service.cpp │ │ ├── mock_rta_service.h │ │ ├── mock_user.cpp │ │ ├── mock_web_socket.cpp │ │ ├── mock_web_socket.h │ │ └── xal_mocks.cpp │ ├── Scripts/ │ │ ├── ETWProfile.wprp │ │ ├── repeat-all-except-ignored.cmd │ │ ├── repeat-focused.cmd │ │ ├── repeat-single-with-etw.cmd │ │ ├── repeat-single.cmd │ │ ├── run-all-tests-once-skip-ignore.cmd │ │ ├── run-all-tests-once.cmd │ │ └── run-focused-once.cmd │ ├── Support/ │ │ ├── DefineTestMacros.h │ │ ├── TAEF/ │ │ │ ├── UnitTestBase.cpp │ │ │ ├── UnitTestBase.h │ │ │ ├── UnitTestIncludes_TAEF.h │ │ │ ├── inc/ │ │ │ │ ├── ITestResource.h │ │ │ │ ├── Interruption.h │ │ │ │ ├── Log.h │ │ │ │ ├── LogTestResults.h │ │ │ │ ├── Logcontext.h │ │ │ │ ├── Logcontroller.h │ │ │ │ ├── PreserveLastError.h │ │ │ │ ├── RuntimeParameters.h │ │ │ │ ├── TE.Common.h │ │ │ │ ├── TestData.h │ │ │ │ ├── Throw.h │ │ │ │ ├── Verify.h │ │ │ │ ├── Wex.Common.h │ │ │ │ ├── Wex.Logger.h │ │ │ │ ├── WexAssert.h │ │ │ │ ├── WexDebug.h │ │ │ │ ├── WexException.h │ │ │ │ ├── WexString.h │ │ │ │ ├── WexTestClass.h │ │ │ │ ├── WexTypes.h │ │ │ │ └── WppDefs.h │ │ │ └── lib/ │ │ │ ├── x64/ │ │ │ │ ├── Te.Common.lib │ │ │ │ ├── Wex.Common.lib │ │ │ │ └── Wex.Logger.lib │ │ │ └── x86/ │ │ │ ├── Te.Common.lib │ │ │ ├── Wex.Common.lib │ │ │ └── Wex.Logger.lib │ │ ├── TE/ │ │ │ ├── UnitTestHelpers_TE.cpp │ │ │ ├── UnitTestHelpers_TE.h │ │ │ └── UnitTestIncludes_TE.h │ │ ├── UnitTestIncludes.h │ │ ├── event.cpp │ │ ├── event.h │ │ ├── iso8601.cpp │ │ ├── iso8601.h │ │ ├── unit_test_helpers.cpp │ │ └── unit_test_helpers.h │ └── Tests/ │ ├── Services/ │ │ ├── AchievementsManagerTests.cpp │ │ ├── AchievementsTests.cpp │ │ ├── ErrorTests.cpp │ │ ├── LeaderboardTests.cpp │ │ ├── MatchmakingTests.cpp │ │ ├── MultiplayerActivityTests.cpp │ │ ├── MultiplayerManagerTests.cpp │ │ ├── MultiplayerTests.cpp │ │ ├── PeoplehubTests.cpp │ │ ├── PresenceTests.cpp │ │ ├── PrivacyTests.cpp │ │ ├── ProfileTests.cpp │ │ ├── RealTimeActivityManagerTests.cpp │ │ ├── ReputationTests.cpp │ │ ├── SocialManagerTests.cpp │ │ ├── SocialTests.cpp │ │ ├── StatsTests.cpp │ │ ├── StringVerifyTests.cpp │ │ ├── TestResponses/ │ │ │ ├── Clubs.json │ │ │ ├── Matchmaking.json │ │ │ ├── Multiplayer.json │ │ │ └── MultiplayerManager.json │ │ ├── TitleManagedStatsTests.cpp │ │ └── TitleStorageTests.cpp │ └── Shared/ │ ├── GlobalTests.cpp │ ├── HttpCallSettingsTests.cpp │ ├── HttpCallTests.cpp │ ├── LogTests.cpp │ ├── PlatformTests.cpp │ ├── XboxLiveCallbackTests.cpp │ └── XboxLiveContextTests.cpp ├── ThirdPartyNotices.txt ├── hc_settings.props ├── xsapi.paths.props └── xsapi.staticlib.props ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitattributes ================================================ ############################################################################### # Set default behavior to automatically normalize line endings. ############################################################################### * text=auto ############################################################################### # Set default behavior for command prompt diff. # # This is need for earlier builds of msysgit that does not have it on by # default for csharp files. # Note: This is only used by command line ############################################################################### #*.cs diff=csharp ############################################################################### # Set the merge driver for project and solution files # # Merging from the command prompt will add diff markers to the files if there # are conflicts (Merging from VS is not affected by the settings below, in VS # the diff markers are never inserted). Diff markers may cause the following # file extensions to fail to load in VS. An alternative would be to treat # these files as binary and thus will always conflict and require user # intervention with every merge. To do so, just uncomment the entries below ############################################################################### #*.sln merge=binary #*.csproj merge=binary #*.vbproj merge=binary #*.vcxproj merge=binary #*.vcproj merge=binary #*.dbproj merge=binary #*.fsproj merge=binary #*.lsproj merge=binary #*.wixproj merge=binary #*.modelproj merge=binary #*.sqlproj merge=binary #*.wwaproj merge=binary ############################################################################### # behavior for image files # # image files are treated as binary by default. ############################################################################### #*.jpg binary #*.png binary #*.gif binary ############################################################################### # diff behavior for common document formats # # Convert binary document formats to text before diffing them. This feature # is only available from the command line. Turn it on by uncommenting the # entries below. ############################################################################### #*.doc diff=astextplain #*.DOC diff=astextplain #*.docx diff=astextplain #*.DOCX diff=astextplain #*.dot diff=astextplain #*.DOT diff=astextplain #*.pdf diff=astextplain #*.PDF diff=astextplain #*.rtf diff=astextplain #*.RTF diff=astextplain ================================================ FILE: .gitignore ================================================ .gitconfig .git-credentials # Ignore iOS Framework binarie Frameworks/ # Ignore OpenSSL and Boost build files for iOS .gitk .config/git/gitk **/.vs/ **/project.lock.json Casablanca*/Build_iOS/OpenSSL-for-iPhone/ Casablanca*/Build_iOS/boostoniphone/ Casablanca*/Build_iOS/openssl/ Casablanca*/Build_iOS/boost.framework/ Casablanca*/Build_iOS/build.ios/ Casablanca*/Build_iOS/ios-cmake/ #nuget packages External/Packages/* #mobile External/sdk.mobile # Xcode - from github ## User settings xcuserdata ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) *.xcscmblueprint *.xccheckout ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) build/ DerivedData/ *.moved-aside *.pbxuser !default.pbxuser *.mode1v3 !default.mode1v3 *.mode2v3 !default.mode2v3 *.perspectivev3 !default.perspectivev3 # Mac .DS_Store # VS temp files **/*.sln.ide/ **/*.opensdf **/*.sdf **/*.suo **/*.opendb **/*.VC.db **/*.user **/ipch/ **/Generated Files/ **/AppPackages/* **/*.orig **/pingme.txt **/*.cache **/dlldata.c **/*_i.c **/*_p.c **/*.metaproj **/*.metaproj.tmp **/*.opendb **/*.coveragexml **/Durango/ **/Gaming.Xbox.Desktop.x64/ **/Gaming.Xbox.XboxOne.x64/ **/Gaming.Xbox.Scarlett.x64/ **/XboxOne.x64/ Samples/**/bin/ Samples/**/Debug/ Samples/**/Release/ Samples/**/Profile/ Samples/**/Lib/ Samples/**/.workspace/ Source/**/bin/ Source/**/gen/ Source/**/Debug/ Source/**/Release/ Archive/**/Debug/ Archive/**/Release/ Archive/**/Durango/ Archive/**/Lib/ Archive/**/*.winmd Build/**/Debug/ Build/**/Release/ External/**/lib/ !External/xal/External/OneDS/lib/ External/**/bin/ External/**/Debug/ External/**/Release/ tests/**/bin/ tests/**/Debug/ tests/**/Release/ Tools/**/bin/ Tools/**/obj/ **/*.classpath APIExplorer.Win32.PrebuiltBins.sln **/local.properties **/.workspace/ #Doxygen temp files Docs/xblsdk_cpp/ Docs/xblsdk_winrt/ Docs/External/ Docs/xim_cpp/ # Unit Test FakesAssemblies/ TestResults/ # Build objects Casablanca/Intermediate/ Obj/ Binaries/ Debug/ Release/ ARM/ Built/ Bins/ # Allow files in our /Build folder !/Build !/Casablanca **/.vs/ xbox-live-samples/ Tests/StressUnitTest/ *.pdb Maven/ output/ ClockSkew.json .idea *.iml .externalNativeBuild **/XblEvents*.json **/XblEvents*.dir Tests/AndroidTestApp/MavenBinary/* Tests/APIExplorer/XDK/Kits/DirectXTK/Src/Shaders/Compiled/* Microsoft.Xbox.Services.*.sln.log Microsoft.Xbox.Services.*.sln.out **/apirunner-log.txt # Android and Android Studio files **/.idea **/*.iml **/.cxx **/local.properties **/.gradle/* ================================================ FILE: .gitmodules ================================================ [submodule "External/Xal/External/libHttpClient"] path = External/Xal/External/libHttpClient url = https://github.com/microsoft/libHttpClient [submodule "External/rapidjson"] path = External/rapidjson url = https://github.com/jasonsandlin/rapidjson ================================================ FILE: Build/GetXsapiAndroidBinaryDir.cmake ================================================ cmake_minimum_required(VERSION 3.6) function(GET_XSAPI_ANDROID_BINARY_DIR PATH_TO_ROOT OUT_BINARY_DIR) string(TOLOWER "${CMAKE_BUILD_TYPE}" PATH_FLAVOR) set(${OUT_BINARY_DIR} "${PATH_TO_ROOT}/Built/Android/${ANDROID_ABI}/${PATH_FLAVOR}" PARENT_SCOPE) endfunction() ================================================ FILE: Build/Microsoft.Xbox.Services.141.GDK.C/Microsoft.Xbox.Services.141.GDK.C.vcxproj ================================================ {60139F62-BF37-4F11-BD93-5FBF4E92100C} StaticLibrary v141 true Microsoft.Xbox.Services.141.GDK.C.$(Configuration) Microsoft.Xbox.Services.141.GDK.C ================================================ FILE: Build/Microsoft.Xbox.Services.141.GDK.C.Thunks/Microsoft.Xbox.Services.141.GDK.C.Thunks.vcxproj ================================================ {e538394b-68cb-4597-87ad-7b6841cc1278} DynamicLibrary v141 true true Microsoft.Xbox.Services.141.GDK.C.Thunks.$(Configuration) Microsoft.Xbox.Services.141.GDK.C.Thunks Create pch.h %(XboxExtensionsDependencies)Appnotify.lib;crypt32.lib;xgameruntime.lib;WINHTTP.LIB;RUNTIMEOBJECT.LIB;ADVAPI32.LIB;ole32.lib; $(ProjectDir)dll\Microsoft.Xbox.Services.141.GDK.C.Thunks.def ================================================ FILE: Build/Microsoft.Xbox.Services.141.GDK.C.Thunks/Microsoft.Xbox.Services.141.GDK.C.Thunks.vcxproj.filters ================================================ ================================================ FILE: Build/Microsoft.Xbox.Services.141.GDK.C.Thunks/dll/Microsoft.Xbox.Services.141.GDK.C.Thunks.def ================================================ LIBRARY Microsoft.Xbox.Services.141.GDK.C.Thunks.dll EXPORTS XblAchievementsGetAchievementAsync XblAchievementsGetAchievementResult XblAchievementsGetAchievementsForTitleIdAsync XblAchievementsGetAchievementsForTitleIdResult XblAchievementsResultCloseHandle XblAchievementsResultDuplicateHandle XblAchievementsResultGetAchievements XblAchievementsResultGetNextAsync XblAchievementsResultGetNextResult XblAchievementsResultHasNext XblAchievementsUpdateAchievementAsync XblAchievementsUpdateAchievementForTitleIdAsync XblAddServiceCallRoutedHandler XblCleanupAsync XblContextCloseHandle XblContextCreateHandle XblContextDuplicateHandle XblContextGetUser XblContextGetXboxUserId XblContextSettingsGetHttpRetryDelay XblContextSettingsGetHttpTimeoutWindow XblContextSettingsGetLongHttpTimeout XblContextSettingsGetUseCrossPlatformQosServers XblContextSettingsGetWebsocketTimeoutWindow XblContextSettingsSetHttpRetryDelay XblContextSettingsSetHttpTimeoutWindow XblContextSettingsSetLongHttpTimeout XblContextSettingsSetUseCrossPlatformQosServers XblContextSettingsSetWebsocketTimeoutWindow XblDisableAssertsForXboxLiveThrottlingInDevSandboxes XblEventsWriteInGameEvent XblFormatSecureDeviceAddress XblGetAsyncQueue XblGetErrorCondition XblGetScid XblHttpCallCloseHandle XblHttpCallCreate XblHttpCallDuplicateHandle XblHttpCallGetHeader XblHttpCallGetHeaderAtIndex XblHttpCallGetNetworkErrorCode XblHttpCallGetNumHeaders XblHttpCallGetPlatformNetworkErrorMessage XblHttpCallGetRequestUrl XblHttpCallGetResponseBodyBytes XblHttpCallGetResponseBodyBytesSize XblHttpCallGetResponseString XblHttpCallGetStatusCode XblHttpCallPerformAsync XblHttpCallRequestSetHeader XblHttpCallRequestSetLongHttpCall XblHttpCallRequestSetRequestBodyBytes XblHttpCallRequestSetRequestBodyString XblHttpCallRequestSetRetryAllowed XblHttpCallRequestSetRetryCacheId XblHttpCallSetTracing XblInitialize XblLeaderboardGetLeaderboardAsync XblLeaderboardGetLeaderboardResult XblLeaderboardGetLeaderboardResultSize XblLeaderboardResultGetNextAsync XblLeaderboardResultGetNextResult XblLeaderboardResultGetNextResultSize XblMatchmakingCreateMatchTicketAsync XblMatchmakingCreateMatchTicketResult XblMatchmakingDeleteMatchTicketAsync XblMatchmakingGetHopperStatisticsAsync XblMatchmakingGetHopperStatisticsResult XblMatchmakingGetHopperStatisticsResultSize XblMatchmakingGetMatchTicketDetailsAsync XblMatchmakingGetMatchTicketDetailsResult XblMatchmakingGetMatchTicketDetailsResultSize XblMemGetFunctions XblMemSetFunctions XblMultiplayerActivityDeleteActivityAsync XblMultiplayerActivityFlushRecentPlayersAsync XblMultiplayerActivityGetActivityAsync XblMultiplayerActivityGetActivityResult XblMultiplayerActivityGetActivityResultSize XblMultiplayerActivitySendInvitesAsync XblMultiplayerActivitySetActivityAsync XblMultiplayerActivityUpdateRecentPlayers XblMultiplayerAddConnectionIdChangedHandler XblMultiplayerAddSessionChangedHandler XblMultiplayerAddSubscriptionLostHandler XblMultiplayerClearActivityAsync XblMultiplayerCreateSearchHandleAsync XblMultiplayerCreateSearchHandleResult XblMultiplayerDeleteSearchHandleAsync XblMultiplayerEventArgsFindMatchCompleted XblMultiplayerEventArgsMember XblMultiplayerEventArgsMembers XblMultiplayerEventArgsMembersCount XblMultiplayerEventArgsPerformQoSMeasurements XblMultiplayerEventArgsPropertiesJson XblMultiplayerEventArgsTournamentGameSessionReady XblMultiplayerEventArgsTournamentRegistrationStateChanged XblMultiplayerEventArgsXuid XblMultiplayerGetActivitiesForSocialGroupAsync XblMultiplayerGetActivitiesForSocialGroupResult XblMultiplayerGetActivitiesForSocialGroupResultCount XblMultiplayerGetActivitiesForUsersAsync XblMultiplayerGetActivitiesForUsersResult XblMultiplayerGetActivitiesForUsersResultCount XblMultiplayerGetActivitiesWithPropertiesForSocialGroupAsync XblMultiplayerGetActivitiesWithPropertiesForSocialGroupResult XblMultiplayerGetActivitiesWithPropertiesForSocialGroupResultSize XblMultiplayerGetActivitiesWithPropertiesForUsersAsync XblMultiplayerGetActivitiesWithPropertiesForUsersResult XblMultiplayerGetActivitiesWithPropertiesForUsersResultSize XblMultiplayerGetSearchHandlesAsync XblMultiplayerGetSearchHandlesResult XblMultiplayerGetSearchHandlesResultCount XblMultiplayerGetSessionAsync XblMultiplayerGetSessionByHandleAsync XblMultiplayerGetSessionByHandleResult XblMultiplayerGetSessionResult XblMultiplayerManagerAutoFillMembersDuringMatchmaking XblMultiplayerManagerCancelMatch XblMultiplayerManagerDoWork XblMultiplayerManagerEstimatedMatchWaitTime XblMultiplayerManagerFindMatch XblMultiplayerManagerGameSessionActive XblMultiplayerManagerGameSessionConstants XblMultiplayerManagerGameSessionCorrelationId XblMultiplayerManagerGameSessionHost XblMultiplayerManagerGameSessionIsHost XblMultiplayerManagerGameSessionMembers XblMultiplayerManagerGameSessionMembersCount XblMultiplayerManagerGameSessionPropertiesJson XblMultiplayerManagerGameSessionSessionReference XblMultiplayerManagerGameSessionSetProperties XblMultiplayerManagerGameSessionSetSynchronizedHost XblMultiplayerManagerGameSessionSetSynchronizedProperties XblMultiplayerManagerInitialize XblMultiplayerManagerJoinability XblMultiplayerManagerJoinGame XblMultiplayerManagerJoinGameFromLobby XblMultiplayerManagerJoinLobby XblMultiplayerManagerLeaveGame XblMultiplayerManagerLobbySessionAddLocalUser XblMultiplayerManagerLobbySessionConstants XblMultiplayerManagerLobbySessionCorrelationId XblMultiplayerManagerLobbySessionDeleteLocalMemberProperties XblMultiplayerManagerLobbySessionHost XblMultiplayerManagerLobbySessionInviteFriends XblMultiplayerManagerLobbySessionInviteUsers XblMultiplayerManagerLobbySessionIsHost XblMultiplayerManagerLobbySessionLastTournamentTeamResult XblMultiplayerManagerLobbySessionLocalMembers XblMultiplayerManagerLobbySessionLocalMembersCount XblMultiplayerManagerLobbySessionMembers XblMultiplayerManagerLobbySessionMembersCount XblMultiplayerManagerLobbySessionPropertiesJson XblMultiplayerManagerLobbySessionRemoveLocalUser XblMultiplayerManagerLobbySessionSessionReference XblMultiplayerManagerLobbySessionSetLocalMemberConnectionAddress XblMultiplayerManagerLobbySessionSetLocalMemberProperties XblMultiplayerManagerLobbySessionSetProperties XblMultiplayerManagerLobbySessionSetSynchronizedHost XblMultiplayerManagerLobbySessionSetSynchronizedProperties XblMultiplayerManagerMatchStatus XblMultiplayerManagerMemberAreMembersOnSameDevice XblMultiplayerManagerSetAutoFillMembersDuringMatchmaking XblMultiplayerManagerSetJoinability XblMultiplayerManagerSetQosMeasurements XblMultiplayerQuerySessionsAsync XblMultiplayerQuerySessionsResult XblMultiplayerQuerySessionsResultCount XblMultiplayerRemoveConnectionIdChangedHandler XblMultiplayerRemoveSessionChangedHandler XblMultiplayerRemoveSubscriptionLostHandler XblMultiplayerSearchHandleCloseHandle XblMultiplayerSearchHandleDuplicateHandle XblMultiplayerSearchHandleGetCreationTime XblMultiplayerSearchHandleGetCustomSessionPropertiesJson XblMultiplayerSearchHandleGetId XblMultiplayerSearchHandleGetJoinRestriction XblMultiplayerSearchHandleGetMemberCounts XblMultiplayerSearchHandleGetNumberAttributes XblMultiplayerSearchHandleGetSessionClosed XblMultiplayerSearchHandleGetSessionOwnerXuids XblMultiplayerSearchHandleGetSessionReference XblMultiplayerSearchHandleGetStringAttributes XblMultiplayerSearchHandleGetTags XblMultiplayerSearchHandleGetVisibility XblMultiplayerSendInvitesAsync XblMultiplayerSendInvitesResult XblMultiplayerSessionAddMemberReservation XblMultiplayerSessionArbitrationServer XblMultiplayerSessionArbitrationStatus XblMultiplayerSessionCloseHandle XblMultiplayerSessionCompare XblMultiplayerSessionConstantsSetArbitrationTimeouts XblMultiplayerSessionConstantsSetCapabilities XblMultiplayerSessionConstantsSetCloudComputePackageJson XblMultiplayerSessionConstantsSetMaxMembersInSession XblMultiplayerSessionConstantsSetMeasurementServerAddressesJson XblMultiplayerSessionConstantsSetMemberInitialization XblMultiplayerSessionConstantsSetPeerToHostRequirements XblMultiplayerSessionConstantsSetPeerToPeerRequirements XblMultiplayerSessionConstantsSetQosConnectivityMetrics XblMultiplayerSessionConstantsSetTimeouts XblMultiplayerSessionConstantsSetVisibility XblMultiplayerSessionCreateHandle XblMultiplayerSessionCurrentUser XblMultiplayerSessionCurrentUserDeleteCustomPropertyJson XblMultiplayerSessionCurrentUserSetCustomPropertyJson XblMultiplayerSessionCurrentUserSetEncounters XblMultiplayerSessionCurrentUserSetGroups XblMultiplayerSessionCurrentUserSetMembersInGroup XblMultiplayerSessionCurrentUserSetQosMeasurements XblMultiplayerSessionCurrentUserSetRoles XblMultiplayerSessionCurrentUserSetSecureDeviceAddressBase64 XblMultiplayerSessionCurrentUserSetServerQosMeasurements XblMultiplayerSessionCurrentUserSetStatus XblMultiplayerSessionDeleteCustomPropertyJson XblMultiplayerSessionDuplicateHandle XblMultiplayerSessionEtag XblMultiplayerSessionGetInfo XblMultiplayerSessionGetInitializationInfo XblMultiplayerSessionGetMember XblMultiplayerSessionGetRoleByName XblMultiplayerSessionHostCandidates XblMultiplayerSessionJoin XblMultiplayerSessionLeave XblMultiplayerSessionMatchmakingServer XblMultiplayerSessionMembers XblMultiplayerSessionMembersAccepted XblMultiplayerSessionPropertiesSetJoinRestriction XblMultiplayerSessionPropertiesSetKeywords XblMultiplayerSessionPropertiesSetReadRestriction XblMultiplayerSessionPropertiesSetTurnCollection XblMultiplayerSessionRawServersJson XblMultiplayerSessionReferenceCreate XblMultiplayerSessionReferenceIsValid XblMultiplayerSessionReferenceParseFromUriPath XblMultiplayerSessionReferenceToUriPath XblMultiplayerSessionRoleTypes XblMultiplayerSessionSessionConstants XblMultiplayerSessionSessionProperties XblMultiplayerSessionSessionReference XblMultiplayerSessionSetAllocateCloudCompute XblMultiplayerSessionSetClosed XblMultiplayerSessionSetCustomPropertyJson XblMultiplayerSessionSetHostDeviceToken XblMultiplayerSessionSetInitializationSucceeded XblMultiplayerSessionSetLocked XblMultiplayerSessionSetMatchmakingResubmit XblMultiplayerSessionSetMatchmakingServerConnectionPath XblMultiplayerSessionSetMatchmakingTargetSessionConstantsJson XblMultiplayerSessionSetMutableRoleSettings XblMultiplayerSessionSetRawServersJson XblMultiplayerSessionSetServerConnectionStringCandidates XblMultiplayerSessionSetSessionChangeSubscription XblMultiplayerSessionSubscribedChangeTypes XblMultiplayerSessionTimeOfSession XblMultiplayerSessionTournamentsServer XblMultiplayerSessionWriteStatus XblMultiplayerSetActivityAsync XblMultiplayerSetSubscriptionsEnabled XblMultiplayerSetTransferHandleAsync XblMultiplayerSetTransferHandleResult XblMultiplayerSubscriptionsEnabled XblMultiplayerWriteSessionAsync XblMultiplayerWriteSessionByHandleAsync XblMultiplayerWriteSessionByHandleResult XblMultiplayerWriteSessionResult XblPresenceAddDevicePresenceChangedHandler XblPresenceAddTitlePresenceChangedHandler XblPresenceGetPresenceAsync XblPresenceGetPresenceForMultipleUsersAsync XblPresenceGetPresenceForMultipleUsersResult XblPresenceGetPresenceForMultipleUsersResultCount XblPresenceGetPresenceForSocialGroupAsync XblPresenceGetPresenceForSocialGroupResult XblPresenceGetPresenceForSocialGroupResultCount XblPresenceGetPresenceResult XblPresenceRecordCloseHandle XblPresenceRecordDuplicateHandle XblPresenceRecordGetDeviceRecords XblPresenceRecordGetUserState XblPresenceRecordGetXuid XblPresenceRemoveDevicePresenceChangedHandler XblPresenceRemoveTitlePresenceChangedHandler XblPresenceSetPresenceAsync XblPresenceSubscribeToDevicePresenceChange XblPresenceSubscribeToTitlePresenceChange XblPresenceUnsubscribeFromDevicePresenceChange XblPresenceUnsubscribeFromTitlePresenceChange XblPrivacyBatchCheckPermissionAsync XblPrivacyBatchCheckPermissionResult XblPrivacyBatchCheckPermissionResultSize XblPrivacyCheckPermissionAsync XblPrivacyCheckPermissionForAnonymousUserAsync XblPrivacyCheckPermissionForAnonymousUserResult XblPrivacyCheckPermissionForAnonymousUserResultSize XblPrivacyCheckPermissionResult XblPrivacyCheckPermissionResultSize XblPrivacyGetAvoidListAsync XblPrivacyGetAvoidListResult XblPrivacyGetAvoidListResultCount XblPrivacyGetMuteListAsync XblPrivacyGetMuteListResult XblPrivacyGetMuteListResultCount XblProfileGetUserProfileAsync XblProfileGetUserProfileResult XblProfileGetUserProfilesAsync XblProfileGetUserProfilesForSocialGroupAsync XblProfileGetUserProfilesForSocialGroupResult XblProfileGetUserProfilesForSocialGroupResultCount XblProfileGetUserProfilesResult XblProfileGetUserProfilesResultCount XblRealTimeActivityActivate XblRealTimeActivityAddConnectionStateChangeHandler XblRealTimeActivityAddResyncHandler XblRealTimeActivityAddSubscriptionErrorHandler XblRealTimeActivityDeactivate XblRealTimeActivityRemoveConnectionStateChangeHandler XblRealTimeActivityRemoveResyncHandler XblRealTimeActivityRemoveSubscriptionErrorHandler XblRealTimeActivitySubscriptionGetId XblRealTimeActivitySubscriptionGetState XblRemoveServiceCallRoutedHandler XblSetOverrideConfiguration XblSocialAddSocialRelationshipChangedHandler XblSocialGetSocialRelationshipsAsync XblSocialGetSocialRelationshipsResult XblSocialManagerAddLocalUser XblSocialManagerCreateSocialUserGroupFromFilters XblSocialManagerCreateSocialUserGroupFromList XblSocialManagerDestroySocialUserGroup XblSocialManagerDoWork XblSocialManagerGetLocalUserCount XblSocialManagerGetLocalUsers XblSocialManagerPresenceRecordIsUserPlayingTitle XblSocialManagerRemoveLocalUser XblSocialManagerSetRichPresencePollingStatus XblSocialManagerUpdateSocialUserGroup XblSocialManagerUserGroupGetFilters XblSocialManagerUserGroupGetLocalUser XblSocialManagerUserGroupGetType XblSocialManagerUserGroupGetUsers XblSocialManagerUserGroupGetUsersTrackedByGroup XblSocialRelationshipResultCloseHandle XblSocialRelationshipResultDuplicateHandle XblSocialRelationshipResultGetNextAsync XblSocialRelationshipResultGetNextResult XblSocialRelationshipResultGetRelationships XblSocialRelationshipResultGetTotalCount XblSocialRelationshipResultHasNext XblSocialRemoveSocialRelationshipChangedHandler XblSocialSubmitBatchReputationFeedbackAsync XblSocialSubmitReputationFeedbackAsync XblSocialSubscribeToSocialRelationshipChange XblSocialUnsubscribeFromSocialRelationshipChange XblStringVerifyStringAsync XblStringVerifyStringResult XblStringVerifyStringResultSize XblStringVerifyStringsAsync XblStringVerifyStringsResult XblStringVerifyStringsResultSize XblTitleManagedStatsDeleteStatsAsync XblTitleManagedStatsUpdateStatsAsync XblTitleManagedStatsWriteAsync XblTitleStorageBlobMetadataResultCloseHandle XblTitleStorageBlobMetadataResultDuplicateHandle XblTitleStorageBlobMetadataResultGetItems XblTitleStorageBlobMetadataResultGetNextAsync XblTitleStorageBlobMetadataResultGetNextResult XblTitleStorageBlobMetadataResultHasNext XblTitleStorageDeleteBlobAsync XblTitleStorageDownloadBlobAsync XblTitleStorageDownloadBlobResult XblTitleStorageGetBlobMetadataAsync XblTitleStorageGetBlobMetadataResult XblTitleStorageGetQuotaAsync XblTitleStorageGetQuotaResult XblTitleStorageUploadBlobAsync XblTitleStorageUploadBlobResult XblUserStatisticsAddStatisticChangedHandler XblUserStatisticsGetMultipleUserStatisticsAsync XblUserStatisticsGetMultipleUserStatisticsForMultipleServiceConfigurationsAsync XblUserStatisticsGetMultipleUserStatisticsForMultipleServiceConfigurationsResult XblUserStatisticsGetMultipleUserStatisticsForMultipleServiceConfigurationsResultSize XblUserStatisticsGetMultipleUserStatisticsResult XblUserStatisticsGetMultipleUserStatisticsResultSize XblUserStatisticsGetSingleUserStatisticAsync XblUserStatisticsGetSingleUserStatisticResult XblUserStatisticsGetSingleUserStatisticResultSize XblUserStatisticsGetSingleUserStatisticsAsync XblUserStatisticsGetSingleUserStatisticsResult XblUserStatisticsGetSingleUserStatisticsResultSize XblUserStatisticsRemoveStatisticChangedHandler XblUserStatisticsSubscribeToStatisticChange XblUserStatisticsUnsubscribeFromStatisticChange XblWrapper_XblInitialize ================================================ FILE: Build/Microsoft.Xbox.Services.141.GDK.C.Thunks/dll/dllmain.cpp ================================================ #include "pch.h" #include #include #include extern "C" { BOOL APIENTRY DllMain(HMODULE /* hModule */, DWORD ul_reason_for_call, LPVOID /* lpReserved */) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; } XBL_DLLEXPORT HRESULT XBL_CALLING_CONV XblWrapper_XblInitialize( _In_z_ const char* scid, _In_ XTaskQueueHandle internalWorkQueue ) noexcept { XGameRuntimeInitialize(); // xal must be initialized to be used by xsapi XalInitArgs xalInitArgs = { }; XalInitialize(&xalInitArgs, internalWorkQueue); XblInitArgs xblInitArgs = { }; xblInitArgs.scid = scid; xblInitArgs.queue = internalWorkQueue; return XblInitialize(&xblInitArgs); } } ================================================ FILE: Build/Microsoft.Xbox.Services.141.GDK.C.Thunks/dll/pch.cpp ================================================ #include "pch.h" ================================================ FILE: Build/Microsoft.Xbox.Services.141.GDK.C.Thunks/dll/pch.h ================================================ #pragma once #include #define _WIN32_WINNT 0x0A00 #include // Use the C++ standard templated min/max #define NOMINMAX // DirectX apps don't need GDI #define NODRAWTEXT #define NOGDI #define NOBITMAP // Include if you need this #define NOMCX // Include if you need this #define NOSERVICE // WinHelp is deprecated #define NOHELP #include #if HC_PLATFORM_IS_MICROSOFT #define XBL_DLLEXPORT __declspec(dllexport) #define XBL_CALLING_CONV __stdcall #else #define XBL_DLLEXPORT #define XBL_CALLING_CONV #endif ================================================ FILE: Build/Microsoft.Xbox.Services.141.GDK.C.Thunks/generator/ThunksGenerator/App.config ================================================  ================================================ FILE: Build/Microsoft.Xbox.Services.141.GDK.C.Thunks/generator/ThunksGenerator/Program.cs ================================================ using System; using System.Collections.Generic; using System.IO; using System.Text.RegularExpressions; namespace ThunksGenerator { class Program { static void Main(string[] args) { var thunksDefFile = new FileInfo("../../../../dll/Microsoft.Xbox.Services.141.GDK.C.Thunks.def"); Console.WriteLine(thunksDefFile.FullName); string xsapiReproFolder = thunksDefFile.Directory.Parent.Parent.Parent.FullName; string cHeadersFolder = Path.Combine(xsapiReproFolder, @"Include\xsapi-c\"); var headerFiles = Directory.EnumerateFiles(cHeadersFolder, "*.h", SearchOption.AllDirectories); Console.WriteLine("Finding apis"); List fns = new List(); foreach (string curHeader in headerFiles) { ProcessHeader(curHeader, fns); } fns.Sort(); Console.WriteLine($"Writing apis to {thunksDefFile.FullName}"); string content = "LIBRARY Microsoft.Xbox.Services.141.GDK.C.Thunks.dll\n"; content += "EXPORTS\n"; foreach (string fn in fns) { string apiName = fn.Substring(0, fn.Length - 1); content += " " + apiName + "\n"; } content += "\n XblWrapper_XblInitialize"; File.WriteAllText(thunksDefFile.FullName, content); } static void ProcessHeader(string curHeader, List fns) { System.IO.StreamReader file = new System.IO.StreamReader(curHeader); while (true) { string line = file.ReadLine(); if (line == null) break; if (line.Contains("STDAPI")) { line = line.Replace("STDAPI ", ""); line = line.Replace(") XBL_NOEXCEPT", ""); Regex regex = new Regex("STDAPI_(.+) "); line = regex.Replace(line, ""); if (line.Contains(")")) { // Remove all the handlers line = string.Empty; } int index = line.IndexOf("("); if (index > 0) line = line.Substring(0, index + 1); if (!string.IsNullOrWhiteSpace(line)) { line = line.Trim(); fns.Add(line); } } } } } } ================================================ FILE: Build/Microsoft.Xbox.Services.141.GDK.C.Thunks/generator/ThunksGenerator/Properties/AssemblyInfo.cs ================================================ using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyTitle("ThunksGenerator")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("ThunksGenerator")] [assembly: AssemblyCopyright("Copyright © 2020")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // Setting ComVisible to false makes the types in this assembly not visible // to COM components. If you need to access a type in this assembly from // COM, set the ComVisible attribute to true on that type. [assembly: ComVisible(false)] // The following GUID is for the ID of the typelib if this project is exposed to COM [assembly: Guid("21c651d1-61d7-46c5-bd23-128e40329aa5")] // Version information for an assembly consists of the following four values: // // Major Version // Minor Version // Build Number // Revision // // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] ================================================ FILE: Build/Microsoft.Xbox.Services.141.GDK.C.Thunks/generator/ThunksGenerator/ThunksGenerator.csproj ================================================  Debug AnyCPU {21C651D1-61D7-46C5-BD23-128E40329AA5} Exe ThunksGenerator ThunksGenerator v4.6.1 512 true true AnyCPU true full false bin\Debug\ DEBUG;TRACE prompt 4 AnyCPU pdbonly true bin\Release\ TRACE prompt 4 ================================================ FILE: Build/Microsoft.Xbox.Services.142.GDK.C/Microsoft.Xbox.Services.142.GDK.C.vcxproj ================================================ {60139F62-BF37-4F11-BD93-5FBF4E92100C} StaticLibrary v142 true 10.0.19041.0 Microsoft.Xbox.Services.142.GDK.C.$(Configuration) Microsoft.Xbox.Services.142.GDK.C /Zi %(AdditionalOptions) /Zi %(AdditionalOptions) ================================================ FILE: Build/Microsoft.Xbox.Services.Common/Microsoft.Xbox.Services.Common.vcxitems ================================================  $(MSBuildAllProjects);$(MSBuildThisFileFullPath) true {cf3350e5-00a2-4647-a996-baf7542b0327} _NO_ASYNCRTIMP;_NO_PPLXIMP;_NO_XSAPIIMP;XBL_API_NONE;%(PreprocessorDefinitions) _XSAPIIMP_EXPORT;XBL_API_EXPORT;%(PreprocessorDefinitions) $(MSBuildThisFileDirectory)..\..\Source\Services; $(MSBuildThisFileDirectory)..\..\Source\Services\Achievements; $(MSBuildThisFileDirectory)..\..\Source\Services\Common; $(MSBuildThisFileDirectory)..\..\Source\Services\Common\Cpp; $(MSBuildThisFileDirectory)..\..\Source\Services\Clubs; $(MSBuildThisFileDirectory)..\..\Source\Services\Leaderboard; $(MSBuildThisFileDirectory)..\..\Source\Services\Matchmaking; $(MSBuildThisFileDirectory)..\..\Source\Services\MultiplayerActivity; $(MSBuildThisFileDirectory)..\..\Source\Services\Multiplayer; $(MSBuildThisFileDirectory)..\..\Source\Services\Multiplayer\Manager; $(MSBuildThisFileDirectory)..\..\Source\Services\Notification; $(MSBuildThisFileDirectory)..\..\Source\Services\Presence; $(MSBuildThisFileDirectory)..\..\Source\Services\RealTimeActivityManager; $(MSBuildThisFileDirectory)..\..\Source\Services\Social; $(MSBuildThisFileDirectory)..\..\Source\Services\Social\Manager; $(MSBuildThisFileDirectory)..\..\Source\Services\StringVerify; $(MSBuildThisFileDirectory)..\..\Source\Services\TCUI; $(MSBuildThisFileDirectory)..\..\Source\Services\TitleStorage; $(MSBuildThisFileDirectory)..\..\Source\Services\Privacy; $(MSBuildThisFileDirectory)..\..\Source\Services\Stats; $(MSBuildThisFileDirectory)..\..\Source\Services\Events; $(MSBuildThisFileDirectory)..\..\Source\Shared; $(MSBuildThisFileDirectory)..\..\Source\Shared\WinRT; $(MSBuildThisFileDirectory)..\..\Source\Shared\Logger; $(MSBuildThisFileDirectory)..\..\Source\System; $(MSBuildThisFileDirectory)..\..\Source\System\WinRT; %(AdditionalIncludeDirectories) Create ================================================ FILE: Build/Microsoft.Xbox.Services.Common/Microsoft.Xbox.Services.Common.vcxitems.filters ================================================  {a406adf2-c55d-4dda-9078-40715f2340f8} {df1d4c6b-2405-4205-853b-d6a74d02a8d6} {3bad286c-8e6c-4279-8fdd-8cf021ffac05} {cf5cf662-fb8f-428f-a1b9-0b6632f1112d} {2973c22a-1e58-4f23-b9d3-b8d37dac8eb7} {a5cc23d7-d8f1-40a6-83a4-cc1a4a322f85} {4caaf7af-cdaf-4799-927f-54ed23a9403e} {1b81eaa4-c07b-453d-a413-f63bec7e4989} {a067c625-5007-4b3f-af24-28c1dc6e06b8} {7a7c2dc9-30fe-48e3-b99d-546b987a1902} {ae9acc62-cd7b-4a6f-914c-dae1b6e5fe5e} {2f1e64b7-6471-4901-bc8c-cbca627e53fd} {537c4093-465f-40cd-acb8-f7c88e561389} {84c91bfd-7d59-4ea9-b6ca-dfdeadae3dad} {32a525db-8eb2-4265-8044-605b4e2a59b2} {2df84b4c-09ea-4757-855d-d838b00cd0b1} {376dffea-8ca7-498b-8779-ce2cf762c84f} {29d52a87-80be-445c-a447-3bf4e232fb64} {4f01071d-6a71-405c-90e2-d8e88e335625} {420eb5db-c38b-4936-a465-06dde3ab0570} {b9ce0a0e-799b-4570-b36d-df5691b38434} {25e21202-08d9-49b7-80ca-f265b8297f15} {2de0f5c8-d34e-4261-9380-19b5475e0672} {c67122a4-c2a6-44df-9145-d2600e70ba8a} {9a02f6ba-2a95-404d-b2a0-55cc395fd788} {61611672-3a9c-4e60-90e7-61f29510918c} {0d3e06c3-440d-441b-b3cb-c3f02eae8ec5} {17ebfdba-54f7-442c-851f-3ae11db585e5} {dcb2c672-4ee9-46df-9eb0-f4dfdb0638a2} {a5780ccd-5941-47bb-8f3d-5925e7d49b98} {1aaa9d6c-c806-4d77-bd9b-209ea4bab896} {a67e3ff0-7b8a-4756-b0a8-a5ae2c85d0ee} {862cb430-0f45-4462-a242-1a0e1c42e8ce} Include\xsapi-c Include\xsapi-c Include\xsapi-c Include\xsapi-c Include\xsapi-c Include\xsapi-c Include\xsapi-c Include\xsapi-c Include\xsapi-c Include\xsapi-c Include\xsapi-c Include\xsapi-c Include\xsapi-c Include\xsapi-c Include\xsapi-c Include\xsapi-c Include\xsapi-c Include\xsapi-c Include\xsapi-c Include\xsapi-c Include\xsapi-c Include\xsapi-c Include\xsapi-c Include\xsapi-c Include\xsapi-c Include\xsapi-c Include\xsapi-c Include\xsapi-c Include\xsapi-c Include\xsapi-cpp Include\xsapi-cpp Include\xsapi-cpp Include\xsapi-cpp Include\xsapi-cpp Include\xsapi-cpp Include\xsapi-cpp Include\xsapi-cpp Include\xsapi-cpp Include\xsapi-cpp Include\xsapi-cpp Include\xsapi-cpp Include\xsapi-cpp Include\xsapi-cpp Include\xsapi-cpp Include\xsapi-cpp Include\xsapi-cpp Include\xsapi-cpp Include\xsapi-cpp Include\xsapi-cpp Include\xsapi-cpp Include\xsapi-cpp Include\xsapi-cpp Include\xsapi-cpp Include\xsapi-cpp Include\xsapi-cpp Include\xsapi-cpp Include\xsapi-cpp\impl Include\xsapi-cpp\impl Include\xsapi-cpp\impl Include\xsapi-cpp\impl Include\xsapi-cpp\impl Include\xsapi-cpp\impl Include\xsapi-cpp\impl Include\xsapi-cpp\impl Include\xsapi-cpp\impl Include\xsapi-cpp\impl Include\xsapi-cpp\impl Include\xsapi-cpp\impl Include\xsapi-cpp\impl Include\xsapi-cpp\impl Include\xsapi-cpp\impl Include\xsapi-cpp\impl Include\xsapi-cpp\impl Include\xsapi-cpp\impl Include\xsapi-cpp\impl Include\xsapi-cpp\impl Include\xsapi-cpp\impl Include\xsapi-cpp\impl Include\xsapi-cpp\impl Include\xsapi-cpp\impl Include\xsapi-cpp\impl Include\xsapi-cpp\impl Include\xsapi-cpp\impl Include\cpprestinclude Include\cpprestinclude\pplx Include\cpprestinclude\pplx Include\cpprestinclude\pplx Include\cpprestinclude\pplx Include\cpprestinclude\pplx Include\cpprestinclude\pplx Include\cpprestinclude\pplx Include\cpprestinclude\pplx\details Include\cpprestinclude\pplx\details Include\cpprestinclude\cpprest Include\cpprestinclude\cpprest Include\cpprestinclude\cpprest Include\cpprestinclude\cpprest Include\cpprestinclude\cpprest Include\cpprestinclude\cpprest Include\cpprestinclude\cpprest Include\cpprestinclude\cpprest Include\cpprestinclude\cpprest Include\cpprestinclude\cpprest Include\cpprestinclude\cpprest Include\cpprestinclude\cpprest\details Include\cpprestinclude\cpprest\details Include\cpprestinclude\cpprest\details Include\cpprestinclude\cpprest\details Include\cpprestinclude\cpprest\details Include\cpprestinclude\cpprest\details Include\cpprestinclude\cpprest\details Include\cpprestinclude\cpprest\details Include\cpprestinclude\cpprest\details Include\cpprestinclude\cpprest\details Include\cpprestinclude\cpprest\details Include\cpprestinclude\cpprest\details Include\cpprestinclude\cpprest\details Include\cpprestinclude\cpprest\details Include\cpprestinclude\cpprest\details Include\cpprestinclude\cpprest\details Include\cpprestinclude\cpprest\details Include\cpprestinclude\cpprest\details Source\Services\Achievements\Manager Source\Services\Achievements Source\Services\Common Source\Services\Common Source\Services\Common Source\Services\Common\Cpp Source\Services\Leaderboard Source\Services\Matchmaking Source\Services\Multiplayer Source\Services\Multiplayer\Manager Source\Services\MultiplayerActivity Source\Services\TitleStorage Source\Services\StringVerify Source\Services\Stats Source\Services\Stats Source\Services\Social Source\Services\Social Source\Services\Social\Manager Source\Services\Social\Manager Source\Services\Social\Manager Source\Services\Social\Manager Source\Services\RealTimeActivityManager Source\Services\RealTimeActivityManager Source\Services\RealTimeActivityManager Source\Services\Privacy Source\Services\Presence Source\Shared\HookedUri\details Source\Shared\HookedUri\details Source\Shared\HookedUri\details Source\Shared\HookedUri\details Source\Shared\HookedUri\details Source\Shared\HookedUri\details Source\Shared\HookedUri\details Source\Shared\HookedUri\details Source\Shared\HookedUri Source\Shared\HookedUri Source\Shared\HookedUri Source\Shared\HookedUri Source\Shared\Logger Source\Shared\Logger Source\Shared Source\Shared Source\Shared Source\Shared Source\Shared Source\Shared Source\Shared Source\Shared Source\Shared Source\Shared Source\Shared Source\Shared Source\Shared Source\Shared Source\Shared Source\Shared Source\Shared Source\Shared Source\Shared Source\Shared Source\Shared Source\Shared Source\Shared Source\Shared Source\Shared Source\System Source\System Include\cpprestinclude\cpprest\details Source\Services\Achievements\Manager Source\Services\Achievements\Manager Source\Services\Achievements Source\Services\Achievements Source\Services\Achievements Source\Services\Achievements Source\Services\Common Source\Services\Common Source\Services\Common Source\Services\Common Source\Services\Common\Cpp Source\Services\Leaderboard Source\Services\Leaderboard Source\Services\Leaderboard Source\Services\Leaderboard Source\Services\Matchmaking Source\Services\Matchmaking Source\Services\Matchmaking Source\Services\Multiplayer Source\Services\Multiplayer Source\Services\Multiplayer Source\Services\Multiplayer Source\Services\Multiplayer Source\Services\Multiplayer Source\Services\Multiplayer Source\Services\Multiplayer Source\Services\Multiplayer Source\Services\Multiplayer Source\Services\Multiplayer Source\Services\Multiplayer Source\Services\Multiplayer Source\Services\Multiplayer Source\Services\Multiplayer\Manager Source\Services\Multiplayer\Manager Source\Services\Multiplayer\Manager Source\Services\Multiplayer\Manager Source\Services\Multiplayer\Manager Source\Services\Multiplayer\Manager Source\Services\Multiplayer\Manager Source\Services\Multiplayer\Manager Source\Services\Multiplayer\Manager Source\Services\Multiplayer\Manager Source\Services\Multiplayer\Manager Source\Services\Multiplayer\Manager Source\Services\Multiplayer\Manager Source\Services\Multiplayer\Manager Source\Services\Multiplayer\Manager Source\Services\Multiplayer\Manager Source\Services\Multiplayer\Manager Source\Services\MultiplayerActivity Source\Services\MultiplayerActivity Source\Services\MultiplayerActivity Source\Services\TitleStorage Source\Services\TitleStorage Source\Services\TitleStorage Source\Services\StringVerify Source\Services\StringVerify Source\Services\Stats Source\Services\Stats Source\Services\Stats Source\Services\Stats Source\Services\Stats Source\Services\Stats Source\Services\Stats Source\Services\Stats Source\Services\Stats Source\Services\Social Source\Services\Social Source\Services\Social Source\Services\Social Source\Services\Social Source\Services\Social Source\Services\Social Source\Services\Social Source\Services\Social\Manager Source\Services\Social\Manager Source\Services\Social\Manager Source\Services\Social\Manager Source\Services\Social\Manager Source\Services\RealTimeActivityManager Source\Services\RealTimeActivityManager Source\Services\RealTimeActivityManager Source\Services\Privacy Source\Services\Privacy Source\Services\Privacy Source\Services\Presence Source\Services\Presence Source\Services\Presence Source\Services\Presence Source\Services\Presence Source\Services\Presence Source\Services\Presence Source\Services\Presence Source\Shared\Logger Source\Shared\Logger Source\Shared\Logger Source\Shared\Logger Source\Shared Source\Shared Source\Shared Source\Shared Source\Shared Source\Shared Source\Shared Source\Shared Source\Shared Source\Shared Source\Shared Source\Shared Source\Shared Source\Shared Source\Shared Source\Shared Source\Shared Source\Shared Source\System ================================================ FILE: Build/Microsoft.Xbox.Services.GDK/Microsoft.Xbox.Services.GDK.vcxitems ================================================  $(MSBuildAllProjects);$(MSBuildThisFileFullPath) true {1e320494-894e-4feb-90ca-df4fe20499f4} XSAPI_NO_PPL=1;%(PreprocessorDefinitions) %(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory) ================================================ FILE: Build/Microsoft.Xbox.Services.GDK/Microsoft.Xbox.Services.GDK.vcxitems.filters ================================================  {a7468e1e-9c65-4f1a-97db-702dd3013d46} {83fa3d12-617a-48df-9de0-edffcc78503e} {7758f7f5-95ff-453a-a531-200c4dec6794} {b451b690-ab47-47b5-a948-e9a544907b98} {4593c653-a41d-403c-90d3-098726781331} Source\Services\Events Source\Services\Events Source\Services\Events Source\Services\Events Source\Services\Events Source\Services\Events ================================================ FILE: Build/Microsoft.Xbox.Services.Win32.C.Dll.def ================================================ LIBRARY xsapi EXPORTS HCAddCallRoutedHandler HCAddWebSocketRoutedHandler HCCleanup HCCleanupAsync HCGetHttpCallPerformFunction HCGetLibVersion HCGetWebSocketConnectResult HCGetWebSocketFunctions HCGetWebSocketSendMessageResult HCHttpCallCloseHandle HCHttpCallCreate HCHttpCallDuplicateHandle HCHttpCallGetContext HCHttpCallGetId HCHttpCallGetRequestUrl HCHttpCallPerformAsync HCHttpCallRequestGetHeader HCHttpCallRequestGetHeaderAtIndex HCHttpCallRequestGetNumHeaders HCHttpCallRequestGetRequestBodyBytes HCHttpCallRequestGetRequestBodyReadFunction HCHttpCallRequestGetRequestBodyString HCHttpCallRequestGetRetryAllowed HCHttpCallRequestGetRetryCacheId HCHttpCallRequestGetRetryDelay HCHttpCallRequestGetTimeout HCHttpCallRequestGetTimeoutWindow HCHttpCallRequestGetUrl HCHttpCallRequestSetHeader HCHttpCallRequestSetRequestBodyBytes HCHttpCallRequestSetRequestBodyReadFunction HCHttpCallRequestSetRequestBodyString HCHttpCallRequestSetRetryAllowed HCHttpCallRequestSetRetryCacheId HCHttpCallRequestSetRetryDelay HCHttpCallRequestSetSSLValidation HCHttpCallRequestSetTimeout HCHttpCallRequestSetTimeoutWindow HCHttpCallRequestSetUrl HCHttpCallResponseAppendResponseBodyBytes HCHttpCallResponseGetHeader HCHttpCallResponseGetHeaderAtIndex HCHttpCallResponseGetNetworkErrorCode HCHttpCallResponseGetNumHeaders HCHttpCallResponseGetPlatformNetworkErrorMessage HCHttpCallResponseGetResponseBodyBytes HCHttpCallResponseGetResponseBodyBytesSize HCHttpCallResponseGetResponseBodyWriteFunction HCHttpCallResponseGetResponseString HCHttpCallResponseGetStatusCode HCHttpCallResponseSetHeader HCHttpCallResponseSetHeaderWithLength HCHttpCallResponseSetNetworkErrorCode HCHttpCallResponseSetPlatformNetworkErrorMessage HCHttpCallResponseSetResponseBodyBytes HCHttpCallResponseSetResponseBodyWriteFunction HCHttpCallResponseSetStatusCode HCHttpCallSetContext HCHttpCallSetTracing HCInitialize HCIsInitialized HCMemGetFunctions HCMemSetFunctions HCMockAddMock HCMockCallCloseHandle HCMockCallCreate HCMockCallDuplicateHandle HCMockClearMocks HCMockRemoveMock HCMockResponseSetHeader HCMockResponseSetNetworkErrorCode HCMockResponseSetResponseBodyBytes HCMockResponseSetStatusCode HCMockSetMockMatchedCallback HCRemoveCallRoutedHandler HCRemoveWebSocketRoutedHandler HCSetGlobalProxy HCSetHttpCallPerformFunction HCSettingsGetTraceLevel HCSettingsSetTraceLevel HCSetWebSocketFunctions HCTraceImplMessage HCTraceImplMessage_v HCTraceImplScopeId HCTraceSetClientCallback HCTraceSetEtwEnabled HCTraceSetPlatformCallbacks HCTraceSetTraceToDebugger HCWebSocketCloseHandle HCWebSocketConnectAsync HCWebSocketCreate HCWebSocketDisconnect HCWebSocketDuplicateHandle HCWebSocketGetBinaryMessageFragmentEventFunction HCWebSocketGetEventFunctions HCWebSocketGetHeader HCWebSocketGetHeaderAtIndex HCWebSocketGetNumHeaders HCWebSocketGetProxyUri HCWebSocketSendBinaryMessageAsync HCWebSocketSendMessageAsync HCWebSocketSetBinaryMessageFragmentEventFunction HCWebSocketSetHeader HCWebSocketSetMaxReceiveBufferSize HCWebSocketSetProxyDecryptsHttps HCWebSocketSetProxyUri XalAddUserWithUiAsync XalAddUserWithUiResult XalCheckUcsConsentForAllUsers XalCleanupAsync XalCleanupResult XalCompareUsers XalFindUserByLocalId XalGetDeviceUser XalGetDeviceUserIsPresent XalGetMaxUsers XalGetSandbox XalGetSandboxSize XalGetTitleId XalInitialize XalMemGetFunctions XalMemSetFunctions XalPlatformCryptoSetCallbacks XalPlatformDateTimeSetCallbacks XalPlatformRemoteConnectCancelPrompt XalPlatformRemoteConnectClearEventHandlers XalPlatformRemoteConnectSetEventHandlers XalPlatformStorageClearComplete XalPlatformStorageClearEventHandlers XalPlatformStorageReadComplete XalPlatformStorageSetEventHandlers XalPlatformStorageWriteComplete XalPlatformWebClearEventHandler XalPlatformWebSetEventHandler XalPlatformWebShowUrlComplete XalSignOutUserAsync XalSignOutUserAsyncIsPresent XalSignOutUserResult XalTryAddDefaultUserSilentlyAsync XalTryAddDefaultUserSilentlyResult XalTryAddUserByIdAsync XalTryAddUserByIdResult XalUserCheckPrivilege XalUserCheckUcsConsent XalUserCloseHandle XalUserCloseSignoutDeferral XalUserDuplicateHandle XalUserGetAgeGroup XalUserGetGamerPictureAsync XalUserGetGamerPictureResult XalUserGetGamerPictureResultSize XalUserGetGamertag XalUserGetGamertagSize XalUserGetId XalUserGetLocalId XalUserGetSignoutDeferral XalUserGetState XalUserGetTokenAndSignatureSilentlyAsync XalUserGetTokenAndSignatureSilentlyResult XalUserGetTokenAndSignatureSilentlyResultSize XalUserIsDevice XalUserIsGuest XalUserManageUcsConsentWithUiAsync XalUserManageUcsConsentWithUiResult XalUserRegisterChangeEventHandler XalUserResolveIssueWithUiAsync XalUserResolveIssueWithUiResult XalUserResolvePrivilegeWithUiIsPresent XalUserResolveUserPrivilegeWithUiAsync XalUserResolveUserPrivilegeWithUiResult XalUserUnregisterChangeEventHandler XAsyncBegin XAsyncCancel XAsyncComplete XAsyncGetResult XAsyncGetResultSize XAsyncGetStatus XAsyncRun XAsyncSchedule XblAchievementsAddAchievementProgressChangeHandler XblAchievementsGetAchievementAsync XblAchievementsGetAchievementResult XblAchievementsGetAchievementsForTitleIdAsync XblAchievementsGetAchievementsForTitleIdResult XblAchievementsManagerAddLocalUser XblAchievementsManagerDoWork XblAchievementsManagerGetAchievement XblAchievementsManagerGetAchievements XblAchievementsManagerGetAchievementsByState XblAchievementsManagerIsUserInitialized XblAchievementsManagerRemoveLocalUser XblAchievementsManagerResultCloseHandle XblAchievementsManagerResultDuplicateHandle XblAchievementsManagerResultGetAchievements XblAchievementsManagerUpdateAchievement XblAchievementsRemoveAchievementProgressChangeHandler XblAchievementsResultCloseHandle XblAchievementsResultDuplicateHandle XblAchievementsResultGetAchievements XblAchievementsResultGetNextAsync XblAchievementsResultGetNextResult XblAchievementsResultHasNext XblAchievementsUpdateAchievementAsync XblAchievementsUpdateAchievementForTitleIdAsync XblAchievementUnlockAddNotificationHandler XblAchievementUnlockRemoveNotificationHandler XblAddServiceCallRoutedHandler XblCleanupAsync XblContextCloseHandle XblContextCreateHandle XblContextDuplicateHandle XblContextGetUser XblContextGetXboxUserId XblContextSettingsGetHttpRetryDelay XblContextSettingsGetHttpTimeoutWindow XblContextSettingsGetLongHttpTimeout XblContextSettingsGetUseCrossPlatformQosServers XblContextSettingsGetWebsocketTimeoutWindow XblContextSettingsSetHttpRetryDelay XblContextSettingsSetHttpTimeoutWindow XblContextSettingsSetLongHttpTimeout XblContextSettingsSetUseCrossPlatformQosServers XblContextSettingsSetWebsocketTimeoutWindow XblDisableAssertsForXboxLiveThrottlingInDevSandboxes XblEventsSetMaxFileSize XblEventsSetStorageAllotment XblEventsWriteInGameEvent XblFormatSecureDeviceAddress XblGameInviteAddNotificationHandler XblGameInviteRemoveNotificationHandler XblGetAsyncQueue XblGetErrorCondition XblGetScid XblHttpCallCloseHandle XblHttpCallCreate XblHttpCallDuplicateHandle XblHttpCallGetHeader XblHttpCallGetHeaderAtIndex XblHttpCallGetNetworkErrorCode XblHttpCallGetNumHeaders XblHttpCallGetPlatformNetworkErrorMessage XblHttpCallGetRequestUrl XblHttpCallGetResponseBodyBytes XblHttpCallGetResponseBodyBytesSize XblHttpCallGetResponseString XblHttpCallGetStatusCode XblHttpCallPerformAsync XblHttpCallRequestSetHeader XblHttpCallRequestSetLongHttpCall XblHttpCallRequestSetRequestBodyBytes XblHttpCallRequestSetRequestBodyString XblHttpCallRequestSetRetryAllowed XblHttpCallRequestSetRetryCacheId XblHttpCallSetTracing XblInitialize XblLeaderboardGetLeaderboardAsync XblLeaderboardGetLeaderboardResult XblLeaderboardGetLeaderboardResultSize XblLeaderboardResultGetNextAsync XblLeaderboardResultGetNextResult XblLeaderboardResultGetNextResultSize XblLocalStorageClearComplete XblLocalStorageReadComplete XblLocalStorageSetHandlers XblLocalStorageWriteComplete XblMatchmakingCreateMatchTicketAsync XblMatchmakingCreateMatchTicketResult XblMatchmakingDeleteMatchTicketAsync XblMatchmakingGetHopperStatisticsAsync XblMatchmakingGetHopperStatisticsResult XblMatchmakingGetHopperStatisticsResultSize XblMatchmakingGetMatchTicketDetailsAsync XblMatchmakingGetMatchTicketDetailsResult XblMatchmakingGetMatchTicketDetailsResultSize XblMemGetFunctions XblMemSetFunctions XblMultiplayerActivityAddInviteHandler XblMultiplayerActivityDeleteActivityAsync XblMultiplayerActivityFlushRecentPlayersAsync XblMultiplayerActivityGetActivityAsync XblMultiplayerActivityGetActivityResult XblMultiplayerActivityGetActivityResultSize XblMultiplayerActivityRemoveInviteHandler XblMultiplayerActivitySendInvitesAsync XblMultiplayerActivitySetActivityAsync XblMultiplayerActivityUpdateRecentPlayers XblMultiplayerAddConnectionIdChangedHandler XblMultiplayerAddSessionChangedHandler XblMultiplayerAddSubscriptionLostHandler XblMultiplayerClearActivityAsync XblMultiplayerCreateSearchHandleAsync XblMultiplayerCreateSearchHandleResult XblMultiplayerDeleteSearchHandleAsync XblMultiplayerEventArgsFindMatchCompleted XblMultiplayerEventArgsMember XblMultiplayerEventArgsMembers XblMultiplayerEventArgsMembersCount XblMultiplayerEventArgsPerformQoSMeasurements XblMultiplayerEventArgsPropertiesJson XblMultiplayerEventArgsXuid XblMultiplayerGetActivitiesForSocialGroupAsync XblMultiplayerGetActivitiesForSocialGroupResult XblMultiplayerGetActivitiesForSocialGroupResultCount XblMultiplayerGetActivitiesForUsersAsync XblMultiplayerGetActivitiesForUsersResult XblMultiplayerGetActivitiesForUsersResultCount XblMultiplayerGetActivitiesWithPropertiesForSocialGroupAsync XblMultiplayerGetActivitiesWithPropertiesForSocialGroupResult XblMultiplayerGetActivitiesWithPropertiesForSocialGroupResultSize XblMultiplayerGetActivitiesWithPropertiesForUsersAsync XblMultiplayerGetActivitiesWithPropertiesForUsersResult XblMultiplayerGetActivitiesWithPropertiesForUsersResultSize XblMultiplayerGetSearchHandlesAsync XblMultiplayerGetSearchHandlesResult XblMultiplayerGetSearchHandlesResultCount XblMultiplayerGetSessionAsync XblMultiplayerGetSessionByHandleAsync XblMultiplayerGetSessionByHandleResult XblMultiplayerGetSessionResult XblMultiplayerManagerAutoFillMembersDuringMatchmaking XblMultiplayerManagerCancelMatch XblMultiplayerManagerDoWork XblMultiplayerManagerEstimatedMatchWaitTime XblMultiplayerManagerFindMatch XblMultiplayerManagerGameSessionActive XblMultiplayerManagerGameSessionConstants XblMultiplayerManagerGameSessionCorrelationId XblMultiplayerManagerGameSessionHost XblMultiplayerManagerGameSessionIsHost XblMultiplayerManagerGameSessionMembers XblMultiplayerManagerGameSessionMembersCount XblMultiplayerManagerGameSessionPropertiesJson XblMultiplayerManagerGameSessionSessionReference XblMultiplayerManagerGameSessionSetProperties XblMultiplayerManagerGameSessionSetSynchronizedHost XblMultiplayerManagerGameSessionSetSynchronizedProperties XblMultiplayerManagerInitialize XblMultiplayerManagerJoinability XblMultiplayerManagerJoinGame XblMultiplayerManagerJoinGameFromLobby XblMultiplayerManagerJoinLobby XblMultiplayerManagerLeaveGame XblMultiplayerManagerLobbySessionAddLocalUser XblMultiplayerManagerLobbySessionConstants XblMultiplayerManagerLobbySessionCorrelationId XblMultiplayerManagerLobbySessionDeleteLocalMemberProperties XblMultiplayerManagerLobbySessionHost XblMultiplayerManagerLobbySessionInviteFriends XblMultiplayerManagerLobbySessionInviteUsers XblMultiplayerManagerLobbySessionIsHost XblMultiplayerManagerLobbySessionLocalMembers XblMultiplayerManagerLobbySessionLocalMembersCount XblMultiplayerManagerLobbySessionMembers XblMultiplayerManagerLobbySessionMembersCount XblMultiplayerManagerLobbySessionPropertiesJson XblMultiplayerManagerLobbySessionRemoveLocalUser XblMultiplayerManagerLobbySessionSessionReference XblMultiplayerManagerLobbySessionSetLocalMemberConnectionAddress XblMultiplayerManagerLobbySessionSetLocalMemberProperties XblMultiplayerManagerLobbySessionSetProperties XblMultiplayerManagerLobbySessionSetSynchronizedHost XblMultiplayerManagerLobbySessionSetSynchronizedProperties XblMultiplayerManagerMatchStatus XblMultiplayerManagerMemberAreMembersOnSameDevice XblMultiplayerManagerSetAutoFillMembersDuringMatchmaking XblMultiplayerManagerSetJoinability XblMultiplayerManagerSetQosMeasurements XblMultiplayerQuerySessionsAsync XblMultiplayerQuerySessionsResult XblMultiplayerQuerySessionsResultCount XblMultiplayerRemoveConnectionIdChangedHandler XblMultiplayerRemoveSessionChangedHandler XblMultiplayerRemoveSubscriptionLostHandler XblMultiplayerSearchHandleCloseHandle XblMultiplayerSearchHandleDuplicateHandle XblMultiplayerSearchHandleGetCreationTime XblMultiplayerSearchHandleGetCustomSessionPropertiesJson XblMultiplayerSearchHandleGetId XblMultiplayerSearchHandleGetJoinRestriction XblMultiplayerSearchHandleGetMemberCounts XblMultiplayerSearchHandleGetNumberAttributes XblMultiplayerSearchHandleGetSessionClosed XblMultiplayerSearchHandleGetSessionOwnerXuids XblMultiplayerSearchHandleGetSessionReference XblMultiplayerSearchHandleGetStringAttributes XblMultiplayerSearchHandleGetTags XblMultiplayerSearchHandleGetVisibility XblMultiplayerSendInvitesAsync XblMultiplayerSendInvitesResult XblMultiplayerSessionAddMemberReservation XblMultiplayerSessionCloseHandle XblMultiplayerSessionCompare XblMultiplayerSessionConstantsSetCapabilities XblMultiplayerSessionConstantsSetCloudComputePackageJson XblMultiplayerSessionConstantsSetMaxMembersInSession XblMultiplayerSessionConstantsSetMeasurementServerAddressesJson XblMultiplayerSessionConstantsSetMemberInitialization XblMultiplayerSessionConstantsSetPeerToHostRequirements XblMultiplayerSessionConstantsSetPeerToPeerRequirements XblMultiplayerSessionConstantsSetQosConnectivityMetrics XblMultiplayerSessionConstantsSetTimeouts XblMultiplayerSessionConstantsSetVisibility XblMultiplayerSessionCreateHandle XblMultiplayerSessionCurrentUser XblMultiplayerSessionCurrentUserDeleteCustomPropertyJson XblMultiplayerSessionCurrentUserSetCustomPropertyJson XblMultiplayerSessionCurrentUserSetEncounters XblMultiplayerSessionCurrentUserSetGroups XblMultiplayerSessionCurrentUserSetMembersInGroup XblMultiplayerSessionCurrentUserSetQosMeasurements XblMultiplayerSessionCurrentUserSetRoles XblMultiplayerSessionCurrentUserSetSecureDeviceAddressBase64 XblMultiplayerSessionCurrentUserSetServerQosMeasurements XblMultiplayerSessionCurrentUserSetStatus XblMultiplayerSessionDeleteCustomPropertyJson XblMultiplayerSessionDuplicateHandle XblMultiplayerSessionEtag XblMultiplayerSessionGetInfo XblMultiplayerSessionGetInitializationInfo XblMultiplayerSessionGetMember XblMultiplayerSessionGetRoleByName XblMultiplayerSessionHostCandidates XblMultiplayerSessionJoin XblMultiplayerSessionLeave XblMultiplayerSessionMatchmakingServer XblMultiplayerSessionMembers XblMultiplayerSessionMembersAccepted XblMultiplayerSessionPropertiesSetJoinRestriction XblMultiplayerSessionPropertiesSetKeywords XblMultiplayerSessionPropertiesSetReadRestriction XblMultiplayerSessionPropertiesSetTurnCollection XblMultiplayerSessionRawServersJson XblMultiplayerSessionReferenceCreate XblMultiplayerSessionReferenceIsValid XblMultiplayerSessionReferenceParseFromUriPath XblMultiplayerSessionReferenceToUriPath XblMultiplayerSessionRoleTypes XblMultiplayerSessionSessionConstants XblMultiplayerSessionSessionProperties XblMultiplayerSessionSessionReference XblMultiplayerSessionSetAllocateCloudCompute XblMultiplayerSessionSetClosed XblMultiplayerSessionSetCustomPropertyJson XblMultiplayerSessionSetHostDeviceToken XblMultiplayerSessionSetInitializationSucceeded XblMultiplayerSessionSetLocked XblMultiplayerSessionSetMatchmakingResubmit XblMultiplayerSessionSetMatchmakingServerConnectionPath XblMultiplayerSessionSetMatchmakingTargetSessionConstantsJson XblMultiplayerSessionSetMutableRoleSettings XblMultiplayerSessionSetRawServersJson XblMultiplayerSessionSetServerConnectionStringCandidates XblMultiplayerSessionSetSessionChangeSubscription XblMultiplayerSessionSubscribedChangeTypes XblMultiplayerSessionTimeOfSession XblMultiplayerSessionWriteStatus XblMultiplayerSetActivityAsync XblMultiplayerSetSubscriptionsEnabled XblMultiplayerSetTransferHandleAsync XblMultiplayerSetTransferHandleResult XblMultiplayerSubscriptionsEnabled XblMultiplayerWriteSessionAsync XblMultiplayerWriteSessionByHandleAsync XblMultiplayerWriteSessionByHandleResult XblMultiplayerWriteSessionResult XblPresenceAddDevicePresenceChangedHandler XblPresenceAddTitlePresenceChangedHandler XblPresenceGetPresenceAsync XblPresenceGetPresenceForMultipleUsersAsync XblPresenceGetPresenceForMultipleUsersResult XblPresenceGetPresenceForMultipleUsersResultCount XblPresenceGetPresenceForSocialGroupAsync XblPresenceGetPresenceForSocialGroupResult XblPresenceGetPresenceForSocialGroupResultCount XblPresenceGetPresenceResult XblPresenceRecordCloseHandle XblPresenceRecordDuplicateHandle XblPresenceRecordGetDeviceRecords XblPresenceRecordGetUserState XblPresenceRecordGetXuid XblPresenceRemoveDevicePresenceChangedHandler XblPresenceRemoveTitlePresenceChangedHandler XblPresenceSetPresenceAsync XblPresenceStopTrackingAdditionalTitles XblPresenceStopTrackingUsers XblPresenceTrackAdditionalTitles XblPresenceTrackUsers XblPrivacyBatchCheckPermissionAsync XblPrivacyBatchCheckPermissionResult XblPrivacyBatchCheckPermissionResultSize XblPrivacyCheckPermissionAsync XblPrivacyCheckPermissionForAnonymousUserAsync XblPrivacyCheckPermissionForAnonymousUserResult XblPrivacyCheckPermissionForAnonymousUserResultSize XblPrivacyCheckPermissionResult XblPrivacyCheckPermissionResultSize XblPrivacyGetAvoidListAsync XblPrivacyGetAvoidListResult XblPrivacyGetAvoidListResultCount XblPrivacyGetMuteListAsync XblPrivacyGetMuteListResult XblPrivacyGetMuteListResultCount XblProfileGetUserProfileAsync XblProfileGetUserProfileResult XblProfileGetUserProfilesAsync XblProfileGetUserProfilesForSocialGroupAsync XblProfileGetUserProfilesForSocialGroupResult XblProfileGetUserProfilesForSocialGroupResultCount XblProfileGetUserProfilesResult XblProfileGetUserProfilesResultCount XblRealTimeActivityAddConnectionStateChangeHandler XblRealTimeActivityAddResyncHandler XblRealTimeActivityRemoveConnectionStateChangeHandler XblRealTimeActivityRemoveResyncHandler XblRemoveServiceCallRoutedHandler XblSetOverrideConfiguration XblSetOverrideLocale XblSocialAddSocialRelationshipChangedHandler XblSocialGetSocialRelationshipsAsync XblSocialGetSocialRelationshipsResult XblSocialManagerAddLocalUser XblSocialManagerCreateSocialUserGroupFromFilters XblSocialManagerCreateSocialUserGroupFromList XblSocialManagerDestroySocialUserGroup XblSocialManagerDoWork XblSocialManagerGetLocalUserCount XblSocialManagerGetLocalUsers XblSocialManagerPresenceRecordIsUserPlayingTitle XblSocialManagerRemoveLocalUser XblSocialManagerSetRichPresencePollingStatus XblSocialManagerUpdateSocialUserGroup XblSocialManagerUserGroupGetFilters XblSocialManagerUserGroupGetLocalUser XblSocialManagerUserGroupGetType XblSocialManagerUserGroupGetUsers XblSocialManagerUserGroupGetUsersTrackedByGroup XblSocialRelationshipResultCloseHandle XblSocialRelationshipResultDuplicateHandle XblSocialRelationshipResultGetNextAsync XblSocialRelationshipResultGetNextResult XblSocialRelationshipResultGetRelationships XblSocialRelationshipResultGetTotalCount XblSocialRelationshipResultHasNext XblSocialRemoveSocialRelationshipChangedHandler XblSocialSubmitBatchReputationFeedbackAsync XblSocialSubmitReputationFeedbackAsync XblStringVerifyStringAsync XblStringVerifyStringResult XblStringVerifyStringResultSize XblStringVerifyStringsAsync XblStringVerifyStringsResult XblStringVerifyStringsResultSize XblTitleManagedStatsDeleteStatsAsync XblTitleManagedStatsUpdateStatsAsync XblTitleManagedStatsWriteAsync XblTitleStorageBlobMetadataResultCloseHandle XblTitleStorageBlobMetadataResultDuplicateHandle XblTitleStorageBlobMetadataResultGetItems XblTitleStorageBlobMetadataResultGetNextAsync XblTitleStorageBlobMetadataResultGetNextResult XblTitleStorageBlobMetadataResultHasNext XblTitleStorageDeleteBlobAsync XblTitleStorageDownloadBlobAsync XblTitleStorageDownloadBlobResult XblTitleStorageGetBlobMetadataAsync XblTitleStorageGetBlobMetadataResult XblTitleStorageGetQuotaAsync XblTitleStorageGetQuotaResult XblTitleStorageUploadBlobAsync XblTitleStorageUploadBlobResult XblUserStatisticsAddStatisticChangedHandler XblUserStatisticsGetMultipleUserStatisticsAsync XblUserStatisticsGetMultipleUserStatisticsForMultipleServiceConfigurationsAsync XblUserStatisticsGetMultipleUserStatisticsForMultipleServiceConfigurationsResult XblUserStatisticsGetMultipleUserStatisticsForMultipleServiceConfigurationsResultSize XblUserStatisticsGetMultipleUserStatisticsResult XblUserStatisticsGetMultipleUserStatisticsResultSize XblUserStatisticsGetSingleUserStatisticAsync XblUserStatisticsGetSingleUserStatisticResult XblUserStatisticsGetSingleUserStatisticResultSize XblUserStatisticsGetSingleUserStatisticsAsync XblUserStatisticsGetSingleUserStatisticsResult XblUserStatisticsGetSingleUserStatisticsResultSize XblUserStatisticsRemoveStatisticChangedHandler XblUserStatisticsStopTrackingStatistics XblUserStatisticsStopTrackingUsers XblUserStatisticsTrackStatistics XTaskQueueCloseHandle XTaskQueueCreate XTaskQueueCreateComposite XTaskQueueDispatch XTaskQueueDuplicateHandle XTaskQueueGetCurrentProcessTaskQueue XTaskQueueGetPort XTaskQueueRegisterMonitor XTaskQueueRegisterWaiter XTaskQueueSetCurrentProcessTaskQueue XTaskQueueSubmitCallback XTaskQueueSubmitDelayedCallback XTaskQueueTerminate XTaskQueueUnregisterMonitor XTaskQueueUnregisterWaiter ================================================ FILE: Build/xsapi.gdk.bwoi.props ================================================ $(GameDK) $(GameDKLatest)WindowsSDK\ <_PlatformFolder>$(GameDKLatest)GXDK\VS2017\flatDeployment\Common7\IDE\VC\VCTargets\Platforms\$(Platform)\ $(_PlatformFolder) $(_PlatformFolder) <_PlatformFolder>$(GameDKLatest)GXDK\VS2017\flatDeployment\Common7\IDE\VC\VCTargets\Platforms\$(Platform)\ $(_PlatformFolder) $(_PlatformFolder) <_PlatformFolder>$(GameDKLatest)GRDK\VS2017\flatDeployment\Common7\IDE\VC\VCTargets\Platforms\$(Platform)\ $(_PlatformFolder) $(_PlatformFolder) <_AlternativeVCTargetsPath160>$(GDKMSBuildForVS2019)v160\ <_AlternativeVCTargetsPath150>$(GDKMSBuildForVS2019)v150\ $(_AlternativeVCTargetsPath160) $(_AlternativeVCTargetsPath150) $(_AlternativeVCTargetsPath160) $(_AlternativeVCTargetsPath160) $(_AlternativeVCTargetsPath150) $(_AlternativeVCTargetsPath160) $(_AlternativeVCTargetsPath160) $(_AlternativeVCTargetsPath150) $(_AlternativeVCTargetsPath160) <_AlternativeVCTargetsPath160>$(GDKMSBuildForVS2019)v160\ <_AlternativeVCTargetsPath150>$(GDKMSBuildForVS2019)v150\ $(_AlternativeVCTargetsPath160) $(_AlternativeVCTargetsPath150) $(_AlternativeVCTargetsPath160) $(_AlternativeVCTargetsPath160) $(_AlternativeVCTargetsPath150) $(_AlternativeVCTargetsPath160) $(_AlternativeVCTargetsPath160) $(_AlternativeVCTargetsPath150) $(_AlternativeVCTargetsPath160) ================================================ FILE: Build/xsapi.gdk.props ================================================ v141 false $(XsapiOutDir) $(XsapiIntDir) ================================================ FILE: Build/xsapi.win32.props ================================================ v141 $(XsapiOutDir) $(XsapiIntDir) ================================================ FILE: CONTRIBUTING.md ================================================ # Contribution Guidelines There are many different ways in which you can contribute! Please submit issues, questions, bug reports, feature requests, bug fixes, improvements, and new features. ### Report bugs and request features Issues and feature requests are submitted through the project's [issue tracker](../issues) section on GitHub. Please use the following guidelines when you submit issues and feature requests: * Make sure the issue is not already reported by searching through the list of issues * Provide a detailed description of the issue including the following information: * Which feature the issue appears in * Under what circumstances the issue appears * What is the desired behavior * What is breaking * What is the impact (things like loss or corruption of data, compromising security, disruption of service etc.) * Any code that will be helpful to reproduce the issue ### Create bug fixes and features Please submit any changes as a Pull Request against the development branch. Make sure to write a detailed message describing the changes in the Pull Request. This will help us quickly determine what changes (if any) need to be made for it to be ready for integration. _Note: Please keep in mind that not all requests will be approved. Requests are reviewed by the team on a regular basis and will be updated with the status at each review. If your request is accepted you will receive information about the next steps and when the request will be integrated in the development branch. If your request is rejected you will receive information about the reasons why it was rejected._ ### Contribution guidelines Before you start working on bug fixes and features it is good idea to discuss those broadly with the community. You can use the [issue tracker](../issues) for this purpose. Before submitting your changes make sure you followed the guidelines below: * You have properly documented any new functionality * For any new functionality you have written complete unit tests * You have run all unit tests and they pass * In order to speed up the process of accepting your contributions, you should try to make your checkins as small as possible, avoid any unnecessary deltas and the need to rebase. ================================================ FILE: Custom.props ================================================ $(MSBuildProjectName) $(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Android SDK Tools@Path) $(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Android SDK Tools@Path) ================================================ FILE: Directory.Packages.props ================================================ true ================================================ FILE: External/Xal/README ================================================ This repository only contains the public headers for the Xbox Authentication Library (XAL), the same headers shipped as part of the GDK. Full source for XAL is not publicly available at this time. ================================================ FILE: External/Xal/Source/Xal/Include/Xal/xal.h ================================================ #pragma once #if !defined(__cplusplus) #error C++11 required #endif #include #include #include extern "C" { //----------------------------------------------------------------------------- // User model //----------------------------------------------------------------------------- /// /// Initializes Xal, must be called before any other Xal functions (except /// XalMemSetFunctions, XalMemGetFunctions and XalPlatform*SetHandler). /// /// The initialization arguments for Xal, /// . /// The async queue Xal should use for internal /// only work such as telemetry and asynchronous init work. Only the work side /// of the queue will be used. /// Result code for this API operation. /// /// Depending on the platform, some of the platform event handlers defined in /// xal_platform.h may be required, in which case they should be set before /// calling this function. If any required event handlers are missing /// XalInitialize will fail with E_XAL_MISSINGPLATFORMEVENTHANDLER. /// /// It is never an error to set a platform event handler that is not required. /// STDAPI XalInitialize( _In_ XalInitArgs const* args, _In_opt_ XTaskQueueHandle internalWorkQueue ) noexcept; /// /// Cleans up Xal, after this function returns Xal will be in the same state as /// if XalInit was never called. /// /// The AsyncBlock for this operation. /// Result code for this API operation. /// /// This functions should only be called when all user handles have been closed /// and there are no outstanding asynchronous operations. /// STDAPI XalCleanupAsync( _In_ XAsyncBlock* async ) noexcept; /// /// Get the result of a successful XalCleanupAsync operation. /// /// The AsyncBlock for this operation. /// Result code for this API operation. STDAPI XalCleanupResult( _In_ XAsyncBlock* async ) noexcept; /// /// Get the maximum number of users that can be added at the same time on the /// current platform. /// /// The maximum number of concurrent users. /// Result code for this API operation. Possible values are S_OK, E_XAL_NOTINITIALIZED, or E_FAIL. STDAPI XalGetMaxUsers( _Out_ uint32_t* maxUsers ) noexcept; /// /// Get the Xbox Live title ID that Xal was initialized with. Must be called /// after XalInitialize. /// /// The Xbox Live title ID. /// Result code for this API operation. Possible values are S_OK, E_XAL_NOTINITIALIZED, or E_FAIL. STDAPI XalGetTitleId( _Out_ uint32_t* titleId ) noexcept; /// /// Get the size of the buffer needed to store the Xbox Live sandbox string. /// /// The size of the sandbox string including the null terminator. STDAPI_(size_t) XalGetSandboxSize() noexcept; /// /// Get the Xbox Live sandbox that Xal was initialized with. Must be called after /// XalInitialize. /// /// The size in bytes of the sandbox buffer. /// Should be the value returned by XalGetSandboxSize. /// The buffer the sandbox will be written to. /// The number of bytes used in the buffer including /// the null terminator. /// Result code for this API operation. Possible values are S_OK, E_XAL_NOTINITIALIZED, or E_FAIL. STDAPI XalGetSandbox( _In_ size_t sandboxSize, _Out_writes_(sandboxSize) char* sandbox, _Out_opt_ size_t* sandboxUsed ) noexcept; /// /// Attempts to add a user without showing any ui. /// /// Client provided identifier for the user. /// /// The AsyncBlock for this operation. /// Result code for this API operation. /// /// userIdentifier will be stored in the user and passed to all XalPlatform* /// callbacks related to this user. /// STDAPI XalTryAddDefaultUserSilentlyAsync( _In_ uint32_t userIdentifier, _In_ XAsyncBlock* async ) noexcept; /// /// Get the result of a successful XalTryAddDefaultUserSilentlyAsync operation. /// /// The AsyncBlock for this operation. /// The user that was just added. /// Result code for this API operation. /// /// If the operations failed, newUser will be NULL. /// STDAPI XalTryAddDefaultUserSilentlyResult( _In_ XAsyncBlock* async, _Out_ XalUserHandle* newUser ) noexcept; /// /// Attempts to add a user with the given Xbox user id. /// /// Client provided identifier for the user. /// /// The Xbox user id. /// The AsyncBlock for this operation. /// Result code for this API operation. /// /// This function may show ui to the user. /// /// userIdentifier will be stored in the user and passed to all XalPlatform* /// callbacks related to this user. /// STDAPI XalTryAddUserByIdAsync( _In_ uint32_t userIdentifier, _In_ uint64_t xboxUserId, _In_ XAsyncBlock* async ) noexcept; /// /// Get the result of a successful XalTryAddUserByIdAsync operation. /// /// The AsyncBlock for this operation. /// The user that was just added. /// Result code for this API operation. /// /// If the operations failed, newUser will be NULL. /// STDAPI XalTryAddUserByIdResult( _In_ XAsyncBlock* async, _Out_ XalUserHandle* newUser ) noexcept; /// /// Attempts to add a user. /// /// Client provided identifier for the user. /// /// The AsyncBlock for this operation. /// Result code for this API operation. /// /// This function will show ui to the user. /// /// userIdentifier will be stored in the user and passed to all XalPlatform* /// callbacks related to this user. /// STDAPI XalAddUserWithUiAsync( _In_ uint32_t userIdentifier, _In_ XAsyncBlock* async ) noexcept; /// /// Get the result of a successful XalAddUserWithUiAsync operation. /// /// The AsyncBlock for this operation. /// The user that was just added. /// Result code for this API operation. /// /// If the operations failed, newUser will be NULL. /// STDAPI XalAddUserWithUiResult( _In_ XAsyncBlock* async, _Out_ XalUserHandle* newUser ) noexcept; /// /// Checks if the current platform supports retrieving a device user handle. /// /// True if device user present, false if not. STDAPI_(bool) XalGetDeviceUserIsPresent() noexcept; /// /// Returns a user which represents the device itself. /// /// The user object. /// Result code for this API operation. Possible values are S_OK, E_XAL_NOTINITIALIZED, or E_FAIL. /// /// This user handle will have had XalUserDuplicateHandle called on it. Be sure /// to call XalUserCloseHandle once it is no longer needed. /// STDAPI XalGetDeviceUser( _Out_ XalUserHandle* deviceUser ) noexcept; /// /// Checks if the user can be signed out on the current platform. /// /// True if user can be signed out, false if user can't. STDAPI_(bool) XalSignOutUserAsyncIsPresent() noexcept; /// /// Signs out a user from the device. /// /// The user to remove. /// The AsyncBlock for this operation. /// Result code for this API operation. /// /// User sign out may not be present on all platforms, on platforms where it is /// missing this function will return E_XAL_FEATURENOTPRESENT. To query if user /// sign out is available see . /// /// On some platforms this call may fail to sign out the user if there is no /// internet connectivity, if that happens the user will not be removed from the /// user set and will still be fully signed in. /// /// On platforms that use the web ui hooks /// (see ) this function will /// invoke the hook. /// STDAPI XalSignOutUserAsync( _In_ XalUserHandle user, _In_ XAsyncBlock* async ) noexcept; /// /// Get the result of a given XalSignOutUserAsync operation. /// /// The AsyncBlock for this operation. /// Result code for this API operation. STDAPI XalSignOutUserResult( _In_ XAsyncBlock* async ) noexcept; /// /// Tries to find a user for the given local id. /// /// The local id it to look up. /// The user object. /// Result code for this API operation. Possible values are S_OK, E_XAL_NOTINITIALIZED, or E_FAIL. /// /// If no user can be found matching the local id, E_XAL_USERNOTFOUND is returned. /// STDAPI XalFindUserByLocalId( _In_ XalUserLocalId localId, _Out_ XalUserHandle* user ) noexcept; /// /// Checks if the given consent is opted in by all the users in the user set. /// /// The UCS consent model name. /// True if all the users in the user set are opted in. /// Result code for this API operation. Possible values are S_OK, /// E_XAL_NOTINITIALIZED, E_XAL_UNLISTEDCONSENT, or E_FAIL. STDAPI XalCheckUcsConsentForAllUsers( _In_z_ char const* consentModelName, _Out_ bool* canOptIn ) noexcept; } // Back compat apis #if XAL_ENABLE_BACK_COMPAT_SHIMS /// /// Attempts to add a user without showing any ui. /// /// Must be null. /// The AsyncBlock for this operation. /// Result code for this API operation. /// /// This version of the api is deprecated, consider the variant taking a /// uint32_t userIdentifier instead. /// inline HRESULT XalTryAddDefaultUserSilentlyAsync( _In_opt_ nullptr_t /*userContext*/, _In_ XAsyncBlock* async ) noexcept { return XalTryAddDefaultUserSilentlyAsync(0u, async); } /// /// Attempts to add a user. /// /// Must be null. /// The AsyncBlock for this operation. /// Result code for this API operation. /// /// This version of the api is deprecated, consider the variant taking a /// uint32_t userIdentifier instead. /// /// This function will show ui to the user. /// inline HRESULT XalAddUserWithUiAsync( _In_opt_ nullptr_t /*userContext*/, _In_ XAsyncBlock* async ) noexcept { return XalAddUserWithUiAsync(0u, async); } #endif #if HC_PLATFORM == HC_PLATFORM_GDK #include #elif HC_PLATFORM == HC_PLATFORM_XDK #include #endif ================================================ FILE: External/Xal/Source/Xal/Include/Xal/xal_android.h ================================================ #pragma once #if !defined(__cplusplus) #error C++11 required #endif #include extern "C" { //------------------------------------------------------------------------------ // Important Information About Xal For Android //------------------------------------------------------------------------------ // // Xal on Android has several hard requirements for how it is used with JNI. // Xal functions may fail if these requirements are not met. // // First and foremost, XalInitialize must be called from a thread which // originated from Java. If this does not happen, Xal will not be able to load // any of the java classes its Android implementation depends on. // // Xal also expects the client to be responsible for attaching and detaching // threads to the Java Virtual Machine. Any thread Xal is running on for // Android should be attached to the Java VM. This is done to save the cost of // repeatedly attaching and detaching to the Java VM. // The recommended pattern for threads that may need to do JNI calls, including // the ones that Xal will be run on, is to attach when the thread starts and // detach just before the thread terminates. // // Xal is configured to work with JNI_VERSION_1_6. Therefore this should be the // return value of the JNI_OnLoad value exported by the native library which // consumes Xal. More information about JNI_OnLoad can be found in the JNI // invocation documentation. //------------------------------------------------------------------------------ // Android types //------------------------------------------------------------------------------ /// /// Struct that encapsulates the Android specific settings for Xal. /// typedef struct XalAndroidArgs { /// /// MSA client id /// _Field_z_ char const* clientId; /// /// Xbox Live title id /// uint32_t titleId; /// /// Xbox Live sandbox /// _Field_z_ char const* sandbox; /// /// A bool indicating whether Xal can send diagnostic telemetry. /// Setting this to true indicates to Xal that it does not have user consent /// to report data about any crashes or errors it encounters during use. /// If this variable is set to false, Xal assumes it can report this data. /// bool disableDiagnosticTelemetry; /// /// A correlation vector string for XAL to use as a base. XAL will extend /// this prior to using it. This argument is optional. /// _Field_z_ char const* correlationVector; /// /// Xal configuration flags. /// uint32_t flags; /// /// The JavaVM for the application consuming Xal. /// JavaVM* javaVM; /// /// The Android App context for the application consuming Xal. /// jobject appContext; /// /// The number of consents present in the ThirdPartyConsents array. /// uint32_t thirdPartyConsentCount; /// /// An optional list of consent requests to access Xbox Live services. /// _Field_size_(thirdPartyConsentCount) char const** thirdPartyConsents; /// /// The Android App custom redirect URI. /// _Field_z_ char const* redirectUri; /// /// The number of consents present in the ucsConsents array /// uint32_t ucsConsentCount; /// /// An optional list of consent requests to UCS /// _Field_size_(ucsConsentCount) char const** ucsConsents; } XalAndroidArgs; typedef XalAndroidArgs XalInitArgs; #define XAL_PLATFORM "Android" } ================================================ FILE: External/Xal/Source/Xal/Include/Xal/xal_apple.h ================================================ #pragma once #include #if !defined(__cplusplus) #error C++11 required #endif extern "C" { //------------------------------------------------------------------------------ // Apple types //------------------------------------------------------------------------------ /// /// Struct that encapsulates the Apple specific arguments for Xal. /// typedef struct XalAppleArgs { /// /// MSA client id /// _Field_z_ char const* clientId; /// /// Xbox Live title id /// uint32_t titleId; /// /// Xbox Live sandbox /// _Field_z_ char const* sandbox; /// /// A bool indicating whether Xal can send diagnostic telemetry. /// Setting this to true indicates to Xal that it does not have user consent /// to report data about any crashes or errors it encounters during use. /// If this variable is set to false, Xal assumes it can report this data. /// bool disableDiagnosticTelemetry; /// /// A correlation vector string for XAL to use as a base. XAL will extend /// this prior to using it. This argument is optional. /// _Field_z_ char const* correlationVector; /// /// Xal configuration flags. /// uint32_t flags; /// /// The number of consents present in the ThirdPartyConsents array. /// uint32_t thirdPartyConsentCount; /// /// An optional list of consent requests to access Xbox Live services. /// _Field_size_(thirdPartyConsentCount) char const** thirdPartyConsents; /// /// The app custom redirect URI. (Optional on macOS). /// _Field_z_ char const* redirectUri; /// /// The number of consents present in the ucsConsents array /// uint32_t ucsConsentCount; /// /// An optional list of consent requests to UCS /// _Field_size_(ucsConsentCount) char const** ucsConsents; } XalAppleArgs; typedef XalAppleArgs XalInitArgs; #if HC_PLATFORM == HC_PLATFORM_IOS #define XAL_PLATFORM "iOS" #elif HC_PLATFORM == HC_PLATFORM_MAC #define XAL_PLATFORM "macOS" #endif } ================================================ FILE: External/Xal/Source/Xal/Include/Xal/xal_generic.h ================================================ #pragma once #if !defined(__cplusplus) #error C++11 required #endif extern "C" { //------------------------------------------------------------------------------ // Generic types //------------------------------------------------------------------------------ typedef struct XalGenericDeviceInfo { _Field_z_ char const* deviceType; _Field_z_ char const* osVersion; _Field_z_ char const* deviceId; } XalGenericDeviceInfo; // TODO collapse the 2 structs? the only thing missing from this one is the // device type, or remove dupes from this struct and ignore it unless telemetry // is enabled? typedef struct XalGenericTelemetryInfo { _Field_z_ char const* appId; // TODO do we want to just use the titleId here? or the msa app id? _Field_z_ char const* appVer; _Field_z_ char const* osName; // TODO use the device type? _Field_z_ char const* osVersion; // TODO dupe _Field_z_ char const* osLocale; _Field_z_ char const* deviceClass; // TODO use the device type? _Field_z_ char const* deviceId; // TODO dupe } XalGenericTelemetryInfo; /// /// Struct that encapsulates the extra information Xal needs. /// typedef struct XalGenericArgs { /// /// MSA client id /// _Field_z_ char const* clientId; /// /// Xbox Live title id /// uint32_t titleId; /// /// Xbox Live sandbox /// _Field_z_ char const* sandbox; /// /// A bool indicating whether Xal can send diagnostic telemetry. /// Setting this to true indicates to Xal that it does not have user consent /// to report data about any crashes or errors it encounters during use. /// If this variable is set to false, Xal assumes it can report this data. /// bool disableDiagnosticTelemetry; /// /// A correlation vector string for XAL to use as a base. XAL will extend /// this prior to using it. This argument is optional. /// _Field_z_ char const* correlationVector; /// /// Reserved for future use. /// uint32_t flags; /// /// The maximum number of users that can be signed in at the same time. /// uint32_t maxSignedInUsers; /// /// The number of consents present in the ThirdPartyConsents array. /// uint32_t thirdPartyConsentCount; /// /// An optional list of consent requests to access Xbox Live services. /// _Field_size_(thirdPartyConsentCount) char const** thirdPartyConsents; /// /// Custom redirect URI. /// _Field_z_ char const* redirectUri; /// /// The number of consents present in the ucsConsents array /// uint32_t ucsConsentCount; /// /// An optional list of consent requests to UCS /// _Field_size_(ucsConsentCount) char const** ucsConsents; bool useRemoteAuth; XalGenericDeviceInfo deviceInfo; XalGenericTelemetryInfo telemetryInfo; } XalGenericArgs; typedef XalGenericArgs XalInitArgs; #define XAL_PLATFORM "Generic" } ================================================ FILE: External/Xal/Source/Xal/Include/Xal/xal_gsdk.h ================================================ #pragma once #if !defined(__cplusplus) #error C++11 required #endif #include #include #include extern "C" { //------------------------------------------------------------------------------ // GDK types //------------------------------------------------------------------------------ #define XAL_OS_ERRORS 1 #define XAL_OS_IMPL 1 typedef XUserHandle XalUserHandle; typedef XUserLocalId XalUserLocalId; /// /// Struct that encapsulates the GDK specific arguments for Xal. /// typedef struct XalGsdkArgs {} XalGsdkArgs; typedef XalGsdkArgs XalInitArgs; typedef XUserChangeEvent XalUserChangeType; XUserChangeEvent const XalUserChange_SignedInAgain = XUserChangeEvent::SignedInAgain; XUserChangeEvent const XalUserChange_SigningOut = XUserChangeEvent::SigningOut; XUserChangeEvent const XalUserChange_SignedOut = XUserChangeEvent::SignedOut; XUserChangeEvent const XalUserChange_Gamertag = XUserChangeEvent::Gamertag; XUserChangeEvent const XalUserChange_GamerPicture = XUserChangeEvent::GamerPicture; XUserChangeEvent const XalUserChange_Privileges = XUserChangeEvent::Privileges; typedef XUserChangeEventCallback XalUserChangeEventHandler; typedef XUserSignOutDeferralHandle XalSignoutDeferralHandle; } ================================================ FILE: External/Xal/Source/Xal/Include/Xal/xal_gsdk_impl.h ================================================ #pragma once #if !defined(__cplusplus) #error C++11 required #endif #include #include #include #include #include extern "C" { static_assert(XalUserState_SignedIn == static_cast(XUserState::SignedIn), "Xal / GDK mismatch"); static_assert(XalUserState_SigningOut == static_cast(XUserState::SigningOut), "Xal / GDK mismatch"); static_assert(XalUserState_SignedOut == static_cast(XUserState::SignedOut), "Xal / GDK mismatch"); static_assert(XalGamerPictureSize_Small == static_cast(XUserGamerPictureSize::Small), "Xal / GDK mismatch"); static_assert(XalGamerPictureSize_Medium == static_cast(XUserGamerPictureSize::Medium), "Xal / GDK mismatch"); static_assert(XalGamerPictureSize_Large == static_cast(XUserGamerPictureSize::Large), "Xal / GDK mismatch"); static_assert(XalGamerPictureSize_ExtraLarge == static_cast(XUserGamerPictureSize::ExtraLarge), "Xal / GDK mismatch"); static_assert(XalAgeGroup_Unknown == static_cast(XUserAgeGroup::Unknown), "Xal / GDK mismatch"); static_assert(XalAgeGroup_Child == static_cast(XUserAgeGroup::Child), "Xal / GDK mismatch"); static_assert(XalAgeGroup_Teen == static_cast(XUserAgeGroup::Teen), "Xal / GDK mismatch"); static_assert(XalAgeGroup_Adult == static_cast(XUserAgeGroup::Adult), "Xal / GDK mismatch"); static_assert(XalPrivilegeCheckDenyReasons_None == static_cast(XUserPrivilegeDenyReason::None), "Xal / GDK mismatch"); static_assert(XalPrivilegeCheckDenyReasons_PurchaseRequired == static_cast(XUserPrivilegeDenyReason::PurchaseRequired), "Xal / GDK mismatch"); static_assert(XalPrivilegeCheckDenyReasons_Restricted == static_cast(XUserPrivilegeDenyReason::Restricted), "Xal / GDK mismatch"); static_assert(XalPrivilegeCheckDenyReasons_Banned == static_cast(XUserPrivilegeDenyReason::Banned), "Xal / GDK mismatch"); static_assert(XalPrivilegeCheckDenyReasons_Unknown == static_cast(XUserPrivilegeDenyReason::Unknown), "Xal / GDK mismatch"); //------------------------------------------------------------------------------ // xal.h //------------------------------------------------------------------------------ inline HRESULT XalInitialize( _In_ XalInitArgs const* /*args*/, _In_opt_ XTaskQueueHandle /*internalWorkQueue*/ ) noexcept { return S_OK; } inline HRESULT XalCleanupAsync( _In_ XAsyncBlock* async ) noexcept { HRESULT hr = XAsyncBegin(async, nullptr, &XalCleanupAsync, "XalCleanupAsync", []( _In_ XAsyncOp op, _In_ XAsyncProviderData const* data ) { if (op == XAsyncOp::DoWork) { XAsyncComplete(data->async, S_OK, 0); } return S_OK; }); if (SUCCEEDED(hr)) { hr = XAsyncSchedule(async, 0); } return hr; } inline HRESULT XalCleanupResult( _In_ XAsyncBlock* async ) noexcept { return XAsyncGetStatus(async, false); } inline HRESULT XalGetMaxUsers( _Out_ uint32_t* maxUsers ) noexcept { return XUserGetMaxUsers(maxUsers); } inline HRESULT XalGetTitleId( _Out_ uint32_t* titleId ) noexcept { return XGameGetXboxTitleId(titleId); } inline size_t XalGetSandboxSize() noexcept { return XSystemXboxLiveSandboxIdMaxBytes; } inline HRESULT XalGetSandbox( _In_ size_t sandboxSize, _Out_writes_(sandboxSize) char* sandbox, _Out_opt_ size_t* sandboxUsed ) noexcept { return XSystemGetXboxLiveSandboxId(sandboxSize, sandbox, sandboxUsed); } inline HRESULT XalTryAddDefaultUserSilentlyAsync( _In_ uint32_t /*userIdentifier*/, // user identifier is not used on GameCore _In_ XAsyncBlock* async ) noexcept { return XUserAddAsync(XUserAddOptions::AddDefaultUserSilently, async); } inline HRESULT XalTryAddDefaultUserSilentlyResult( _In_ XAsyncBlock* async, _Out_ XalUserHandle* newUser ) noexcept { return XUserAddResult(async, newUser); } inline HRESULT XalAddUserWithUiAsync( _In_ uint32_t /*userIdentifier*/, // user identifier is not used on GameCore _In_ XAsyncBlock* async ) noexcept { return XUserAddAsync(XUserAddOptions::None, async); } inline HRESULT XalAddUserWithUiResult( _In_ XAsyncBlock* async, _Out_ XalUserHandle* newUser ) noexcept { return XUserAddResult(async, newUser); } inline bool XalGetDeviceUserIsPresent() noexcept { return false; } inline HRESULT XalGetDeviceUser( _Out_ XalUserHandle* /*deviceUser*/ ) noexcept { return E_NOT_SUPPORTED; } inline bool XalSignOutUserAsyncIsPresent() noexcept { return false; } inline HRESULT XalSignOutUserAsync( _In_ XalUserHandle /*user*/, _In_ XAsyncBlock* /*async*/ ) noexcept { return E_NOT_SUPPORTED; } inline HRESULT XalSignOutUserResult( _In_ XAsyncBlock* /*async*/ ) noexcept { return E_NOT_SUPPORTED; } inline HRESULT XalFindUserByLocalId( _In_ XalUserLocalId localId, _Out_ XalUserHandle* user ) noexcept { return XUserFindUserByLocalId(localId, user); } //------------------------------------------------------------------------------ // xal_user.h //------------------------------------------------------------------------------ inline HRESULT XalUserDuplicateHandle( _In_ XalUserHandle user, _Out_ XalUserHandle* duplicatedUser ) noexcept { return XUserDuplicateHandle(user, duplicatedUser); } inline void XalUserCloseHandle( _In_ XalUserHandle user ) noexcept { XUserCloseHandle(user); } inline int32_t XalCompareUsers( _In_ XalUserHandle user1, _In_ XalUserHandle user2 ) noexcept { return XUserCompare(user1, user2); } inline HRESULT XalUserGetId( _In_ XalUserHandle user, _Out_ uint64_t* id ) noexcept { return XUserGetId(user, id); } inline HRESULT XalUserGetLocalId( _In_ XalUserHandle user, _Out_ XalUserLocalId* localId ) noexcept { HRESULT hr = XUserGetLocalId(user, localId); return hr; } inline bool XalUserIsDevice( _In_ XalUserHandle /*user*/ ) noexcept { return false; } inline bool XalUserIsGuest( _In_ XalUserHandle user ) noexcept { bool isGuest = false; XUserGetIsGuest(user, &isGuest); return isGuest; } inline HRESULT XalUserGetState( _In_ XalUserHandle user, _Out_ XalUserState* state ) noexcept { HRESULT hr = XUserGetState(user, reinterpret_cast(state)); return hr; } inline size_t XalUserGetGamertagSize( _In_ XalUserHandle /*user*/, _In_ XalGamertagComponent component ) noexcept { switch (component) { case XalGamertagComponent_Classic: return XUserGamertagComponentClassicMaxBytes; case XalGamertagComponent_Modern: return XUserGamertagComponentModernMaxBytes; case XalGamertagComponent_ModernSuffix: return XUserGamertagComponentModernSuffixMaxBytes; case XalGamertagComponent_UniqueModern: return XUserGamertagComponentUniqueModernMaxBytes; default: return 0; } } inline HRESULT XalUserGetGamertag( _In_ XalUserHandle user, _In_ XalGamertagComponent component, _In_ size_t gamertagSize, _Out_writes_(gamertagSize) char* gamertag, _Out_opt_ size_t* gamertagUsed ) noexcept { return XUserGetGamertag(user, static_cast(component), gamertagSize, gamertag, gamertagUsed); } inline HRESULT XalUserGetGamerPictureAsync( _In_ XalUserHandle user, _In_ XalGamerPictureSize pictureSize, _In_ XAsyncBlock* async ) noexcept { return XUserGetGamerPictureAsync(user, static_cast(pictureSize), async); } inline HRESULT XalUserGetGamerPictureResultSize( _In_ XAsyncBlock* async, _Out_ size_t* bufferSize ) noexcept { return XUserGetGamerPictureResultSize(async, bufferSize); } inline HRESULT XalUserGetGamerPictureResult( _In_ XAsyncBlock* async, _In_ size_t bufferSize, _Out_writes_(bufferSize) void* buffer ) noexcept { return XUserGetGamerPictureResult(async, bufferSize, buffer, nullptr); } inline HRESULT XalUserGetAgeGroup( _In_ XalUserHandle user, _Out_ XalAgeGroup* ageGroup ) noexcept { HRESULT hr = XUserGetAgeGroup(user, reinterpret_cast(ageGroup)); return hr; } inline HRESULT XalUserCheckPrivilege( _In_ XalUserHandle user, _In_ XalPrivilege privilege, _Out_ bool* hasPrivilege, _Out_opt_ XalPrivilegeCheckDenyReasons* reasons ) noexcept { HRESULT hr = XUserCheckPrivilege( user, XUserPrivilegeOptions::None, static_cast(privilege), hasPrivilege, reinterpret_cast(reasons) ); return hr; } inline bool XalUserResolvePrivilegeWithUiIsPresent() noexcept { return true; } inline HRESULT XalUserResolveUserPrivilegeWithUiAsync( _In_ XalUserHandle user, _In_ XalPrivilege privilege, _In_ XAsyncBlock* async ) noexcept { return XUserResolvePrivilegeWithUiAsync( user, XUserPrivilegeOptions::None, static_cast(privilege), async ); } inline HRESULT XalUserResolveUserPrivilegeWithUiResult( _In_ XAsyncBlock* async ) noexcept { return XUserResolvePrivilegeWithUiResult(async); } inline HRESULT XalUserGetTokenAndSignatureSilentlyAsync( _In_ XalUserHandle user, _In_ XalUserGetTokenAndSignatureArgs const* args, _In_ XAsyncBlock* async ) noexcept { static_assert(sizeof(XalHttpHeader) == sizeof(XUserGetTokenAndSignatureHttpHeader), "Xal / GDK mismatch"); XUserGetTokenAndSignatureOptions opts = XUserGetTokenAndSignatureOptions::None; if (args->forceRefresh) { opts |= XUserGetTokenAndSignatureOptions::ForceRefresh; } if (args->allUsers) { opts |= XUserGetTokenAndSignatureOptions::AllUsers; } return XUserGetTokenAndSignatureAsync( user, opts, args->method, args->url, args->headerCount, reinterpret_cast(args->headers), args->bodySize, args->body, async ); } inline HRESULT XalUserGetTokenAndSignatureSilentlyResultSize( _In_ XAsyncBlock* async, _Out_ size_t* bufferSize ) noexcept { return XUserGetTokenAndSignatureResultSize(async, bufferSize); } inline HRESULT XalUserGetTokenAndSignatureSilentlyResult( _In_ XAsyncBlock* async, _In_ size_t bufferSize, _Out_writes_bytes_to_(bufferSize, *bufferUsed) void* buffer, _Outptr_ XalUserGetTokenAndSignatureData** result, _Out_opt_ size_t* bufferUsed ) noexcept { static_assert(sizeof(XalUserGetTokenAndSignatureData) == sizeof(XUserGetTokenAndSignatureData), "Xal / GDK mismatch"); return XUserGetTokenAndSignatureResult( async, bufferSize, buffer, reinterpret_cast(result), bufferUsed ); } inline HRESULT XalUserResolveIssueWithUiAsync( _In_ XalUserHandle user, _In_opt_z_ char const* url, _In_ XAsyncBlock* async ) noexcept { return XUserResolveIssueWithUiAsync(user, url, async); } inline HRESULT XalUserResolveIssueWithUiResult( _In_ XAsyncBlock* async ) noexcept { return XUserResolveIssueWithUiResult(async); } inline HRESULT XalUserRegisterChangeEventHandler( _In_opt_ XTaskQueueHandle queue, _In_opt_ void* context, _In_ XalUserChangeEventHandler* handler, _Out_ XalRegistrationToken* token ) noexcept { static_assert(sizeof(XalRegistrationToken) == sizeof(XTaskQueueRegistrationToken), "Xal / GDK mismatch"); static_assert(sizeof(XalRegistrationToken::token) == sizeof(XTaskQueueRegistrationToken::token), "Xal / GDK mismatch"); return XUserRegisterForChangeEvent( queue, context, handler, reinterpret_cast(token) ); } inline void XalUserUnregisterChangeEventHandler( _In_ XalRegistrationToken token ) noexcept { XTaskQueueRegistrationToken gdkToken{ token.token }; XUserUnregisterForChangeEvent(gdkToken, true); } inline HRESULT XalUserGetSignoutDeferral( _Out_ XalSignoutDeferralHandle* deferral ) noexcept { return XUserGetSignOutDeferral(deferral); } inline void XalUserCloseSignoutDeferral( _In_ XalSignoutDeferralHandle deferral ) noexcept { XUserCloseSignOutDeferralHandle(deferral); } } ================================================ FILE: External/Xal/Source/Xal/Include/Xal/xal_internal_marketing.h ================================================ #pragma once #if !defined(__cplusplus) #error C++11 required #endif // Attention: This file is intended for internal uses only. // Its use is not recommended and not supported. #include extern "C" { //----------------------------------------------------------------------------- // Marketing State //----------------------------------------------------------------------------- /// /// Enum defining the various marketing states. /// typedef enum XalMarketingState { /// Existing user XalMarketingState_ExistingUser = 0, /// User went through account creation XalMarketingState_NewUser = 1, /// User went through account creation and saw the first party marketing notice XalMarketingState_NewUserFirstPartyNotice = 2, } XalMarketingState; STDAPI XalUserGetMarketingState( _In_ XalUserHandle user, _Out_ XalMarketingState* marketingState ) noexcept; } ================================================ FILE: External/Xal/Source/Xal/Include/Xal/xal_internal_telemetry.h ================================================ #pragma once #if !defined(__cplusplus) #error C++11 required #endif // Attention: This file is intended for internal uses only. // Its use is not recommended and not supported. #include extern "C" { //----------------------------------------------------------------------------- // Telemetry //----------------------------------------------------------------------------- /// /// Controls if the event is to be uploaded immediately or can be batched. /// /// /// This is a copy of MAE::EventLatency and should be kept in sync with it. /// typedef enum XalTelemetryLatency { XalTelemetryLatency_Unspecified = -1, XalTelemetryLatency_Off = 0, XalTelemetryLatency_Normal = 0x0100, XalTelemetryLatency_CostDeferred = 0x0200, XalTelemetryLatency_Realtime = 0x0300, XalTelemetryLatency_Max = 0x0400, } XalTelemetryLatency; /// /// Controls the priority of keeping the event in case 1DS needs to evict some. /// /// /// This is a copy of MAE::EventPersistence and should be kept in sync with it. /// typedef enum XalTelemetryPersistence { XalTelemetryPersistence_Normal = 0x00, XalTelemetryPersistence_Critical = 0x01, XalTelemetryPersistence_DoNotStoreOnDisk = 0x02, } XalTelemetryPersistence; /// /// Controls the sampling rate of the event. /// typedef enum XalTelemetrySampleRate { XalTelemetrySampleRate_Unspecified = 0, XalTelemetrySampleRate_NoSampling = 1, XalTelemetrySampleRate_10_percent = 2, XalTelemetrySampleRate_0_percent = 3, } XalTelemetrySampleRate; /// /// Describes the type of a ticket. /// typedef enum XalTelemetryTicketType { //XalTelemetryTicketType_Unspecified = 0, // currently unsupported //XalTelemetryTicketType_MsaUser = 0x01, // currently unsupported //XalTelemetryTicketType_MsaDevice = 0x02, // currently unsupported XalTelemetryTicketType_XauthUser = 0x03, XalTelemetryTicketType_XauthDevice = 0x04 } XalTelemetryTicketType; /// /// Describes a ticket that should be included with a telemetry event. /// typedef struct XalTelemetryTicket { /// /// The url for the ticket audience. /// _Field_z_ char const* Url; /// /// The ticket id. /// uint32_t Id; /// /// The type of ticket. /// XalTelemetryTicketType Type; } XalTelemetryTicket; /// /// A method to write a telemetry event and send it to vortex. /// /// The user to send this event for. /// The game's iKey. /// The null terminated event name string. This will be the /// full name of the event with the provider prefix. /// The null terminated event data string. The string should /// be properly formatted JSON. /// The number of tickets to send with the event. /// Information about the tickets to send. /// The 1DS latency for this event. /// The 1DS persistence for this event. /// The 1DS sampleRate for this event. STDAPI XalTelemetryWriteEvent( _In_ XalUserHandle user, _In_z_ char const* iKey, _In_z_ char const* eventNameWithProvider, _In_z_ char const* data, _In_ uint32_t ticketCount, _In_reads_(ticketCount) XalTelemetryTicket* tickets, _In_ XalTelemetryLatency latency, _In_ XalTelemetryPersistence persistence, _In_ XalTelemetrySampleRate sampleRate ) noexcept; } ================================================ FILE: External/Xal/Source/Xal/Include/Xal/xal_internal_types.h ================================================ #pragma once #if !defined(__cplusplus) #error C++11 required #endif // Attention: This file is intended for internal uses only. // Its use is not recommended and not supported. #include extern "C" { //------------------------------------------------------------------------------ // Xal init flags //------------------------------------------------------------------------------ /// /// Flag that instructs Xal to attempt to use cached MSA refresh tokens from Xsapi to /// silently sign existing users in. Do not use this flag unless you have been /// instructed to do so. /// /// /// This flag is supported on Android and iOS device types. /// uint32_t const XAL_INIT_OPTION_MIGRATE_XSAPI_USER_TOKENS = 1u; /// /// Flag that instructs Xal that your app is set up for FOCI token sharing and to attempt /// to use FOCI SSO. If your app is not set up for FOCI do not set this flag. /// /// /// This flag is supported on Win32, Android, iOS, Mac, and generic device types. /// uint32_t const XAL_INIT_OPTION_USE_FOCI = 1u << 24; /// /// Flag that provides a hint to Xal that your app is allowed to sign out the user on the /// UWP platform. Note that setting this does not give your app permission to sign /// out the user, but only acts as clue for Xal that you have that permission. /// /// /// This flag is supported on UWP device types. /// uint32_t const XAL_INIT_OPTION_ALLOW_UWP_SIGNOUT = 1u << 25; /// /// Flag to disable SSO browser usage on platforms where a secure browser exists. /// This flag is provided for testing purposes only. /// /// /// This flag is supported on Android and iOS device types. /// uint32_t const XAL_INIT_OPTION_USE_IN_PROC_BROWSER = 1u << 27; /// /// Flag to tell XAL to use file storage for token storage on Win32 platforms instead of the Windows /// Credential Manager. Using this flag will break many SSO scenarios. /// /// /// This flag is supported on Win32 device types. /// uint32_t const XAL_INIT_OPTION_WIN32_USE_FILE_STORAGE = 1u << 28; /// /// Flag to instruct Xal to use the beta Xbox service cloud. /// /// /// This flag is supported on Win32, Android, iOS, Mac, and generic device types. /// uint32_t const XAL_INIT_OPTION_USE_BETA_SERVICES_FLAG = 1u << 29; /// /// Flag to instruct Xal to use the 1st party auth flow. /// /// /// This flag is supported on Win32, UWP, Android, iOS, Mac, and generic device types. /// uint32_t const XAL_INIT_OPTION_TITLE_TYPE_FIRST_PARTY_FLAG = 1u << 31; } ================================================ FILE: External/Xal/Source/Xal/Include/Xal/xal_internal_web_account.h ================================================ #pragma once #if !defined(__cplusplus) #error C++11 required #endif // Attention: This file is intended for internal uses only. // Its use is not recommended and not supported. #include #include extern "C" { //----------------------------------------------------------------------------- // User properties /// /// Returns the size of the buffer needed to store the web account ID string. /// /// The user object. STDAPI_(size_t) XalUserGetWebAccountIdSize( _In_ XalUserHandle user ) noexcept; /// /// Returns the web account ID of the user. /// /// The user object. /// The size in bytes of the webAccountId buffer. /// Should be the value returned by XalUserGetWebAccountIdSize. /// The buffer the web account ID will be written to. /// /// The number of bytes used in the buffer /// including the null terminator. STDAPI XalUserGetWebAccountId( _In_ XalUserHandle user, _In_ size_t webAccountIdSize, _Out_writes_(webAccountIdSize) char* webAccountId, _Out_opt_ size_t* webAccountIdUsed ) noexcept; //----------------------------------------------------------------------------- // Get web account token /// /// Struct that represents a parameter that has a key and a value for web account APIs. /// typedef struct XalWebAccountParameter { /// Parameter name. _Field_z_ char const* name; /// Parameter value. _Field_z_ char const* value; } XalWebAccountParameter; /// /// Struct that encapsulates the arguments for XalUserGetWebAccountTokenSilentlyAsync. /// /// /// Xal will copy the data before XalUserGetWebAccountTokenSilentlyAsync returns. /// typedef struct XalUserGetWebAccountTokenArgs { /// /// The token scope string being requested. /// _Field_z_ char const* Scope; /// /// Ignore cached tokens. /// /// /// This flag should only be set if an http request using a previously /// fetched token failed with a 401 error. In that case the entire call /// should be retried after getting a new token using this flag. /// bool ForceRefresh; /// /// The number of request parameters that will be added to the token request. /// uint32_t parameterCount; /// /// The array of request parameters that will be added to the token request. /// _Field_size_(parameterCount) XalWebAccountParameter const* requestParameters; } XalUserGetWebAccountTokenArgs; /// /// Gets a token with the specified scope for the user without showing UI. /// /// The user the token is for. /// The requested token details. /// The AsyncBlock for this operation. /// . STDAPI XalUserGetWebAccountTokenSilentlyAsync( _In_ XalUserHandle user, _In_ XalUserGetWebAccountTokenArgs const* args, _In_ XAsyncBlock* async ) noexcept; /// /// Gets the size in bytes of the web account token result buffer. /// /// The AsyncBlock for this operation. /// The size in bytes for the result buffer. STDAPI XalUserGetWebAccountTokenSilentlyResultSize( _In_ XAsyncBlock* async, _Out_ size_t* bufferSize ) noexcept; /// /// Gets the results of a successful XalUserGetWebAccountTokenSilentlyAsync operation. /// /// The AsyncBlock for this operation. /// The size of the buffer for the result object. /// /// The result token. STDAPI XalUserGetWebAccountTokenSilentlyResult( _In_ XAsyncBlock* async, _In_ size_t bufferSize, _Out_writes_z_(bufferSize) char* result ) noexcept; /// /// Gets a token with the specified scope for the user showing UI if necessary. /// /// The user the token is for. /// The requested token details. /// The AsyncBlock for this operation. /// . STDAPI XalUserGetWebAccountTokenWithUiAsync( _In_ XalUserHandle user, _In_ XalUserGetWebAccountTokenArgs const* args, _In_ XAsyncBlock* async ) noexcept; /// /// Gets the size in bytes of the web account token result buffer. /// /// The AsyncBlock for this operation. /// The size in bytes for the result buffer. STDAPI XalUserGetWebAccountTokenWithUiResultSize( _In_ XAsyncBlock* async, _Out_ size_t* bufferSize ) noexcept; /// /// Gets the results of a successful XalUserGetWebAccountTokenWithUiAsync operation. /// /// The AsyncBlock for this operation. /// The size of the buffer for the result object. /// /// The result token. STDAPI XalUserGetWebAccountTokenWithUiResult( _In_ XAsyncBlock* async, _In_ size_t bufferSize, _Out_writes_z_(bufferSize) char* result ) noexcept; } ================================================ FILE: External/Xal/Source/Xal/Include/Xal/xal_platform.h ================================================ #pragma once #if !defined(__cplusplus) #error C++11 required #endif #include #include #include extern "C" { //----------------------------------------------------------------------------- // Hooks for platform specific behaviour //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // Memory (optional on all platforms) /// /// Sets the memory hook functions to allow callers to route memory allocations /// to their own memory manager. This must be called before XalInitialize and /// cannot be called again until after XalCleanup and all XAL_USER_HANDLEs have /// been closed. /// /// This method allows the application to install custom memory allocation /// routines in order to service all requests for new memory buffers instead of /// using default allocation routines. /// /// The and /// parameters can be null pointers to restore the default routines. Both /// callback pointers must be null or both must be non-null. Mixing custom and /// default routines is not permitted and will cause the function to fail. /// /// A pointer to the custom allocation callback to /// use, or a null pointer to restore the default. /// A pointer to the custom freeing callback to use, /// or a null pointer to restore the default. /// Result code for this API operation. STDAPI XalMemSetFunctions( _In_opt_ XalMemAllocFunc* memAllocFunc, _In_opt_ XalMemFreeFunc* memFreeFunc ) noexcept; /// /// Gets the memory hook functions to allow callers to route memory allocations /// to their own memory manager. This method allows the application get the /// default memory allocation routines. This can be used along with /// XalMemSetFunctions to monitor all memory allocations. /// /// Set to the current allocation callback. Returns /// the default routine if not previously set. /// Set to the to the current memory free callback. /// Returns the default routine if not previously set. /// Result code for this API operation. STDAPI XalMemGetFunctions( _Out_ XalMemAllocFunc** memAllocFunc, _Out_ XalMemFreeFunc** memFreeFunc ) noexcept; //----------------------------------------------------------------------------- // Web view (ignored on OneCore platforms, optional on iOS and Android) /// /// Registers the show url event handler. /// /// The async queue the callback should be invoked on. /// /// Optional pointer to data used by the event handler. /// /// The event handler, /// . /// Result code for this API operation. /// /// Must be called before XalInitialize. /// STDAPI XalPlatformWebSetEventHandler( _In_opt_ XTaskQueueHandle queue, _In_opt_ void* context, _In_ XalPlatformWebShowUrlEventHandler2* handler ) noexcept; /// /// Clears the show url event handler. /// /// Result code for this API operation. /// /// Must be called before XalInitialize or after XalCleanupAsync completes. /// STDAPI XalPlatformWebClearEventHandler() noexcept; /// /// Completes a show url operation. /// /// The handle for this operation. /// The result of the operation. /// The full url for the final redirection. /// Result code for this API operation. /// /// This should only be called in response to a show url event, once the web /// view is redirected to the final url or if an error occurs and the operation /// cannot be completed. /// /// STDAPI XalPlatformWebShowUrlComplete( _In_ XalPlatformOperation operation, _In_ XalPlatformOperationResult result, _In_opt_z_ char const* url ) noexcept; //----------------------------------------------------------------------------- // Storage (ignored on OneCore platforms, optional on iOS and Android) /// /// Sets the storage event handlers. /// /// The async queue the callbacks should be invoked on. /// /// The event handlers, /// . /// Result code for this API operation. /// /// Must be called before XalInitialize. /// STDAPI XalPlatformStorageSetEventHandlers( _In_opt_ XTaskQueueHandle queue, _In_ XalPlatformStorageEventHandlers2* handlers ) noexcept; /// /// Clears the storage event handlers. /// /// Result code for this API operation. /// /// Must be called before XalInitialize or after XalCleanupAsync completes. /// STDAPI XalPlatformStorageClearEventHandlers() noexcept; /// /// Completes write to storage operation. /// /// The handle for this operation. /// The result of the operation. /// Result code for this API operation. /// /// This should only be called in response to a write to storage event, once /// the write is completed or if an error occurs and the operation cannot be /// completed . /// STDAPI XalPlatformStorageWriteComplete( _In_ XalPlatformOperation operation, _In_ XalPlatformOperationResult result ) noexcept; /// /// Completes read from storage operation. /// /// The handle for this operation. /// The result of the operation. /// The size (in bytes) of the data. /// The data read. /// Result code for this API operation. /// /// This should only be called in response to a read from storage event, once /// the read is completed or if an error occurs and the operation cannot be /// completed . /// /// If the requested key cannot be found, the operation should be completed /// with XalClientOperationResult_Success, data = nullptr and dataSize = 0. /// STDAPI XalPlatformStorageReadComplete( _In_ XalPlatformOperation operation, _In_ XalPlatformOperationResult result, _In_ size_t dataSize, _In_reads_bytes_opt_(dataSize) void const* data ) noexcept; /// /// Completes clear from storage operation. /// /// The handle for this operation. /// The result of the operation. /// Result code for this API operation. /// /// This should only be called in response to a clear from storage event, once /// the data is cleared or if an error occurs and the operation cannot be /// completed . /// STDAPI XalPlatformStorageClearComplete( _In_ XalPlatformOperation operation, _In_ XalPlatformOperationResult result ) noexcept; //----------------------------------------------------------------------------- // Remote Connect (only used in generic mode, when configured for it) /// /// Sets the remote connect event handlers. /// /// The async queue the callbacks should be invoked on. /// /// The event handlers, /// . /// Result code for this API operation. /// /// Must be called before XalInitialize. /// STDAPI XalPlatformRemoteConnectSetEventHandlers( _In_opt_ XTaskQueueHandle queue, _In_ XalPlatformRemoteConnectEventHandlers3* handlers ) noexcept; /// /// Clears the remote connect event handlers. /// /// Result code for this API operation. /// /// Must be called before XalInitialize or after XalCleanupAsync completes. /// STDAPI XalPlatformRemoteConnectClearEventHandlers() noexcept; /// /// Signal to Xal that the remote connect prompt has been dismissed by the user. /// /// The handle for this operation. /// Result code for this API operation. /// /// This should be called after a XalPlatformRemoteConnectShowPromptEventHandler /// if the user dismisses the prompt. The whole remote connect process will be /// cancelled and the starting AddUserAsync operation will complete with /// E_ABORT. The XalPlatformRemoteConnectClosePromptEventHandler will be called /// as normal. /// STDAPI XalPlatformRemoteConnectCancelPrompt( _In_ XalPlatformOperation operation ) noexcept; //------------------------------------------------------------------------------ // Crypto (only used in generic mode) /// /// Sets the crypto callbacks. /// /// The callbacks, /// . /// Result code for this API operation. /// /// Must be called before XalInitialize. /// STDAPI XalPlatformCryptoSetCallbacks( _In_ XalPlatformCryptoCallbacks* callbacks ) noexcept; //------------------------------------------------------------------------------ // Date & Time (only used in generic mode) /// /// Sets the date/time callbacks. /// /// The callbacks, /// . /// Result code for this API operation. /// /// Must be called before XalInitialize. /// STDAPI XalPlatformDateTimeSetCallbacks( _In_ XalPlatformDateTimeCallbacks* callbacks ) noexcept; //----------------------------------------------------------------------------- // Spop Prompt (only used in generic mode, when configured for it) /// /// Sets the SPOP prompt event handler. /// /// The event handler, /// . /// Result code for this API operation. /// /// Must be called before XalInitialize. /// STDAPI XalPlatformSpopPromptSetEventHandlers( _In_opt_ XTaskQueueHandle queue, _In_ XalPlatformSpopPromptEventHandler* handler, _In_opt_ void* context ) noexcept; /// /// Clears the SPOP event handlers. /// /// Result code for this API operation. /// /// Must be called before XalInitialize or after XalCleanupAsync completes. /// STDAPI XalPlatformSpopPromptClearEventHandler() noexcept; /// /// Signal to Xal that the user finished interacting with the SPOP prompt. /// /// The handle for this operation. /// The result of the user interaction. /// Result code for this API operation. /// /// This should be called after a XalPlatformSpopPromptEventHandler when the /// user makes a choice or if a failure occurs. /// STDAPI XalPlatformSpopPromptComplete( _In_ XalPlatformOperation operation, _In_ XalSpopOperationResult result ) noexcept; } // Back compat hooks #if XAL_ENABLE_BACK_COMPAT_SHIMS //----------------------------------------------------------------------------- // Web view (ignored on OneCore platforms, optional on iOS and Android) /// /// Registers the show url event handler. /// /// The async queue the callback should be invoked on. /// /// Optional pointer to data used by the event handler. /// /// The event handler, /// . /// Result code for this API operation. /// /// Must be called before XalInitialize. /// inline HRESULT XalPlatformWebSetEventHandler( _In_opt_ XTaskQueueHandle queue, _In_opt_ void* context, _In_ XalPlatformWebShowUrlEventHandler* handler ) noexcept { static struct WebShowUrlHandler { XalPlatformWebShowUrlEventHandler* handler; void* context; } s_handlers{}; s_handlers.handler = handler; s_handlers.context = context; XalPlatformWebShowUrlEventHandler2* trampoline = []( void* ctx, uint32_t /*cuid*/, XalPlatformOperation op, char const* sUrl, char const* fUrl, XalShowUrlType t, uint32_t /*requestHeaderCount*/, XalHttpHeader const* /*requestHeaders*/ ) { auto handler = static_cast(ctx); handler->handler(handler->context, nullptr, op, sUrl, fUrl, t); }; return XalPlatformWebSetEventHandler(queue, &s_handlers, trampoline); } //----------------------------------------------------------------------------- // Storage (ignored on OneCore platforms, optional on iOS and Android) /// /// Sets the storage event handlers. /// /// The async queue the callbacks should be invoked on. /// /// The event handlers, /// . /// Result code for this API operation. /// /// Must be called before XalInitialize. /// inline HRESULT XalPlatformStorageSetEventHandlers( _In_opt_ XTaskQueueHandle queue, _In_ XalPlatformStorageEventHandlers* handlers ) noexcept { static XalPlatformStorageEventHandlers s_handlers{}; s_handlers = *handlers; XalPlatformStorageEventHandlers2 trampolines = {}; trampolines.write = [](void* ctx, uint32_t /*cuid*/, XalPlatformOperation op, char const* key, size_t size, void const* data) { auto handlers = static_cast(ctx); handlers->write(handlers->context, nullptr, op, key, size, data); }; trampolines.read = [](void* ctx, uint32_t /*cuid*/, XalPlatformOperation op, char const* key) { auto handlers = static_cast(ctx); handlers->read(handlers->context, nullptr, op, key); }; trampolines.clear = [](void* ctx, uint32_t /*cuid*/, XalPlatformOperation op, char const* key) { auto handlers = static_cast(ctx); handlers->clear(handlers->context, nullptr, op, key); }; trampolines.context = &s_handlers; return XalPlatformStorageSetEventHandlers(queue, &trampolines); } //----------------------------------------------------------------------------- // Remote Connect (only used in generic mode, when configured for it) /// /// Sets the remote connect event handlers. /// /// The async queue the callbacks should be invoked on. /// /// The event handlers, /// . /// Result code for this API operation. /// /// Must be called before XalInitialize. /// inline HRESULT XalPlatformRemoteConnectSetEventHandlers( _In_opt_ XTaskQueueHandle queue, _In_ XalPlatformRemoteConnectEventHandlers2* handlers ) noexcept { static XalPlatformRemoteConnectEventHandlers2 s_handlers{}; s_handlers = *handlers; XalPlatformRemoteConnectEventHandlers3 trampolines = {}; trampolines.show = [](void* ctx, uint32_t cuid, XalPlatformOperation op, char const* url, char const* code, size_t /*qrCodeSize*/, void const* /*qrCode*/) { auto handlers = static_cast(ctx); handlers->show(handlers->context, cuid, op, url, code); }; trampolines.close = [](void* ctx, uint32_t cuid, XalPlatformOperation op) { auto handlers = static_cast(ctx); handlers->close(handlers->context, cuid, op); }; trampolines.context = &s_handlers; return XalPlatformRemoteConnectSetEventHandlers(queue, &trampolines); } /// /// Sets the remote connect event handlers. /// /// The async queue the callbacks should be invoked on. /// /// The event handlers, /// . /// Result code for this API operation. /// /// Must be called before XalInitialize. /// inline HRESULT XalPlatformRemoteConnectSetEventHandlers( _In_opt_ XTaskQueueHandle queue, _In_ XalPlatformRemoteConnectEventHandlers* handlers ) noexcept { static XalPlatformRemoteConnectEventHandlers s_handlers{}; s_handlers = *handlers; XalPlatformRemoteConnectEventHandlers3 trampolines = {}; trampolines.show = [](void* ctx, uint32_t /*cuid*/, XalPlatformOperation op, char const* url, char const* code, size_t /*qrCodeSize*/, void const* /*qrCode*/) { auto handlers = static_cast(ctx); handlers->show(handlers->context, nullptr, op, url, code); }; trampolines.close = [](void* ctx, uint32_t /*cuid*/, XalPlatformOperation op) { auto handlers = static_cast(ctx); handlers->close(handlers->context, nullptr, op); }; trampolines.context = &s_handlers; return XalPlatformRemoteConnectSetEventHandlers(queue, &trampolines); } #endif ================================================ FILE: External/Xal/Source/Xal/Include/Xal/xal_platform_types.h ================================================ #pragma once #if !defined(__cplusplus) #error C++11 required #endif #include extern "C" { //----------------------------------------------------------------------------- // Types for for platform hooks //----------------------------------------------------------------------------- /// /// Handle to an operation Xal requested the client to perform. /// typedef struct XalPlatformOperationToken* XalPlatformOperation; /// /// Enum defining the results of a client operation. /// typedef enum XalPlatformOperationResult { /// /// Successful client operation. /// XalPlatformOperationResult_Success = 0, /// /// Failed client operation. /// XalPlatformOperationResult_Failure = 1, /// /// Canceled client operation. /// XalPlatformOperationResult_Canceled = 2, } XalPlatformOperationResult; /// /// Enum defining the possible reasons a platform web event might occur. /// typedef enum XalShowUrlType { /// /// The client should show the URL in a shared system browser if /// one is present, otherwise the client should use an embedded browser. /// XalShowUrlType_Normal = 0, /// /// This case is deprecated and no longer used. Cookie removal is now /// signaled exclusively using /// XalShowUrlType_CookieRemovalSkipIfSharedCredentials. /// /// /// Formerly, this value indicated that the browser was being raised for the /// purposes of deleting cookies from both shared and embedded browsers. /// XalShowUrlType_CookieRemoval_DEPRECATED = 1, /// /// The browser is being raised for the purposes of deleting /// cookies. If the client is using a shared system browser, this call /// should be ignored and the client should immediately call /// XalPlatformWebShowUrlComplete passing in success, and forwarding the /// received final URL back into Xal. If an embedded browser is being used, /// the cookies should be cleared without showing UI if possible. If silent /// cookie-clearing is impossible, the URL should be loaded as normal. /// XalShowUrlType_CookieRemovalSkipIfSharedCredentials = 2, /// /// This is a web flow which does not rely on cookies. The client /// may use a shared system browser or an embedded browser depending on /// whichever browser would give the best user experience. If headers are /// required for the web request, this will be the show type asked for. If /// this is the case, an embedded browser might be required so those /// request headers can be set. /// XalShowUrlType_NonAuthFlow = 3, } XalShowUrlType; /// /// The userIdentifier value Xal will use for data that is not specific to a /// single user. /// uint32_t const XAL_NO_USER_IDENTIFIER = static_cast(-1); //----------------------------------------------------------------------------- // Memory (optional on all platforms) /// /// A callback invoked every time a new memory buffer must be dynamically /// allocated by the library. This callback is optionally installed by calling /// XalMemSetFunctions. /// /// The callback must allocate and return a pointer to a contiguous block of /// memory of the specified size that will remain valid until the app's /// corresponding XalMemFreeFunc callback is invoked to release it. /// /// Every non-null pointer returned by this method will be subsequently passed /// to the corresponding XalMemFreeFunc callback once the memory is no longer /// needed. /// /// The size of the allocation to be made. This value will /// never be zero. /// An opaque identifier representing the internal category /// of memory being allocated. /// A pointer to an allocated block of memory of the specified size, or /// a null pointer if allocation failed. typedef _Ret_maybenull_ _Post_writable_byte_size_(size) void* XalMemAllocFunc(size_t size, uint32_t tag); /// /// A callback invoked every time a previously allocated memory buffer is no /// longer needed by the library and can be freed. This callback is optionally /// installed by calling XalMemSetFunctions. /// /// The callback is invoked whenever the library has finished using a memory /// buffer previously returned by the app's corresponding XalMemAllocFunc such /// that the application can free the memory buffer. /// /// The pointer to the memory buffer previously allocated. /// This value will never be a null pointer. /// An opaque identifier representing the internal category /// of memory being allocated. /// typedef void XalMemFreeFunc(_In_ _Post_invalid_ void* pointer, uint32_t tag); //----------------------------------------------------------------------------- // Web view (ignored on OneCore platforms, optional on iOS and Android) /// /// Show url event handler. /// /// Optional pointer to data used by the event handler. /// /// The user identifier that was passed to Xal when /// the user was added. /// The handle for this operation. /// The url to navigate to. /// The url that indicates the web flow is /// complete. /// Enum indicating the type of flow occurring. This /// flag dictates what correct behavior for the client is expected to be. /// /// The number of request headers present in /// the requestHeaders array. /// Request headers that must be added to the web /// session request for the best user experience. /// /// /// This event is raised when Xal needs to show a web flow to the user, the /// client should navigate to startUrl and wait for a redirect to finalUrl. Once /// the redirect to finalUrl occurs the client should close the web ui and invoke /// XalPlatformWebShowUrlComplete passing the full redirect url. /// /// This handler is optional for Android and iOS platforms. If it is not set on /// these platforms, Xal will provide default browser behavior. On UWP, and XDK /// platforms, this handler is ignored. /// /// Depending on the value of showUrlType and the type of browser the client is /// using, different behavior is expected. See the definition for /// XalShowUrlType for more information. /// /// All arguments are owned by the caller (except context). /// typedef void (XalPlatformWebShowUrlEventHandler2)( _In_opt_ void* context, _In_ uint32_t userIdentifier, _In_ XalPlatformOperation operation, _In_z_ char const* startUrl, _In_z_ char const* finalUrl, _In_ XalShowUrlType showUrlType, _In_ uint32_t requestHeaderCount, _In_reads_(requestHeaderCount) XalHttpHeader const* requestHeaders ); //----------------------------------------------------------------------------- // Storage (ignored on OneCore platforms, optional on iOS and Android) /// /// Write to storage event handler. /// /// Optional pointer to data used by the event handler. /// /// The user identifier that was passed to Xal when /// the user was added. /// The handle for this operation. /// Identifies the data being written. /// The size (in bytes) of the data. /// The data to write. /// /// /// This event is raised when Xal needs to write data to storage, the client /// should write the data and when done invoke XalPlatformStorageWriteComplete. /// /// All arguments are owned by the caller (except context). /// typedef void (XalPlatformStorageWriteEventHandler2)( _In_opt_ void* context, _In_ uint32_t userIdentifier, _In_ XalPlatformOperation operation, _In_z_ char const* key, _In_ size_t dataSize, _In_reads_bytes_(dataSize) void const* data ); /// /// Read from storage event handler. /// /// Optional pointer to data used by the event handler. /// /// The user identifier that was passed to Xal when /// the user was added. /// The handle for this operation. /// Identifies the data being read. /// /// /// This event is raised when Xal needs to read data from storage, the client /// should read the data and when done invoke XalPlatformStorageReadComplete. /// If the key is not found, the client should complete with /// XalPlatformOperationResult_Success and no data. /// /// All arguments are owned by the caller (except context). /// typedef void (XalPlatformStorageReadEventHandler2)( _In_opt_ void* context, _In_ uint32_t userIdentifier, _In_ XalPlatformOperation operation, _In_z_ char const* key ); /// /// Clear from storage event handler. /// /// Optional pointer to data used by the event handler. /// /// The user identifier that was passed to Xal when /// the user was added. /// The handle for this operation. /// Identifies the data being cleared. /// /// /// This event is raised when Xal needs to clear data from storage, the client /// should clear the data and when done invoke XalPlatformStorageClearComplete. /// /// All arguments are owned by the caller (except context). /// typedef void (XalPlatformStorageClearEventHandler2)( _In_opt_ void* context, _In_ uint32_t userIdentifier, _In_ XalPlatformOperation operation, _In_z_ char const* key ); /// /// Struct encapsulating the storage event handlers. /// /// /// All 3 handlers must be set at the same time. /// typedef struct XalPlatformStorageEventHandlers2 { /// /// Write to storage handler. /// XalPlatformStorageWriteEventHandler2* write; /// /// Read from storage handler. /// XalPlatformStorageReadEventHandler2* read; /// /// Clear from storage handler. /// XalPlatformStorageClearEventHandler2* clear; /// /// Optional pointer to data used by the event handlers. /// void* context; } XalPlatformStorageEventHandlers2; //----------------------------------------------------------------------------- // Remote Connect (only used in generic mode, when configured for it) /// /// Show prompt for remote connect authentication event handler. /// /// Optional pointer to data used by the event handler. /// /// The user identifier that was passed to Xal when /// the user was added. /// The handle for this operation. /// The url to show in the prompt. /// The code to show in the prompt. /// Size of the qrCode buffer /// A pointer to a buffer containing the QR code for the url as PNG. /// /// /// This event is raised when Xal needs to prompt the user to perform the /// remote connect authentication process. /// The prompt ui should be displayed until /// XalPlatformRemoteConnectClosePromptEventHandler is called or it is dismissed /// by the user. /// /// Game should still render the URL and code that it got back in case the user can’t scan /// the QR code. The QR code will also not contain the code embedded into it. /// /// All arguments are owned by the caller (except context). /// typedef void (XalPlatformRemoteConnectShowPromptEventHandler3)( _In_opt_ void* context, _In_ uint32_t userIdentifier, _In_ XalPlatformOperation operation, _In_z_ char const* url, _In_z_ char const* code, _In_ size_t qrCodeSize, _In_reads_bytes_(qrCodeSize) void const* qrCode ); /// /// Close prompt for remote authentication event handler. /// /// Optional pointer to data used by the event handler. /// /// The user identifier that was passed to Xal when /// the user was added. /// The handle for this operation. /// /// /// This event is raised when the remote connect authentication process has been /// completed and the prompt is no longer necessary. /// This event will always be called with the same operation as a previous /// XalPlatformRemoteConnectShowPromptEventHandler event. /// /// All arguments are owned by the caller (except context). /// typedef void (XalPlatformRemoteConnectClosePromptEventHandler3)( _In_opt_ void* context, _In_ uint32_t userIdentifier, _In_ XalPlatformOperation operation ); /// /// Struct encapsulating the remote connect event handlers. /// /// /// Both handlers must be set at the same time. /// typedef struct XalPlatformRemoteConnectEventHandlers3 { /// /// Show the prompt handler. /// XalPlatformRemoteConnectShowPromptEventHandler3* show; /// /// Close the prompt handler. /// XalPlatformRemoteConnectClosePromptEventHandler3* close; /// /// Optional pointer to data used by the event handlers. /// void* context; } XalPlatformRemoteConnectEventHandlers3; //------------------------------------------------------------------------------ // Crypto (only used in generic mode) /// /// Struct representing a UUID. /// /// /// UUIDs should conform to RFC4122 (https://tools.ietf.org/html/rfc4122). /// typedef struct XalUuid { /// /// Stores the time_low field. /// uint32_t data1; /// /// Stores the time_mid field. /// uint16_t data2; /// /// Stores the time_hi_and_version field. /// uint16_t data3; /// /// Stores the clock_seq_hi_and_reserved field [0], clock_seq_low field [1], and node fields [2-7]. /// uint8_t data4[8]; } XalUuid; /// /// Generate UUID callback. /// /// Optional pointer to data used by the callback. /// /// The new UUID. /// The results of the client operation. /// /// This callback is invoked when Xal needs a new UUID. /// /// This callback will be invoked on a thread Xal is running on. /// typedef XalPlatformOperationResult (XalPlatformCryptoGenerateUuidCallback)( _In_opt_ void* context, _Out_ XalUuid* newUuid ); /// /// Generate random bytes callback. /// /// Optional pointer to data used by the callback. /// /// The number of random bytes needed. /// The buffer the random data should be written to. /// /// The results of the client operation. /// /// This callback is invoked when Xal needs random data, which should be /// generated using the platform cryptographic RNG. /// /// This callback will be invoked on a thread Xal is running on. /// typedef XalPlatformOperationResult (XalPlatformCryptoGenerateRandomBytesCallback)( _In_opt_ void* context, _In_ size_t bufferSize, _Out_writes_bytes_(bufferSize) uint8_t* buffer ); /// /// Struct encapsulating the crypto callbacks. /// /// /// Both callbacks must be set at the same time. /// typedef struct XalPlatformCryptoCallbacks { /// /// The UUID callback to be invoked. /// XalPlatformCryptoGenerateUuidCallback* uuid; /// /// The random bytes callback to be invoked. /// XalPlatformCryptoGenerateRandomBytesCallback* random; /// /// Optional pointer to data used by the callback. /// void* context; } XalPlatformCryptoCallbacks; //------------------------------------------------------------------------------ // Date & Time (only used in generic mode) /// /// Generate Unix timestamp callback. /// /// Optional pointer to data used by the callback. /// /// Number of seconds from the Unix Epoch /// (1970-01-01T00:00:00Z) in the UTC timezone. /// The fraction of second, in /// milliseconds. /// The results of the client operation. /// /// This callback is invoked when Xal needs a timestamp. /// The subsecond value is optional and can be set to 0. /// /// This callback will be invoked on a thread Xal is running on. /// typedef XalPlatformOperationResult (XalPlatformDateTimeGetUtcTimestampCallback)( _In_opt_ void* context, _Out_ int64_t* secondsFromUnixEpoch, _Out_ uint32_t* subsecondMilliseconds ); /// /// Convert a Unix timestamp into date/time components. /// /// Optional pointer to data used by the callback. /// /// Number of seconds from the Unix Epoch /// (1970-01-01T00:00:00Z) in the UTC timezone. /// The resulting date/time components. /// The results of the client operation. /// /// This callback is invoked when Xal needs to convert a timestamp into a date, /// the date produced should be in the UTC timezone. /// /// This callback will be invoked on a thread Xal is running on. /// typedef XalPlatformOperationResult (XalPlatformDateTimeTimestampToComponentsCallback)( _In_opt_ void* context, _In_ int64_t secondsFromUnixEpoch, _Out_ XalTimestampComponents* components ); /// /// Convert a date/time components into a Unix timestamp. /// /// Optional pointer to data used by the callback. /// /// The date/time components. /// Number of seconds from the Unix Epoch /// (1970-01-01T00:00:00Z) in the UTC timezone. /// The results of the client operation. /// /// This callback is invoked when Xal needs to convert a date into a timestamp, /// the date is always in the UTC timezone. /// /// This callback will be invoked on a thread Xal is running on. /// typedef XalPlatformOperationResult(XalPlatformDateTimeComponentsToTimestampCallback)( _In_opt_ void* context, _In_ XalTimestampComponents const* components, _Out_ int64_t* secondsFromUnixEpoch ); /// /// Struct encapsulating the date/time callbacks. /// /// /// All 3 callbacks must be set at the same time. /// struct XalPlatformDateTimeCallbacks { /// /// The get utc timestamp callback to be invoked. /// XalPlatformDateTimeGetUtcTimestampCallback* timestamp; /// /// The timestamp to components callback to be invoked. /// XalPlatformDateTimeTimestampToComponentsCallback* timestampToComponents; /// /// The components to timestamp callback to be invoked. /// XalPlatformDateTimeComponentsToTimestampCallback* componentsToTimestamp; /// /// Optional pointer to data used by the callback. /// void* context; }; //----------------------------------------------------------------------------- // Spop Prompt (only used in generic mode, when configured for it) /// /// Show prompt for SPOP operation event handler. /// /// Optional pointer to data used by the event handler. /// /// The user identifier that was passed to Xal when /// the user was added. /// The handle for this operation. /// Modern gamertag /// Modern gamertag suffix if there is one. /// /// /// This event is raised when the user is already signed in on a different /// device and therefore hits an SPOP veto. Xal needs to prompt the user to /// decide whether they want to sign-out the other session and sign-in on the /// current device. This will only occur as a response to a call to /// XalAddUserWithUiAsync that resulted in an SPOP veto. /// /// The modern gamertag suffix might be null for certain users. /// /// All arguments are owned by the caller (except context). /// typedef void (XalPlatformSpopPromptEventHandler)( _In_opt_ void* context, _In_ uint32_t userIdentifier, _In_ XalPlatformOperation operation, _In_z_ char const* modernGamertag, _In_opt_z_ char const* modernGamertagSuffix ); /// /// Enum defining the results of a client operation. /// typedef enum XalSpopOperationResult { /// /// User agreed to sign-out the existing session, and sign-in on the current device /// XalSpopOperationResult_SignInHere = 0, /// /// User selected a "switch account" option /// XalSpopOperationResult_SwitchAccount = 1, /// /// Canceled client operation. /// XalSpopOperationResult_Canceled = 2, /// /// Unrecoverable failure in client operation. /// XalSpopOperationResult_Failure = 3, } XalSpopOperationResult; } // Back compat handlers #if XAL_ENABLE_BACK_COMPAT_SHIMS //----------------------------------------------------------------------------- // Web view (ignored on OneCore platforms, optional on iOS and Android) /// /// Show url event handler. /// /// Optional pointer to data used by the event handler. /// /// Always null. /// The handle for this operation. /// The url to navigate to. /// The url that indicates the web flow is /// complete. /// Enum indicating the type of flow occurring. This /// flag dictates what correct behavior for the client is expected to be. /// /// /// /// This version of the handler is deprecated, please switch to /// XalPlatformWebShowUrlEventHandler2. /// /// This event is raised when Xal needs to show a web flow to the user, the /// client should navigate to startUrl and wait for a redirect to finalUrl. Once /// the redirect to finalUrl occurs the client should close the web ui and invoke /// XalPlatformWebShowUrlComplete passing the full redirect url. /// /// This handler is optional for Android and iOS platforms. If it is not set on /// these platforms, Xal will provide default browser behavior. On UWP, and XDK /// platforms, this handler is ignored. /// /// Depending on the value of showUrlType and the type of browser the client is /// using, different behavior is expected. See the definition for /// XalShowUrlType for more information. /// /// All arguments are owned by the caller (except context). /// typedef void (XalPlatformWebShowUrlEventHandler)( _In_opt_ void* context, _In_opt_ void* userContext, _In_ XalPlatformOperation operation, _In_z_ char const* startUrl, _In_z_ char const* finalUrl, _In_ XalShowUrlType showUrlType ); //----------------------------------------------------------------------------- // Storage (ignored on OneCore platforms, optional on iOS and Android) /// /// Write to storage event handler. /// /// Optional pointer to data used by the event handler. /// /// Always null. /// The handle for this operation. /// Identifies the data being written. /// The size (in bytes) of the data. /// The data to write. /// /// /// This version of the handler is deprecated, please switch to /// XalPlatformStorageWriteEventHandler2. /// /// This event is raised when Xal needs to write data to storage, the client /// should write the data and when done invoke XalPlatformStorageWriteComplete. /// /// All arguments are owned by the caller (except context). /// typedef void (XalPlatformStorageWriteEventHandler)( _In_opt_ void* context, _In_opt_ void* userContext, _In_ XalPlatformOperation operation, _In_z_ char const* key, _In_ size_t dataSize, _In_reads_bytes_(dataSize) void const* data ); /// /// Read from storage event handler. /// /// Optional pointer to data used by the event handler. /// /// Always null. /// The handle for this operation. /// Identifies the data being read. /// /// /// This version of the handler is deprecated, please switch to /// XalPlatformStorageReadEventHandler2. /// /// This event is raised when Xal needs to read data from storage, the client /// should read the data and when done invoke XalPlatformStorageReadComplete. /// If the key is not found, the client should complete with /// XalPlatformOperationResult_Success and no data. /// /// All arguments are owned by the caller (except context). /// typedef void (XalPlatformStorageReadEventHandler)( _In_opt_ void* context, _In_opt_ void* userContext, _In_ XalPlatformOperation operation, _In_z_ char const* key ); /// /// Clear from storage event handler. /// /// Optional pointer to data used by the event handler. /// /// Always null. /// The handle for this operation. /// Identifies the data being cleared. /// /// /// This version of the handler is deprecated, please switch to /// XalPlatformStorageClearEventHandler2. /// /// This event is raised when Xal needs to clear data from storage, the client /// should clear the data and when done invoke XalPlatformStorageClearComplete. /// /// All arguments are owned by the caller (except context). /// typedef void (XalPlatformStorageClearEventHandler)( _In_opt_ void* context, _In_opt_ void* userContext, _In_ XalPlatformOperation operation, _In_z_ char const* key ); /// /// Struct encapsulating the storage event handlers. /// /// /// This version of the api is deprecated, please switch to /// XalPlatformStorageEventHandlers2. /// /// All 3 handlers must be set at the same time. /// typedef struct XalPlatformStorageEventHandlers { /// /// Write to storage handler. /// XalPlatformStorageWriteEventHandler* write; /// /// Read from storage handler. /// XalPlatformStorageReadEventHandler* read; /// /// Clear from storage handler. /// XalPlatformStorageClearEventHandler* clear; /// /// Optional pointer to data used by the event handlers. /// void* context; } XalPlatformStorageEventHandlers; //----------------------------------------------------------------------------- // Remote Connect (only used in generic mode, when configured for it) /// /// Show prompt for remote connect authentication event handler. /// /// Optional pointer to data used by the event handler. /// /// The user identifier that was passed to Xal when /// the user was added. /// The handle for this operation. /// The url to show in the prompt. /// The code to show in the prompt. /// /// /// This version of the handler is deprecated, please switch to /// XalPlatformRemoteConnectShowPromptEventHandler3. /// /// This event is raised when Xal needs to prompt the user to perform the /// remote connect authentication process. /// The prompt ui should be displayed until /// XalPlatformRemoteConnectClosePromptEventHandler is called or it is dismissed /// by the user. /// /// All arguments are owned by the caller (except context). /// typedef void (XalPlatformRemoteConnectShowPromptEventHandler2)( _In_opt_ void* context, _In_ uint32_t userIdentifier, _In_ XalPlatformOperation operation, _In_z_ char const* url, _In_z_ char const* code ); /// /// Close prompt for remote authentication event handler. /// /// Optional pointer to data used by the event handler. /// /// The user identifier that was passed to Xal when /// the user was added. /// The handle for this operation. /// /// /// This version of the handler is deprecated, please switch to /// XalPlatformRemoteConnectClosePromptEventHandler3. /// /// This event is raised when the remote connect authentication process has been /// completed and the prompt is no longer necessary. /// This event will always be called with the same operation as a previous /// XalPlatformRemoteConnectShowPromptEventHandler event. /// /// All arguments are owned by the caller (except context). /// typedef void (XalPlatformRemoteConnectClosePromptEventHandler2)( _In_opt_ void* context, _In_ uint32_t userIdentifier, _In_ XalPlatformOperation operation ); /// /// Struct encapsulating the remote connect event handlers. /// /// /// This version of the api is deprecated, please switch to /// XalPlatformRemoteConnectEventHandlers3. /// /// Both handlers must be set at the same time. /// typedef struct XalPlatformRemoteConnectEventHandlers2 { /// /// Show the prompt handler. /// XalPlatformRemoteConnectShowPromptEventHandler2* show; /// /// Close the prompt handler. /// XalPlatformRemoteConnectClosePromptEventHandler2* close; /// /// Optional pointer to data used by the event handlers. /// void* context; } XalPlatformRemoteConnectEventHandlers2; /// /// Show prompt for remote connect authentication event handler. /// /// Optional pointer to data used by the event handler. /// /// Always null. /// The handle for this operation. /// The url to show in the prompt. /// The code to show in the prompt. /// /// /// This version of the handler is deprecated, please switch to /// XalPlatformRemoteConnectShowPromptEventHandler2. /// /// This event is raised when Xal needs to prompt the user to perform the /// remote connect authentication process. /// The prompt ui should be displayed until /// XalPlatformRemoteConnectClosePromptEventHandler is called or it is dismissed /// by the user. /// /// All arguments are owned by the caller (except context). /// typedef void (XalPlatformRemoteConnectShowPromptEventHandler)( _In_opt_ void* context, _In_opt_ void* userContext, _In_ XalPlatformOperation operation, _In_z_ char const* url, _In_z_ char const* code ); /// /// Close prompt for remote authentication event handler. /// /// Optional pointer to data used by the event handler. /// /// Always null. /// The handle for this operation. /// /// /// This version of the handler is deprecated, please switch to /// XalPlatformRemoteConnectClosePromptEventHandler2. /// /// This event is raised when the remote connect authentication process has been /// completed and the prompt is no longer necessary. /// This event will always be called with the same operation as a previous /// XalPlatformRemoteConnectShowPromptEventHandler event. /// /// All arguments are owned by the caller (except context). /// typedef void (XalPlatformRemoteConnectClosePromptEventHandler)( _In_opt_ void* context, _In_opt_ void* userContext, _In_ XalPlatformOperation operation ); /// /// Struct encapsulating the remote connect event handlers. /// /// /// This version of the api is deprecated, please switch to /// XalPlatformRemoteConnectEventHandlers2. /// /// Both handlers must be set at the same time. /// typedef struct XalPlatformRemoteConnectEventHandlers { /// /// Show the prompt handler. /// XalPlatformRemoteConnectShowPromptEventHandler* show; /// /// Close the prompt handler. /// XalPlatformRemoteConnectClosePromptEventHandler* close; /// /// Optional pointer to data used by the event handlers. /// void* context; } XalPlatformRemoteConnectEventHandlers; #endif ================================================ FILE: External/Xal/Source/Xal/Include/Xal/xal_types.h ================================================ #pragma once #if !defined(__cplusplus) #error C++11 required #endif #include #include #if HC_PLATFORM_HEADER_OVERRIDE #ifdef XAL_PLATFORM_TYPES_PATH #include XAL_PLATFORM_TYPES_PATH #else #error Platform header override is enabled but XAL_PLATFORM_TYPES_PATH is undefined #endif #elif HC_PLATFORM == HC_PLATFORM_WIN32 #include #elif HC_PLATFORM == HC_PLATFORM_UWP #include #elif HC_PLATFORM == HC_PLATFORM_XDK #include #elif HC_PLATFORM == HC_PLATFORM_GDK #include #elif HC_PLATFORM == HC_PLATFORM_ANDROID #include #elif HC_PLATFORM_IS_APPLE #include #elif HC_PLATFORM_IS_EXTERNAL #include #else #error Xal does not recognize this platform, do you need to set HC_PLATFORM = HC_PLATFORM_GENERIC? #endif #ifndef XAL_OS_IMPL #define XAL_OS_IMPL 0 #endif #ifndef XAL_ENABLE_BACK_COMPAT_SHIMS #define XAL_ENABLE_BACK_COMPAT_SHIMS 1 #endif #ifndef XAL_OS_ERRORS #define XAL_OS_ERRORS 0 #endif #ifndef XAL_CUSTOM_ALLOCATOR #define XAL_CUSTOM_ALLOCATOR 1 #endif extern "C" { //------------------------------------------------------------------------------ // Results //------------------------------------------------------------------------------ #define E_XAL_NOTINITIALIZED MAKE_E_HC(0x5100L) // 0x89235100 #define E_XAL_ALREADYINITIALIZED MAKE_E_HC(0x5101L) // 0x89235101 #define E_XAL_USERSETNOTEMPTY MAKE_E_HC(0x5102L) // 0x89235102 #define E_XAL_USERSETFULL MAKE_E_HC(0x5103L) // 0x89235103 #define E_XAL_USERSIGNEDOUT MAKE_E_HC(0x5104L) // 0x89235104 #define E_XAL_DUPLICATEDUSER MAKE_E_HC(0x5105L) // 0x89235105 #define E_XAL_NETWORK MAKE_E_HC(0x5106L) // 0x89235106 #define E_XAL_CLIENTERROR MAKE_E_HC(0x5107L) // 0x89235107 #define E_XAL_UIREQUIRED MAKE_E_HC(0x5108L) // 0x89235108 #define E_XAL_HANDLERALREADYREGISTERED MAKE_E_HC(0x5109L) // 0x89235109 #define E_XAL_UNEXPECTEDUSERSIGNEDIN MAKE_E_HC(0x510AL) // 0x8923510A #define E_XAL_NOTATTACHEDTOJVM MAKE_E_HC(0x510BL) // 0x8923510B #define E_XAL_DEVICEUSER MAKE_E_HC(0x510CL) // 0x8923510C #define E_XAL_DEFERRALNOTAVAILABLE MAKE_E_HC(0x510DL) // 0x8923510D #define E_XAL_MISSINGPLATFORMEVENTHANDLER MAKE_E_HC(0x510EL) // 0x8923510E #define E_XAL_USERNOTFOUND MAKE_E_HC(0x510FL) // 0x8923510F #define E_XAL_NOTOKENREQUIRED MAKE_E_HC(0x5110L) // 0x89235110 #define E_XAL_NODEFAULTUSER MAKE_E_HC(0x5111L) // 0x89235111 #define E_XAL_FAILEDTORESOLVE MAKE_E_HC(0x5112L) // 0x89235112 #define E_XAL_NOACCOUNTPROVIDER MAKE_E_HC(0x5113L) // 0x89235113 #define E_XAL_MISMATCHEDTITLEANDCLIENTIDS MAKE_E_HC(0x5114L) // 0x89235114 #define E_XAL_INVALIDAPPCONFIGURATION MAKE_E_HC(0x5115L) // 0x89235115 #define E_XAL_MALFORMEDCLIENTID MAKE_E_HC(0x5116L) // 0x89235116 #define E_XAL_MISSINGCLIENTID MAKE_E_HC(0x5117L) // 0x89235117 #define E_XAL_MISSINGTITLEID MAKE_E_HC(0x5118L) // 0x89235118 #define E_XAL_CONTENT_ISOLATION MAKE_E_HC(0x5119L) // 0x89235119 #define E_XAL_SANDBOX_NOT_ALLOWED MAKE_E_HC(0x511AL) // 0x8923511A #define E_XAL_GAMEWINDOWNOTFOREGROUND MAKE_E_HC(0x511BL) // 0x8923511B #define E_XAL_UNLISTEDCONSENT MAKE_E_HC(0x511CL) // 0x8923511C #define E_XAL_CONSENTNOTAPPLICABLE MAKE_E_HC(0x511DL) // 0x8923511D #define E_XAL_NO_SIGNED_IN_USER_FOUND MAKE_E_HC(0x511EL) // 0x8923511E // E_XAL_INTERNAL_* values should never be returned to callers of XAL. #define E_XAL_INTERNAL_SWITCHUSER MAKE_E_HC(0x5171L) // 0x89235171 #define E_XAL_INTERNAL_NOUSERFOUND MAKE_E_HC(0x5172L) // 0x89235172 #define E_XAL_INTERNAL_TOOMANYCACHEDUSERS MAKE_E_HC(0x5173L) // 0x89235173 #define E_XAL_INTERNAL_BADUSERTOKEN MAKE_E_HC(0x5174L) // 0x89235174 #define E_XAL_INTERNAL_BADDEVICEIDENTITY MAKE_E_HC(0x5175L) // 0x89235175 #define E_XAL_INTERNAL_UNAUTHORIZED MAKE_E_HC(0x5176L) // 0x89235176 #define E_XAL_INTERNAL_NODISPLAYCLAIMSFOUND MAKE_E_HC(0x5177L) // 0x89235177 // GDK has system definitions for some error values #if XAL_OS_ERRORS #undef E_XAL_USERSETFULL #undef E_XAL_USERSIGNEDOUT #undef E_XAL_UIREQUIRED #undef E_XAL_DEFERRALNOTAVAILABLE #undef E_XAL_USERNOTFOUND #undef E_XAL_NOTOKENREQUIRED #undef E_XAL_NODEFAULTUSER #undef E_XAL_FAILEDTORESOLVE #undef E_XAL_MISMATCHEDTITLEANDCLIENTIDS #undef E_XAL_INVALIDAPPCONFIGURATION #undef E_XAL_MALFORMEDCLIENTID #undef E_XAL_MISSINGCLIENTID #undef E_XAL_MISSINGTITLEID #undef E_XAL_CONTENT_ISOLATION #undef E_XAL_SANDBOX_NOT_ALLOWED #undef E_XAL_GAMEWINDOWNOTFOREGROUND #define E_XAL_USERSETFULL E_GAMEUSER_MAX_USERS_ADDED // 0x89245100 #define E_XAL_USERSIGNEDOUT E_GAMEUSER_SIGNED_OUT // 0x89245101 #define E_XAL_UIREQUIRED E_GAMEUSER_RESOLVE_USER_ISSUE_REQUIRED // 0x89245102 #define E_XAL_DEFERRALNOTAVAILABLE E_GAMEUSER_DEFERRAL_NOT_AVAILABLE // 0x89245103 #define E_XAL_USERNOTFOUND E_GAMEUSER_USER_NOT_FOUND // 0x89245104 #define E_XAL_NOTOKENREQUIRED E_GAMEUSER_NO_TOKEN_REQUIRED // 0x89245105 #define E_XAL_NODEFAULTUSER E_GAMEUSER_NO_DEFAULT_USER // 0x89245106 #define E_XAL_FAILEDTORESOLVE E_GAMEUSER_FAILED_TO_RESOLVE // 0x89245107 #define E_XAL_MISSINGTITLEID E_GAMEUSER_NO_TITLE_ID // 0x89245108 #define E_XAL_INVALIDAPPCONFIGURATION E_GAMEUSER_INVALID_APP_CONFIGURATION // 0x89245112 #define E_XAL_MALFORMEDCLIENTID E_GAMEUSER_MALFORMED_MSAAPPID // 0x89245113 #define E_XAL_MISMATCHEDTITLEANDCLIENTIDS E_GAMEUSER_INCONSISTENT_MSAAPPID_AND_TITLEID // 0x89245114 #define E_XAL_MISSINGCLIENTID E_GAMEUSER_NO_MSAAPPID // 0x89245115 #define E_XAL_CONTENT_ISOLATION XO_E_CONTENT_ISOLATION // 0x8015DC12 #define E_XAL_SANDBOX_NOT_ALLOWED XO_E_SANDBOX_NOT_ALLOWED // 0x8015DC19 #define E_XAL_GAMEWINDOWNOTFOREGROUND E_GAMERUNTIME_WINDOW_NOT_FOREGROUND // 0x89240103 #endif //------------------------------------------------------------------------------ // Xal init flags //------------------------------------------------------------------------------ /// /// Flag to instruct Xal to use MPOP behavior when adding users. MPOP behavior /// allows Xal to rely on cached Xtokens when adding users so refreshing is not /// required. This lowers the time it takes for add user calls to finish /// because less network traffic is needed, but it implies that SPOP vetoes /// will not be checked prior to returning a user. /// /// /// This flag is supported on Win32, Android, iOS, Mac, and generic device types. /// uint32_t const XAL_INIT_OPTION_USE_MPOP_BEHAVIOR = 1u << 2; /// /// Flag to instruct Xal to use the modern Gamertag features during sign up. /// /// /// This flag is supported on Win32, UWP, Android, iOS, Mac, and generic device types. /// uint32_t const XAL_INIT_OPTION_REQUEST_MODERN_GAMERTAG_FLOW = 1u << 26; //------------------------------------------------------------------------------ // Privileges //------------------------------------------------------------------------------ /// /// Enum defining the values for Xbox Live privileges /// typedef enum XalPrivilege { /// The user can play with people outside of Xbox Live XalPrivilege_CrossPlay = 185, /// Create/join/participate in Clubs XalPrivilege_Clubs = 188, /// Create/join non interactive multiplayer sessions XalPrivilege_Sessions = 189, /// Broadcast live gameplay XalPrivilege_BroadCast = 190, /// Change settings to show real name XalPrivilege_ManageProfilePrivacy = 196, /// Upload GameDVR XalPrivilege_GameDvr = 198, /// Join parties XalPrivilege_MultiplayerParties = 203, /// Use Voice Chat in game or in parties XalPrivilege_CommsInGameVoice = 205, /// Allocate cloud compute resources for their session XalPrivilege_CloudManageSession = 207, /// Join cloud compute sessions XalPrivilege_CloudJoinSession = 208, /// Save games on the cloud XalPrivilege_CloudSavedGames = 209, /// Share progress to social networks XalPrivilege_SocialNetworkSharing = 220, /// Access user generated content in game XalPrivilege_Ugc = 247, /// Use real time voice and text communication with users in their friends list XalPrivilege_CommsFriendsOnly = 251, /// Use real time voice and text communication with all users XalPrivilege_Comms = 252, /// Join multiplayer sessions XalPrivilege_Multiplayer = 254, /// Add friends / people to follow XalPrivilege_AddFriends = 255, } XalPrivilege; //------------------------------------------------------------------------------ // User api types //------------------------------------------------------------------------------ /// /// Handle to a user object. All operations on a user object are threadsafe. /// /// /// User objects returned by Xal as out parameters already have had their /// reference count incremented, so XalUserRelease should be called when the /// caller is done with them. /// User objects passed as arguments to callbacks did not have their reference /// count incremented, the callback should call XalUserDuplicateHandle if they /// wish to hold onto the object (and XalUserCloseHandle when they are done). /// #if !XAL_OS_IMPL typedef struct XalUser* XalUserHandle; #else // XalUserHandle is defined in the platform specific header. // That header is included at the top of this file #endif /// /// Struct holding local user ID data. /// #if !XAL_OS_IMPL typedef struct XalUserLocalId { /// The local user ID uint64_t value; } XalUserLocalId; #else // XalUserLocalId is defined in the platform specific header. // That header is included at the top of this file #endif /// /// Enum defining the possible states for a user object. /// typedef enum XalUserState { /// XAL signed in state XalUserState_SignedIn = 0, /// XAL signing out state XalUserState_SigningOut = 1, /// XAL signed out state XalUserState_SignedOut = 2, } XalUserState; /// /// Enum defining the possible gamer picture sizes. /// typedef enum XalGamerPictureSize { /// 64x64 XalGamerPictureSize_Small = 0, /// 208x208 XalGamerPictureSize_Medium = 1, /// 424x424 XalGamerPictureSize_Large = 2, /// 1080x1080 XalGamerPictureSize_ExtraLarge = 3, } XalGamerPictureSize; /// /// Enum defining the possible consent states. /// /// /// The different states provide additional information for the client to adapt /// the UX, but only Opted In can be used as a positive signal, the rest should /// all be treated as Opted Out. /// typedef enum XalConsentState { /// Unkown consent state due to query failure /// Should treat as Opted Out XalConsentState_QueryFailure = 0, /// Model does not exist or does not apply for user /// Should treat as Opted Out XalConsentState_NotApplicable = 1, /// User has been opted out XalConsentState_OptedOut = 2, /// User has been opted in XalConsentState_OptedIn = 3, } XalConsentState; /// /// Enum defining the various gamertag components. /// typedef enum XalGamertagComponent { /// The classic gamertag XalGamertagComponent_Classic = 0, /// The modern gamertag without the suffix XalGamertagComponent_Modern = 1, /// The modern gamertag suffix if the user has one (otherwise empty) XalGamertagComponent_ModernSuffix = 2, /// The combined modern gamertag with the suffix (if the suffix exists) XalGamertagComponent_UniqueModern = 3, } XalGamertagComponent; /// /// Enum defining the various age groups. /// typedef enum XalAgeGroup { /// Unknown age group XalAgeGroup_Unknown = 0, /// Child age group XalAgeGroup_Child = 1, /// Teen age group XalAgeGroup_Teen = 2, /// Adult age group XalAgeGroup_Adult = 3, } XalAgeGroup; /// /// Enum defining the various reasons for a privilege being denied. /// typedef enum XalPrivilegeCheckDenyReasons { /// None XalPrivilegeCheckDenyReasons_None = 0, /// Purchase required XalPrivilegeCheckDenyReasons_PurchaseRequired = 1, /// Restricted XalPrivilegeCheckDenyReasons_Restricted = 2, /// Banned XalPrivilegeCheckDenyReasons_Banned = 3, /// Unknown XalPrivilegeCheckDenyReasons_Unknown = 0xFFFFFFFF } XalPrivilegeCheckDenyReasons; //----------------------------------------------------------------------------- // Get token and signature /// /// Struct that represents an HTTP header. /// typedef struct XalHttpHeader { /// HTTP header name _Field_z_ char const* name; /// HTTP header value _Field_z_ char const* value; } XalHttpHeader; /// /// Struct that encapsulates the arguments for /// XalUserGetTokenAndSignatureSilentlyAsync. /// /// /// Xal will copy the data before XalUserGetTokenAndSignatureSilentlyAsync /// returns. /// typedef struct XalUserGetTokenAndSignatureArgs { /// /// The method for the request /// _Field_z_ char const* method; /// /// The url to get the token and to signature for (fully escaped). /// _Field_z_ char const* url; /// /// The number of headers that will be added to the HTTP request. /// uint32_t headerCount; /// /// The array of headers that will be added to the HTTP request. /// _Field_size_(headerCount) XalHttpHeader const* headers; /// /// The size of the request body in bytes. /// size_t bodySize; /// /// The request body. /// _Field_size_bytes_(bodySize) uint8_t const* body; /// /// Ignore cached tokens. /// /// /// This flag should only be set if an http request using a token and /// signature failed with a 401 error. In that case the entire call should /// be retried after getting a new token and signature using this flag. /// bool forceRefresh; /// /// Get a token for all users. /// bool allUsers; /// /// Get a token with or without TitleIdentity. /// bool ignoreTitleIdentity; } XalUserGetTokenAndSignatureArgs; /// /// Struct that encapsulates the results for /// XalUserGetTokenAndSignatureSilentlyAsync. /// typedef struct XalUserGetTokenAndSignatureData { /// /// The size of the Token string in bytes including the null terminator. /// size_t tokenSize; /// /// The size of the Signature string in bytes including the null terminator. /// size_t signatureSize; /// /// The token for the request, if necessary, as a null terminated string. /// _Field_size_opt_(tokenSize) _Null_terminated_ char const* token; /// /// The signature for the request, if necessary, as a null terminated /// string. /// _Field_size_opt_(signatureSize) _Null_terminated_ char const* signature; } XalUserGetTokenAndSignatureData; //----------------------------------------------------------------------------- // Events /// /// Enum describing the possible types of changes to a user's details. /// #if !XAL_OS_IMPL typedef enum XalUserChangeType { /// Changed to signed in XalUserChange_SignedInAgain = 0, /// Changed to signing out XalUserChange_SigningOut = 1, /// Changed to signed out XalUserChange_SignedOut = 2, /// Changed gamertag XalUserChange_Gamertag = 3, /// Changed gamer picture XalUserChange_GamerPicture = 4, /// Changed privileges XalUserChange_Privileges = 5, } XalUserChangeType; #else // XalUserChangeType is defined in the platform specific header. // That header is included at the top of this file #endif /// /// A token returned when registering a callback to identify the registration. This token /// is later used to unregister the callback. /// struct XalRegistrationToken { /// The registration token uint64_t token; }; /// /// Handle to a deferral object. /// #if !XAL_OS_IMPL typedef struct XalSignoutDeferral* XalSignoutDeferralHandle; #else // XalSignoutDeferralHandle is defined in the platform specific header. // That header is included at the top of this file #endif //------------------------------------------------------------------------------ // Date & time /// /// This struct represents a date. /// /// /// The date is always in the Gregorian calendar and in the UTC timezone. /// typedef struct XalTimestampComponents { /// /// The year. /// uint16_t year; /// /// The month [1, 12]. /// uint8_t month; /// /// The day of the month [1, 31]. /// uint8_t day; /// /// The hour in the day [0, 24). /// uint8_t hour; /// /// The minute in the hour [0, 60). /// uint8_t minute; /// /// The second in the minute [0, 60). /// uint8_t second; } XalTimestampComponents; } ================================================ FILE: External/Xal/Source/Xal/Include/Xal/xal_user.h ================================================ #pragma once #if !defined(__cplusplus) #error C++11 required #endif #include #include extern "C" { //----------------------------------------------------------------------------- // User Api //----------------------------------------------------------------------------- /// /// Increments the reference count on the user object. /// /// The user handle. /// The new user handle. /// Result code for this API operation. Possible values are S_OK, E_INVALIDARG, or E_FAIL. STDAPI XalUserDuplicateHandle( _In_ XalUserHandle user, _Out_ XalUserHandle* duplicatedUser ) noexcept; /// /// Decrements the reference count on the user object. /// /// The user object /// STDAPI_(void) XalUserCloseHandle( _In_ XalUserHandle user ) noexcept; /// /// Compares 2 user handler. /// /// The first user. /// The second user. /// /// 0 if the two handles refer to the same xbox live identity, -1 if user1 /// identity is "less" than user2, 1 if user1 identity "greater" than user2. /// /// /// User identity ordering is arbitrary, but sutiable for sorting. /// STDAPI_(int32_t) XalCompareUsers( _In_ XalUserHandle user1, _In_ XalUserHandle user2 ) noexcept; //----------------------------------------------------------------------------- // User properties /// /// Returns the Xbox Live User ID (XUID) of the user. /// /// The user object. /// The Xbox Live User ID (XUID) of the user. /// Result code for this API operation. Possible values are S_OK, E_INVALIDARG, or E_FAIL. STDAPI XalUserGetId( // TODO any valid error scenarios? local users, consent _In_ XalUserHandle user, _Out_ uint64_t* id ) noexcept; /// /// Gets the local id of the user. /// /// The user object. /// The local id of the user. /// Result code for this API operation. Possible values are S_OK, E_INVALIDARG, or E_FAIL. STDAPI XalUserGetLocalId( _In_ XalUserHandle user, _Out_ XalUserLocalId* localId ) noexcept; /// /// Returns a boolean indicating if the user is the device user. /// /// The user object. /// STDAPI_(bool) XalUserIsDevice( _In_ XalUserHandle user ) noexcept; /// /// Returns a boolean indicating if the user is a guest. /// /// The user object. /// STDAPI_(bool) XalUserIsGuest( _In_ XalUserHandle user ) noexcept; /// /// Returns the sign-in state of the user. /// /// The user object. /// The sign-in state of the user /// . /// Result code for this API operation. STDAPI XalUserGetState( _In_ XalUserHandle user, _Out_ XalUserState* state ) noexcept; /// /// Returns the size of the buffer needed to store the gamertag string. /// /// The user object. /// The component of the gamertag to get the size of. /// The size of the buffer needed to store the gamertag string /// /// If XalGamertagComponent_Modern or XalGamertagComponent_UniqueModern are /// specified but not available on the platform, this function will execute for /// XalGamertagComponent_Classic instead. XalGamertagComponent_Suffix will be /// empty if the modern components are unavailable. /// STDAPI_(size_t) XalUserGetGamertagSize( _In_ XalUserHandle user, _In_ XalGamertagComponent component ) noexcept; /// /// Returns the gamertag of the user. /// /// The user object. /// The component of the gamertag to get. /// The size in bytes of the gamertag buffer. /// Should be the value returned by XalUserGetGamertagSize. /// The buffer the gamertag will be written to. /// The number of bytes used in the buffer including /// the null terminator. /// Result code for this API operation. Possible values are S_OK, E_INVALIDARG, or E_FAIL. /// /// If XalGamertagComponent_Modern or XalGamertagComponent_UniqueModern are /// specified but not available on the platform, this function will execute for /// XalGamertagComponent_Classic instead. XalGamertagComponent_Suffix will be /// empty if the modern components are unavailable. /// STDAPI XalUserGetGamertag( _In_ XalUserHandle user, _In_ XalGamertagComponent component, _In_ size_t gamertagSize, _Out_writes_(gamertagSize) char* gamertag, _Out_opt_ size_t* gamertagUsed ) noexcept; /// /// Gets the gamer picture for the user as a png in memory buffer. /// /// The user object. /// The size wanted. /// The AsyncBlock for this operation. /// Result code for this API operation. Possible values are S_OK or E_FAIL. STDAPI XalUserGetGamerPictureAsync( _In_ XalUserHandle user, _In_ XalGamerPictureSize pictureSize, _In_ XAsyncBlock* async ) noexcept; /// /// Gets the size in bytes of gamer picture buffer. /// /// The AsyncBlock for this operation. /// The size in bytes for the result buffer. /// Result code for this API operation. STDAPI XalUserGetGamerPictureResultSize( _In_ XAsyncBlock* async, _Out_ size_t* bufferSize ) noexcept; /// /// Gets the results of a successful XalUserGetGamerPictureAsync operation. /// /// The AsyncBlock for this operation. /// The size of the gamer picture buffer. /// The gamer picture png data. /// Result code for this API operation. STDAPI XalUserGetGamerPictureResult( _In_ XAsyncBlock* async, _In_ size_t bufferSize, _Out_writes_(bufferSize) void* buffer ) noexcept; /// /// Returns the age group of the user. /// /// The user object. /// The age group. /// Result code for this API operation. Possible values are S_OK, E_INVALIDARG, or E_FAIL. STDAPI XalUserGetAgeGroup( _In_ XalUserHandle user, _Out_ XalAgeGroup* ageGroup ) noexcept; /// /// Checks if the user has the given privilege. /// /// The user object. /// The privilege to check. /// true if the user has the privilege, false /// otherwise. /// Bitmask of the various reasons why the user could be /// denied a privilege. If the user has the privilege it will always be /// XAL_PRIVILEGE_CHECK_DENY_REASON_NONE. /// Result code for this API operation. Possible values are S_OK, E_XAL_NOTINITIALIZED, or E_FAIL. STDAPI XalUserCheckPrivilege( _In_ XalUserHandle user, _In_ XalPrivilege privilege, _Out_ bool* hasPrivilege, _Out_opt_ XalPrivilegeCheckDenyReasons* reasons ) noexcept; /// /// Checks if the current platform supports resolving missing privileges. /// /// STDAPI_(bool) XalUserResolvePrivilegeWithUiIsPresent() noexcept; /// /// Shows ui explaining why the user is missing the given privilege and /// allows acquiring it. /// /// The user object. /// The privilege to check. /// The AsyncBlock for this operation. /// Result code for this API operation. STDAPI XalUserResolveUserPrivilegeWithUiAsync( _In_ XalUserHandle user, _In_ XalPrivilege privilege, _In_ XAsyncBlock* async ) noexcept; /// /// Get the result of a given XalUserResolveUserPrivilegeWithUiAsync operation. /// /// The AsyncBlock for this operation. /// Result code for this API operation. STDAPI XalUserResolveUserPrivilegeWithUiResult( _In_ XAsyncBlock* async ) noexcept; //----------------------------------------------------------------------------- // Get token and signature /// /// Gets the appropriate token and signature for an HTTP request. /// /// The user the token and signature are for. /// The HTTP request details. /// The AsyncBlock for this operation. /// . /// Result code for this API operation. STDAPI XalUserGetTokenAndSignatureSilentlyAsync( _In_ XalUserHandle user, _In_ XalUserGetTokenAndSignatureArgs const* args, _In_ XAsyncBlock* async ) noexcept; /// /// Gets the size in bytes of the token and signature buffers. /// /// The AsyncBlock for this operation. /// The size in bytes for the result buffer. /// Result code for this API operation. STDAPI XalUserGetTokenAndSignatureSilentlyResultSize( _In_ XAsyncBlock* async, _Out_ size_t* bufferSize ) noexcept; /// /// Gets the results of a successful XalUserGetTokenAndSignatureSilentlyAsync operation. /// /// The AsyncBlock for this operation. /// The size of the buffer for the result object. /// Byte buffer used for result value and its fields. /// Pointer to the result object. /// The number of bytes in the provided buffer that were used. /// Result code for this API operation. /// /// result is a pointer within buffer and does not need to be freed separately. /// STDAPI XalUserGetTokenAndSignatureSilentlyResult( _In_ XAsyncBlock* async, _In_ size_t bufferSize, _Out_writes_bytes_to_(bufferSize, *bufferUsed) void* buffer, _Outptr_ XalUserGetTokenAndSignatureData** result, _Out_opt_ size_t* bufferUsed ) noexcept; /// /// This function will show the ui required to resolve certain errors in /// XalUserGetTokenAndSignatureSilentlyAsync. /// /// The user the token and signature are for. /// The url of the request that failed. /// The AsyncBlock for this operation. /// Result code for this API operation. /// /// This function should only be called after a call to /// XalUserGetTokenAndSignatureSilentlyAsync fails with E_XAL_UIREQUIRED. /// STDAPI XalUserResolveIssueWithUiAsync( _In_ XalUserHandle user, _In_opt_z_ char const* url, _In_ XAsyncBlock* async ) noexcept; /// /// Get the result of a given XalUserResolveIssueWithUiAsync operation. /// /// The AsyncBlock for this operation. /// Result code for this API operation. STDAPI XalUserResolveIssueWithUiResult( _In_ XAsyncBlock* async ) noexcept; //----------------------------------------------------------------------------- // UCS consent /// /// Checks the state of the given UCS consent for the user. /// /// The user object. /// The UCS consent model name. /// The state of the consent. /// Result code for this API operation. STDAPI XalUserCheckUcsConsent( _In_ XalUserHandle user, _In_z_ char const* consentModelName, _Out_ XalConsentState* consentState ) noexcept; /// /// Shows ui explaining why the user is missing the given privilege and /// allows acquiring it. /// /// The user object. /// The UCS consent model name. /// The AsyncBlock for this operation. /// Result code for this API operation. STDAPI XalUserManageUcsConsentWithUiAsync( _In_ XalUserHandle user, _In_z_ char const* consentModelName, _In_ XAsyncBlock* async ) noexcept; /// /// Get the result of a given XalUserManageUcsConsentWithUiAsync operation. /// /// The AsyncBlock for this operation. /// The state of the consent. /// Result code for this API operation. STDAPI XalUserManageUcsConsentWithUiResult( _In_ XAsyncBlock* async, _Out_ XalConsentState* consentState ) noexcept; //----------------------------------------------------------------------------- // Events /// /// User detail change event handler. /// /// Optional pointer to data used by the event handler. /// /// The local id of the user that changed. /// The type of change. /// #if !XAL_OS_IMPL typedef void (XalUserChangeEventHandler)( _In_opt_ void* context, _In_ XalUserLocalId userId, _In_ XalUserChangeType change ); #else // XalUserChangeEventHandler is defined in the platform specific header. // That header is included in xal_types.h #endif /// /// Register the event handler for user detail changes. /// /// The async queue the callback should be invoked on. /// Optional pointer to data used by the event handler. /// The event handler, . /// The token for unregistering this callback /// Result code for this API operation. Possible values are S_OK, E_XAL_NOTINITIALIZED, or E_FAIL. STDAPI XalUserRegisterChangeEventHandler( _In_opt_ XTaskQueueHandle queue, _In_opt_ void* context, _In_ XalUserChangeEventHandler* handler, _Out_ XalRegistrationToken* token ) noexcept; /// /// Unregisters a previously registered callback. /// /// The token returned from /// XalUserRegisterChangeEventHandler. /// STDAPI_(void) XalUserUnregisterChangeEventHandler( _In_ XalRegistrationToken token ) noexcept; /// /// Get a signout deferral. /// /// The deferral handle. /// Result code for this API operation. /// /// May only be called from within a XalUserChangeEventHandler during a /// XalUserChange_SigningOut event. The signout process will be halted until /// the deferral handle is closed (or a timeout is reached). /// /// May fail with E_XAL_DEFERRALNOTAVAILABLE. /// STDAPI XalUserGetSignoutDeferral( _Out_ XalSignoutDeferralHandle* deferral ) noexcept; /// /// Closes a signout deferral. /// /// The deferral handle. /// STDAPI_(void) XalUserCloseSignoutDeferral( _In_ XalSignoutDeferralHandle deferral ) noexcept; } ================================================ FILE: External/Xal/Source/Xal/Include/Xal/xal_uwp.h ================================================ #pragma once #if !defined(__cplusplus) #error C++11 required #endif extern "C" { //------------------------------------------------------------------------------ // Uwp types //------------------------------------------------------------------------------ /// /// Struct that encapsulates the Uwp specific arguments for Xal. /// typedef struct XalUwpArgs { /// /// Xbox Live title id. /// uint32_t titleId; /// /// The package family name. /// _Field_z_ char const* packageFamilyName; /// /// A correlation vector string for XAL to use as a base. XAL will extend /// this prior to using it. This argument is optional. /// _Field_z_ char const* correlationVector; /// /// Xal configuration flags. /// uint32_t flags; /// /// The user that launched the application, as provided by Application::OnLaunched() /// /// /// If left null, Xal will bring up the User Picker on Xbox when signing in with UI. /// This field has no effect on the PC sign in flow. /// Windows::System::User^ launchUser; /// /// The hwnd of the window that launched the sign in request /// /// /// If a centennial build, this is required for identifying the main window that /// launched a sign in request. Otherwise this is unused. /// HWND mainWindow; /// /// Optional Cobrand ID for MSA /// _Field_z_ char const* cobrandId; } XalUwpArgs; typedef XalUwpArgs XalInitArgs; #define XAL_PLATFORM "UWP" } ================================================ FILE: External/Xal/Source/Xal/Include/Xal/xal_uwp_user.h ================================================ #pragma once #if !defined(__cplusplus) #error C++11 required #endif #include #include extern "C" { //------------------------------------------------------------------------------ // Uwp user functions //------------------------------------------------------------------------------ STDAPI XalUserGetPlatformWebAccount( _In_ XalUserHandle user, _Out_ Windows::Security::Credentials::WebAccount^* webAccount ) noexcept; /// /// Adds the given system user. /// /// The system user. /// The AsyncBlock for this operation. /// Result code for this API operation. STDAPI XalAddUwpSystemUserSilentAsync( _In_ Windows::System::User^ user, _In_ XAsyncBlock* async ) noexcept; } ================================================ FILE: External/Xal/Source/Xal/Include/Xal/xal_version.h ================================================ #pragma once #if !defined(__cplusplus) #error C++11 required #endif extern "C" { //----------------------------------------------------------------------------- // Version //----------------------------------------------------------------------------- /// /// The macro definition of the current library version. It is a '.' delimited /// string where the fields have the following meanings. They are in order from /// left to right: /// YYYY Release year /// MM Release month /// YYYYMMDD Date string describing the date the build was created /// rrr QFE number (000 indicates base release) /// #define XAL_VERSION "2025.07.20250718.000" } ================================================ FILE: External/Xal/Source/Xal/Include/Xal/xal_win32.h ================================================ #pragma once #if !defined(__cplusplus) #error C++11 required #endif extern "C" { //------------------------------------------------------------------------------ // Win32 types //------------------------------------------------------------------------------ /// /// Struct that encapsulates the Win32 specific arguments for Xal. /// typedef struct XalWin32Args { /// /// MSA client id. /// _Field_z_ char const* clientId; /// /// Xbox Live title id. /// uint32_t titleId; /// /// Xbox Live sandbox. /// /// /// If Xal detects a sandbox key in the registry it will use that value and /// ignore the value passed in here. /// _Field_z_ char const* sandbox; /// /// A bool indicating whether Xal can send diagnostic telemetry. /// Setting this to true indicates to Xal that it does not have user consent /// to report data about any crashes or errors it encounters during use. /// If this variable is set to false, Xal assumes it can report this data. /// bool disableDiagnosticTelemetry; /// /// A correlation vector string for XAL to use as a base. XAL will extend /// this prior to using it. This argument is optional. /// _Field_z_ char const* correlationVector; /// /// Xal configuration flags. /// uint32_t flags; /// /// The number of consents present in the ThirdPartyConsents array. /// uint32_t thirdPartyConsentCount; /// /// An optional list of consent requests to access Xbox Live services. /// _Field_size_(thirdPartyConsentCount) char const** thirdPartyConsents; /// /// Win32 optional custom redirect URI. /// _Field_z_ char const* redirectUri; /// /// The number of consents present in the ucsConsents array /// uint32_t ucsConsentCount; /// /// An optional list of consent requests to UCS /// _Field_size_(ucsConsentCount) char const** ucsConsents; } XalWin32Args; typedef XalWin32Args XalInitArgs; #define XAL_PLATFORM "Win32" } ================================================ FILE: External/Xal/Source/Xal/Include/Xal/xal_xdk.h ================================================ #pragma once #if !defined(__cplusplus) #error C++11 required #endif extern "C" { //------------------------------------------------------------------------------ // Xdk types //------------------------------------------------------------------------------ /// /// Struct that encapsulates the Xdk specific arguments for Xal. /// typedef struct XalXdkArgs {} XalXdkArgs; typedef XalXdkArgs XalInitArgs; #define XAL_PLATFORM "XDK" } ================================================ FILE: External/Xal/Source/Xal/Include/Xal/xal_xdk_ext.h ================================================ #pragma once extern "C" { /// /// Returns the system user associated with the user handle. /// /// The user handle. /// The associated system user. STDAPI XalUserToXboxSystemUser( _In_ XalUserHandle user, _Out_ Windows::Xbox::System::IUser^* systemUser ) noexcept; /// /// Add the given system user as a Xal user. /// /// The system user. /// The new user handle. STDAPI XalAddXboxSystemUser( _In_ Windows::Xbox::System::IUser^ systemUser, _Out_ XalUserHandle* user ) noexcept; } ================================================ FILE: Include/cpprestinclude/cpprest/astreambuf.h ================================================ #if !XSAPI_NO_PPL #if HC_PLATFORM_IS_MICROSOFT #pragma warning( push ) #pragma warning( disable : 26444 ) // ignore various unnamed objects #endif /*** * Copyright (C) Microsoft. All rights reserved. * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. * * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ * * Asynchronous I/O: stream buffer. This is an extension to the PPL concurrency features and therefore * lives in the Concurrency namespace. * * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk * * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ****/ #pragma once #include "cpprest/asyncrt_utils.h" #include "cpprest/details/basic_types.h" #include "pplx/pplxtasks.h" #include #include #include #include #include #if (defined(_MSC_VER) && (_MSC_VER >= 1800)) && !CPPREST_FORCE_PPLX namespace Concurrency // since namespace pplx = Concurrency #else namespace pplx #endif { namespace details { template pplx::task _do_while(F func) { pplx::task first = func(); return first.then([=](bool guard) -> pplx::task { if (guard) return pplx::details::_do_while(func); else return first; }); } } // namespace details } namespace Concurrency { /// Library for asynchronous streams. namespace streams { /// /// Extending the standard char_traits type with one that adds values and types /// that are unique to "C++ REST SDK" streams. /// /// /// The data type of the basic element of the stream. /// template struct char_traits : std::char_traits<_CharType> { /// /// Some synchronous functions will return this value if the operation /// requires an asynchronous call in a given situation. /// /// An int_type value which implies that an asynchronous call is required. static typename std::char_traits<_CharType>::int_type requires_async() { return std::char_traits<_CharType>::eof() - 1; } }; #if !defined(_WIN32) template<> struct char_traits : private std::char_traits { public: typedef unsigned char char_type; using std::char_traits::eof; using std::char_traits::int_type; using std::char_traits::off_type; using std::char_traits::pos_type; static size_t length(const unsigned char* str) { return std::char_traits::length(reinterpret_cast(str)); } static void assign(unsigned char& left, const unsigned char& right) { left = right; } static unsigned char* assign(unsigned char* left, size_t n, unsigned char value) { return reinterpret_cast( std::char_traits::assign(reinterpret_cast(left), n, static_cast(value))); } static unsigned char* copy(unsigned char* left, const unsigned char* right, size_t n) { return reinterpret_cast( std::char_traits::copy(reinterpret_cast(left), reinterpret_cast(right), n)); } static unsigned char* move(unsigned char* left, const unsigned char* right, size_t n) { return reinterpret_cast( std::char_traits::move(reinterpret_cast(left), reinterpret_cast(right), n)); } static int_type requires_async() { return eof() - 1; } }; #endif namespace details { /// /// Stream buffer base class. /// template class basic_streambuf { public: typedef _CharType char_type; typedef ::concurrency::streams::char_traits<_CharType> traits; typedef typename traits::int_type int_type; typedef typename traits::pos_type pos_type; typedef typename traits::off_type off_type; /// /// Virtual constructor for stream buffers. /// virtual ~basic_streambuf() {} /// /// can_read is used to determine whether a stream buffer will support read operations (get). /// virtual bool can_read() const = 0; /// /// can_write is used to determine whether a stream buffer will support write operations (put). /// virtual bool can_write() const = 0; /// /// can_seek is used to determine whether a stream buffer supports seeking. /// virtual bool can_seek() const = 0; /// /// has_size is used to determine whether a stream buffer supports size(). /// virtual bool has_size() const = 0; /// /// is_eof is used to determine whether a read head has reached the end of the buffer. /// virtual bool is_eof() const = 0; /// /// Gets the stream buffer size, if one has been set. /// /// The direction of buffering (in or out) /// The size of the internal buffer (for the given direction). /// An implementation that does not support buffering will always return 0. virtual size_t buffer_size(std::ios_base::openmode direction = std::ios_base::in) const = 0; /// /// Sets the stream buffer implementation to buffer or not buffer. /// /// The size to use for internal buffering, 0 if no buffering should be done. /// The direction of buffering (in or out) /// An implementation that does not support buffering will silently ignore calls to this function and it /// will not have any effect on what is returned by subsequent calls to . virtual void set_buffer_size(size_t size, std::ios_base::openmode direction = std::ios_base::in) = 0; /// /// For any input stream, in_avail returns the number of characters that are immediately available /// to be consumed without blocking. May be used in conjunction with to read data without /// incurring the overhead of using tasks. /// virtual size_t in_avail() const = 0; /// /// Checks if the stream buffer is open. /// /// No separation is made between open for reading and open for writing. virtual bool is_open() const = 0; /// /// Closes the stream buffer, preventing further read or write operations. /// /// The I/O mode (in or out) to close for. virtual pplx::task close(std::ios_base::openmode mode = (std::ios_base::in | std::ios_base::out)) = 0; /// /// Closes the stream buffer with an exception. /// /// The I/O mode (in or out) to close for. /// Pointer to the exception. virtual pplx::task close(std::ios_base::openmode mode, std::exception_ptr eptr) = 0; /// /// Writes a single character to the stream. /// /// The character to write /// A task that holds the value of the character. This value is EOF if the write operation /// fails. virtual pplx::task putc(_CharType ch) = 0; /// /// Writes a number of characters to the stream. /// /// A pointer to the block of data to be written. /// The number of characters to write. /// A task that holds the number of characters actually written, either 'count' or 0. virtual pplx::task putn(const _CharType* ptr, size_t count) = 0; /// /// Writes a number of characters to the stream. Note: callers must make sure the data to be written is valid until /// the returned task completes. /// /// A pointer to the block of data to be written. /// The number of characters to write. /// A task that holds the number of characters actually written, either 'count' or 0. virtual pplx::task putn_nocopy(const _CharType* ptr, size_t count) = 0; /// /// Reads a single character from the stream and advances the read position. /// /// A task that holds the value of the character. This value is EOF if the read fails. virtual pplx::task bumpc() = 0; /// /// Reads a single character from the stream and advances the read position. /// /// The value of the character. -1 if the read fails. -2 if an asynchronous read is /// required This is a synchronous operation, but is guaranteed to never block. virtual int_type sbumpc() = 0; /// /// Reads a single character from the stream without advancing the read position. /// /// A task that holds the value of the byte. This value is EOF if the read fails. virtual pplx::task getc() = 0; /// /// Reads a single character from the stream without advancing the read position. /// /// The value of the character. EOF if the read fails. if an /// asynchronous read is required This is a synchronous operation, but is guaranteed to never /// block. virtual int_type sgetc() = 0; /// /// Advances the read position, then returns the next character without advancing again. /// /// A task that holds the value of the character. This value is EOF if the read fails. virtual pplx::task nextc() = 0; /// /// Retreats the read position, then returns the current character without advancing. /// /// A task that holds the value of the character. This value is EOF if the read fails, /// requires_async if an asynchronous read is required virtual pplx::task ungetc() = 0; /// /// Reads up to a given number of characters from the stream. /// /// The address of the target memory area. /// The maximum number of characters to read. /// A task that holds the number of characters read. This value is O if the end of the stream is /// reached. virtual pplx::task getn(_Out_writes_(count) _CharType* ptr, _In_ size_t count) = 0; /// /// Copies up to a given number of characters from the stream, synchronously. /// /// The address of the target memory area. /// The maximum number of characters to read. /// The number of characters copied. O if the end of the stream is reached or an asynchronous read is /// required. This is a synchronous operation, but is guaranteed to never block. virtual size_t scopy(_Out_writes_(count) _CharType* ptr, _In_ size_t count) = 0; /// /// Gets the current read or write position in the stream. /// /// The I/O direction to seek (see remarks) /// The current position. EOF if the operation fails. /// Some streams may have separate write and read cursors. /// For such streams, the direction parameter defines whether to move the read or the write /// cursor. virtual pos_type getpos(std::ios_base::openmode direction) const = 0; /// /// Gets the size of the stream, if known. Calls to has_size will determine whether /// the result of size can be relied on. /// virtual utility::size64_t size() const = 0; /// /// Seeks to the given position. /// /// The offset from the beginning of the stream. /// The I/O direction to seek (see remarks). /// The position. EOF if the operation fails. /// Some streams may have separate write and read cursors. For such streams, the direction parameter /// defines whether to move the read or the write cursor. virtual pos_type seekpos(pos_type pos, std::ios_base::openmode direction) = 0; /// /// Seeks to a position given by a relative offset. /// /// The relative position to seek to /// The starting point (beginning, end, current) for the seek. /// The I/O direction to seek (see remarks) /// The position. EOF if the operation fails. /// Some streams may have separate write and read cursors. /// For such streams, the mode parameter defines whether to move the read or the write cursor. virtual pos_type seekoff(off_type offset, std::ios_base::seekdir way, std::ios_base::openmode mode) = 0; /// /// For output streams, flush any internally buffered data to the underlying medium. /// /// A task that returns true if the sync succeeds, false if not. virtual pplx::task sync() = 0; // // Efficient read and write. // // The following routines are intended to be used for more efficient, copy-free, reading and // writing of data from/to the stream. Rather than having the caller provide a buffer into which // data is written or from which it is read, the stream buffer provides a pointer directly to the // internal data blocks that it is using. Since not all stream buffers use internal data structures // to copy data, the functions may not be supported by all. An application that wishes to use this // functionality should therefore first try them and check for failure to support. If there is // such failure, the application should fall back on the copying interfaces (putn / getn) // /// /// Allocates a contiguous memory block and returns it. /// /// The number of characters to allocate. /// A pointer to a block to write to, null if the stream buffer implementation does not support /// alloc/commit. virtual _CharType* alloc(_In_ size_t count) = 0; /// /// Submits a block already allocated by the stream buffer. /// /// The number of characters to be committed. virtual void commit(_In_ size_t count) = 0; /// /// Gets a pointer to the next already allocated contiguous block of data. /// /// A reference to a pointer variable that will hold the address of the block on success. /// The number of contiguous characters available at the address in 'ptr'. /// true if the operation succeeded, false otherwise. /// /// A return of false does not necessarily indicate that a subsequent read operation would fail, only that /// there is no block to return immediately or that the stream buffer does not support the operation. /// The stream buffer may not de-allocate the block until is called. /// If the end of the stream is reached, the function will return true, a null pointer, and a count of zero; /// a subsequent read will not succeed. /// virtual bool acquire(_Out_ _CharType*& ptr, _Out_ size_t& count) = 0; /// /// Releases a block of data acquired using . This frees the stream buffer to /// de-allocate the memory, if it so desires. Move the read position ahead by the count. /// /// A pointer to the block of data to be released. /// The number of characters that were read. virtual void release(_Out_writes_(count) _CharType* ptr, _In_ size_t count) = 0; /// /// Retrieves the stream buffer exception_ptr if it has been set. /// /// Pointer to the exception, if it has been set; otherwise, nullptr will be returned virtual std::exception_ptr exception() const = 0; }; template class streambuf_state_manager : public basic_streambuf<_CharType>, public std::enable_shared_from_this> { public: typedef typename details::basic_streambuf<_CharType>::traits traits; typedef typename details::basic_streambuf<_CharType>::int_type int_type; typedef typename details::basic_streambuf<_CharType>::pos_type pos_type; typedef typename details::basic_streambuf<_CharType>::off_type off_type; /// /// can_read is used to determine whether a stream buffer will support read operations (get). /// virtual bool can_read() const { return m_stream_can_read; } /// /// can_write is used to determine whether a stream buffer will support write operations (put). /// virtual bool can_write() const { return m_stream_can_write; } /// /// Checks if the stream buffer is open. /// /// No separation is made between open for reading and open for writing. virtual bool is_open() const { return can_read() || can_write(); } /// /// Closes the stream buffer, preventing further read or write operations. /// /// The I/O mode (in or out) to close for. virtual pplx::task close(std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) { pplx::task closeOp = pplx::task_from_result(); if (mode & std::ios_base::in && can_read()) { closeOp = _close_read(); } // After the flush_internal task completed, "this" object may have been destroyed, // accessing the members is invalid, use shared_from_this to avoid access violation exception. auto this_ptr = std::static_pointer_cast(this->shared_from_this()); if (mode & std::ios_base::out && can_write()) { if (closeOp.is_done()) closeOp = closeOp && _close_write().then([this_ptr] {}); // passing down exceptions from closeOp else closeOp = closeOp.then([this_ptr] { return this_ptr->_close_write().then([this_ptr] {}); }); } return closeOp; } /// /// Closes the stream buffer with an exception. /// /// The I/O mode (in or out) to close for. /// Pointer to the exception. virtual pplx::task close(std::ios_base::openmode mode, std::exception_ptr eptr) { if (m_currentException == nullptr) m_currentException = eptr; return close(mode); } /// /// is_eof is used to determine whether a read head has reached the end of the buffer. /// virtual bool is_eof() const { return m_stream_read_eof; } /// /// Writes a single character to the stream. /// /// The character to write /// The value of the character. EOF if the write operation fails virtual pplx::task putc(_CharType ch) { if (!can_write()) return create_exception_checked_value_task(traits::eof()); return create_exception_checked_task(_putc(ch), [](int_type) { return false; // no EOF for write }); } /// /// Writes a number of characters to the stream. /// /// A pointer to the block of data to be written. /// The number of characters to write. /// The number of characters actually written, either 'count' or 0. CASABLANCA_DEPRECATED("This API in some cases performs a copy. It is deprecated and will be removed in a future " "release. Use putn_nocopy instead.") virtual pplx::task putn(const _CharType* ptr, size_t count) { if (!can_write()) return create_exception_checked_value_task(0); if (count == 0) return pplx::task_from_result(0); return create_exception_checked_task(_putn(ptr, count, true), [](size_t) { return false; // no EOF for write }); } /// /// Writes a number of characters to the stream. Note: callers must make sure the data to be written is valid until /// the returned task completes. /// /// A pointer to the block of data to be written. /// The number of characters to write. /// A task that holds the number of characters actually written, either 'count' or 0. virtual pplx::task putn_nocopy(const _CharType* ptr, size_t count) { if (!can_write()) return create_exception_checked_value_task(0); if (count == 0) return pplx::task_from_result(0); return create_exception_checked_task(_putn(ptr, count), [](size_t) { return false; // no EOF for write }); } /// /// Reads a single character from the stream and advances the read position. /// /// The value of the character. EOF if the read fails. virtual pplx::task bumpc() { if (!can_read()) return create_exception_checked_value_task(streambuf_state_manager<_CharType>::traits::eof()); return create_exception_checked_task( _bumpc(), [](int_type val) { return val == streambuf_state_manager<_CharType>::traits::eof(); }); } /// /// Reads a single character from the stream and advances the read position. /// /// The value of the character. -1 if the read fails. -2 if an asynchronous read is /// required This is a synchronous operation, but is guaranteed to never block. virtual int_type sbumpc() { if (!(m_currentException == nullptr)) std::rethrow_exception(m_currentException); if (!can_read()) return traits::eof(); return check_sync_read_eof(_sbumpc()); } /// /// Reads a single character from the stream without advancing the read position. /// /// The value of the byte. EOF if the read fails. virtual pplx::task getc() { if (!can_read()) return create_exception_checked_value_task(traits::eof()); return create_exception_checked_task( _getc(), [](int_type val) { return val == streambuf_state_manager<_CharType>::traits::eof(); }); } /// /// Reads a single character from the stream without advancing the read position. /// /// The value of the character. EOF if the read fails. if an /// asynchronous read is required This is a synchronous operation, but is guaranteed to never /// block. virtual int_type sgetc() { if (!(m_currentException == nullptr)) std::rethrow_exception(m_currentException); if (!can_read()) return traits::eof(); return check_sync_read_eof(_sgetc()); } /// /// Advances the read position, then returns the next character without advancing again. /// /// The value of the character. EOF if the read fails. virtual pplx::task nextc() { if (!can_read()) return create_exception_checked_value_task(traits::eof()); return create_exception_checked_task( _nextc(), [](int_type val) { return val == streambuf_state_manager<_CharType>::traits::eof(); }); } /// /// Retreats the read position, then returns the current character without advancing. /// /// The value of the character. EOF if the read fails. if an /// asynchronous read is required virtual pplx::task ungetc() { if (!can_read()) return create_exception_checked_value_task(traits::eof()); return create_exception_checked_task(_ungetc(), [](int_type) { return false; }); } /// /// Reads up to a given number of characters from the stream. /// /// The address of the target memory area. /// The maximum number of characters to read. /// The number of characters read. O if the end of the stream is reached. virtual pplx::task getn(_Out_writes_(count) _CharType* ptr, _In_ size_t count) { if (!can_read()) return create_exception_checked_value_task(0); if (count == 0) return pplx::task_from_result(0); return create_exception_checked_task(_getn(ptr, count), [](size_t val) { return val == 0; }); } /// /// Copies up to a given number of characters from the stream, synchronously. /// /// The address of the target memory area. /// The maximum number of characters to read. /// The number of characters copied. O if the end of the stream is reached or an asynchronous read is /// required. This is a synchronous operation, but is guaranteed to never block. virtual size_t scopy(_Out_writes_(count) _CharType* ptr, _In_ size_t count) { if (!(m_currentException == nullptr)) std::rethrow_exception(m_currentException); if (!can_read()) return 0; return _scopy(ptr, count); } /// /// For output streams, flush any internally buffered data to the underlying medium. /// /// true if the flush succeeds, false if not virtual pplx::task sync() { if (!can_write()) { if (m_currentException == nullptr) return pplx::task_from_result(); else return pplx::task_from_exception(m_currentException); } return create_exception_checked_task(_sync(), [](bool) { return false; }).then([](bool) {}); } /// /// Retrieves the stream buffer exception_ptr if it has been set. /// /// Pointer to the exception, if it has been set; otherwise, nullptr will be returned. virtual std::exception_ptr exception() const { return m_currentException; } /// /// Allocates a contiguous memory block and returns it. /// /// The number of characters to allocate. /// A pointer to a block to write to, null if the stream buffer implementation does not support /// alloc/commit. This is intended as an advanced API to be used only when it is important to /// avoid extra copies. _CharType* alloc(size_t count) { if (m_alloced) throw std::logic_error( "The buffer is already allocated, this maybe caused by overlap of stream read or write"); _CharType* alloc_result = _alloc(count); if (alloc_result) m_alloced = true; return alloc_result; } /// /// Submits a block already allocated by the stream buffer. /// /// The number of characters to be committed. /// This is intended as an advanced API to be used only when it is important to avoid extra /// copies. void commit(size_t count) { if (!m_alloced) throw std::logic_error("The buffer needs to allocate first"); _commit(count); m_alloced = false; } public: virtual bool can_seek() const = 0; virtual bool has_size() const = 0; virtual utility::size64_t size() const { return 0; } virtual size_t buffer_size(std::ios_base::openmode direction = std::ios_base::in) const = 0; virtual void set_buffer_size(size_t size, std::ios_base::openmode direction = std::ios_base::in) = 0; virtual size_t in_avail() const = 0; virtual pos_type getpos(std::ios_base::openmode direction) const = 0; virtual pos_type seekpos(pos_type pos, std::ios_base::openmode direction) = 0; virtual pos_type seekoff(off_type offset, std::ios_base::seekdir way, std::ios_base::openmode mode) = 0; virtual bool acquire(_Out_writes_(count) _CharType*& ptr, _In_ size_t& count) = 0; virtual void release(_Out_writes_(count) _CharType* ptr, _In_ size_t count) = 0; protected: virtual pplx::task _putc(_CharType ch) = 0; // This API is only needed for file streams and until we remove the deprecated stream buffer putn overload. virtual pplx::task _putn(const _CharType* ptr, size_t count, bool) { // Default to no copy, only the file streams API overloads and performs a copy. return _putn(ptr, count); } virtual pplx::task _putn(const _CharType* ptr, size_t count) = 0; virtual pplx::task _bumpc() = 0; virtual int_type _sbumpc() = 0; virtual pplx::task _getc() = 0; virtual int_type _sgetc() = 0; virtual pplx::task _nextc() = 0; virtual pplx::task _ungetc() = 0; virtual pplx::task _getn(_Out_writes_(count) _CharType* ptr, _In_ size_t count) = 0; virtual size_t _scopy(_Out_writes_(count) _CharType* ptr, _In_ size_t count) = 0; virtual pplx::task _sync() = 0; virtual _CharType* _alloc(size_t count) = 0; virtual void _commit(size_t count) = 0; /// /// The real read head close operation, implementation should override it if there is any resource to be released. /// virtual pplx::task _close_read() { m_stream_can_read = false; return pplx::task_from_result(); } /// /// The real write head close operation, implementation should override it if there is any resource to be released. /// virtual pplx::task _close_write() { m_stream_can_write = false; return pplx::task_from_result(); } protected: streambuf_state_manager(std::ios_base::openmode mode) { m_stream_can_read = (mode & std::ios_base::in) != 0; m_stream_can_write = (mode & std::ios_base::out) != 0; m_stream_read_eof = false; m_alloced = false; } std::exception_ptr m_currentException; // The in/out mode for the buffer std::atomic m_stream_can_read; std::atomic m_stream_can_write; std::atomic m_stream_read_eof; std::atomic m_alloced; private: template pplx::task<_CharType1> create_exception_checked_value_task(const _CharType1& val) const { if (this->exception() == nullptr) return pplx::task_from_result<_CharType1>(static_cast<_CharType1>(val)); else return pplx::task_from_exception<_CharType1>(this->exception()); } // Set exception and eof states for async read template pplx::task<_CharType1> create_exception_checked_task(pplx::task<_CharType1> result, std::function eof_test, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) { auto thisPointer = this->shared_from_this(); auto func1 = [=](pplx::task<_CharType1> t1) -> pplx::task<_CharType1> { try { thisPointer->m_stream_read_eof = eof_test(t1.get()); } catch (...) { thisPointer->close(mode, std::current_exception()).get(); return pplx::task_from_exception<_CharType1>(thisPointer->exception(), pplx::task_options()); } if (thisPointer->m_stream_read_eof && !(thisPointer->exception() == nullptr)) return pplx::task_from_exception<_CharType1>(thisPointer->exception(), pplx::task_options()); return t1; }; if (result.is_done()) { // If the data is already available, we should avoid scheduling a continuation, so we do it inline. return func1(result); } else { return result.then(func1); } } // Set eof states for sync read int_type check_sync_read_eof(int_type ch) { m_stream_read_eof = ch == traits::eof(); return ch; } }; } // namespace details // Forward declarations template class basic_istream; template class basic_ostream; /// /// Reference-counted stream buffer. /// /// /// The data type of the basic element of the streambuf. /// /// /// The data type of the basic element of the streambuf. /// template class streambuf : public details::basic_streambuf<_CharType> { public: typedef typename details::basic_streambuf<_CharType>::traits traits; typedef typename details::basic_streambuf<_CharType>::int_type int_type; typedef typename details::basic_streambuf<_CharType>::pos_type pos_type; typedef typename details::basic_streambuf<_CharType>::off_type off_type; typedef typename details::basic_streambuf<_CharType>::char_type char_type; template friend class streambuf; /// /// Constructor. /// /// A pointer to the concrete stream buffer implementation. streambuf(_In_ const std::shared_ptr>& ptr) : m_buffer(ptr) {} /// /// Default constructor. /// streambuf() {} /// /// Converter Constructor. /// /// /// The data type of the basic element of the source streambuf. /// /// The source buffer to be converted. template streambuf(const streambuf& other) : m_buffer(std::static_pointer_cast>( std::static_pointer_cast(other.m_buffer))) { static_assert(std::is_same::pos_type>::value && std::is_same::off_type>::value && std::is_integral<_CharType>::value && std::is_integral::value && std::is_integral::value && std::is_integral::int_type>::value && sizeof(_CharType) == sizeof(AlterCharType) && sizeof(int_type) == sizeof(typename details::basic_streambuf::int_type), "incompatible stream character types"); } /// /// Constructs an input stream head for this stream buffer. /// /// basic_istream. concurrency::streams::basic_istream<_CharType> create_istream() const { if (!can_read()) throw std::runtime_error("stream buffer not set up for input of data"); return concurrency::streams::basic_istream<_CharType>(*this); } /// /// Constructs an output stream for this stream buffer. /// /// basic_ostream concurrency::streams::basic_ostream<_CharType> create_ostream() const { if (!can_write()) throw std::runtime_error("stream buffer not set up for output of data"); return concurrency::streams::basic_ostream<_CharType>(*this); } /// /// Checks if the stream buffer has been initialized or not. /// operator bool() const { return (bool)m_buffer; } /// /// Destructor /// virtual ~streambuf() {} const std::shared_ptr>& get_base() const { if (!m_buffer) { throw std::invalid_argument("Invalid streambuf object"); } return m_buffer; } /// /// can_read is used to determine whether a stream buffer will support read operations (get). /// virtual bool can_read() const { return get_base()->can_read(); } /// /// can_write is used to determine whether a stream buffer will support write operations (put). /// virtual bool can_write() const { return get_base()->can_write(); } /// /// can_seek is used to determine whether a stream buffer supports seeking. /// /// True if seeking is supported, false otherwise. virtual bool can_seek() const { return get_base()->can_seek(); } /// /// has_size is used to determine whether a stream buffer supports size(). /// /// True if the size API is supported, false otherwise. virtual bool has_size() const { return get_base()->has_size(); } /// /// Gets the total number of characters in the stream buffer, if known. Calls to has_size will determine /// whether the result of size can be relied on. /// /// The total number of characters in the stream buffer. virtual utility::size64_t size() const { return get_base()->size(); } /// /// Gets the stream buffer size, if one has been set. /// /// The direction of buffering (in or out) /// The size of the internal buffer (for the given direction). /// An implementation that does not support buffering will always return 0. virtual size_t buffer_size(std::ios_base::openmode direction = std::ios_base::in) const { return get_base()->buffer_size(direction); } /// /// Sets the stream buffer implementation to buffer or not buffer. /// /// The size to use for internal buffering, 0 if no buffering should be done. /// The direction of buffering (in or out) /// An implementation that does not support buffering will silently ignore calls to this function and it /// will not have any effect on what is returned by subsequent calls to . virtual void set_buffer_size(size_t size, std::ios_base::openmode direction = std::ios_base::in) { get_base()->set_buffer_size(size, direction); } /// /// For any input stream, in_avail returns the number of characters that are immediately available /// to be consumed without blocking. May be used in conjunction with to read data without /// incurring the overhead of using tasks. /// /// Number of characters that are ready to read. virtual size_t in_avail() const { return get_base()->in_avail(); } /// /// Checks if the stream buffer is open. /// /// No separation is made between open for reading and open for writing. /// True if the stream buffer is open for reading or writing, false otherwise. virtual bool is_open() const { return get_base()->is_open(); } /// /// is_eof is used to determine whether a read head has reached the end of the buffer. /// /// True if at the end of the buffer, false otherwise. virtual bool is_eof() const { return get_base()->is_eof(); } /// /// Closes the stream buffer, preventing further read or write operations. /// /// The I/O mode (in or out) to close for. virtual pplx::task close(std::ios_base::openmode mode = (std::ios_base::in | std::ios_base::out)) { // We preserve the check here to workaround a Dev10 compiler crash auto buffer = get_base(); return buffer ? buffer->close(mode) : pplx::task_from_result(); } /// /// Closes the stream buffer with an exception. /// /// The I/O mode (in or out) to close for. /// Pointer to the exception. virtual pplx::task close(std::ios_base::openmode mode, std::exception_ptr eptr) { // We preserve the check here to workaround a Dev10 compiler crash auto buffer = get_base(); return buffer ? buffer->close(mode, eptr) : pplx::task_from_result(); } /// /// Writes a single character to the stream. /// /// The character to write /// The value of the character. EOF if the write operation fails virtual pplx::task putc(_CharType ch) { return get_base()->putc(ch); } /// /// Allocates a contiguous memory block and returns it. /// /// The number of characters to allocate. /// A pointer to a block to write to, null if the stream buffer implementation does not support /// alloc/commit. virtual _CharType* alloc(size_t count) { return get_base()->alloc(count); } /// /// Submits a block already allocated by the stream buffer. /// /// The number of characters to be committed. virtual void commit(size_t count) { get_base()->commit(count); } /// /// Gets a pointer to the next already allocated contiguous block of data. /// /// A reference to a pointer variable that will hold the address of the block on success. /// The number of contiguous characters available at the address in 'ptr'. /// true if the operation succeeded, false otherwise. /// /// A return of false does not necessarily indicate that a subsequent read operation would fail, only that /// there is no block to return immediately or that the stream buffer does not support the operation. /// The stream buffer may not de-allocate the block until is called. /// If the end of the stream is reached, the function will return true, a null pointer, and a count of zero; /// a subsequent read will not succeed. /// virtual bool acquire(_Out_ _CharType*& ptr, _Out_ size_t& count) { ptr = nullptr; count = 0; return get_base()->acquire(ptr, count); } /// /// Releases a block of data acquired using . This frees the stream buffer to /// de-allocate the memory, if it so desires. Move the read position ahead by the count. /// /// A pointer to the block of data to be released. /// The number of characters that were read. virtual void release(_Out_writes_(count) _CharType* ptr, _In_ size_t count) { get_base()->release(ptr, count); } /// /// Writes a number of characters to the stream. /// /// A pointer to the block of data to be written. /// The number of characters to write. /// The number of characters actually written, either 'count' or 0. CASABLANCA_DEPRECATED("This API in some cases performs a copy. It is deprecated and will be removed in a future " "release. Use putn_nocopy instead.") virtual pplx::task putn(const _CharType* ptr, size_t count) { return get_base()->putn(ptr, count); } /// /// Writes a number of characters to the stream. Note: callers must make sure the data to be written is valid until /// the returned task completes. /// /// A pointer to the block of data to be written. /// The number of characters to write. /// The number of characters actually written, either 'count' or 0. virtual pplx::task putn_nocopy(const _CharType* ptr, size_t count) { return get_base()->putn_nocopy(ptr, count); } /// /// Reads a single character from the stream and advances the read position. /// /// The value of the character. EOF if the read fails. virtual pplx::task bumpc() { return get_base()->bumpc(); } /// /// Reads a single character from the stream and advances the read position. /// /// The value of the character. -1 if the read fails. -2 if an asynchronous read is /// required This is a synchronous operation, but is guaranteed to never block. virtual typename details::basic_streambuf<_CharType>::int_type sbumpc() { return get_base()->sbumpc(); } /// /// Reads a single character from the stream without advancing the read position. /// /// The value of the byte. EOF if the read fails. virtual pplx::task getc() { return get_base()->getc(); } /// /// Reads a single character from the stream without advancing the read position. /// /// The value of the character. EOF if the read fails. if an /// asynchronous read is required This is a synchronous operation, but is guaranteed to never /// block. virtual typename details::basic_streambuf<_CharType>::int_type sgetc() { return get_base()->sgetc(); } /// /// Advances the read position, then returns the next character without advancing again. /// /// The value of the character. EOF if the read fails. pplx::task nextc() { return get_base()->nextc(); } /// /// Retreats the read position, then returns the current character without advancing. /// /// The value of the character. EOF if the read fails. if an /// asynchronous read is required pplx::task ungetc() { return get_base()->ungetc(); } /// /// Reads up to a given number of characters from the stream. /// /// The address of the target memory area. /// The maximum number of characters to read. /// The number of characters read. O if the end of the stream is reached. virtual pplx::task getn(_Out_writes_(count) _CharType* ptr, _In_ size_t count) { return get_base()->getn(ptr, count); } /// /// Copies up to a given number of characters from the stream, synchronously. /// /// The address of the target memory area. /// The maximum number of characters to read. /// The number of characters copied. O if the end of the stream is reached or an asynchronous read is /// required. This is a synchronous operation, but is guaranteed to never block. virtual size_t scopy(_Out_writes_(count) _CharType* ptr, _In_ size_t count) { return get_base()->scopy(ptr, count); } /// /// Gets the current read or write position in the stream. /// /// The I/O direction to seek (see remarks) /// The current position. EOF if the operation fails. /// Some streams may have separate write and read cursors. /// For such streams, the direction parameter defines whether to move the read or the write /// cursor. virtual typename details::basic_streambuf<_CharType>::pos_type getpos(std::ios_base::openmode direction) const { return get_base()->getpos(direction); } /// /// Seeks to the given position. /// /// The offset from the beginning of the stream. /// The I/O direction to seek (see remarks). /// The position. EOF if the operation fails. /// Some streams may have separate write and read cursors. For such streams, the direction parameter /// defines whether to move the read or the write cursor. virtual typename details::basic_streambuf<_CharType>::pos_type seekpos( typename details::basic_streambuf<_CharType>::pos_type pos, std::ios_base::openmode direction) { return get_base()->seekpos(pos, direction); } /// /// Seeks to a position given by a relative offset. /// /// The relative position to seek to /// The starting point (beginning, end, current) for the seek. /// The I/O direction to seek (see remarks) /// The position. EOF if the operation fails. /// Some streams may have separate write and read cursors. /// For such streams, the mode parameter defines whether to move the read or the write cursor. virtual typename details::basic_streambuf<_CharType>::pos_type seekoff( typename details::basic_streambuf<_CharType>::off_type offset, std::ios_base::seekdir way, std::ios_base::openmode mode) { return get_base()->seekoff(offset, way, mode); } /// /// For output streams, flush any internally buffered data to the underlying medium. /// /// true if the flush succeeds, false if not virtual pplx::task sync() { return get_base()->sync(); } /// /// Retrieves the stream buffer exception_ptr if it has been set. /// /// Pointer to the exception, if it has been set; otherwise, nullptr will be returned virtual std::exception_ptr exception() const { return get_base()->exception(); } private: std::shared_ptr> m_buffer; }; } // namespace streams } // namespace Concurrency #if HC_PLATFORM_IS_MICROSOFT #pragma warning( pop ) #endif #endif // !XSAPI_NO_PPL ================================================ FILE: Include/cpprestinclude/cpprest/asyncrt_utils.h ================================================ /*** * Copyright (C) Microsoft. All rights reserved. * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. * * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ * * Various common utilities. * * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk * * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ****/ #pragma once #include #include #include #include #include #include #include "cpprest/details/basic_types.h" #if !defined(_WIN32) || (_MSC_VER >= 1700) #include #endif #ifndef _WIN32 #include #endif #if HC_PLATFORM_IS_MICROSOFT #pragma warning( push ) #pragma warning( disable : 26444 ) // ignore various unnamed objects #endif /// Various utilities for string conversions and date and time manipulation. namespace utility { // Left over from VS2010 support, remains to avoid breaking. typedef std::chrono::seconds seconds; /// Functions for converting to/from std::chrono::seconds to xml string. namespace timespan { /// /// Converts a timespan/interval in seconds to xml duration string as specified by /// http://www.w3.org/TR/xmlschema-2/#duration /// _ASYNCRTIMP utility::string_t __cdecl seconds_to_xml_duration(utility::seconds numSecs); /// /// Converts an xml duration to timespan/interval in seconds /// http://www.w3.org/TR/xmlschema-2/#duration /// _ASYNCRTIMP utility::seconds __cdecl xml_duration_to_seconds(const utility::string_t ×panString); } /// Functions for Unicode string conversions. namespace conversions { /// /// Converts a UTF-16 string to a UTF-8 string. /// /// A two byte character UTF-16 string. /// A single byte character UTF-8 string. _ASYNCRTIMP std::string __cdecl utf16_to_utf8(const utf16string &w); /// /// Converts a UTF-8 string to a UTF-16 /// /// A single byte character UTF-8 string. /// A two byte character UTF-16 string. _ASYNCRTIMP utf16string __cdecl utf8_to_utf16(const std::string &s); /// /// Converts a ASCII (us-ascii) string to a UTF-16 string. /// /// A single byte character us-ascii string. /// A two byte character UTF-16 string. _ASYNCRTIMP utf16string __cdecl usascii_to_utf16(const std::string &s); /// /// Converts a Latin1 (iso-8859-1) string to a UTF-16 string. /// /// A single byte character UTF-8 string. /// A two byte character UTF-16 string. _ASYNCRTIMP utf16string __cdecl latin1_to_utf16(const std::string &s); /// /// Converts a Latin1 (iso-8859-1) string to a UTF-8 string. /// /// A single byte character UTF-8 string. /// A single byte character UTF-8 string. _ASYNCRTIMP utf8string __cdecl latin1_to_utf8(const std::string &s); /// /// Converts to a platform dependent Unicode string type. /// /// A single byte character UTF-8 string. /// A platform dependent string type. _ASYNCRTIMP utility::string_t __cdecl to_string_t(std::string &&s); /// /// Converts to a platform dependent Unicode string type. /// /// A two byte character UTF-16 string. /// A platform dependent string type. _ASYNCRTIMP utility::string_t __cdecl to_string_t(utf16string &&s); /// /// Converts to a platform dependent Unicode string type. /// /// A single byte character UTF-8 string. /// A platform dependent string type. _ASYNCRTIMP utility::string_t __cdecl to_string_t(const std::string &s); /// /// Converts to a platform dependent Unicode string type. /// /// A two byte character UTF-16 string. /// A platform dependent string type. _ASYNCRTIMP utility::string_t __cdecl to_string_t(const utf16string &s); /// /// Converts to a UTF-16 from string. /// /// A single byte character UTF-8 string. /// A two byte character UTF-16 string. _ASYNCRTIMP utf16string __cdecl to_utf16string(const std::string &value); /// /// Converts to a UTF-16 from string. /// /// A two byte character UTF-16 string. /// A two byte character UTF-16 string. _ASYNCRTIMP utf16string __cdecl to_utf16string(utf16string value); /// /// Converts to a UTF-8 string. /// /// A single byte character UTF-8 string. /// A single byte character UTF-8 string. _ASYNCRTIMP std::string __cdecl to_utf8string(std::string value); /// /// Converts to a UTF-8 string. /// /// A two byte character UTF-16 string. /// A single byte character UTF-8 string. _ASYNCRTIMP std::string __cdecl to_utf8string(const utf16string &value); /// /// Encode the given byte array into a base64 string /// _ASYNCRTIMP utility::string_t __cdecl to_base64(const unsigned char* data, size_t dataSize); /// /// Encode the given byte array into a base64 string /// _ASYNCRTIMP utility::string_t __cdecl to_base64(const std::vector& data); /// /// Encode the given 8-byte integer into a base64 string /// _ASYNCRTIMP utility::string_t __cdecl to_base64(uint64_t data); /// /// Decode the given base64 string to a byte array /// _ASYNCRTIMP std::vector __cdecl from_base64(const utility::string_t& str); template utility::string_t print_string(const Source &val, const std::locale &loc) { utility::ostringstream_t oss; oss.imbue(loc); oss << val; if (oss.bad()) { throw std::bad_cast(); } return oss.str(); } template utility::string_t print_string(const Source &val) { return print_string(val, std::locale()); } inline utility::string_t print_string(const utility::string_t &val) { return val; } template Target scan_string(const utility::string_t &str, const std::locale &loc) { Target t; utility::istringstream_t iss(str); iss.imbue(loc); iss >> t; if (iss.bad()) { throw std::bad_cast(); } return t; } template Target scan_string(const utility::string_t &str) { return scan_string(str, std::locale()); } inline utility::string_t scan_string(const utility::string_t &str) { return str; } } namespace details { /// /// Cross platform RAII container for setting thread local locale. /// class scoped_c_thread_locale { public: _ASYNCRTIMP scoped_c_thread_locale(); _ASYNCRTIMP ~scoped_c_thread_locale(); #if !defined(ANDROID) && !defined(__ANDROID__) && !defined(PAVO) // CodePlex 269 #ifdef _WIN32 typedef _locale_t xplat_locale; #else typedef locale_t xplat_locale; #endif static _ASYNCRTIMP xplat_locale __cdecl c_locale(); #endif private: #ifdef _WIN32 std::string m_prevLocale; int m_prevThreadSetting; #elif !(defined(ANDROID) || defined(__ANDROID__) || defined(PAVO)) locale_t m_prevLocale; #endif scoped_c_thread_locale(const scoped_c_thread_locale &); scoped_c_thread_locale & operator=(const scoped_c_thread_locale &); }; /// /// Our own implementation of alpha numeric instead of std::isalnum to avoid /// taking global lock for performance reasons. /// inline bool __cdecl is_alnum(char ch) { return (ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'); } /// /// Simplistic implementation of make_unique. A better implementation would be based on variadic templates /// and therefore not be compatible with Dev10. /// template std::unique_ptr<_Type> make_unique() { return std::unique_ptr<_Type>(new _Type()); } template std::unique_ptr<_Type> make_unique(_Arg1&& arg1) { return std::unique_ptr<_Type>(new _Type(std::forward<_Arg1>(arg1))); } template std::unique_ptr<_Type> make_unique(_Arg1&& arg1, _Arg2&& arg2) { return std::unique_ptr<_Type>(new _Type(std::forward<_Arg1>(arg1), std::forward<_Arg2>(arg2))); } template std::unique_ptr<_Type> make_unique(_Arg1&& arg1, _Arg2&& arg2, _Arg3&& arg3) { return std::unique_ptr<_Type>(new _Type(std::forward<_Arg1>(arg1), std::forward<_Arg2>(arg2), std::forward<_Arg3>(arg3))); } template std::unique_ptr<_Type> make_unique(_Arg1&& arg1, _Arg2&& arg2, _Arg3&& arg3, _Arg4&& arg4) { return std::unique_ptr<_Type>(new _Type(std::forward<_Arg1>(arg1), std::forward<_Arg2>(arg2), std::forward<_Arg3>(arg3), std::forward<_Arg4>(arg4))); } /// /// Cross platform utility function for performing case insensitive string comparision. /// /// First string to compare. /// Second strong to compare. /// true if the strings are equivalent, false otherwise inline bool str_icmp(const utility::string_t &left, const utility::string_t &right) { #ifdef _WIN32 return _wcsicmp(left.c_str(), right.c_str()) == 0; #else return strcasecmp(left.c_str(), right.c_str()); #endif } #ifdef _WIN32 /// /// Category error type for Windows OS errors. /// class windows_category_impl : public std::error_category { public: virtual const char *name() const CPPREST_NOEXCEPT { return "windows"; } _ASYNCRTIMP virtual std::string message(int errorCode) const CPPREST_NOEXCEPT; _ASYNCRTIMP virtual std::error_condition default_error_condition(int errorCode) const CPPREST_NOEXCEPT; }; /// /// Gets the one global instance of the windows error category. /// /// An error category instance. _ASYNCRTIMP const std::error_category & __cdecl windows_category(); #else /// /// Gets the one global instance of the linux error category. /// /// An error category instance. _ASYNCRTIMP const std::error_category & __cdecl linux_category(); #endif /// /// Gets the one global instance of the current platform's error category. /// _ASYNCRTIMP const std::error_category & __cdecl platform_category(); /// /// Creates an instance of std::system_error from a OS error code. /// inline std::system_error __cdecl create_system_error(unsigned long errorCode) { std::error_code code((int)errorCode, platform_category()); return std::system_error(code, code.message()); } /// /// Creates a std::error_code from a OS error code. /// inline std::error_code __cdecl create_error_code(unsigned long errorCode) { return std::error_code((int)errorCode, platform_category()); } /// /// Creates the corresponding error message from a OS error code. /// inline utility::string_t __cdecl create_error_message(unsigned long errorCode) { return utility::conversions::to_string_t(create_error_code(errorCode).message()); } } class datetime { public: typedef uint64_t interval_type; /// /// Defines the supported date and time string formats. /// enum date_format { RFC_1123, ISO_8601 }; /// /// Returns the current UTC time. /// static _ASYNCRTIMP datetime __cdecl utc_now(); /// /// An invalid UTC timestamp value. /// enum:interval_type { utc_timestamp_invalid = static_cast(-1) }; /// /// Returns seconds since Unix/POSIX time epoch at 01-01-1970 00:00:00. /// If time is before epoch, utc_timestamp_invalid is returned. /// static interval_type utc_timestamp() { const auto seconds = utc_now().to_interval() / _secondTicks; if (seconds >= 11644473600LL) { return seconds - 11644473600LL; } else { return utc_timestamp_invalid; } } datetime() : m_interval(0) { } /// /// Creates datetime from a string representing time in UTC in RFC 1123 format. /// /// Returns a datetime of zero if not successful. static _ASYNCRTIMP datetime __cdecl from_string(const utility::string_t& timestring, date_format format = RFC_1123); /// /// Returns a string representation of the datetime. /// _ASYNCRTIMP utility::string_t to_string(date_format format = RFC_1123) const; /// /// Returns the integral time value. /// interval_type to_interval() const { return m_interval; } datetime operator- (interval_type value) const { return datetime(m_interval - value); } datetime operator+ (interval_type value) const { return datetime(m_interval + value); } bool operator== (datetime dt) const { return m_interval == dt.m_interval; } bool operator!= (const datetime& dt) const { return !(*this == dt); } static interval_type from_milliseconds(unsigned int milliseconds) { return milliseconds*_msTicks; } static interval_type from_seconds(unsigned int seconds) { return seconds*_secondTicks; } static interval_type from_minutes(unsigned int minutes) { return minutes*_minuteTicks; } static interval_type from_hours(unsigned int hours) { return hours*_hourTicks; } static interval_type from_days(unsigned int days) { return days*_dayTicks; } bool is_initialized() const { return m_interval != 0; } private: friend int operator- (datetime t1, datetime t2); static const interval_type _msTicks = static_cast(10000); static const interval_type _secondTicks = 1000*_msTicks; static const interval_type _minuteTicks = 60*_secondTicks; static const interval_type _hourTicks = 60*60*_secondTicks; static const interval_type _dayTicks = 24*60*60*_secondTicks; #ifdef _WIN32 // void* to avoid pulling in windows.h static _ASYNCRTIMP bool __cdecl system_type_to_datetime(/*SYSTEMTIME*/ void* psysTime, uint64_t seconds, datetime * pdt); #else static datetime timeval_to_datetime(const timeval &time); #endif // Private constructor. Use static methods to create an instance. datetime(interval_type interval) : m_interval(interval) { } // Storing as hundreds of nanoseconds 10e-7, i.e. 1 here equals 100ns. interval_type m_interval; }; #ifndef _WIN32 // temporary workaround for the fact that // utf16char is not fully supported in GCC class cmp { public: static int icmp(std::string left, std::string right) { size_t i; for (i = 0; i < left.size(); ++i) { if (i == right.size()) return 1; auto l = cmp::tolower(left[i]); auto r = cmp::tolower(right[i]); if (l > r) return 1; if (l < r) return -1; } if (i < right.size()) return -1; return 0; } private: static char tolower(char c) { if (c >= 'A' && c <= 'Z') return static_cast(c - 'A' + 'a'); return c; } }; #endif inline int operator- (datetime t1, datetime t2) { auto diff = (t1.m_interval - t2.m_interval); // Round it down to seconds diff /= 10 * 1000 * 1000; return static_cast(diff); } /// /// Nonce string generator class. /// class nonce_generator { public: /// /// Define default nonce length. /// enum { default_length = 32 }; /// /// Nonce generator constructor. /// /// Length of the generated nonce string. nonce_generator(int length=default_length) : m_random(static_cast(utility::datetime::utc_timestamp())), m_length(length) {} /// /// Generate a nonce string containing random alphanumeric characters (A-Za-z0-9). /// Length of the generated string is set by length(). /// /// The generated nonce string. _ASYNCRTIMP utility::string_t generate(); /// /// Get length of generated nonce string. /// /// Nonce string length. int length() const { return m_length; } /// /// Set length of the generated nonce string. /// /// Lenght of nonce string. void set_length(int length) { m_length = length; } private: static const utility::char_t* c_allowed_chars; std::mt19937 m_random; int m_length; }; } // namespace utility; #if HC_PLATFORM_IS_MICROSOFT #pragma warning( pop ) #endif ================================================ FILE: Include/cpprestinclude/cpprest/base_uri.h ================================================ /*** * ==++== * * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * ==--== * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ * * Protocol independent support for URIs. * * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk * * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ****/ #pragma once #include #include #include #include #include #include "cpprest/asyncrt_utils.h" #include "cpprest/details/basic_types.h" namespace web { namespace details { struct uri_components { uri_components() : m_path(_XPLATSTR("/")), m_port(-1) {} uri_components(const uri_components &other) : m_scheme(other.m_scheme), m_host(other.m_host), m_user_info(other.m_user_info), m_path(other.m_path), m_query(other.m_query), m_fragment(other.m_fragment), m_port(other.m_port) {} uri_components & operator=(const uri_components &other) { if (this != &other) { m_scheme = other.m_scheme; m_host = other.m_host; m_user_info = other.m_user_info; m_path = other.m_path; m_query = other.m_query; m_fragment = other.m_fragment; m_port = other.m_port; } return *this; } uri_components(uri_components &&other) CPPREST_NOEXCEPT : m_scheme(std::move(other.m_scheme)), m_host(std::move(other.m_host)), m_user_info(std::move(other.m_user_info)), m_path(std::move(other.m_path)), m_query(std::move(other.m_query)), m_fragment(std::move(other.m_fragment)), m_port(other.m_port) {} uri_components & operator=(uri_components &&other) CPPREST_NOEXCEPT { if (this != &other) { m_scheme = std::move(other.m_scheme); m_host = std::move(other.m_host); m_user_info = std::move(other.m_user_info); m_path = std::move(other.m_path); m_query = std::move(other.m_query); m_fragment = std::move(other.m_fragment); m_port = other.m_port; } return *this; } _ASYNCRTIMP utility::string_t join(); utility::string_t m_scheme; utility::string_t m_host; utility::string_t m_user_info; utility::string_t m_path; utility::string_t m_query; utility::string_t m_fragment; int m_port; }; } /// /// A single exception type to represent errors in parsing, encoding, and decoding URIs. /// class uri_exception : public std::exception { public: uri_exception(std::string msg) : m_msg(std::move(msg)) {} ~uri_exception() CPPREST_NOEXCEPT {} const char* what() const CPPREST_NOEXCEPT { return m_msg.c_str(); } private: std::string m_msg; }; /// /// A flexible, protocol independent URI implementation. /// /// URI instances are immutable. Querying the various fields on an emtpy URI will return empty strings. Querying /// various diagnostic members on an empty URI will return false. /// /// /// This implementation accepts both URIs ('http://msn.com/path') and URI relative-references /// ('/path?query#frag'). /// /// This implementation does not provide any scheme-specific handling -- an example of this /// would be the following: 'http://path1/path'. This is a valid URI, but it's not a valid /// http-uri -- that is, it's syntactically correct but does not conform to the requirements /// of the http scheme (http requires a host). /// We could provide this by allowing a pluggable 'scheme' policy-class, which would provide /// extra capability for validating and canonicalizing a URI according to scheme, and would /// introduce a layer of type-safety for URIs of differing schemes, and thus differing semantics. /// /// One issue with implementing a scheme-independent URI facility is that of comparing for equality. /// For instance, these URIs are considered equal 'http://msn.com', 'http://msn.com:80'. That is -- /// the 'default' port can be either omitted or explicit. Since we don't have a way to map a scheme /// to it's default port, we don't have a way to know these are equal. This is just one of a class of /// issues with regard to scheme-specific behavior. /// class uri { public: /// /// The various components of a URI. This enum is used to indicate which /// URI component is being encoded to the encode_uri_component. This allows /// specific encoding to be performed. /// /// Scheme and port don't allow '%' so they don't need to be encoded. /// class components { public: enum component { user_info, host, path, query, fragment, full_uri }; }; /// /// Encodes a URI component according to RFC 3986. /// Note if a full URI is specified instead of an individual URI component all /// characters not in the unreserved set are escaped. /// /// The URI as a string. /// The encoded string. _ASYNCRTIMP static utility::string_t __cdecl encode_uri(const utility::string_t &raw, uri::components::component = components::full_uri); /// /// Encodes a string by converting all characters except for RFC 3986 unreserved characters to their /// hexadecimal representation. /// /// The UTF-8 string data. /// The encoded string. _ASYNCRTIMP static utility::string_t __cdecl encode_data_string(const utility::string_t &utf8data); /// /// Decodes an encoded string. /// /// The URI as a string. /// The decoded string. _ASYNCRTIMP static utility::string_t __cdecl decode(const utility::string_t &encoded); /// /// Splits a path into its hierarchical components. /// /// The path as a string /// A std::vector<utility::string_t> containing the segments in the path. _ASYNCRTIMP static std::vector __cdecl split_path(const utility::string_t &path); /// /// Splits a query into its key-value components. /// /// The query string /// A std::map<utility::string_t, utility::string_t> containing the key-value components of the query. _ASYNCRTIMP static std::map __cdecl split_query(const utility::string_t &query); /// /// Validates a string as a URI. /// /// The URI string to be validated. /// true if the given string represents a valid URI, false otherwise. _ASYNCRTIMP static bool __cdecl validate(const utility::string_t &uri_string); /// /// Creates an empty uri /// uri() { m_uri = _XPLATSTR("/");}; /// /// Creates a URI from the given URI components. /// /// A URI components object to create the URI instance. _ASYNCRTIMP uri(const details::uri_components &components); /// /// Creates a URI from the given encoded string. This will throw an exception if the string /// does not contain a valid URI. Use uri::validate if processing user-input. /// /// A pointer to an encoded string to create the URI instance. _ASYNCRTIMP uri(const utility::char_t *uri_string); /// /// Creates a URI from the given encoded string. This will throw an exception if the string /// does not contain a valid URI. Use uri::validate if processing user-input. /// /// An encoded URI string to create the URI instance. _ASYNCRTIMP uri(const utility::string_t &uri_string); /// /// Copy constructor. /// uri(const uri &other) : m_uri(other.m_uri), m_components(other.m_components) {} /// /// Copy assignment operator. /// uri & operator=(const uri &other) { if (this != &other) { m_uri = other.m_uri; m_components = other.m_components; } return *this; } /// /// Move constructor. /// uri(uri &&other) CPPREST_NOEXCEPT : m_uri(std::move(other.m_uri)), m_components(std::move(other.m_components)) {} /// /// Move assignment operator /// uri & operator=(uri &&other) CPPREST_NOEXCEPT { if (this != &other) { m_uri = std::move(other.m_uri); m_components = std::move(other.m_components); } return *this; } /// /// Get the scheme component of the URI as an encoded string. /// /// The URI scheme as a string. const utility::string_t &scheme() const { return m_components.m_scheme; } /// /// Get the user information component of the URI as an encoded string. /// /// The URI user information as a string. const utility::string_t &user_info() const { return m_components.m_user_info; } /// /// Get the host component of the URI as an encoded string. /// /// The URI host as a string. const utility::string_t &host() const { return m_components.m_host; } /// /// Get the port component of the URI. Returns -1 if no port is specified. /// /// The URI port as an integer. int port() const { return m_components.m_port; } /// /// Get the path component of the URI as an encoded string. /// /// The URI path as a string. const utility::string_t &path() const { return m_components.m_path; } /// /// Get the query component of the URI as an encoded string. /// /// The URI query as a string. const utility::string_t &query() const { return m_components.m_query; } /// /// Get the fragment component of the URI as an encoded string. /// /// The URI fragment as a string. const utility::string_t &fragment() const { return m_components.m_fragment; } /// /// Creates a new uri object with the same authority portion as this one, omitting the resource and query portions. /// /// The new uri object with the same authority. _ASYNCRTIMP uri authority() const; /// /// Gets the path, query, and fragment portion of this uri, which may be empty. /// /// The new URI object with the path, query and fragment portion of this URI. _ASYNCRTIMP uri resource() const; /// /// An empty URI specifies no components, and serves as a default value /// bool is_empty() const { return this->m_uri.empty() || this->m_uri == _XPLATSTR("/"); } /// /// A loopback URI is one which refers to a hostname or ip address with meaning only on the local machine. /// /// /// Examples include "locahost", or ip addresses in the loopback range (127.0.0.0/24). /// /// true if this URI references the local host, false otherwise. bool is_host_loopback() const { return !is_empty() && ((host() == _XPLATSTR("localhost")) || (host().size() > 4 && host().substr(0,4) == _XPLATSTR("127."))); } /// /// A wildcard URI is one which refers to all hostnames that resolve to the local machine (using the * or +) /// /// /// http://*:80 /// bool is_host_wildcard() const { return !is_empty() && (this->host() == _XPLATSTR("*") || this->host() == _XPLATSTR("+")); } /// /// A portable URI is one with a hostname that can be resolved globally (used from another machine). /// /// true if this URI can be resolved globally (used from another machine), false otherwise. /// /// The hostname "localhost" is a reserved name that is guaranteed to resolve to the local machine, /// and cannot be used for inter-machine communication. Likewise the hostnames "*" and "+" on Windows /// represent wildcards, and do not map to a resolvable address. /// bool is_host_portable() const { return !(is_empty() || is_host_loopback() || is_host_wildcard()); } /// /// A default port is one where the port is unspecified, and will be determined by the operating system. /// The choice of default port may be dictated by the scheme (http -> 80) or not. /// /// true if this URI instance has a default port, false otherwise. bool is_port_default() const { return !is_empty() && this->port() == 0; } /// /// An "authority" URI is one with only a scheme, optional userinfo, hostname, and (optional) port. /// /// true if this is an "authority" URI, false otherwise. bool is_authority() const { return !is_empty() && is_path_empty() && query().empty() && fragment().empty(); } /// /// Returns whether the other URI has the same authority as this one /// /// The URI to compare the authority with. /// true if both the URI's have the same authority, false otherwise. bool has_same_authority(const uri &other) const { return !is_empty() && this->authority() == other.authority(); } /// /// Returns whether the path portion of this URI is empty /// /// true if the path portion of this URI is empty, false otherwise. bool is_path_empty() const { return path().empty() || path() == _XPLATSTR("/"); } /// /// Returns the full (encoded) URI as a string. /// /// The full encoded URI string. utility::string_t to_string() const { return m_uri; } _ASYNCRTIMP bool operator == (const uri &other) const; bool operator < (const uri &other) const { return m_uri < other.m_uri; } bool operator != (const uri &other) const { return !(this->operator == (other)); } private: friend class uri_builder; // Encodes all characters not in given set determined by given function. _ASYNCRTIMP static utility::string_t __cdecl encode_impl(const utility::string_t &raw, const std::function& should_encode); utility::string_t m_uri; details::uri_components m_components; }; } // namespace web ================================================ FILE: Include/cpprestinclude/cpprest/containerstream.h ================================================ #if !XSAPI_NO_PPL #if HC_PLATFORM_IS_MICROSOFT #pragma warning( push ) #pragma warning( disable : 26444 ) // ignore various unnamed objects #endif /*** * ==++== * * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * ==--== * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ * * This file defines a basic STL-container-based stream buffer. Reading from the buffer will not remove any data * from it and seeking is thus supported. * * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk * * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ****/ #pragma once #include #include #include #include #include "pplx/pplxtasks.h" #include "cpprest/astreambuf.h" #include "cpprest/streams.h" namespace Concurrency { namespace streams { // Forward declarations template class container_buffer; namespace details { /// /// The basic_container_buffer class serves as a memory-based steam buffer that supports writing or reading /// sequences of characters. /// The class itself should not be used in application code, it is used by the stream definitions farther down in the header file. /// /// When closed, neither writing nor reading is supported any longer. basic_container_buffer does not support simultaneous use of the buffer /// for reading and writing. template class basic_container_buffer : public streams::details::streambuf_state_manager { public: typedef typename _CollectionType::value_type _CharType; typedef typename basic_streambuf<_CharType>::traits traits; typedef typename basic_streambuf<_CharType>::int_type int_type; typedef typename basic_streambuf<_CharType>::pos_type pos_type; typedef typename basic_streambuf<_CharType>::off_type off_type; /// /// Returns the underlying data container /// _CollectionType& collection() { return m_data; } /// /// Destructor /// virtual ~basic_container_buffer() { // Invoke the synchronous versions since we need to // purge the request queue before deleting the buffer this->_close_read(); this->_close_write(); } protected: /// /// can_seek is used to determine whether a stream buffer supports seeking. /// virtual bool can_seek() const { return this->is_open(); } /// /// has_size is used to determine whether a stream buffer supports size(). /// virtual bool has_size() const { return this->is_open(); } /// /// Gets the size of the stream, if known. Calls to has_size will determine whether /// the result of size can be relied on. /// virtual utility::size64_t size() const { return utility::size64_t(m_data.size()); } /// /// Get the stream buffer size, if one has been set. /// /// The direction of buffering (in or out) /// An implementation that does not support buffering will always return '0'. virtual size_t buffer_size(std::ios_base::openmode = std::ios_base::in) const { return 0; } /// /// Sets the stream buffer implementation to buffer or not buffer. /// /// The size to use for internal buffering, 0 if no buffering should be done. /// The direction of buffering (in or out) /// An implementation that does not support buffering will silently ignore calls to this function and it will not have any effect on what is returned by subsequent calls to . virtual void set_buffer_size(size_t , std::ios_base::openmode = std::ios_base::in) { return; } /// /// For any input stream, in_avail returns the number of characters that are immediately available /// to be consumed without blocking. May be used in conjunction with to read data without /// incurring the overhead of using tasks. /// virtual size_t in_avail() const { // See the comment in seek around the restriction that we do not allow read head to // seek beyond the current write_end. _ASSERTE(m_current_position <= m_data.size()); msl::safeint3::SafeInt readhead(m_current_position); msl::safeint3::SafeInt writeend(m_data.size()); return (size_t)(writeend - readhead); } virtual pplx::task _sync() { return pplx::task_from_result(true); } virtual pplx::task _putc(_CharType ch) { int_type retVal = (this->write(&ch, 1) == 1) ? static_cast(ch) : traits::eof(); return pplx::task_from_result(retVal); } virtual pplx::task _putn(const _CharType *ptr, size_t count) { return pplx::task_from_result(this->write(ptr, count)); } /// /// Allocates a contiguous memory block and returns it. /// /// The number of characters to allocate. /// A pointer to a block to write to, null if the stream buffer implementation does not support alloc/commit. _CharType* _alloc(size_t count) { if (!this->can_write()) return nullptr; // Allocate space resize_for_write(m_current_position+count); // Let the caller copy the data return (_CharType*)&m_data[m_current_position]; } /// /// Submits a block already allocated by the stream buffer. /// /// The number of characters to be committed. void _commit(size_t actual ) { // Update the write position and satisfy any pending reads update_current_position(m_current_position+actual); } /// /// Gets a pointer to the next already allocated contiguous block of data. /// /// A reference to a pointer variable that will hold the address of the block on success. /// The number of contiguous characters available at the address in 'ptr.' /// true if the operation succeeded, false otherwise. /// /// A return of false does not necessarily indicate that a subsequent read operation would fail, only that /// there is no block to return immediately or that the stream buffer does not support the operation. /// The stream buffer may not de-allocate the block until is called. /// If the end of the stream is reached, the function will return true, a null pointer, and a count of zero; /// a subsequent read will not succeed. /// virtual bool acquire(_Out_ _CharType*& ptr, _Out_ size_t& count) { ptr = nullptr; count = 0; if (!this->can_read()) return false; count = in_avail(); if (count > 0) { ptr = (_CharType*)&m_data[m_current_position]; return true; } else { // Can only be open for read OR write, not both. If there is no data then // we have reached the end of the stream so indicate such with true. return true; } } /// /// Releases a block of data acquired using . This frees the stream buffer to de-allocate the /// memory, if it so desires. Move the read position ahead by the count. /// /// A pointer to the block of data to be released. /// The number of characters that were read. virtual void release(_Out_writes_opt_ (count) _CharType *ptr, _In_ size_t count) { if (ptr != nullptr) update_current_position(m_current_position + count); } virtual pplx::task _getn(_Out_writes_ (count) _CharType *ptr, _In_ size_t count) { return pplx::task_from_result(this->read(ptr, count)); } size_t _sgetn(_Out_writes_ (count) _CharType *ptr, _In_ size_t count) { return this->read(ptr, count); } virtual size_t _scopy(_Out_writes_ (count) _CharType *ptr, _In_ size_t count) { return this->read(ptr, count, false); } virtual pplx::task _bumpc() { return pplx::task_from_result(this->read_byte(true)); } virtual int_type _sbumpc() { return this->read_byte(true); } virtual pplx::task _getc() { return pplx::task_from_result(this->read_byte(false)); } int_type _sgetc() { return this->read_byte(false); } virtual pplx::task _nextc() { this->read_byte(true); return pplx::task_from_result(this->read_byte(false)); } virtual pplx::task _ungetc() { auto pos = seekoff(-1, std::ios_base::cur, std::ios_base::in); if ( pos == (pos_type)traits::eof()) return pplx::task_from_result(traits::eof()); return this->getc(); } /// /// Gets the current read or write position in the stream. /// /// The I/O direction to seek (see remarks) /// The current position. EOF if the operation fails. /// Some streams may have separate write and read cursors. /// For such streams, the direction parameter defines whether to move the read or the write cursor. virtual pos_type getpos(std::ios_base::openmode mode) const { if ( ((mode & std::ios_base::in) && !this->can_read()) || ((mode & std::ios_base::out) && !this->can_write())) return static_cast(traits::eof()); return static_cast(m_current_position); } /// /// Seeks to the given position. /// /// The offset from the beginning of the stream. /// The I/O direction to seek (see remarks). /// The position. EOF if the operation fails. /// Some streams may have separate write and read cursors. For such streams, the direction parameter defines whether to move the read or the write cursor. virtual pos_type seekpos(pos_type position, std::ios_base::openmode mode) { pos_type beg(0); // In order to support relative seeking from the end position we need to fix an end position. // Technically, there is no end for the stream buffer as new writes would just expand the buffer. // For now, we assume that the current write_end is the end of the buffer. We use this artificial // end to restrict the read head from seeking beyond what is available. pos_type end(m_data.size()); if (position >= beg) { auto pos = static_cast(position); // Read head if ((mode & std::ios_base::in) && this->can_read()) { if (position <= end) { // We do not allow reads to seek beyond the end or before the start position. update_current_position(pos); return static_cast(m_current_position); } } // Write head if ((mode & std::ios_base::out) && this->can_write()) { // Allocate space resize_for_write(pos); // Nothing to really copy // Update write head and satisfy read requests if any update_current_position(pos); return static_cast(m_current_position); } } return static_cast(traits::eof()); } /// /// Seeks to a position given by a relative offset. /// /// The relative position to seek to /// The starting point (beginning, end, current) for the seek. /// The I/O direction to seek (see remarks) /// The position. EOF if the operation fails. /// Some streams may have separate write and read cursors. /// For such streams, the mode parameter defines whether to move the read or the write cursor. virtual pos_type seekoff(off_type offset, std::ios_base::seekdir way, std::ios_base::openmode mode) { pos_type beg = 0; pos_type cur = static_cast(m_current_position); pos_type end = static_cast(m_data.size()); switch ( way ) { case std::ios_base::beg: return seekpos(beg + offset, mode); case std::ios_base::cur: return seekpos(cur + offset, mode); case std::ios_base::end: return seekpos(end + offset, mode); default: return static_cast(traits::eof()); } } private: template friend class streams::container_buffer; /// /// Constructor /// basic_container_buffer(std::ios_base::openmode mode) : streambuf_state_manager(mode), m_current_position(0) { validate_mode(mode); } /// /// Constructor /// basic_container_buffer(_CollectionType data, std::ios_base::openmode mode) : streambuf_state_manager(mode), m_data(std::move(data)), m_current_position((mode & std::ios_base::in) ? 0 : m_data.size()) { validate_mode(mode); } static void validate_mode(std::ios_base::openmode mode) { // Disallow simultaneous use of the stream buffer for writing and reading. if ((mode & std::ios_base::in) && (mode & std::ios_base::out)) throw std::invalid_argument("this combination of modes on container stream not supported"); } /// /// Determine if the request can be satisfied. /// bool can_satisfy(size_t) { // We can always satisfy a read, at least partially, unless the // read position is at the very end of the buffer. return (in_avail() > 0); } /// /// Reads a byte from the stream and returns it as int_type. /// Note: This routine shall only be called if can_satisfy() returned true. /// int_type read_byte(bool advance = true) { _CharType value; auto read_size = this->read(&value, 1, advance); return read_size == 1 ? static_cast(value) : traits::eof(); } /// /// Reads up to count characters into ptr and returns the count of characters copied. /// The return value (actual characters copied) could be <= count. /// Note: This routine shall only be called if can_satisfy() returned true. /// size_t read(_Out_writes_ (count) _CharType *ptr, _In_ size_t count, bool advance = true) { if (!can_satisfy(count)) return 0; msl::safeint3::SafeInt request_size(count); msl::safeint3::SafeInt read_size = request_size.Min(in_avail()); size_t newPos = m_current_position + read_size; auto readBegin = std::begin(m_data) + m_current_position; auto readEnd = std::begin(m_data) + newPos; #ifdef _WIN32 // Avoid warning C4996: Use checked iterators under SECURE_SCL std::copy(readBegin, readEnd, stdext::checked_array_iterator<_CharType *>(ptr, count)); #else std::copy(readBegin, readEnd, ptr); #endif // _WIN32 if (advance) { update_current_position(newPos); } return (size_t) read_size; } /// /// Write count characters from the ptr into the stream buffer /// size_t write(const _CharType *ptr, size_t count) { if (!this->can_write() || (count == 0)) return 0; auto newSize = m_current_position + count; // Allocate space resize_for_write(newSize); // Copy the data std::copy(ptr, ptr + count, std::begin(m_data) + m_current_position); // Update write head and satisfy pending reads if any update_current_position(newSize); return count; } /// /// Resize the underlying container to match the new write head /// void resize_for_write(size_t newPos) { // Resize the container if required if (newPos > m_data.size()) { m_data.resize(newPos); } } /// /// Updates the write head to the new position /// void update_current_position(size_t newPos) { // The new write head m_current_position = newPos; _ASSERTE(m_current_position <= m_data.size()); } // The actual data store _CollectionType m_data; // Read/write head size_t m_current_position; }; } // namespace details /// /// The basic_container_buffer class serves as a memory-based steam buffer that supports writing or reading /// sequences of characters. Note that it cannot be used as a consumer producer buffer. /// /// /// The type of the container. /// /// /// This is a reference-counted version of basic_container_buffer. /// template class container_buffer : public streambuf { public: typedef typename _CollectionType::value_type char_type; /// /// Creates a container_buffer given a collection, copying its data into the buffer. /// /// The collection that is the starting point for the buffer /// The I/O mode that the buffer should use (in / out) container_buffer(_CollectionType data, std::ios_base::openmode mode = std::ios_base::in) : streambuf( std::shared_ptr>(new streams::details::basic_container_buffer<_CollectionType>(std::move(data), mode))) { } /// /// Creates a container_buffer starting from an empty collection. /// /// The I/O mode that the buffer should use (in / out) container_buffer(std::ios_base::openmode mode = std::ios_base::out) : streambuf( std::shared_ptr>(new details::basic_container_buffer<_CollectionType>(mode))) { } _CollectionType& collection() const { auto listBuf = static_cast *>(this->get_base().get()); return listBuf->collection(); } }; /// /// A static class to allow users to create input and out streams based off STL /// collections. The sole purpose of this class to avoid users from having to know /// anything about stream buffers. /// /// The type of the STL collection. template class container_stream { public: typedef typename _CollectionType::value_type char_type; typedef container_buffer<_CollectionType> buffer_type; /// /// Creates an input stream given an STL container. /// /// STL container to back the input stream. /// An input stream. static concurrency::streams::basic_istream open_istream(_CollectionType data) { return concurrency::streams::basic_istream(buffer_type(std::move(data), std::ios_base::in)); } /// /// Creates an output stream using an STL container as the storage. /// /// An output stream. static concurrency::streams::basic_ostream open_ostream() { return concurrency::streams::basic_ostream(buffer_type(std::ios_base::out)); } }; /// /// The stringstream allows an input stream to be constructed from std::string or std::wstring /// For output streams the underlying string container could be retrieved using buf->collection(). /// typedef container_stream> stringstream; typedef stringstream::buffer_type stringstreambuf; typedef container_stream wstringstream; typedef wstringstream::buffer_type wstringstreambuf; /// /// The bytestream is a static class that allows an input stream to be constructed from any STL container. /// class bytestream { public: /// /// Creates a single byte character input stream given an STL container. /// /// The type of the STL collection. /// STL container to back the input stream. /// An single byte character input stream. template static concurrency::streams::istream open_istream(_CollectionType data) { return concurrency::streams::istream(streams::container_buffer<_CollectionType>(std::move(data), std::ios_base::in)); } /// /// Creates a single byte character output stream using an STL container as storage. /// /// The type of the STL collection. /// A single byte character output stream. template static concurrency::streams::ostream open_ostream() { return concurrency::streams::ostream(streams::container_buffer<_CollectionType>()); } }; }} // namespaces #if HC_PLATFORM_IS_MICROSOFT #pragma warning( pop ) #endif #endif // !XSAPI_NO_PPL ================================================ FILE: Include/cpprestinclude/cpprest/details/SafeInt3.hpp ================================================ /*----------------------------------------------------------------------------------------------------------- SafeInt.hpp Version 3.0.18p This software is licensed under the Microsoft Public License (Ms-PL). For more information about Microsoft open source licenses, refer to http://www.microsoft.com/opensource/licenses.mspx This license governs use of the accompanying software. If you use the software, you accept this license. If you do not accept the license, do not use the software. Definitions The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under U.S. copyright law. A "contribution" is the original software, or any additions or changes to the software. A "contributor" is any person that distributes its contribution under this license. "Licensed patents" are a contributor's patent claims that read directly on its contribution. Grant of Rights (A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create. (B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software. Conditions and Limitations (A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks. (B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically. (C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software. (D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license. (E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees, or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement. Copyright (c) Microsoft Corporation. All rights reserved. This header implements an integer handling class designed to catch unsafe integer operations This header compiles properly at Wall on Visual Studio, -Wall on gcc, and -Weverything on clang. Please read the leading comments before using the class. ---------------------------------------------------------------*/ #pragma once // It is a bit tricky to sort out what compiler we are actually using, // do this once here, and avoid cluttering the code #define VISUAL_STUDIO_COMPILER 0 #define CLANG_COMPILER 1 #define GCC_COMPILER 2 #define UNKNOWN_COMPILER -1 // Clang will sometimes pretend to be Visual Studio // and does pretend to be gcc. Check it first, as nothing else pretends to be clang #if defined __clang__ #define SAFEINT_COMPILER CLANG_COMPILER #elif defined __GNUC__ #define SAFEINT_COMPILER GCC_COMPILER #elif defined _MSC_VER #define SAFEINT_COMPILER VISUAL_STUDIO_COMPILER #else #define SAFEINT_COMPILER UNKNOWN_COMPILER #endif // Enable compiling with /Wall under VC #if SAFEINT_COMPILER == VISUAL_STUDIO_COMPILER #pragma warning( push ) // Disable warnings coming from headers #pragma warning( disable:4987 4820 4987 4820 ) #endif // Need this for ptrdiff_t on some compilers #include #include #if SAFEINT_COMPILER == VISUAL_STUDIO_COMPILER && defined _M_AMD64 #include #define SAFEINT_USE_INTRINSICS 1 #else #define SAFEINT_USE_INTRINSICS 0 #endif #if SAFEINT_COMPILER == VISUAL_STUDIO_COMPILER #pragma warning( pop ) #endif // Various things needed for GCC #if SAFEINT_COMPILER == GCC_COMPILER || SAFEINT_COMPILER == CLANG_COMPILER #define NEEDS_INT_DEFINED #if !defined NULL #define NULL 0 #endif // GCC warning suppression #if SAFEINT_COMPILER == GCC_COMPILER #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-local-typedefs" #endif #include // clang only #if SAFEINT_COMPILER == CLANG_COMPILER #if __has_feature(cxx_nullptr) #define NEEDS_NULLPTR_DEFINED 0 #endif #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wc++11-long-long" #pragma clang diagnostic ignored "-Wold-style-cast" #pragma clang diagnostic ignored "-Wunused-local-typedef" #endif #endif // If the user made a choice, respect it #if !defined #if !defined NEEDS_NULLPTR_DEFINED // Visual Studio 2010 and higher support this #if SAFEINT_COMPILER == VISUAL_STUDIO_COMPILER #if (_MSC_VER < 1600) #define NEEDS_NULLPTR_DEFINED 1 #else #define NEEDS_NULLPTR_DEFINED 0 #endif #else // Let everything else trigger based on whether we use c++11 or above #if __cplusplus >= 201103L #define NEEDS_NULLPTR_DEFINED 0 #else #define NEEDS_NULLPTR_DEFINED 1 #endif #endif #endif #if NEEDS_NULLPTR_DEFINED #define nullptr NULL #endif #ifndef C_ASSERT #define C_ASSERT(e) typedef char __C_ASSERT__[(e)?1:-1] #endif // Let's test some assumptions // We're assuming two's complement negative numbers C_ASSERT( -1 == static_cast(0xffffffff) ); /************* Compiler Options ***************************************************************************************************** SafeInt supports several compile-time options that can change the behavior of the class. Compiler options: SAFEINT_WARN_64BIT_PORTABILITY - this re-enables various warnings that happen when /Wp64 is used. Enabling this option is not recommended. NEEDS_INT_DEFINED - if your compiler does not support __int8, __int16, __int32 and __int64, you can enable this. SAFEINT_ASSERT_ON_EXCEPTION - it is often easier to stop on an assert and figure out a problem than to try and figure out how you landed in the catch block. SafeIntDefaultExceptionHandler - if you'd like to replace the exception handlers SafeInt provides, define your replacement and define this. Note - two built in (Windows-specific) options exist: - SAFEINT_FAILFAST - bypasses all exception handlers, exits the app with an exception - SAFEINT_RAISE_EXCEPTION - throws Win32 exceptions, which can be caught SAFEINT_DISALLOW_UNSIGNED_NEGATION - Invoking the unary negation operator creates warnings, but if you'd like it to completely fail to compile, define this. ANSI_CONVERSIONS - This changes the class to use default comparison behavior, which may be unsafe. Enabling this option is not recommended. SAFEINT_DISABLE_BINARY_ASSERT - binary AND, OR or XOR operations on mixed size types can produce unexpected results. If you do this, the default is to assert. Set this if you prefer not to assert under these conditions. SIZE_T_CAST_NEEDED - some compilers complain if there is not a cast to size_t, others complain if there is one. This lets you not have your compiler complain. SAFEINT_DISABLE_SHIFT_ASSERT - Set this option if you don't want to assert when shifting more bits than the type has. Enabling this option is not recommended. ************************************************************************************************************************************/ /* * The SafeInt class is designed to have as low an overhead as possible * while still ensuring that all integer operations are conducted safely. * Nearly every operator has been overloaded, with a very few exceptions. * * A usability-safety trade-off has been made to help ensure safety. This * requires that every operation return either a SafeInt or a bool. If we * allowed an operator to return a base integer type T, then the following * can happen: * * char i = SafeInt(32) * 2 + SafeInt(16) * 4; * * The * operators take precedence, get overloaded, return a char, and then * you have: * * char i = (char)64 + (char)64; //overflow! * * This situation would mean that safety would depend on usage, which isn't * acceptable. * * One key operator that is missing is an implicit cast to type T. The reason for * this is that if there is an implicit cast operator, then we end up with * an ambiguous compile-time precedence. Because of this amiguity, there * are two methods that are provided: * * Casting operators for every native integer type * Version 3 note - it now compiles correctly for size_t without warnings * * SafeInt::Ptr() - returns the address of the internal integer * Note - the '&' (address of) operator has been overloaded and returns * the address of the internal integer. * * The SafeInt class should be used in any circumstances where ensuring * integrity of the calculations is more important than performance. See Performance * Notes below for additional information. * * Many of the conditionals will optimize out or be inlined for a release * build (especially with /Ox), but it does have significantly more overhead, * especially for signed numbers. If you do not _require_ negative numbers, use * unsigned integer types - certain types of problems cannot occur, and this class * performs most efficiently. * * Here's an example of when the class should ideally be used - * * void* AllocateMemForStructs(int StructSize, int HowMany) * { * SafeInt s(StructSize); * * s *= HowMany; * * return malloc(s); * * } * * Here's when it should NOT be used: * * void foo() * { * int i; * * for(i = 0; i < 0xffff; i++) * .... * } * * Error handling - a SafeInt class will throw exceptions if something * objectionable happens. The exceptions are SafeIntException classes, * which contain an enum as a code. * * Typical usage might be: * * bool foo() * { * SafeInt s; //note that s == 0 unless set * * try{ * s *= 23; * .... * } * catch(SafeIntException err) * { * //handle errors here * } * } * * Update for 3.0 - the exception class is now a template parameter. * You can replace the exception class with any exception class you like. This is accomplished by: * 1) Create a class that has the following interface: * template <> class YourSafeIntExceptionHandler < YourException > { public: static __declspec(noreturn) void __stdcall SafeIntOnOverflow() { throw YourException( YourSafeIntArithmeticOverflowError ); } static __declspec(noreturn) void __stdcall SafeIntOnDivZero() { throw YourException( YourSafeIntDivideByZeroError ); } }; * * Note that you don't have to throw C++ exceptions, you can throw Win32 exceptions, or do * anything you like, just don't return from the call back into the code. * * 2) Either explicitly declare SafeInts like so: * SafeInt< int, YourSafeIntExceptionHandler > si; * or * #define SafeIntDefaultExceptionHandler YourSafeIntExceptionHandler * * Performance: * * Due to the highly nested nature of this class, you can expect relatively poor * performance in unoptimized code. In tests of optimized code vs. correct inline checks * in native code, this class has been found to take approximately 8% more CPU time (this varies), * most of which is due to exception handling. Solutions: * * 1) Compile optimized code - /Ox is best, /O2 also performs well. Interestingly, /O1 * (optimize for size) does not work as well. * 2) If that 8% hit is really a serious problem, walk through the code and inline the * exact same checks as the class uses. * 3) Some operations are more difficult than others - avoid using signed integers, and if * possible keep them all the same size. 64-bit integers are also expensive. Mixing * different integer sizes and types may prove expensive. Be aware that literals are * actually ints. For best performance, cast literals to the type desired. * * * Performance update * The current version of SafeInt uses template specialization to force the compiler to invoke only the * operator implementation needed for any given pair of types. This will dramatically improve the perf * of debug builds. * * 3.0 update - not only have we maintained the specialization, there were some cases that were overly complex, * and using some additional cases (e.g. signed __int64 and unsigned __int64) resulted in some simplification. * Additionally, there was a lot of work done to better optimize the 64-bit multiplication. * * Binary Operators * * All of the binary operators have certain assumptions built into the class design. * This is to ensure correctness. Notes on each class of operator follow: * * Arithmetic Operators (*,/,+,-,%) * There are three possible variants: * SafeInt< T, E > op SafeInt< T, E > * SafeInt< T, E > op U * U op SafeInt< T, E > * * The SafeInt< T, E > op SafeInt< U, E > variant is explicitly not supported, and if you try to do * this the compiler with throw the following error: * * error C2593: 'operator *' is ambiguous * * This is because the arithmetic operators are required to return a SafeInt of some type. * The compiler cannot know whether you'd prefer to get a type T or a type U returned. If * you need to do this, you need to extract the value contained within one of the two using * the casting operator. For example: * * SafeInt< T, E > t, result; * SafeInt< U, E > u; * * result = t * (U)u; * * Comparison Operators * Because each of these operators return type bool, mixing SafeInts of differing types is * allowed. * * Shift Operators * Shift operators always return the type on the left hand side of the operator. Mixed type * operations are allowed because the return type is always known. * * Boolean Operators * Like comparison operators, these overloads always return type bool, and mixed-type SafeInts * are allowed. Additionally, specific overloads exist for type bool on both sides of the * operator. * * Binary Operators * Mixed-type operations are discouraged, however some provision has been made in order to * enable things like: * * SafeInt c = 2; * * if(c & 0x02) * ... * * The "0x02" is actually an int, and it needs to work. * In the case of binary operations on integers smaller than 32-bit, or of mixed type, corner * cases do exist where you could get unexpected results. In any case where SafeInt returns a different * result than the underlying operator, it will call assert(). You should examine your code and cast things * properly so that you are not programming with side effects. * * Documented issues: * * This header compiles correctly at /W4 using VC++ 8 (Version 14.00.50727.42) and later. * As of this writing, I believe it will also work for VC 7.1, but not for VC 7.0 or below. * If you need a version that will work with lower level compilers, try version 1.0.7. None * of them work with Visual C++ 6, and gcc didn't work very well, either, though this hasn't * been tried recently. * * It is strongly recommended that any code doing integer manipulation be compiled at /W4 * - there are a number of warnings which pertain to integer manipulation enabled that are * not enabled at /W3 (default for VC++) * * Perf note - postfix operators are slightly more costly than prefix operators. * Unless you're actually assigning it to something, ++SafeInt is less expensive than SafeInt++ * * The comparison operator behavior in this class varies from the ANSI definition, which is * arguably broken. As an example, consider the following: * * unsigned int l = 0xffffffff; * char c = -1; * * if(c == l) * printf("Why is -1 equal to 4 billion???\n"); * * The problem here is that c gets cast to an int, now has a value of 0xffffffff, and then gets * cast again to an unsigned int, losing the true value. This behavior is despite the fact that * an __int64 exists, and the following code will yield a different (and intuitively correct) * answer: * * if((__int64)c == (__int64)l)) * printf("Why is -1 equal to 4 billion???\n"); * else * printf("Why doesn't the compiler upcast to 64-bits when needed?\n"); * * Note that combinations with smaller integers won't display the problem - if you * changed "unsigned int" above to "unsigned short", you'd get the right answer. * * If you prefer to retain the ANSI standard behavior insert * #define ANSI_CONVERSIONS * into your source. Behavior differences occur in the following cases: * 8, 16, and 32-bit signed int, unsigned 32-bit int * any signed int, unsigned 64-bit int * Note - the signed int must be negative to show the problem * * * Revision history: * * Oct 12, 2003 - Created * Author - David LeBlanc - dleblanc@microsoft.com * * Oct 27, 2003 - fixed numerous items pointed out by michmarc and bdawson * Dec 28, 2003 - 1.0 * added support for mixed-type operations * thanks to vikramh * also fixed broken __int64 multiplication section * added extended support for mixed-type operations where possible * Jan 28, 2004 - 1.0.1 * changed WCHAR to wchar_t * fixed a construct in two mixed-type assignment overloads that was * not compiling on some compilers * Also changed name of private method to comply with standards on * reserved names * Thanks to Niels Dekker for the input * Feb 12, 2004 - 1.0.2 * Minor changes to remove dependency on Windows headers * Consistently used __int16, __int32 and __int64 to ensure * portability * May 10, 2004 - 1.0.3 * Corrected bug in one case of GreaterThan * July 22, 2004 - 1.0.4 * Tightened logic in addition check (saving 2 instructions) * Pulled error handler out into function to enable user-defined replacement * Made internal type of SafeIntException an enum (as per Niels' suggestion) * Added casts for base integer types (as per Scott Meyers' suggestion) * Updated usage information - see important new perf notes. * Cleaned up several const issues (more thanks to Niels) * * Oct 1, 2004 - 1.0.5 * Added support for SEH exceptions instead of C++ exceptions - Win32 only * Made handlers for DIV0 and overflows individually overridable * Commented out the destructor - major perf gains here * Added cast operator for type long, since long != __int32 * Corrected a couple of missing const modifiers * Fixed broken >= and <= operators for type U op SafeInt< T, E > * Nov 5, 2004 - 1.0.6 * Implemented new logic in binary operators to resolve issues with * implicit casts * Fixed casting operator because char != signed char * Defined __int32 as int instead of long * Removed unsafe SafeInt::Value method * Re-implemented casting operator as a result of removing Value method * Dec 1, 2004 - 1.0.7 * Implemented specialized operators for pointer arithmetic * Created overloads for cases of U op= SafeInt. What you do with U * after that may be dangerous. * Fixed bug in corner case of MixedSizeModulus * Fixed bug in MixedSizeMultiply and MixedSizeDivision with input of 0 * Added throw() decorations * * Apr 12, 2005 - 2.0 * Extensive revisions to leverage template specialization. * April, 2007 Extensive revisions for version 3.0 * Nov 22, 2009 Forked from MS internal code * Changes needed to support gcc compiler - many thanks to Niels Dekker * for determining not just the issues, but also suggesting fixes. * Also updating some of the header internals to be the same as the upcoming Visual Studio version. * * Jan 16, 2010 64-bit gcc has long == __int64, which means that many of the existing 64-bit * templates are over-specialized. This forces a redefinition of all the 64-bit * multiplication routines to use pointers instead of references for return * values. Also, let's use some intrinsics for x64 Microsoft compiler to * reduce code size, and hopefully improve efficiency. * * June 21, 2014 Better support for clang, higher warning levels supported for all 3 primary supported compilers (Visual Studio, clang, gcc). Also started to converge the code base such that the public CodePlex version will be a drop-in replacement for the Visual Studio version. * Note about code style - throughout this class, casts will be written using C-style (T), * not C++ style static_cast< T >. This is because the class is nearly always dealing with integer * types, and in this case static_cast and a C cast are equivalent. Given the large number of casts, * the code is a little more readable this way. In the event a cast is needed where static_cast couldn't * be substituted, we'll use the new templatized cast to make it explicit what the operation is doing. * ************************************************************************************************************ * Version 3.0 changes: * * 1) The exception type thrown is now replacable, and you can throw your own exception types. This should help * those using well-developed exception classes. * 2) The 64-bit multiplication code has had a lot of perf work done, and should be faster than 2.0. * 3) There is now limited floating point support. You can initialize a SafeInt with a floating point type, * and you can cast it out (or assign) to a float as well. * 4) There is now an Align method. I noticed people use this a lot, and rarely check errors, so now you have one. * * Another major improvement is the addition of external functions - if you just want to check an operation, this can now happen: * All of the following can be invoked without dealing with creating a class, or managing exceptions. This is especially handy * for 64-bit porting, since SafeCast compiles away for a 32-bit cast from size_t to unsigned long, but checks it for 64-bit. * * inline bool SafeCast( const T From, U& To ) throw() * inline bool SafeEquals( const T t, const U u ) throw() * inline bool SafeNotEquals( const T t, const U u ) throw() * inline bool SafeGreaterThan( const T t, const U u ) throw() * inline bool SafeGreaterThanEquals( const T t, const U u ) throw() * inline bool SafeLessThan( const T t, const U u ) throw() * inline bool SafeLessThanEquals( const T t, const U u ) throw() * inline bool SafeModulus( const T& t, const U& u, T& result ) throw() * inline bool SafeMultiply( T t, U u, T& result ) throw() * inline bool SafeDivide( T t, U u, T& result ) throw() * inline bool SafeAdd( T t, U u, T& result ) throw() * inline bool SafeSubtract( T t, U u, T& result ) throw() * */ //use these if the compiler does not support _intXX #ifdef NEEDS_INT_DEFINED #define __int8 char #define __int16 short #define __int32 int #define __int64 long long #endif namespace msl { namespace safeint3 { // catch these to handle errors // Currently implemented code values: // ERROR_ARITHMETIC_OVERFLOW // EXCEPTION_INT_DIVIDE_BY_ZERO enum SafeIntError { SafeIntNoError = 0, SafeIntArithmeticOverflow, SafeIntDivideByZero }; } // safeint3 } // msl /* * Error handler classes * Using classes to deal with exceptions is going to allow the most * flexibility, and we can mix different error handlers in the same project * or even the same file. It isn't advisable to do this in the same function * because a SafeInt< int, MyExceptionHandler > isn't the same thing as * SafeInt< int, YourExceptionHander >. * If for some reason you have to translate between the two, cast one of them back to its * native type. * * To use your own exception class with SafeInt, first create your exception class, * which may look something like the SafeIntException class below. The second step is to * create a template specialization that implements SafeIntOnOverflow and SafeIntOnDivZero. * For example: * * template <> class SafeIntExceptionHandler < YourExceptionClass > * { * static __declspec(noreturn) void __stdcall SafeIntOnOverflow() * { * throw YourExceptionClass( EXCEPTION_INT_OVERFLOW ); * } * * static __declspec(noreturn) void __stdcall SafeIntOnDivZero() * { * throw YourExceptionClass( EXCEPTION_INT_DIVIDE_BY_ZERO ); * } * }; * * typedef SafeIntExceptionHandler < YourExceptionClass > YourSafeIntExceptionHandler * You'd then declare your SafeInt objects like this: * SafeInt< int, YourSafeIntExceptionHandler > * * Unfortunately, there is no such thing as partial template specialization in typedef * statements, so you have three options if you find this cumbersome: * * 1) Create a holder class: * * template < typename T > * class MySafeInt * { * public: * SafeInt< T, MyExceptionClass> si; * }; * * You'd then declare an instance like so: * MySafeInt< int > i; * * You'd lose handy things like initialization - it would have to be initialized as: * * i.si = 0; * * 2) You could create a typedef for every int type you deal with: * * typedef SafeInt< int, MyExceptionClass > MySafeInt; * typedef SafeInt< char, MyExceptionClass > MySafeChar; * * and so on. The second approach is probably more usable, and will just drop into code * better, which is the original intent of the SafeInt class. * * 3) If you're going to consistently use a different class to handle your exceptions, * you can override the default typedef like so: * * #define SafeIntDefaultExceptionHandler YourSafeIntExceptionHandler * * Overall, this is probably the best approach. * */ // On the Microsoft compiler, violating a throw() annotation is a silent error. // Other compilers might turn these into exceptions, and some users may want to not have throw() enabled. // In addition, some error handlers may not throw C++ exceptions, which makes everything no throw. #if defined SAFEINT_REMOVE_NOTHROW #define SAFEINT_NOTHROW #else #define SAFEINT_NOTHROW throw() #endif namespace msl { namespace safeint3 { // If you would like to use your own custom assert // Define SAFEINT_ASSERT #if !defined SAFEINT_ASSERT #include #define SAFEINT_ASSERT(x) assert(x) #endif #if defined SAFEINT_ASSERT_ON_EXCEPTION inline void SafeIntExceptionAssert() SAFEINT_NOTHROW { SAFEINT_ASSERT(false); } #else inline void SafeIntExceptionAssert() SAFEINT_NOTHROW {} #endif #if SAFEINT_COMPILER == GCC_COMPILER || SAFEINT_COMPILER == CLANG_COMPILER #define SAFEINT_NORETURN __attribute__((noreturn)) #define SAFEINT_STDCALL #define SAFEINT_VISIBLE __attribute__ ((__visibility__("default"))) #define SAFEINT_WEAK __attribute__ ((weak)) #else #define SAFEINT_NORETURN __declspec(noreturn) #define SAFEINT_STDCALL __stdcall #define SAFEINT_VISIBLE #define SAFEINT_WEAK #endif class SAFEINT_VISIBLE SafeIntException { public: SafeIntException() SAFEINT_NOTHROW { m_code = SafeIntNoError; } SafeIntException( SafeIntError code ) SAFEINT_NOTHROW { m_code = code; } SafeIntError m_code; }; namespace SafeIntInternal { // Visual Studio version of SafeInt provides for two possible error // handlers: // SafeIntErrorPolicy_SafeIntException - C++ exception, default if not otherwise defined // SafeIntErrorPolicy_InvalidParameter - Calls fail fast (Windows-specific), bypasses any exception handlers, // exits the app with a crash template < typename E > class SafeIntExceptionHandler; template <> class SafeIntExceptionHandler < SafeIntException > { public: static SAFEINT_NORETURN void SAFEINT_STDCALL SafeIntOnOverflow() { SafeIntExceptionAssert(); throw SafeIntException( SafeIntArithmeticOverflow ); } static SAFEINT_NORETURN void SAFEINT_STDCALL SafeIntOnDivZero() { SafeIntExceptionAssert(); throw SafeIntException( SafeIntDivideByZero ); } }; #if !defined _CRT_SECURE_INVALID_PARAMETER // Calling fail fast is somewhat more robust than calling abort, // but abort is the closest we can manage without Visual Studio support // Need the header for abort() #include #define _CRT_SECURE_INVALID_PARAMETER(msg) abort() #endif class SafeInt_InvalidParameter { public: static SAFEINT_NORETURN void SafeIntOnOverflow() SAFEINT_NOTHROW { SafeIntExceptionAssert(); _CRT_SECURE_INVALID_PARAMETER("SafeInt Arithmetic Overflow"); } static SAFEINT_NORETURN void SafeIntOnDivZero() SAFEINT_NOTHROW { SafeIntExceptionAssert(); _CRT_SECURE_INVALID_PARAMETER("SafeInt Divide By Zero"); } }; #if defined _WINDOWS_ class SafeIntWin32ExceptionHandler { public: static SAFEINT_NORETURN void SAFEINT_STDCALL SafeIntOnOverflow() SAFEINT_NOTHROW { SafeIntExceptionAssert(); RaiseException( static_cast(EXCEPTION_INT_OVERFLOW), EXCEPTION_NONCONTINUABLE, 0, 0); } static SAFEINT_NORETURN void SAFEINT_STDCALL SafeIntOnDivZero() SAFEINT_NOTHROW { SafeIntExceptionAssert(); RaiseException( static_cast(EXCEPTION_INT_DIVIDE_BY_ZERO), EXCEPTION_NONCONTINUABLE, 0, 0); } }; #endif } // namespace SafeIntInternal // both of these have cross-platform support typedef SafeIntInternal::SafeIntExceptionHandler < SafeIntException > CPlusPlusExceptionHandler; typedef SafeIntInternal::SafeInt_InvalidParameter InvalidParameterExceptionHandler; // This exception handler is no longer recommended, but is left here in order not to break existing users #if defined _WINDOWS_ typedef SafeIntInternal::SafeIntWin32ExceptionHandler Win32ExceptionHandler; #endif // For Visual Studio compatibility #if defined VISUAL_STUDIO_SAFEINT_COMPAT typedef CPlusPlusExceptionHandler SafeIntErrorPolicy_SafeIntException; typedef InvalidParameterExceptionHandler SafeIntErrorPolicy_InvalidParameter; #endif // If the user hasn't defined a default exception handler, // define one now, depending on whether they would like Win32 or C++ exceptions // This library will use conditional noexcept soon, but not in this release // Some users might mix exception handlers, which is not advised, but is supported #if !defined SafeIntDefaultExceptionHandler #if defined SAFEINT_RAISE_EXCEPTION #if !defined _WINDOWS_ #error Include windows.h in order to use Win32 exceptions #endif #define SafeIntDefaultExceptionHandler Win32ExceptionHandler #elif defined SAFEINT_FAILFAST #define SafeIntDefaultExceptionHandler InvalidParameterExceptionHandler #else #define SafeIntDefaultExceptionHandler CPlusPlusExceptionHandler #if !defined SAFEINT_EXCEPTION_HANDLER_CPP #define SAFEINT_EXCEPTION_HANDLER_CPP 1 #endif #endif #endif #if !defined SAFEINT_EXCEPTION_HANDLER_CPP #define SAFEINT_EXCEPTION_HANDLER_CPP 0 #endif // If an error handler is chosen other than C++ exceptions, such as Win32 exceptions, fail fast, // or abort, then all methods become no throw. Some teams track throw() annotations closely, // and the following option provides for this. #if SAFEINT_EXCEPTION_HANDLER_CPP #define SAFEINT_CPP_THROW #else #define SAFEINT_CPP_THROW SAFEINT_NOTHROW #endif // Turns out we can fool the compiler into not seeing compile-time constants with // a simple template specialization template < int method > class CompileConst; template <> class CompileConst { public: static bool Value() SAFEINT_NOTHROW { return true; } }; template <> class CompileConst { public: static bool Value() SAFEINT_NOTHROW { return false; } }; // The following template magic is because we're now not allowed // to cast a float to an enum. This means that if we happen to assign // an enum to a SafeInt of some type, it won't compile, unless we prevent // isFloat = ( (T)( (float)1.1 ) > (T)1 ) // from compiling in the case of an enum, which is the point of the specialization // that follows. // If we have support for std, then we can do this easily, and detect enums as well template < typename T > class NumericType; #if defined _LIBCPP_TYPE_TRAITS || defined _TYPE_TRAITS_ // Continue to special case bool template <> class NumericType { public: enum{ isBool = true, isFloat = false, isInt = false }; }; template < typename T > class NumericType { public: enum { isBool = false, // We specialized out a bool isFloat = std::is_floating_point::value, // If it is an enum, then consider it an int type // This does allow someone to make a SafeInt from an enum type, which is not recommended, // but it also allows someone to add an enum value to a SafeInt, which is handy. isInt = std::is_integral::value || std::is_enum::value }; }; #else template <> class NumericType { public: enum{ isBool = true, isFloat = false, isInt = false }; }; template <> class NumericType { public: enum{ isBool = false, isFloat = false, isInt = true }; }; template <> class NumericType { public: enum{ isBool = false, isFloat = false, isInt = true }; }; template <> class NumericType { public: enum{ isBool = false, isFloat = false, isInt = true }; }; template <> class NumericType { public: enum{ isBool = false, isFloat = false, isInt = true }; }; template <> class NumericType { public: enum{ isBool = false, isFloat = false, isInt = true }; }; #if defined SAFEINT_USE_WCHAR_T || defined _NATIVE_WCHAR_T_DEFINED template <> class NumericType { public: enum{ isBool = false, isFloat = false, isInt = true }; }; #endif template <> class NumericType { public: enum{ isBool = false, isFloat = false, isInt = true }; }; template <> class NumericType { public: enum{ isBool = false, isFloat = false, isInt = true }; }; template <> class NumericType { public: enum{ isBool = false, isFloat = false, isInt = true }; }; template <> class NumericType { public: enum{ isBool = false, isFloat = false, isInt = true }; }; template <> class NumericType<__int64> { public: enum{ isBool = false, isFloat = false, isInt = true }; }; template <> class NumericType { public: enum{ isBool = false, isFloat = false, isInt = true }; }; template <> class NumericType { public: enum{ isBool = false, isFloat = true, isInt = false }; }; template <> class NumericType { public: enum{ isBool = false, isFloat = true, isInt = false }; }; template <> class NumericType { public: enum{ isBool = false, isFloat = true, isInt = false }; }; // Catch-all for anything not supported template < typename T > class NumericType { public: // We have some unknown type, which could be an enum. For parity with the code that uses , // We can try a static_cast - it if compiles, then it might be an enum, and should work. // If it is something else that just happens to have a constructor that takes an int, and a casting operator, // then it is possible something will go wrong, and for best results, cast it directly to an int before letting it // interact with a SafeInt enum { isBool = false, isFloat = false, isInt = static_cast( static_cast(0) ) == 0 }; }; #endif // type traits // Use this to avoid compile-time const truncation warnings template < int fSigned, int bits > class SafeIntMinMax; template <> class SafeIntMinMax< true, 8 > { public: const static signed __int8 min = (-0x7f - 1); const static signed __int8 max = 0x7f; }; template <> class SafeIntMinMax< true, 16 > { public: const static __int16 min = ( -0x7fff - 1 ); const static __int16 max = 0x7fff; }; template <> class SafeIntMinMax< true, 32 > { public: const static __int32 min = ( -0x7fffffff -1 ); const static __int32 max = 0x7fffffff; }; template <> class SafeIntMinMax< true, 64 > { public: const static __int64 min = static_cast<__int64>(0x8000000000000000LL); const static __int64 max = 0x7fffffffffffffffLL; }; template <> class SafeIntMinMax< false, 8 > { public: const static unsigned __int8 min = 0; const static unsigned __int8 max = 0xff; }; template <> class SafeIntMinMax< false, 16 > { public: const static unsigned __int16 min = 0; const static unsigned __int16 max = 0xffff; }; template <> class SafeIntMinMax< false, 32 > { public: const static unsigned __int32 min = 0; const static unsigned __int32 max = 0xffffffff; }; template <> class SafeIntMinMax< false, 64 > { public: const static unsigned __int64 min = 0; const static unsigned __int64 max = 0xffffffffffffffffULL; }; template < typename T > class IntTraits { public: C_ASSERT( NumericType::isInt ); enum { isSigned = ( (T)(-1) < 0 ), is64Bit = ( sizeof(T) == 8 ), is32Bit = ( sizeof(T) == 4 ), is16Bit = ( sizeof(T) == 2 ), is8Bit = ( sizeof(T) == 1 ), isLT32Bit = ( sizeof(T) < 4 ), isLT64Bit = ( sizeof(T) < 8 ), isInt8 = ( sizeof(T) == 1 && isSigned ), isUint8 = ( sizeof(T) == 1 && !isSigned ), isInt16 = ( sizeof(T) == 2 && isSigned ), isUint16 = ( sizeof(T) == 2 && !isSigned ), isInt32 = ( sizeof(T) == 4 && isSigned ), isUint32 = ( sizeof(T) == 4 && !isSigned ), isInt64 = ( sizeof(T) == 8 && isSigned ), isUint64 = ( sizeof(T) == 8 && !isSigned ), bitCount = ( sizeof(T)*8 ), isBool = ( (T)2 == (T)1 ) }; // On version 13.10 enums cannot define __int64 values // so we'll use const statics instead! // These must be cast to deal with the possibility of a SafeInt being given an enum as an argument const static T maxInt = static_cast(SafeIntMinMax< isSigned, bitCount >::max); const static T minInt = static_cast(SafeIntMinMax< isSigned, bitCount >::min); }; template < typename T > const T IntTraits< T >::maxInt; template < typename T > const T IntTraits< T >::minInt; template < typename T, typename U > class SafeIntCompare { public: enum { isBothSigned = (IntTraits< T >::isSigned && IntTraits< U >::isSigned), isBothUnsigned = (!IntTraits< T >::isSigned && !IntTraits< U >::isSigned), isLikeSigned = ((bool)(IntTraits< T >::isSigned) == (bool)(IntTraits< U >::isSigned)), isCastOK = ((isLikeSigned && sizeof(T) >= sizeof(U)) || (IntTraits< T >::isSigned && sizeof(T) > sizeof(U))), isBothLT32Bit = (IntTraits< T >::isLT32Bit && IntTraits< U >::isLT32Bit), isBothLT64Bit = (IntTraits< T >::isLT64Bit && IntTraits< U >::isLT64Bit) }; }; //all of the arithmetic operators can be solved by the same code within //each of these regions without resorting to compile-time constant conditionals //most operators collapse the problem into less than the 22 zones, but this is used //as the first cut //using this also helps ensure that we handle all of the possible cases correctly template < typename T, typename U > class IntRegion { public: enum { //unsigned-unsigned zone IntZone_UintLT32_UintLT32 = SafeIntCompare< T,U >::isBothUnsigned && SafeIntCompare< T,U >::isBothLT32Bit, IntZone_Uint32_UintLT64 = SafeIntCompare< T,U >::isBothUnsigned && IntTraits< T >::is32Bit && IntTraits< U >::isLT64Bit, IntZone_UintLT32_Uint32 = SafeIntCompare< T,U >::isBothUnsigned && IntTraits< T >::isLT32Bit && IntTraits< U >::is32Bit, IntZone_Uint64_Uint = SafeIntCompare< T,U >::isBothUnsigned && IntTraits< T >::is64Bit, IntZone_UintLT64_Uint64 = SafeIntCompare< T,U >::isBothUnsigned && IntTraits< T >::isLT64Bit && IntTraits< U >::is64Bit, //unsigned-signed IntZone_UintLT32_IntLT32 = !IntTraits< T >::isSigned && IntTraits< U >::isSigned && SafeIntCompare< T,U >::isBothLT32Bit, IntZone_Uint32_IntLT64 = IntTraits< T >::isUint32 && IntTraits< U >::isSigned && IntTraits< U >::isLT64Bit, IntZone_UintLT32_Int32 = !IntTraits< T >::isSigned && IntTraits< T >::isLT32Bit && IntTraits< U >::isInt32, IntZone_Uint64_Int = IntTraits< T >::isUint64 && IntTraits< U >::isSigned && IntTraits< U >::isLT64Bit, IntZone_UintLT64_Int64 = !IntTraits< T >::isSigned && IntTraits< T >::isLT64Bit && IntTraits< U >::isInt64, IntZone_Uint64_Int64 = IntTraits< T >::isUint64 && IntTraits< U >::isInt64, //signed-signed IntZone_IntLT32_IntLT32 = SafeIntCompare< T,U >::isBothSigned && SafeIntCompare< T, U >::isBothLT32Bit, IntZone_Int32_IntLT64 = SafeIntCompare< T,U >::isBothSigned && IntTraits< T >::is32Bit && IntTraits< U >::isLT64Bit, IntZone_IntLT32_Int32 = SafeIntCompare< T,U >::isBothSigned && IntTraits< T >::isLT32Bit && IntTraits< U >::is32Bit, IntZone_Int64_Int64 = SafeIntCompare< T,U >::isBothSigned && IntTraits< T >::isInt64 && IntTraits< U >::isInt64, IntZone_Int64_Int = SafeIntCompare< T,U >::isBothSigned && IntTraits< T >::is64Bit && IntTraits< U >::isLT64Bit, IntZone_IntLT64_Int64 = SafeIntCompare< T,U >::isBothSigned && IntTraits< T >::isLT64Bit && IntTraits< U >::is64Bit, //signed-unsigned IntZone_IntLT32_UintLT32 = IntTraits< T >::isSigned && !IntTraits< U >::isSigned && SafeIntCompare< T,U >::isBothLT32Bit, IntZone_Int32_UintLT32 = IntTraits< T >::isInt32 && !IntTraits< U >::isSigned && IntTraits< U >::isLT32Bit, IntZone_IntLT64_Uint32 = IntTraits< T >::isSigned && IntTraits< T >::isLT64Bit && IntTraits< U >::isUint32, IntZone_Int64_UintLT64 = IntTraits< T >::isInt64 && !IntTraits< U >::isSigned && IntTraits< U >::isLT64Bit, IntZone_Int_Uint64 = IntTraits< T >::isSigned && IntTraits< U >::isUint64 && IntTraits< T >::isLT64Bit, IntZone_Int64_Uint64 = IntTraits< T >::isInt64 && IntTraits< U >::isUint64 }; }; // In all of the following functions, we have two versions // One for SafeInt, which throws C++ (or possibly SEH) exceptions // The non-throwing versions are for use by the helper functions that return success and failure. // Some of the non-throwing functions are not used, but are maintained for completeness. // There's no real alternative to duplicating logic, but keeping the two versions // immediately next to one another will help reduce problems // useful function to help with getting the magnitude of a negative number enum AbsMethod { AbsMethodInt, AbsMethodInt64, AbsMethodNoop }; template < typename T > class GetAbsMethod { public: enum { method = IntTraits< T >::isLT64Bit && IntTraits< T >::isSigned ? AbsMethodInt : IntTraits< T >::isInt64 ? AbsMethodInt64 : AbsMethodNoop }; }; // let's go ahead and hard-code a dependency on the // representation of negative numbers to keep compilers from getting overly // happy with optimizing away things like -MIN_INT. template < typename T, int > class AbsValueHelper; template < typename T > class AbsValueHelper < T, AbsMethodInt> { public: static unsigned __int32 Abs( T t ) SAFEINT_NOTHROW { SAFEINT_ASSERT( t < 0 ); return ~(unsigned __int32)t + 1; } }; template < typename T > class AbsValueHelper < T, AbsMethodInt64 > { public: static unsigned __int64 Abs( T t ) SAFEINT_NOTHROW { SAFEINT_ASSERT( t < 0 ); return ~(unsigned __int64)t + 1; } }; template < typename T > class AbsValueHelper < T, AbsMethodNoop > { public: static T Abs( T t ) SAFEINT_NOTHROW { // Why are you calling Abs on an unsigned number ??? SAFEINT_ASSERT( false ); return t; } }; template < typename T, bool > class NegationHelper; // Previous versions had an assert that the type being negated was 32-bit or higher // In retrospect, this seems like something to just document // Negation will normally upcast to int // For example -(unsigned short)0xffff == (int)0xffff0001 // This class will retain the type, and will truncate, which may not be what // you wanted // If you want normal operator casting behavior, do this: // SafeInt ss = 0xffff; // then: // -(SafeInt(ss)) // will then emit a signed int with the correct value and bitfield template < typename T > class NegationHelper // Signed { public: template static T NegativeThrow( T t ) SAFEINT_CPP_THROW { // corner case if( t != IntTraits< T >::minInt ) { // cast prevents unneeded checks in the case of small ints return -t; } E::SafeIntOnOverflow(); } static bool Negative( T t, T& ret ) SAFEINT_NOTHROW { // corner case if( t != IntTraits< T >::minInt ) { // cast prevents unneeded checks in the case of small ints ret = -t; return true; } return false; } }; // Helper classes to work keep compilers from // optimizing away negation template < typename T > class SignedNegation; template <> class SignedNegation { public: static signed __int32 Value(unsigned __int64 in) SAFEINT_NOTHROW { return (signed __int32)(~(unsigned __int32)in + 1); } static signed __int32 Value(unsigned __int32 in) SAFEINT_NOTHROW { return (signed __int32)(~in + 1); } }; template <> class SignedNegation { public: static signed __int64 Value(unsigned __int64 in) SAFEINT_NOTHROW { return (signed __int64)(~in + 1); } }; template < typename T > class NegationHelper // unsigned { public: template static T NegativeThrow( T t ) SAFEINT_CPP_THROW { #if defined SAFEINT_DISALLOW_UNSIGNED_NEGATION C_ASSERT( sizeof(T) == 0 ); #endif #if SAFEINT_COMPILER == VISUAL_STUDIO_COMPILER #pragma warning(push) //this avoids warnings from the unary '-' operator being applied to unsigned numbers #pragma warning(disable:4146) #endif // Note - this could be quenched on gcc // by doing something like: // return (T)-((__int64)t); // but it seems like you would want a warning when doing this. return (T)-t; #if SAFEINT_COMPILER == VISUAL_STUDIO_COMPILER #pragma warning(pop) #endif } static bool Negative( T t, T& ret ) SAFEINT_NOTHROW { if( IntTraits::isLT32Bit ) { // See above SAFEINT_ASSERT( false ); } #if defined SAFEINT_DISALLOW_UNSIGNED_NEGATION C_ASSERT( sizeof(T) == 0 ); #endif // Do it this way to avoid warning ret = -t; return true; } }; //core logic to determine casting behavior enum CastMethod { CastOK = 0, CastCheckLTZero, CastCheckGTMax, CastCheckSafeIntMinMaxUnsigned, CastCheckSafeIntMinMaxSigned, CastToFloat, CastFromFloat, CastToBool, CastFromBool }; template < typename ToType, typename FromType > class GetCastMethod { public: enum { method = ( IntTraits< FromType >::isBool && !IntTraits< ToType >::isBool ) ? CastFromBool : ( !IntTraits< FromType >::isBool && IntTraits< ToType >::isBool ) ? CastToBool : ( SafeIntCompare< ToType, FromType >::isCastOK ) ? CastOK : ( ( IntTraits< ToType >::isSigned && !IntTraits< FromType >::isSigned && sizeof( FromType ) >= sizeof( ToType ) ) || ( SafeIntCompare< ToType, FromType >::isBothUnsigned && sizeof( FromType ) > sizeof( ToType ) ) ) ? CastCheckGTMax : ( !IntTraits< ToType >::isSigned && IntTraits< FromType >::isSigned && sizeof( ToType ) >= sizeof( FromType ) ) ? CastCheckLTZero : ( !IntTraits< ToType >::isSigned ) ? CastCheckSafeIntMinMaxUnsigned : CastCheckSafeIntMinMaxSigned }; }; template < typename FromType > class GetCastMethod < float, FromType > { public: enum{ method = CastOK }; }; template < typename FromType > class GetCastMethod < double, FromType > { public: enum{ method = CastOK }; }; template < typename FromType > class GetCastMethod < long double, FromType > { public: enum{ method = CastOK }; }; template < typename ToType > class GetCastMethod < ToType, float > { public: enum{ method = CastFromFloat }; }; template < typename ToType > class GetCastMethod < ToType, double > { public: enum{ method = CastFromFloat }; }; template < typename ToType > class GetCastMethod < ToType, long double > { public: enum{ method = CastFromFloat }; }; template < typename T, typename U, int > class SafeCastHelper; template < typename T, typename U > class SafeCastHelper < T, U, CastOK > { public: static bool Cast( U u, T& t ) SAFEINT_NOTHROW { t = (T)u; return true; } template < typename E > static void CastThrow( U u, T& t ) SAFEINT_CPP_THROW { t = (T)u; } }; // special case floats and doubles // tolerate loss of precision template < typename T, typename U > class SafeCastHelper < T, U, CastFromFloat > { public: static bool Cast( U u, T& t ) SAFEINT_NOTHROW { if( u <= (U)IntTraits< T >::maxInt && u >= (U)IntTraits< T >::minInt ) { t = (T)u; return true; } return false; } template < typename E > static void CastThrow( U u, T& t ) SAFEINT_CPP_THROW { if( u <= (U)IntTraits< T >::maxInt && u >= (U)IntTraits< T >::minInt ) { t = (T)u; return; } E::SafeIntOnOverflow(); } }; // Match on any method where a bool is cast to type T template < typename T > class SafeCastHelper < T, bool, CastFromBool > { public: static bool Cast( bool b, T& t ) SAFEINT_NOTHROW { t = (T)( b ? 1 : 0 ); return true; } template < typename E > static void CastThrow( bool b, T& t ) SAFEINT_CPP_THROW { t = (T)( b ? 1 : 0 ); } }; template < typename T > class SafeCastHelper < bool, T, CastToBool > { public: static bool Cast( T t, bool& b ) SAFEINT_NOTHROW { b = !!t; return true; } template < typename E > static void CastThrow( bool b, T& t ) SAFEINT_CPP_THROW { b = !!t; } }; template < typename T, typename U > class SafeCastHelper < T, U, CastCheckLTZero > { public: static bool Cast( U u, T& t ) SAFEINT_NOTHROW { if( u < 0 ) return false; t = (T)u; return true; } template < typename E > static void CastThrow( U u, T& t ) SAFEINT_CPP_THROW { if( u < 0 ) E::SafeIntOnOverflow(); t = (T)u; } }; template < typename T, typename U > class SafeCastHelper < T, U, CastCheckGTMax > { public: static bool Cast( U u, T& t ) SAFEINT_NOTHROW { if( u > (U)IntTraits< T >::maxInt ) return false; t = (T)u; return true; } template < typename E > static void CastThrow( U u, T& t ) SAFEINT_CPP_THROW { if( u > (U)IntTraits< T >::maxInt ) E::SafeIntOnOverflow(); t = (T)u; } }; template < typename T, typename U > class SafeCastHelper < T, U, CastCheckSafeIntMinMaxUnsigned > { public: static bool Cast( U u, T& t ) SAFEINT_NOTHROW { // U is signed - T could be either signed or unsigned if( u > IntTraits< T >::maxInt || u < 0 ) return false; t = (T)u; return true; } template < typename E > static void CastThrow( U u, T& t ) SAFEINT_CPP_THROW { // U is signed - T could be either signed or unsigned if( u > IntTraits< T >::maxInt || u < 0 ) E::SafeIntOnOverflow(); t = (T)u; } }; template < typename T, typename U > class SafeCastHelper < T, U, CastCheckSafeIntMinMaxSigned > { public: static bool Cast( U u, T& t ) SAFEINT_NOTHROW { // T, U are signed if( u > IntTraits< T >::maxInt || u < IntTraits< T >::minInt ) return false; t = (T)u; return true; } template < typename E > static void CastThrow( U u, T& t ) SAFEINT_CPP_THROW { //T, U are signed if( u > IntTraits< T >::maxInt || u < IntTraits< T >::minInt ) E::SafeIntOnOverflow(); t = (T)u; } }; //core logic to determine whether a comparison is valid, or needs special treatment enum ComparisonMethod { ComparisonMethod_Ok = 0, ComparisonMethod_CastInt, ComparisonMethod_CastInt64, ComparisonMethod_UnsignedT, ComparisonMethod_UnsignedU }; // Note - the standard is arguably broken in the case of some integer // conversion operations // For example, signed char a = -1 = 0xff // unsigned int b = 0xffffffff // If you then test if a < b, a value-preserving cast // is made, and you're essentially testing // (unsigned int)a < b == false // // I do not think this makes sense - if you perform // a cast to an __int64, which can clearly preserve both value and signedness // then you get a different and intuitively correct answer // IMHO, -1 should be less than 4 billion // If you prefer to retain the ANSI standard behavior // insert #define ANSI_CONVERSIONS into your source // Behavior differences occur in the following cases: // 8, 16, and 32-bit signed int, unsigned 32-bit int // any signed int, unsigned 64-bit int // Note - the signed int must be negative to show the problem template < typename T, typename U > class ValidComparison { public: enum { #ifdef ANSI_CONVERSIONS method = ComparisonMethod_Ok #else method = ( ( SafeIntCompare< T, U >::isLikeSigned ) ? ComparisonMethod_Ok : ( ( IntTraits< T >::isSigned && sizeof(T) < 8 && sizeof(U) < 4 ) || ( IntTraits< U >::isSigned && sizeof(T) < 4 && sizeof(U) < 8 ) ) ? ComparisonMethod_CastInt : ( ( IntTraits< T >::isSigned && sizeof(U) < 8 ) || ( IntTraits< U >::isSigned && sizeof(T) < 8 ) ) ? ComparisonMethod_CastInt64 : ( !IntTraits< T >::isSigned ) ? ComparisonMethod_UnsignedT : ComparisonMethod_UnsignedU ) #endif }; }; template class EqualityTest; template < typename T, typename U > class EqualityTest< T, U, ComparisonMethod_Ok > { public: static bool IsEquals( const T t, const U u ) SAFEINT_NOTHROW { return ( t == u ); } }; template < typename T, typename U > class EqualityTest< T, U, ComparisonMethod_CastInt > { public: static bool IsEquals( const T t, const U u ) SAFEINT_NOTHROW { return ( (int)t == (int)u ); } }; template < typename T, typename U > class EqualityTest< T, U, ComparisonMethod_CastInt64 > { public: static bool IsEquals( const T t, const U u ) SAFEINT_NOTHROW { return ( (__int64)t == (__int64)u ); } }; template < typename T, typename U > class EqualityTest< T, U, ComparisonMethod_UnsignedT > { public: static bool IsEquals( const T t, const U u ) SAFEINT_NOTHROW { //one operand is 32 or 64-bit unsigned, and the other is signed and the same size or smaller if( u < 0 ) return false; //else safe to cast to type T return ( t == (T)u ); } }; template < typename T, typename U > class EqualityTest< T, U, ComparisonMethod_UnsignedU> { public: static bool IsEquals( const T t, const U u ) SAFEINT_NOTHROW { //one operand is 32 or 64-bit unsigned, and the other is signed and the same size or smaller if( t < 0 ) return false; //else safe to cast to type U return ( (U)t == u ); } }; template class GreaterThanTest; template < typename T, typename U > class GreaterThanTest< T, U, ComparisonMethod_Ok > { public: static bool GreaterThan( const T t, const U u ) SAFEINT_NOTHROW { return ( t > u ); } }; template < typename T, typename U > class GreaterThanTest< T, U, ComparisonMethod_CastInt > { public: static bool GreaterThan( const T t, const U u ) SAFEINT_NOTHROW { return ( (int)t > (int)u ); } }; template < typename T, typename U > class GreaterThanTest< T, U, ComparisonMethod_CastInt64 > { public: static bool GreaterThan( const T t, const U u ) SAFEINT_NOTHROW { return ( (__int64)t > (__int64)u ); } }; template < typename T, typename U > class GreaterThanTest< T, U, ComparisonMethod_UnsignedT > { public: static bool GreaterThan( const T t, const U u ) SAFEINT_NOTHROW { // one operand is 32 or 64-bit unsigned, and the other is signed and the same size or smaller if( u < 0 ) return true; // else safe to cast to type T return ( t > (T)u ); } }; template < typename T, typename U > class GreaterThanTest< T, U, ComparisonMethod_UnsignedU > { public: static bool GreaterThan( const T t, const U u ) SAFEINT_NOTHROW { // one operand is 32 or 64-bit unsigned, and the other is signed and the same size or smaller if( t < 0 ) return false; // else safe to cast to type U return ( (U)t > u ); } }; // Modulus is simpler than comparison, but follows much the same logic // using this set of functions, it can't fail except in a div 0 situation template class ModulusHelper; template class ModulusHelper { public: static SafeIntError Modulus( const T& t, const U& u, T& result ) SAFEINT_NOTHROW { if(u == 0) return SafeIntDivideByZero; //trap corner case if( CompileConst< IntTraits< U >::isSigned >::Value() ) { // Some compilers don't notice that this only compiles when u is signed // Add cast to make them happy if( u == (U)-1 ) { result = 0; return SafeIntNoError; } } result = (T)(t % u); return SafeIntNoError; } template < typename E > static void ModulusThrow( const T& t, const U& u, T& result ) SAFEINT_CPP_THROW { if(u == 0) E::SafeIntOnDivZero(); //trap corner case if( CompileConst< IntTraits< U >::isSigned >::Value() ) { if( u == (U)-1 ) { result = 0; return; } } result = (T)(t % u); } }; template class ModulusHelper { public: static SafeIntError Modulus( const T& t, const U& u, T& result ) SAFEINT_NOTHROW { if(u == 0) return SafeIntDivideByZero; //trap corner case if( CompileConst< IntTraits< U >::isSigned >::Value() ) { if( u == (U)-1 ) { result = 0; return SafeIntNoError; } } result = (T)(t % u); return SafeIntNoError; } template < typename E > static void ModulusThrow( const T& t, const U& u, T& result ) SAFEINT_CPP_THROW { if(u == 0) E::SafeIntOnDivZero(); //trap corner case if( CompileConst< IntTraits< U >::isSigned >::Value() ) { if( u == (U)-1 ) { result = 0; return; } } result = (T)(t % u); } }; template < typename T, typename U > class ModulusHelper< T, U, ComparisonMethod_CastInt64> { public: static SafeIntError Modulus( const T& t, const U& u, T& result ) SAFEINT_NOTHROW { if(u == 0) return SafeIntDivideByZero; //trap corner case if( CompileConst< IntTraits< U >::isSigned >::Value() ) { if( u == (U)-1 ) { result = 0; return SafeIntNoError; } } result = (T)((__int64)t % (__int64)u); return SafeIntNoError; } template < typename E > static void ModulusThrow( const T& t, const U& u, T& result ) SAFEINT_CPP_THROW { if(u == 0) E::SafeIntOnDivZero(); if( CompileConst< IntTraits< U >::isSigned >::Value() ) { if( u == (U)-1 ) { result = 0; return; } } result = (T)((__int64)t % (__int64)u); } }; // T is unsigned __int64, U is any signed int template < typename T, typename U > class ModulusHelper< T, U, ComparisonMethod_UnsignedT> { public: static SafeIntError Modulus( const T& t, const U& u, T& result ) SAFEINT_NOTHROW { if(u == 0) return SafeIntDivideByZero; // u could be negative - if so, need to convert to positive // casts below are always safe due to the way modulus works if(u < 0) result = (T)(t % AbsValueHelper< U, GetAbsMethod< U >::method >::Abs(u)); else result = (T)(t % u); return SafeIntNoError; } template < typename E > static void ModulusThrow( const T& t, const U& u, T& result ) SAFEINT_CPP_THROW { if(u == 0) E::SafeIntOnDivZero(); // u could be negative - if so, need to convert to positive if(u < 0) result = (T)(t % AbsValueHelper< U, GetAbsMethod< U >::method >::Abs( u )); else result = (T)(t % u); } }; // U is unsigned __int64, T any signed int template < typename T, typename U > class ModulusHelper< T, U, ComparisonMethod_UnsignedU> { public: static SafeIntError Modulus( const T& t, const U& u, T& result ) SAFEINT_NOTHROW { if(u == 0) return SafeIntDivideByZero; //t could be negative - if so, need to convert to positive if(t < 0) result = (T)( ~( AbsValueHelper< T, GetAbsMethod< T >::method >::Abs( t ) % u ) + 1 ); else result = (T)((T)t % u); return SafeIntNoError; } template < typename E > static void ModulusThrow( const T& t, const U& u, T& result ) SAFEINT_CPP_THROW { if(u == 0) E::SafeIntOnDivZero(); //t could be negative - if so, need to convert to positive if(t < 0) result = (T)( ~( AbsValueHelper< T, GetAbsMethod< T >::method >::Abs( t ) % u ) + 1); else result = (T)( (T)t % u ); } }; //core logic to determine method to check multiplication enum MultiplicationState { MultiplicationState_CastInt = 0, // One or both signed, smaller than 32-bit MultiplicationState_CastInt64, // One or both signed, smaller than 64-bit MultiplicationState_CastUint, // Both are unsigned, smaller than 32-bit MultiplicationState_CastUint64, // Both are unsigned, both 32-bit or smaller MultiplicationState_Uint64Uint, // Both are unsigned, lhs 64-bit, rhs 32-bit or smaller MultiplicationState_Uint64Uint64, // Both are unsigned int64 MultiplicationState_Uint64Int, // lhs is unsigned int64, rhs int32 MultiplicationState_Uint64Int64, // lhs is unsigned int64, rhs signed int64 MultiplicationState_UintUint64, // Both are unsigned, lhs 32-bit or smaller, rhs 64-bit MultiplicationState_UintInt64, // lhs unsigned 32-bit or less, rhs int64 MultiplicationState_Int64Uint, // lhs int64, rhs unsigned int32 MultiplicationState_Int64Int64, // lhs int64, rhs int64 MultiplicationState_Int64Int, // lhs int64, rhs int32 MultiplicationState_IntUint64, // lhs int, rhs unsigned int64 MultiplicationState_IntInt64, // lhs int, rhs int64 MultiplicationState_Int64Uint64, // lhs int64, rhs uint64 MultiplicationState_Error }; template < typename T, typename U > class MultiplicationMethod { public: enum { // unsigned-unsigned method = (IntRegion< T,U >::IntZone_UintLT32_UintLT32 ? MultiplicationState_CastUint : (IntRegion< T,U >::IntZone_Uint32_UintLT64 || IntRegion< T,U >::IntZone_UintLT32_Uint32) ? MultiplicationState_CastUint64 : SafeIntCompare< T,U >::isBothUnsigned && IntTraits< T >::isUint64 && IntTraits< U >::isUint64 ? MultiplicationState_Uint64Uint64 : (IntRegion< T,U >::IntZone_Uint64_Uint) ? MultiplicationState_Uint64Uint : (IntRegion< T,U >::IntZone_UintLT64_Uint64) ? MultiplicationState_UintUint64 : // unsigned-signed (IntRegion< T,U >::IntZone_UintLT32_IntLT32) ? MultiplicationState_CastInt : (IntRegion< T,U >::IntZone_Uint32_IntLT64 || IntRegion< T,U >::IntZone_UintLT32_Int32) ? MultiplicationState_CastInt64 : (IntRegion< T,U >::IntZone_Uint64_Int) ? MultiplicationState_Uint64Int : (IntRegion< T,U >::IntZone_UintLT64_Int64) ? MultiplicationState_UintInt64 : (IntRegion< T,U >::IntZone_Uint64_Int64) ? MultiplicationState_Uint64Int64 : // signed-signed (IntRegion< T,U >::IntZone_IntLT32_IntLT32) ? MultiplicationState_CastInt : (IntRegion< T,U >::IntZone_Int32_IntLT64 || IntRegion< T,U >::IntZone_IntLT32_Int32) ? MultiplicationState_CastInt64 : (IntRegion< T,U >::IntZone_Int64_Int64) ? MultiplicationState_Int64Int64 : (IntRegion< T,U >::IntZone_Int64_Int) ? MultiplicationState_Int64Int : (IntRegion< T,U >::IntZone_IntLT64_Int64) ? MultiplicationState_IntInt64 : // signed-unsigned (IntRegion< T,U >::IntZone_IntLT32_UintLT32) ? MultiplicationState_CastInt : (IntRegion< T,U >::IntZone_Int32_UintLT32 || IntRegion< T,U >::IntZone_IntLT64_Uint32) ? MultiplicationState_CastInt64 : (IntRegion< T,U >::IntZone_Int64_UintLT64) ? MultiplicationState_Int64Uint : (IntRegion< T,U >::IntZone_Int_Uint64) ? MultiplicationState_IntUint64 : (IntRegion< T,U >::IntZone_Int64_Uint64 ? MultiplicationState_Int64Uint64 : MultiplicationState_Error ) ) }; }; template class MultiplicationHelper; template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_CastInt> { public: //accepts signed, both less than 32-bit static bool Multiply( const T& t, const U& u, T& ret ) SAFEINT_NOTHROW { int tmp = t * u; if( tmp > IntTraits< T >::maxInt || tmp < IntTraits< T >::minInt ) return false; ret = (T)tmp; return true; } template < typename E > static void MultiplyThrow( const T& t, const U& u, T& ret ) SAFEINT_CPP_THROW { int tmp = t * u; if( tmp > IntTraits< T >::maxInt || tmp < IntTraits< T >::minInt ) E::SafeIntOnOverflow(); ret = (T)tmp; } }; template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_CastUint > { public: //accepts unsigned, both less than 32-bit static bool Multiply( const T& t, const U& u, T& ret ) SAFEINT_NOTHROW { unsigned int tmp = (unsigned int)(t * u); if( tmp > IntTraits< T >::maxInt ) return false; ret = (T)tmp; return true; } template < typename E > static void MultiplyThrow( const T& t, const U& u, T& ret ) SAFEINT_CPP_THROW { unsigned int tmp = (unsigned int)( t * u ); if( tmp > IntTraits< T >::maxInt ) E::SafeIntOnOverflow(); ret = (T)tmp; } }; template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_CastInt64> { public: //mixed signed or both signed where at least one argument is 32-bit, and both a 32-bit or less static bool Multiply( const T& t, const U& u, T& ret ) SAFEINT_NOTHROW { __int64 tmp = (__int64)t * (__int64)u; if(tmp > (__int64)IntTraits< T >::maxInt || tmp < (__int64)IntTraits< T >::minInt) return false; ret = (T)tmp; return true; } template < typename E > static void MultiplyThrow( const T& t, const U& u, T& ret ) SAFEINT_CPP_THROW { __int64 tmp = (__int64)t * (__int64)u; if(tmp > (__int64)IntTraits< T >::maxInt || tmp < (__int64)IntTraits< T >::minInt) E::SafeIntOnOverflow(); ret = (T)tmp; } }; template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_CastUint64> { public: //both unsigned where at least one argument is 32-bit, and both are 32-bit or less static bool Multiply( const T& t, const U& u, T& ret ) SAFEINT_NOTHROW { unsigned __int64 tmp = (unsigned __int64)t * (unsigned __int64)u; if(tmp > (unsigned __int64)IntTraits< T >::maxInt) return false; ret = (T)tmp; return true; } template < typename E > static void MultiplyThrow( const T& t, const U& u, T& ret ) SAFEINT_CPP_THROW { unsigned __int64 tmp = (unsigned __int64)t * (unsigned __int64)u; if(tmp > (unsigned __int64)IntTraits< T >::maxInt) E::SafeIntOnOverflow(); ret = (T)tmp; } }; // T = left arg and return type // U = right arg template < typename T, typename U > class LargeIntRegMultiply; #if SAFEINT_USE_INTRINSICS // As usual, unsigned is easy inline bool IntrinsicMultiplyUint64( const unsigned __int64& a, const unsigned __int64& b, unsigned __int64* pRet ) SAFEINT_NOTHROW { unsigned __int64 ulHigh = 0; *pRet = _umul128(a , b, &ulHigh); return ulHigh == 0; } // Signed, is not so easy inline bool IntrinsicMultiplyInt64( const signed __int64& a, const signed __int64& b, signed __int64* pRet ) SAFEINT_NOTHROW { __int64 llHigh = 0; *pRet = _mul128(a , b, &llHigh); // Now we need to figure out what we expect // If llHigh is 0, then treat *pRet as unsigned // If llHigh is < 0, then treat *pRet as signed if( (a ^ b) < 0 ) { // Negative result expected if( llHigh == -1 && *pRet < 0 || llHigh == 0 && *pRet == 0 ) { // Everything is within range return true; } } else { // Result should be positive // Check for overflow if( llHigh == 0 && (unsigned __int64)*pRet <= IntTraits< signed __int64 >::maxInt ) return true; } return false; } #endif template<> class LargeIntRegMultiply< unsigned __int64, unsigned __int64 > { public: static bool RegMultiply( const unsigned __int64& a, const unsigned __int64& b, unsigned __int64* pRet ) SAFEINT_NOTHROW { #if SAFEINT_USE_INTRINSICS return IntrinsicMultiplyUint64( a, b, pRet ); #else unsigned __int32 aHigh, aLow, bHigh, bLow; // Consider that a*b can be broken up into: // (aHigh * 2^32 + aLow) * (bHigh * 2^32 + bLow) // => (aHigh * bHigh * 2^64) + (aLow * bHigh * 2^32) + (aHigh * bLow * 2^32) + (aLow * bLow) // Note - same approach applies for 128 bit math on a 64-bit system aHigh = (unsigned __int32)(a >> 32); aLow = (unsigned __int32)a; bHigh = (unsigned __int32)(b >> 32); bLow = (unsigned __int32)b; *pRet = 0; if(aHigh == 0) { if(bHigh != 0) { *pRet = (unsigned __int64)aLow * (unsigned __int64)bHigh; } } else if(bHigh == 0) { if(aHigh != 0) { *pRet = (unsigned __int64)aHigh * (unsigned __int64)bLow; } } else { return false; } if(*pRet != 0) { unsigned __int64 tmp; if((unsigned __int32)(*pRet >> 32) != 0) return false; *pRet <<= 32; tmp = (unsigned __int64)aLow * (unsigned __int64)bLow; *pRet += tmp; if(*pRet < tmp) return false; return true; } *pRet = (unsigned __int64)aLow * (unsigned __int64)bLow; return true; #endif } template < typename E > static void RegMultiplyThrow( const unsigned __int64& a, const unsigned __int64& b, unsigned __int64* pRet ) SAFEINT_CPP_THROW { #if SAFEINT_USE_INTRINSICS if( !IntrinsicMultiplyUint64( a, b, pRet ) ) E::SafeIntOnOverflow(); #else unsigned __int32 aHigh, aLow, bHigh, bLow; // Consider that a*b can be broken up into: // (aHigh * 2^32 + aLow) * (bHigh * 2^32 + bLow) // => (aHigh * bHigh * 2^64) + (aLow * bHigh * 2^32) + (aHigh * bLow * 2^32) + (aLow * bLow) // Note - same approach applies for 128 bit math on a 64-bit system aHigh = (unsigned __int32)(a >> 32); aLow = (unsigned __int32)a; bHigh = (unsigned __int32)(b >> 32); bLow = (unsigned __int32)b; *pRet = 0; if(aHigh == 0) { if(bHigh != 0) { *pRet = (unsigned __int64)aLow * (unsigned __int64)bHigh; } } else if(bHigh == 0) { if(aHigh != 0) { *pRet = (unsigned __int64)aHigh * (unsigned __int64)bLow; } } else { E::SafeIntOnOverflow(); } if(*pRet != 0) { unsigned __int64 tmp; if((unsigned __int32)(*pRet >> 32) != 0) E::SafeIntOnOverflow(); *pRet <<= 32; tmp = (unsigned __int64)aLow * (unsigned __int64)bLow; *pRet += tmp; if(*pRet < tmp) E::SafeIntOnOverflow(); return; } *pRet = (unsigned __int64)aLow * (unsigned __int64)bLow; #endif } }; template<> class LargeIntRegMultiply< unsigned __int64, unsigned __int32 > { public: static bool RegMultiply( const unsigned __int64& a, unsigned __int32 b, unsigned __int64* pRet ) SAFEINT_NOTHROW { #if SAFEINT_USE_INTRINSICS return IntrinsicMultiplyUint64( a, (unsigned __int64)b, pRet ); #else unsigned __int32 aHigh, aLow; // Consider that a*b can be broken up into: // (aHigh * 2^32 + aLow) * b // => (aHigh * b * 2^32) + (aLow * b) aHigh = (unsigned __int32)(a >> 32); aLow = (unsigned __int32)a; *pRet = 0; if(aHigh != 0) { *pRet = (unsigned __int64)aHigh * (unsigned __int64)b; unsigned __int64 tmp; if((unsigned __int32)(*pRet >> 32) != 0) return false; *pRet <<= 32; tmp = (unsigned __int64)aLow * (unsigned __int64)b; *pRet += tmp; if(*pRet < tmp) return false; return true; } *pRet = (unsigned __int64)aLow * (unsigned __int64)b; return true; #endif } template < typename E > static void RegMultiplyThrow( const unsigned __int64& a, unsigned __int32 b, unsigned __int64* pRet ) SAFEINT_CPP_THROW { #if SAFEINT_USE_INTRINSICS if( !IntrinsicMultiplyUint64( a, (unsigned __int64)b, pRet ) ) E::SafeIntOnOverflow(); #else unsigned __int32 aHigh, aLow; // Consider that a*b can be broken up into: // (aHigh * 2^32 + aLow) * b // => (aHigh * b * 2^32) + (aLow * b) aHigh = (unsigned __int32)(a >> 32); aLow = (unsigned __int32)a; *pRet = 0; if(aHigh != 0) { *pRet = (unsigned __int64)aHigh * (unsigned __int64)b; unsigned __int64 tmp; if((unsigned __int32)(*pRet >> 32) != 0) E::SafeIntOnOverflow(); *pRet <<= 32; tmp = (unsigned __int64)aLow * (unsigned __int64)b; *pRet += tmp; if(*pRet < tmp) E::SafeIntOnOverflow(); return; } *pRet = (unsigned __int64)aLow * (unsigned __int64)b; return; #endif } }; template<> class LargeIntRegMultiply< unsigned __int64, signed __int32 > { public: // Intrinsic not needed static bool RegMultiply( const unsigned __int64& a, signed __int32 b, unsigned __int64* pRet ) SAFEINT_NOTHROW { if( b < 0 && a != 0 ) return false; #if SAFEINT_USE_INTRINSICS return IntrinsicMultiplyUint64( a, (unsigned __int64)b, pRet ); #else return LargeIntRegMultiply< unsigned __int64, unsigned __int32 >::RegMultiply(a, (unsigned __int32)b, pRet); #endif } template < typename E > static void RegMultiplyThrow( const unsigned __int64& a, signed __int32 b, unsigned __int64* pRet ) SAFEINT_CPP_THROW { if( b < 0 && a != 0 ) E::SafeIntOnOverflow(); #if SAFEINT_USE_INTRINSICS if( !IntrinsicMultiplyUint64( a, (unsigned __int64)b, pRet ) ) E::SafeIntOnOverflow(); #else LargeIntRegMultiply< unsigned __int64, unsigned __int32 >::template RegMultiplyThrow< E >( a, (unsigned __int32)b, pRet ); #endif } }; template<> class LargeIntRegMultiply< unsigned __int64, signed __int64 > { public: static bool RegMultiply( const unsigned __int64& a, signed __int64 b, unsigned __int64* pRet ) SAFEINT_NOTHROW { if( b < 0 && a != 0 ) return false; #if SAFEINT_USE_INTRINSICS return IntrinsicMultiplyUint64( a, (unsigned __int64)b, pRet ); #else return LargeIntRegMultiply< unsigned __int64, unsigned __int64 >::RegMultiply(a, (unsigned __int64)b, pRet); #endif } template < typename E > static void RegMultiplyThrow( const unsigned __int64& a, signed __int64 b, unsigned __int64* pRet ) SAFEINT_CPP_THROW { if( b < 0 && a != 0 ) E::SafeIntOnOverflow(); #if SAFEINT_USE_INTRINSICS if( !IntrinsicMultiplyUint64( a, (unsigned __int64)b, pRet ) ) E::SafeIntOnOverflow(); #else LargeIntRegMultiply< unsigned __int64, unsigned __int64 >::template RegMultiplyThrow< E >( a, (unsigned __int64)b, pRet ); #endif } }; template<> class LargeIntRegMultiply< signed __int32, unsigned __int64 > { public: // Devolves into ordinary 64-bit calculation static bool RegMultiply( signed __int32 a, const unsigned __int64& b, signed __int32* pRet ) SAFEINT_NOTHROW { unsigned __int32 bHigh, bLow; bool fIsNegative = false; // Consider that a*b can be broken up into: // (aHigh * 2^32 + aLow) * (bHigh * 2^32 + bLow) // => (aHigh * bHigh * 2^64) + (aLow * bHigh * 2^32) + (aHigh * bLow * 2^32) + (aLow * bLow) // aHigh == 0 implies: // ( aLow * bHigh * 2^32 ) + ( aLow + bLow ) // If the first part is != 0, fail bHigh = (unsigned __int32)(b >> 32); bLow = (unsigned __int32)b; *pRet = 0; if(bHigh != 0 && a != 0) return false; if( a < 0 ) { a = (signed __int32)AbsValueHelper< signed __int32, GetAbsMethod< signed __int32 >::method >::Abs(a); fIsNegative = true; } unsigned __int64 tmp = (unsigned __int32)a * (unsigned __int64)bLow; if( !fIsNegative ) { if( tmp <= (unsigned __int64)IntTraits< signed __int32 >::maxInt ) { *pRet = (signed __int32)tmp; return true; } } else { if( tmp <= (unsigned __int64)IntTraits< signed __int32 >::maxInt+1 ) { *pRet = SignedNegation< signed __int32 >::Value( tmp ); return true; } } return false; } template < typename E > static void RegMultiplyThrow( signed __int32 a, const unsigned __int64& b, signed __int32* pRet ) SAFEINT_CPP_THROW { unsigned __int32 bHigh, bLow; bool fIsNegative = false; // Consider that a*b can be broken up into: // (aHigh * 2^32 + aLow) * (bHigh * 2^32 + bLow) // => (aHigh * bHigh * 2^64) + (aLow * bHigh * 2^32) + (aHigh * bLow * 2^32) + (aLow * bLow) bHigh = (unsigned __int32)(b >> 32); bLow = (unsigned __int32)b; *pRet = 0; if(bHigh != 0 && a != 0) E::SafeIntOnOverflow(); if( a < 0 ) { a = (signed __int32)AbsValueHelper< signed __int32, GetAbsMethod< signed __int32 >::method >::Abs(a); fIsNegative = true; } unsigned __int64 tmp = (unsigned __int32)a * (unsigned __int64)bLow; if( !fIsNegative ) { if( tmp <= (unsigned __int64)IntTraits< signed __int32 >::maxInt ) { *pRet = (signed __int32)tmp; return; } } else { if( tmp <= (unsigned __int64)IntTraits< signed __int32 >::maxInt+1 ) { *pRet = SignedNegation< signed __int32 >::Value( tmp ); return; } } E::SafeIntOnOverflow(); } }; template<> class LargeIntRegMultiply< unsigned __int32, unsigned __int64 > { public: // Becomes ordinary 64-bit multiplication, intrinsic not needed static bool RegMultiply( unsigned __int32 a, const unsigned __int64& b, unsigned __int32* pRet ) SAFEINT_NOTHROW { // Consider that a*b can be broken up into: // (bHigh * 2^32 + bLow) * a // => (bHigh * a * 2^32) + (bLow * a) // In this case, the result must fit into 32-bits // If bHigh != 0 && a != 0, immediate error. if( (unsigned __int32)(b >> 32) != 0 && a != 0 ) return false; unsigned __int64 tmp = b * (unsigned __int64)a; if( (unsigned __int32)(tmp >> 32) != 0 ) // overflow return false; *pRet = (unsigned __int32)tmp; return true; } template < typename E > static void RegMultiplyThrow( unsigned __int32 a, const unsigned __int64& b, unsigned __int32* pRet ) SAFEINT_CPP_THROW { if( (unsigned __int32)(b >> 32) != 0 && a != 0 ) E::SafeIntOnOverflow(); unsigned __int64 tmp = b * (unsigned __int64)a; if( (unsigned __int32)(tmp >> 32) != 0 ) // overflow E::SafeIntOnOverflow(); *pRet = (unsigned __int32)tmp; } }; template<> class LargeIntRegMultiply< unsigned __int32, signed __int64 > { public: static bool RegMultiply( unsigned __int32 a, const signed __int64& b, unsigned __int32* pRet ) SAFEINT_NOTHROW { if( b < 0 && a != 0 ) return false; return LargeIntRegMultiply< unsigned __int32, unsigned __int64 >::RegMultiply( a, (unsigned __int64)b, pRet ); } template < typename E > static void RegMultiplyThrow( unsigned __int32 a, const signed __int64& b, unsigned __int32* pRet ) SAFEINT_CPP_THROW { if( b < 0 && a != 0 ) E::SafeIntOnOverflow(); LargeIntRegMultiply< unsigned __int32, unsigned __int64 >::template RegMultiplyThrow< E >( a, (unsigned __int64)b, pRet ); } }; template<> class LargeIntRegMultiply< signed __int64, signed __int64 > { public: static bool RegMultiply( const signed __int64& a, const signed __int64& b, signed __int64* pRet ) SAFEINT_NOTHROW { #if SAFEINT_USE_INTRINSICS return IntrinsicMultiplyInt64( a, b, pRet ); #else bool aNegative = false; bool bNegative = false; unsigned __int64 tmp; __int64 a1 = a; __int64 b1 = b; if( a1 < 0 ) { aNegative = true; a1 = (signed __int64)AbsValueHelper< signed __int64, GetAbsMethod< signed __int64 >::method >::Abs(a1); } if( b1 < 0 ) { bNegative = true; b1 = (signed __int64)AbsValueHelper< signed __int64, GetAbsMethod< signed __int64 >::method >::Abs(b1); } if( LargeIntRegMultiply< unsigned __int64, unsigned __int64 >::RegMultiply( (unsigned __int64)a1, (unsigned __int64)b1, &tmp ) ) { // The unsigned multiplication didn't overflow if( aNegative ^ bNegative ) { // Result must be negative if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::minInt ) { *pRet = SignedNegation< signed __int64 >::Value( tmp ); return true; } } else { // Result must be positive if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::maxInt ) { *pRet = (signed __int64)tmp; return true; } } } return false; #endif } template < typename E > static void RegMultiplyThrow( const signed __int64& a, const signed __int64& b, signed __int64* pRet ) SAFEINT_CPP_THROW { #if SAFEINT_USE_INTRINSICS if( !IntrinsicMultiplyInt64( a, b, pRet ) ) E::SafeIntOnOverflow(); #else bool aNegative = false; bool bNegative = false; unsigned __int64 tmp; __int64 a1 = a; __int64 b1 = b; if( a1 < 0 ) { aNegative = true; a1 = (signed __int64)AbsValueHelper< signed __int64, GetAbsMethod< signed __int64 >::method >::Abs(a1); } if( b1 < 0 ) { bNegative = true; b1 = (signed __int64)AbsValueHelper< signed __int64, GetAbsMethod< signed __int64 >::method >::Abs(b1); } LargeIntRegMultiply< unsigned __int64, unsigned __int64 >::template RegMultiplyThrow< E >( (unsigned __int64)a1, (unsigned __int64)b1, &tmp ); // The unsigned multiplication didn't overflow or we'd be in the exception handler if( aNegative ^ bNegative ) { // Result must be negative if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::minInt ) { *pRet = SignedNegation< signed __int64 >::Value( tmp ); return; } } else { // Result must be positive if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::maxInt ) { *pRet = (signed __int64)tmp; return; } } E::SafeIntOnOverflow(); #endif } }; template<> class LargeIntRegMultiply< signed __int64, unsigned __int32 > { public: static bool RegMultiply( const signed __int64& a, unsigned __int32 b, signed __int64* pRet ) SAFEINT_NOTHROW { #if SAFEINT_USE_INTRINSICS return IntrinsicMultiplyInt64( a, (signed __int64)b, pRet ); #else bool aNegative = false; unsigned __int64 tmp; __int64 a1 = a; if( a1 < 0 ) { aNegative = true; a1 = (signed __int64)AbsValueHelper< signed __int64, GetAbsMethod< signed __int64 >::method >::Abs(a1); } if( LargeIntRegMultiply< unsigned __int64, unsigned __int32 >::RegMultiply( (unsigned __int64)a1, b, &tmp ) ) { // The unsigned multiplication didn't overflow if( aNegative ) { // Result must be negative if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::minInt ) { *pRet = SignedNegation< signed __int64 >::Value( tmp ); return true; } } else { // Result must be positive if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::maxInt ) { *pRet = (signed __int64)tmp; return true; } } } return false; #endif } template < typename E > static void RegMultiplyThrow( const signed __int64& a, unsigned __int32 b, signed __int64* pRet ) SAFEINT_CPP_THROW { #if SAFEINT_USE_INTRINSICS if( !IntrinsicMultiplyInt64( a, (signed __int64)b, pRet ) ) E::SafeIntOnOverflow(); #else bool aNegative = false; unsigned __int64 tmp; __int64 a1 = a; if( a1 < 0 ) { aNegative = true; a1 = (signed __int64)AbsValueHelper< signed __int64, GetAbsMethod< signed __int64 >::method >::Abs(a1); } LargeIntRegMultiply< unsigned __int64, unsigned __int32 >::template RegMultiplyThrow< E >( (unsigned __int64)a1, b, &tmp ); // The unsigned multiplication didn't overflow if( aNegative ) { // Result must be negative if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::minInt ) { *pRet = SignedNegation< signed __int64 >::Value( tmp ); return; } } else { // Result must be positive if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::maxInt ) { *pRet = (signed __int64)tmp; return; } } E::SafeIntOnOverflow(); #endif } }; template<> class LargeIntRegMultiply< signed __int64, signed __int32 > { public: static bool RegMultiply( const signed __int64& a, signed __int32 b, signed __int64* pRet ) SAFEINT_NOTHROW { #if SAFEINT_USE_INTRINSICS return IntrinsicMultiplyInt64( a, (signed __int64)b, pRet ); #else bool aNegative = false; bool bNegative = false; unsigned __int64 tmp; __int64 a1 = a; __int64 b1 = b; if( a1 < 0 ) { aNegative = true; a1 = (signed __int64)AbsValueHelper< signed __int64, GetAbsMethod< signed __int64 >::method >::Abs(a1); } if( b1 < 0 ) { bNegative = true; b1 = (signed __int64)AbsValueHelper< signed __int64, GetAbsMethod< signed __int64 >::method >::Abs(b1); } if( LargeIntRegMultiply< unsigned __int64, unsigned __int32 >::RegMultiply( (unsigned __int64)a1, (unsigned __int32)b1, &tmp ) ) { // The unsigned multiplication didn't overflow if( aNegative ^ bNegative ) { // Result must be negative if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::minInt ) { *pRet = SignedNegation< signed __int64 >::Value( tmp ); return true; } } else { // Result must be positive if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::maxInt ) { *pRet = (signed __int64)tmp; return true; } } } return false; #endif } template < typename E > static void RegMultiplyThrow( signed __int64 a, signed __int32 b, signed __int64* pRet ) SAFEINT_CPP_THROW { #if SAFEINT_USE_INTRINSICS if( !IntrinsicMultiplyInt64( a, (signed __int64)b, pRet ) ) E::SafeIntOnOverflow(); #else bool aNegative = false; bool bNegative = false; unsigned __int64 tmp; if( a < 0 ) { aNegative = true; a = (signed __int64)AbsValueHelper< signed __int64, GetAbsMethod< signed __int64 >::method >::Abs(a); } if( b < 0 ) { bNegative = true; b = (signed __int32)AbsValueHelper< signed __int32, GetAbsMethod< signed __int32 >::method >::Abs(b); } LargeIntRegMultiply< unsigned __int64, unsigned __int32 >::template RegMultiplyThrow< E >( (unsigned __int64)a, (unsigned __int32)b, &tmp ); // The unsigned multiplication didn't overflow if( aNegative ^ bNegative ) { // Result must be negative if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::minInt ) { *pRet = SignedNegation< signed __int64 >::Value( tmp ); return; } } else { // Result must be positive if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::maxInt ) { *pRet = (signed __int64)tmp; return; } } E::SafeIntOnOverflow(); #endif } }; template<> class LargeIntRegMultiply< signed __int32, signed __int64 > { public: static bool RegMultiply( signed __int32 a, const signed __int64& b, signed __int32* pRet ) SAFEINT_NOTHROW { #if SAFEINT_USE_INTRINSICS __int64 tmp; if( IntrinsicMultiplyInt64( a, b, &tmp ) ) { if( tmp > IntTraits< signed __int32 >::maxInt || tmp < IntTraits< signed __int32 >::minInt ) { return false; } *pRet = (__int32)tmp; return true; } return false; #else bool aNegative = false; bool bNegative = false; unsigned __int32 tmp; __int64 b1 = b; if( a < 0 ) { aNegative = true; a = (signed __int32)AbsValueHelper< signed __int32, GetAbsMethod< signed __int32 >::method >::Abs(a); } if( b1 < 0 ) { bNegative = true; b1 = (signed __int64)AbsValueHelper< signed __int64, GetAbsMethod< signed __int64 >::method >::Abs(b1); } if( LargeIntRegMultiply< unsigned __int32, unsigned __int64 >::RegMultiply( (unsigned __int32)a, (unsigned __int64)b1, &tmp ) ) { // The unsigned multiplication didn't overflow if( aNegative ^ bNegative ) { // Result must be negative if( tmp <= (unsigned __int32)IntTraits< signed __int32 >::minInt ) { *pRet = SignedNegation< signed __int32 >::Value( tmp ); return true; } } else { // Result must be positive if( tmp <= (unsigned __int32)IntTraits< signed __int32 >::maxInt ) { *pRet = (signed __int32)tmp; return true; } } } return false; #endif } template < typename E > static void RegMultiplyThrow( signed __int32 a, const signed __int64& b, signed __int32* pRet ) SAFEINT_CPP_THROW { #if SAFEINT_USE_INTRINSICS __int64 tmp; if( IntrinsicMultiplyInt64( a, b, &tmp ) ) { if( tmp > IntTraits< signed __int32 >::maxInt || tmp < IntTraits< signed __int32 >::minInt ) { E::SafeIntOnOverflow(); } *pRet = (__int32)tmp; return; } E::SafeIntOnOverflow(); #else bool aNegative = false; bool bNegative = false; unsigned __int32 tmp; signed __int64 b2 = b; if( a < 0 ) { aNegative = true; a = (signed __int32)AbsValueHelper< signed __int32, GetAbsMethod< signed __int32 >::method >::Abs(a); } if( b < 0 ) { bNegative = true; b2 = (signed __int64)AbsValueHelper< signed __int64, GetAbsMethod< signed __int64 >::method >::Abs(b2); } LargeIntRegMultiply< unsigned __int32, unsigned __int64 >::template RegMultiplyThrow< E >( (unsigned __int32)a, (unsigned __int64)b2, &tmp ); // The unsigned multiplication didn't overflow if( aNegative ^ bNegative ) { // Result must be negative if( tmp <= (unsigned __int32)IntTraits< signed __int32 >::minInt ) { *pRet = SignedNegation< signed __int32 >::Value( tmp ); return; } } else { // Result must be positive if( tmp <= (unsigned __int32)IntTraits< signed __int32 >::maxInt ) { *pRet = (signed __int32)tmp; return; } } E::SafeIntOnOverflow(); #endif } }; template<> class LargeIntRegMultiply< signed __int64, unsigned __int64 > { public: // Leave this one as-is - will call unsigned intrinsic internally static bool RegMultiply( const signed __int64& a, const unsigned __int64& b, signed __int64* pRet ) SAFEINT_NOTHROW { bool aNegative = false; unsigned __int64 tmp; __int64 a1 = a; if( a1 < 0 ) { aNegative = true; a1 = (signed __int64)AbsValueHelper< signed __int64, GetAbsMethod< signed __int64 >::method >::Abs(a1); } if( LargeIntRegMultiply< unsigned __int64, unsigned __int64 >::RegMultiply( (unsigned __int64)a1, (unsigned __int64)b, &tmp ) ) { // The unsigned multiplication didn't overflow if( aNegative ) { // Result must be negative if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::minInt ) { *pRet = SignedNegation< signed __int64 >::Value( tmp ); return true; } } else { // Result must be positive if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::maxInt ) { *pRet = (signed __int64)tmp; return true; } } } return false; } template < typename E > static void RegMultiplyThrow( const signed __int64& a, const unsigned __int64& b, signed __int64* pRet ) SAFEINT_CPP_THROW { bool aNegative = false; unsigned __int64 tmp; __int64 a1 = a; if( a1 < 0 ) { aNegative = true; a1 = (signed __int64)AbsValueHelper< signed __int64, GetAbsMethod< signed __int64 >::method >::Abs(a1); } if( LargeIntRegMultiply< unsigned __int64, unsigned __int64 >::RegMultiply( (unsigned __int64)a1, (unsigned __int64)b, &tmp ) ) { // The unsigned multiplication didn't overflow if( aNegative ) { // Result must be negative if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::minInt ) { *pRet = SignedNegation< signed __int64 >::Value( tmp ); return; } } else { // Result must be positive if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::maxInt ) { *pRet = (signed __int64)tmp; return; } } } E::SafeIntOnOverflow(); } }; // In all of the following functions where LargeIntRegMultiply methods are called, // we need to properly transition types. The methods need __int64, __int32, etc. // but the variables being passed to us could be long long, long int, or long, depending on // the compiler. Microsoft compiler knows that long long is the same type as __int64, but gcc doesn't template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_Uint64Uint64 > { public: // T, U are unsigned __int64 static bool Multiply( const T& t, const U& u, T& ret ) SAFEINT_NOTHROW { C_ASSERT( IntTraits::isUint64 && IntTraits::isUint64 ); unsigned __int64 t1 = t; unsigned __int64 u1 = u; return LargeIntRegMultiply< unsigned __int64, unsigned __int64 >::RegMultiply( t1, u1, reinterpret_cast(&ret) ); } template < typename E > static void MultiplyThrow(const unsigned __int64& t, const unsigned __int64& u, T& ret) SAFEINT_CPP_THROW { C_ASSERT( IntTraits::isUint64 && IntTraits::isUint64 ); unsigned __int64 t1 = t; unsigned __int64 u1 = u; LargeIntRegMultiply< unsigned __int64, unsigned __int64 >::template RegMultiplyThrow< E >( t1, u1, reinterpret_cast(&ret) ); } }; template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_Uint64Uint > { public: // T is unsigned __int64 // U is any unsigned int 32-bit or less static bool Multiply( const T& t, const U& u, T& ret ) SAFEINT_NOTHROW { C_ASSERT( IntTraits::isUint64 ); unsigned __int64 t1 = t; return LargeIntRegMultiply< unsigned __int64, unsigned __int32 >::RegMultiply( t1, (unsigned __int32)u, reinterpret_cast(&ret) ); } template < typename E > static void MultiplyThrow( const T& t, const U& u, T& ret ) SAFEINT_CPP_THROW { C_ASSERT( IntTraits::isUint64 ); unsigned __int64 t1 = t; LargeIntRegMultiply< unsigned __int64, unsigned __int32 >::template RegMultiplyThrow< E >( t1, (unsigned __int32)u, reinterpret_cast(&ret) ); } }; // converse of the previous function template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_UintUint64 > { public: // T is any unsigned int up to 32-bit // U is unsigned __int64 static bool Multiply(const T& t, const U& u, T& ret) SAFEINT_NOTHROW { C_ASSERT( IntTraits::isUint64 ); unsigned __int64 u1 = u; unsigned __int32 tmp; if( LargeIntRegMultiply< unsigned __int32, unsigned __int64 >::RegMultiply( t, u1, &tmp ) && SafeCastHelper< T, unsigned __int32, GetCastMethod< T, unsigned __int32 >::method >::Cast(tmp, ret) ) { return true; } return false; } template < typename E > static void MultiplyThrow(const T& t, const U& u, T& ret) SAFEINT_CPP_THROW { C_ASSERT( IntTraits::isUint64 ); unsigned __int64 u1 = u; unsigned __int32 tmp; LargeIntRegMultiply< unsigned __int32, unsigned __int64 >::template RegMultiplyThrow< E >( t, u1, &tmp ); SafeCastHelper< T, unsigned __int32, GetCastMethod< T, unsigned __int32 >::method >::template CastThrow< E >(tmp, ret); } }; template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_Uint64Int > { public: // T is unsigned __int64 // U is any signed int, up to 64-bit static bool Multiply(const T& t, const U& u, T& ret) SAFEINT_NOTHROW { C_ASSERT( IntTraits::isUint64 ); unsigned __int64 t1 = t; return LargeIntRegMultiply< unsigned __int64, signed __int32 >::RegMultiply(t1, (signed __int32)u, reinterpret_cast< unsigned __int64* >(&ret)); } template < typename E > static void MultiplyThrow(const T& t, const U& u, T& ret) SAFEINT_CPP_THROW { C_ASSERT( IntTraits::isUint64 ); unsigned __int64 t1 = t; LargeIntRegMultiply< unsigned __int64, signed __int32 >::template RegMultiplyThrow< E >(t1, (signed __int32)u, reinterpret_cast< unsigned __int64* >(&ret)); } }; template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_Uint64Int64 > { public: // T is unsigned __int64 // U is __int64 static bool Multiply(const T& t, const U& u, T& ret) SAFEINT_NOTHROW { C_ASSERT( IntTraits::isUint64 && IntTraits::isInt64 ); unsigned __int64 t1 = t; __int64 u1 = u; return LargeIntRegMultiply< unsigned __int64, __int64 >::RegMultiply(t1, u1, reinterpret_cast< unsigned __int64* >(&ret)); } template < typename E > static void MultiplyThrow(const T& t, const U& u, T& ret) SAFEINT_CPP_THROW { C_ASSERT( IntTraits::isUint64 && IntTraits::isInt64 ); unsigned __int64 t1 = t; __int64 u1 = u; LargeIntRegMultiply< unsigned __int64, __int64 >::template RegMultiplyThrow< E >(t1, u1, reinterpret_cast< unsigned __int64* >(&ret)); } }; template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_UintInt64 > { public: // T is unsigned up to 32-bit // U is __int64 static bool Multiply(const T& t, const U& u, T& ret) SAFEINT_NOTHROW { C_ASSERT( IntTraits::isInt64 ); __int64 u1 = u; unsigned __int32 tmp; if( LargeIntRegMultiply< unsigned __int32, __int64 >::RegMultiply( (unsigned __int32)t, u1, &tmp ) && SafeCastHelper< T, unsigned __int32, GetCastMethod< T, unsigned __int32 >::method >::Cast(tmp, ret) ) { return true; } return false; } template < typename E > static void MultiplyThrow(const T& t, const U& u, T& ret) SAFEINT_CPP_THROW { C_ASSERT( IntTraits::isInt64 ); __int64 u1 = u; unsigned __int32 tmp; LargeIntRegMultiply< unsigned __int32, __int64 >::template RegMultiplyThrow< E >( (unsigned __int32)t, u1, &tmp ); SafeCastHelper< T, unsigned __int32, GetCastMethod< T, unsigned __int32 >::method >::template CastThrow< E >(tmp, ret); } }; template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_Int64Uint > { public: // T is __int64 // U is unsigned up to 32-bit static bool Multiply( const T& t, const U& u, T& ret ) SAFEINT_NOTHROW { C_ASSERT( IntTraits::isInt64 ); __int64 t1 = t; return LargeIntRegMultiply< __int64, unsigned __int32 >::RegMultiply( t1, (unsigned __int32)u, reinterpret_cast< __int64* >(&ret) ); } template < typename E > static void MultiplyThrow( const T& t, const U& u, T& ret ) SAFEINT_CPP_THROW { C_ASSERT( IntTraits::isInt64 ); __int64 t1 = t; LargeIntRegMultiply< __int64, unsigned __int32 >::template RegMultiplyThrow< E >( t1, (unsigned __int32)u, reinterpret_cast< __int64* >(&ret) ); } }; template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_Int64Int64 > { public: // T, U are __int64 static bool Multiply( const T& t, const U& u, T& ret ) SAFEINT_NOTHROW { C_ASSERT( IntTraits::isInt64 && IntTraits::isInt64 ); __int64 t1 = t; __int64 u1 = u; return LargeIntRegMultiply< __int64, __int64 >::RegMultiply( t1, u1, reinterpret_cast< __int64* >(&ret) ); } template < typename E > static void MultiplyThrow( const T& t, const U& u, T& ret ) SAFEINT_CPP_THROW { C_ASSERT( IntTraits::isInt64 && IntTraits::isInt64 ); __int64 t1 = t; __int64 u1 = u; LargeIntRegMultiply< __int64, __int64 >::template RegMultiplyThrow< E >( t1, u1, reinterpret_cast< __int64* >(&ret)); } }; template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_Int64Int > { public: // T is __int64 // U is signed up to 32-bit static bool Multiply( const T& t, U u, T& ret ) SAFEINT_NOTHROW { C_ASSERT( IntTraits::isInt64 ); __int64 t1 = t; return LargeIntRegMultiply< __int64, __int32 >::RegMultiply( t1, (__int32)u, reinterpret_cast< __int64* >(&ret)); } template < typename E > static void MultiplyThrow( const __int64& t, U u, T& ret ) SAFEINT_CPP_THROW { C_ASSERT( IntTraits::isInt64 ); __int64 t1 = t; LargeIntRegMultiply< __int64, __int32 >::template RegMultiplyThrow< E >(t1, (__int32)u, reinterpret_cast< __int64* >(&ret)); } }; template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_IntUint64 > { public: // T is signed up to 32-bit // U is unsigned __int64 static bool Multiply(T t, const U& u, T& ret) SAFEINT_NOTHROW { C_ASSERT( IntTraits::isUint64 ); unsigned __int64 u1 = u; __int32 tmp; if( LargeIntRegMultiply< __int32, unsigned __int64 >::RegMultiply( (__int32)t, u1, &tmp ) && SafeCastHelper< T, __int32, GetCastMethod< T, __int32 >::method >::Cast( tmp, ret ) ) { return true; } return false; } template < typename E > static void MultiplyThrow(T t, const unsigned __int64& u, T& ret) SAFEINT_CPP_THROW { C_ASSERT( IntTraits::isUint64 ); unsigned __int64 u1 = u; __int32 tmp; LargeIntRegMultiply< __int32, unsigned __int64 >::template RegMultiplyThrow< E >( (__int32)t, u1, &tmp ); SafeCastHelper< T, __int32, GetCastMethod< T, __int32 >::method >::template CastThrow< E >( tmp, ret ); } }; template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_Int64Uint64> { public: // T is __int64 // U is unsigned __int64 static bool Multiply( const T& t, const U& u, T& ret ) SAFEINT_NOTHROW { C_ASSERT( IntTraits::isInt64 && IntTraits::isUint64 ); __int64 t1 = t; unsigned __int64 u1 = u; return LargeIntRegMultiply< __int64, unsigned __int64 >::RegMultiply( t1, u1, reinterpret_cast< __int64* >(&ret) ); } template < typename E > static void MultiplyThrow( const __int64& t, const unsigned __int64& u, T& ret ) SAFEINT_CPP_THROW { C_ASSERT( IntTraits::isInt64 && IntTraits::isUint64 ); __int64 t1 = t; unsigned __int64 u1 = u; LargeIntRegMultiply< __int64, unsigned __int64 >::template RegMultiplyThrow< E >( t1, u1, reinterpret_cast< __int64* >(&ret) ); } }; template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_IntInt64> { public: // T is signed, up to 32-bit // U is __int64 static bool Multiply( T t, const U& u, T& ret ) SAFEINT_NOTHROW { C_ASSERT( IntTraits::isInt64 ); __int64 u1 = u; __int32 tmp; if( LargeIntRegMultiply< __int32, __int64 >::RegMultiply( (__int32)t, u1, &tmp ) && SafeCastHelper< T, __int32, GetCastMethod< T, __int32 >::method >::Cast( tmp, ret ) ) { return true; } return false; } template < typename E > static void MultiplyThrow(T t, const U& u, T& ret) SAFEINT_CPP_THROW { C_ASSERT( IntTraits::isInt64 ); __int64 u1 = u; __int32 tmp; LargeIntRegMultiply< __int32, __int64 >::template RegMultiplyThrow< E >( (__int32)t, u1, &tmp ); SafeCastHelper< T, __int32, GetCastMethod< T, __int32 >::method >::template CastThrow< E >( tmp, ret ); } }; enum DivisionState { DivisionState_OK, DivisionState_UnsignedSigned, DivisionState_SignedUnsigned32, DivisionState_SignedUnsigned64, DivisionState_SignedUnsigned, DivisionState_SignedSigned }; template < typename T, typename U > class DivisionMethod { public: enum { method = (SafeIntCompare< T, U >::isBothUnsigned ? DivisionState_OK : (!IntTraits< T >::isSigned && IntTraits< U >::isSigned) ? DivisionState_UnsignedSigned : (IntTraits< T >::isSigned && IntTraits< U >::isUint32 && IntTraits< T >::isLT64Bit) ? DivisionState_SignedUnsigned32 : (IntTraits< T >::isSigned && IntTraits< U >::isUint64) ? DivisionState_SignedUnsigned64 : (IntTraits< T >::isSigned && !IntTraits< U >::isSigned) ? DivisionState_SignedUnsigned : DivisionState_SignedSigned) }; }; template < typename T, typename U, int state > class DivisionHelper; template < typename T, typename U > class DivisionHelper< T, U, DivisionState_OK > { public: static SafeIntError Divide( const T& t, const U& u, T& result ) SAFEINT_NOTHROW { if( u == 0 ) return SafeIntDivideByZero; if( t == 0 ) { result = 0; return SafeIntNoError; } result = (T)( t/u ); return SafeIntNoError; } template < typename E > static void DivideThrow( const T& t, const U& u, T& result ) SAFEINT_CPP_THROW { if( u == 0 ) E::SafeIntOnDivZero(); if( t == 0 ) { result = 0; return; } result = (T)( t/u ); } }; template < typename T, typename U > class DivisionHelper< T, U, DivisionState_UnsignedSigned> { public: static SafeIntError Divide( const T& t, const U& u, T& result ) SAFEINT_NOTHROW { if( u == 0 ) return SafeIntDivideByZero; if( t == 0 ) { result = 0; return SafeIntNoError; } if( u > 0 ) { result = (T)( t/u ); return SafeIntNoError; } // it is always an error to try and divide an unsigned number by a negative signed number // unless u is bigger than t if( AbsValueHelper< U, GetAbsMethod< U >::method >::Abs( u ) > t ) { result = 0; return SafeIntNoError; } return SafeIntArithmeticOverflow; } template < typename E > static void DivideThrow( const T& t, const U& u, T& result ) SAFEINT_CPP_THROW { if( u == 0 ) E::SafeIntOnDivZero(); if( t == 0 ) { result = 0; return; } if( u > 0 ) { result = (T)( t/u ); return; } // it is always an error to try and divide an unsigned number by a negative signed number // unless u is bigger than t if( AbsValueHelper< U, GetAbsMethod< U >::method >::Abs( u ) > t ) { result = 0; return; } E::SafeIntOnOverflow(); } }; template < typename T, typename U > class DivisionHelper< T, U, DivisionState_SignedUnsigned32 > { public: static SafeIntError Divide( const T& t, const U& u, T& result ) SAFEINT_NOTHROW { if( u == 0 ) return SafeIntDivideByZero; if( t == 0 ) { result = 0; return SafeIntNoError; } // Test for t > 0 // If t < 0, must explicitly upcast, or implicit upcast to ulong will cause errors // As it turns out, 32-bit division is about twice as fast, which justifies the extra conditional if( t > 0 ) result = (T)( t/u ); else result = (T)( (__int64)t/(__int64)u ); return SafeIntNoError; } template < typename E > static void DivideThrow( const T& t, const U& u, T& result ) SAFEINT_CPP_THROW { if( u == 0 ) { E::SafeIntOnDivZero(); } if( t == 0 ) { result = 0; return; } // Test for t > 0 // If t < 0, must explicitly upcast, or implicit upcast to ulong will cause errors // As it turns out, 32-bit division is about twice as fast, which justifies the extra conditional if( t > 0 ) result = (T)( t/u ); else result = (T)( (__int64)t/(__int64)u ); } }; template < typename T, typename U > class DivisionHelper< T, U, DivisionState_SignedUnsigned64 > { public: static SafeIntError Divide( const T& t, const unsigned __int64& u, T& result ) SAFEINT_NOTHROW { C_ASSERT( IntTraits< U >::isUint64 ); if( u == 0 ) { return SafeIntDivideByZero; } if( t == 0 ) { result = 0; return SafeIntNoError; } if( u <= (unsigned __int64)IntTraits< T >::maxInt ) { // Else u can safely be cast to T if( CompileConst< sizeof( T ) < sizeof( __int64 )>::Value() ) result = (T)( (int)t/(int)u ); else result = (T)((__int64)t/(__int64)u); } else // Corner case if( t == IntTraits< T >::minInt && u == (unsigned __int64)IntTraits< T >::minInt ) { // Min int divided by it's own magnitude is -1 result = -1; } else { result = 0; } return SafeIntNoError; } template < typename E > static void DivideThrow( const T& t, const unsigned __int64& u, T& result ) SAFEINT_CPP_THROW { C_ASSERT( IntTraits< U >::isUint64 ); if( u == 0 ) { E::SafeIntOnDivZero(); } if( t == 0 ) { result = 0; return; } if( u <= (unsigned __int64)IntTraits< T >::maxInt ) { // Else u can safely be cast to T if( CompileConst< sizeof( T ) < sizeof( __int64 ) >::Value() ) result = (T)( (int)t/(int)u ); else result = (T)((__int64)t/(__int64)u); } else // Corner case if( t == IntTraits< T >::minInt && u == (unsigned __int64)IntTraits< T >::minInt ) { // Min int divided by it's own magnitude is -1 result = -1; } else { result = 0; } } }; template < typename T, typename U > class DivisionHelper< T, U, DivisionState_SignedUnsigned> { public: // T is any signed, U is unsigned and smaller than 32-bit // In this case, standard operator casting is correct static SafeIntError Divide( const T& t, const U& u, T& result ) SAFEINT_NOTHROW { if( u == 0 ) { return SafeIntDivideByZero; } if( t == 0 ) { result = 0; return SafeIntNoError; } result = (T)( t/u ); return SafeIntNoError; } template < typename E > static void DivideThrow( const T& t, const U& u, T& result ) SAFEINT_CPP_THROW { if( u == 0 ) { E::SafeIntOnDivZero(); } if( t == 0 ) { result = 0; return; } result = (T)( t/u ); } }; template < typename T, typename U > class DivisionHelper< T, U, DivisionState_SignedSigned> { public: static SafeIntError Divide( const T& t, const U& u, T& result ) SAFEINT_NOTHROW { if( u == 0 ) { return SafeIntDivideByZero; } if( t == 0 ) { result = 0; return SafeIntNoError; } // Must test for corner case if( t == IntTraits< T >::minInt && u == (U)-1 ) return SafeIntArithmeticOverflow; result = (T)( t/u ); return SafeIntNoError; } template < typename E > static void DivideThrow( const T& t, const U& u, T& result ) SAFEINT_CPP_THROW { if(u == 0) { E::SafeIntOnDivZero(); } if( t == 0 ) { result = 0; return; } // Must test for corner case if( t == IntTraits< T >::minInt && u == (U)-1 ) E::SafeIntOnOverflow(); result = (T)( t/u ); } }; enum AdditionState { AdditionState_CastIntCheckMax, AdditionState_CastUintCheckOverflow, AdditionState_CastUintCheckOverflowMax, AdditionState_CastUint64CheckOverflow, AdditionState_CastUint64CheckOverflowMax, AdditionState_CastIntCheckSafeIntMinMax, AdditionState_CastInt64CheckSafeIntMinMax, AdditionState_CastInt64CheckMax, AdditionState_CastUint64CheckSafeIntMinMax, AdditionState_CastUint64CheckSafeIntMinMax2, AdditionState_CastInt64CheckOverflow, AdditionState_CastInt64CheckOverflowSafeIntMinMax, AdditionState_CastInt64CheckOverflowMax, AdditionState_ManualCheckInt64Uint64, AdditionState_ManualCheck, AdditionState_Error }; template< typename T, typename U > class AdditionMethod { public: enum { //unsigned-unsigned method = (IntRegion< T,U >::IntZone_UintLT32_UintLT32 ? AdditionState_CastIntCheckMax : (IntRegion< T,U >::IntZone_Uint32_UintLT64) ? AdditionState_CastUintCheckOverflow : (IntRegion< T,U >::IntZone_UintLT32_Uint32) ? AdditionState_CastUintCheckOverflowMax : (IntRegion< T,U >::IntZone_Uint64_Uint) ? AdditionState_CastUint64CheckOverflow : (IntRegion< T,U >::IntZone_UintLT64_Uint64) ? AdditionState_CastUint64CheckOverflowMax : //unsigned-signed (IntRegion< T,U >::IntZone_UintLT32_IntLT32) ? AdditionState_CastIntCheckSafeIntMinMax : (IntRegion< T,U >::IntZone_Uint32_IntLT64 || IntRegion< T,U >::IntZone_UintLT32_Int32) ? AdditionState_CastInt64CheckSafeIntMinMax : (IntRegion< T,U >::IntZone_Uint64_Int || IntRegion< T,U >::IntZone_Uint64_Int64) ? AdditionState_CastUint64CheckSafeIntMinMax : (IntRegion< T,U >::IntZone_UintLT64_Int64) ? AdditionState_CastUint64CheckSafeIntMinMax2 : //signed-signed (IntRegion< T,U >::IntZone_IntLT32_IntLT32) ? AdditionState_CastIntCheckSafeIntMinMax : (IntRegion< T,U >::IntZone_Int32_IntLT64 || IntRegion< T,U >::IntZone_IntLT32_Int32) ? AdditionState_CastInt64CheckSafeIntMinMax : (IntRegion< T,U >::IntZone_Int64_Int || IntRegion< T,U >::IntZone_Int64_Int64) ? AdditionState_CastInt64CheckOverflow : (IntRegion< T,U >::IntZone_IntLT64_Int64) ? AdditionState_CastInt64CheckOverflowSafeIntMinMax : //signed-unsigned (IntRegion< T,U >::IntZone_IntLT32_UintLT32) ? AdditionState_CastIntCheckMax : (IntRegion< T,U >::IntZone_Int32_UintLT32 || IntRegion< T,U >::IntZone_IntLT64_Uint32) ? AdditionState_CastInt64CheckMax : (IntRegion< T,U >::IntZone_Int64_UintLT64) ? AdditionState_CastInt64CheckOverflowMax : (IntRegion< T,U >::IntZone_Int64_Uint64) ? AdditionState_ManualCheckInt64Uint64 : (IntRegion< T,U >::IntZone_Int_Uint64) ? AdditionState_ManualCheck : AdditionState_Error) }; }; template < typename T, typename U, int method > class AdditionHelper; template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastIntCheckMax > { public: static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW { //16-bit or less unsigned addition __int32 tmp = lhs + rhs; if( tmp <= (__int32)IntTraits< T >::maxInt ) { result = (T)tmp; return true; } return false; } template < typename E > static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW { //16-bit or less unsigned addition __int32 tmp = lhs + rhs; if( tmp <= (__int32)IntTraits< T >::maxInt ) { result = (T)tmp; return; } E::SafeIntOnOverflow(); } }; template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastUintCheckOverflow > { public: static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW { // 32-bit or less - both are unsigned unsigned __int32 tmp = (unsigned __int32)lhs + (unsigned __int32)rhs; //we added didn't get smaller if( tmp >= lhs ) { result = (T)tmp; return true; } return false; } template < typename E > static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW { // 32-bit or less - both are unsigned unsigned __int32 tmp = (unsigned __int32)lhs + (unsigned __int32)rhs; //we added didn't get smaller if( tmp >= lhs ) { result = (T)tmp; return; } E::SafeIntOnOverflow(); } }; template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastUintCheckOverflowMax> { public: static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW { // 32-bit or less - both are unsigned unsigned __int32 tmp = (unsigned __int32)lhs + (unsigned __int32)rhs; // We added and it didn't get smaller or exceed maxInt if( tmp >= lhs && tmp <= IntTraits< T >::maxInt ) { result = (T)tmp; return true; } return false; } template < typename E > static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW { //32-bit or less - both are unsigned unsigned __int32 tmp = (unsigned __int32)lhs + (unsigned __int32)rhs; // We added and it didn't get smaller or exceed maxInt if( tmp >= lhs && tmp <= IntTraits< T >::maxInt ) { result = (T)tmp; return; } E::SafeIntOnOverflow(); } }; template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastUint64CheckOverflow> { public: static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW { // lhs unsigned __int64, rhs unsigned unsigned __int64 tmp = (unsigned __int64)lhs + (unsigned __int64)rhs; // We added and it didn't get smaller if(tmp >= lhs) { result = (T)tmp; return true; } return false; } template < typename E > static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW { // lhs unsigned __int64, rhs unsigned unsigned __int64 tmp = (unsigned __int64)lhs + (unsigned __int64)rhs; // We added and it didn't get smaller if(tmp >= lhs) { result = (T)tmp; return; } E::SafeIntOnOverflow(); } }; template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastUint64CheckOverflowMax > { public: static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW { //lhs unsigned __int64, rhs unsigned unsigned __int64 tmp = (unsigned __int64)lhs + (unsigned __int64)rhs; // We added and it didn't get smaller if( tmp >= lhs && tmp <= IntTraits< T >::maxInt ) { result = (T)tmp; return true; } return false; } template < typename E > static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW { //lhs unsigned __int64, rhs unsigned unsigned __int64 tmp = (unsigned __int64)lhs + (unsigned __int64)rhs; // We added and it didn't get smaller if( tmp >= lhs && tmp <= IntTraits< T >::maxInt ) { result = (T)tmp; return; } E::SafeIntOnOverflow(); } }; template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastIntCheckSafeIntMinMax > { public: static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW { // 16-bit or less - one or both are signed __int32 tmp = lhs + rhs; if( tmp <= (__int32)IntTraits< T >::maxInt && tmp >= (__int32)IntTraits< T >::minInt ) { result = (T)tmp; return true; } return false; } template < typename E > static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW { // 16-bit or less - one or both are signed __int32 tmp = lhs + rhs; if( tmp <= (__int32)IntTraits< T >::maxInt && tmp >= (__int32)IntTraits< T >::minInt ) { result = (T)tmp; return; } E::SafeIntOnOverflow(); } }; template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastInt64CheckSafeIntMinMax > { public: static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW { // 32-bit or less - one or both are signed __int64 tmp = (__int64)lhs + (__int64)rhs; if( tmp <= (__int64)IntTraits< T >::maxInt && tmp >= (__int64)IntTraits< T >::minInt ) { result = (T)tmp; return true; } return false; } template < typename E > static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW { // 32-bit or less - one or both are signed __int64 tmp = (__int64)lhs + (__int64)rhs; if( tmp <= (__int64)IntTraits< T >::maxInt && tmp >= (__int64)IntTraits< T >::minInt ) { result = (T)tmp; return; } E::SafeIntOnOverflow(); } }; template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastInt64CheckMax > { public: static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW { // 32-bit or less - lhs signed, rhs unsigned __int64 tmp = (__int64)lhs + (__int64)rhs; if( tmp <= IntTraits< T >::maxInt ) { result = (T)tmp; return true; } return false; } template < typename E > static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW { // 32-bit or less - lhs signed, rhs unsigned __int64 tmp = (__int64)lhs + (__int64)rhs; if( tmp <= IntTraits< T >::maxInt ) { result = (T)tmp; return; } E::SafeIntOnOverflow(); } }; template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastUint64CheckSafeIntMinMax > { public: static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW { // lhs is unsigned __int64, rhs signed unsigned __int64 tmp; if( rhs < 0 ) { // So we're effectively subtracting tmp = AbsValueHelper< U, GetAbsMethod< U >::method >::Abs( rhs ); if( tmp <= lhs ) { result = lhs - tmp; return true; } } else { // now we know that rhs can be safely cast into an unsigned __int64 tmp = (unsigned __int64)lhs + (unsigned __int64)rhs; // We added and it did not become smaller if( tmp >= lhs ) { result = (T)tmp; return true; } } return false; } template < typename E > static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW { // lhs is unsigned __int64, rhs signed unsigned __int64 tmp; if( rhs < 0 ) { // So we're effectively subtracting tmp = AbsValueHelper< U, GetAbsMethod< U >::method >::Abs( rhs ); if( tmp <= lhs ) { result = lhs - tmp; return; } } else { // now we know that rhs can be safely cast into an unsigned __int64 tmp = (unsigned __int64)lhs + (unsigned __int64)rhs; // We added and it did not become smaller if( tmp >= lhs ) { result = (T)tmp; return; } } E::SafeIntOnOverflow(); } }; template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastUint64CheckSafeIntMinMax2> { public: static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW { // lhs is unsigned and < 64-bit, rhs signed __int64 if( rhs < 0 ) { if( lhs >= ~(unsigned __int64)( rhs ) + 1 )//negation is safe, since rhs is 64-bit { result = (T)( lhs + rhs ); return true; } } else { // now we know that rhs can be safely cast into an unsigned __int64 unsigned __int64 tmp = (unsigned __int64)lhs + (unsigned __int64)rhs; // special case - rhs cannot be larger than 0x7fffffffffffffff, lhs cannot be larger than 0xffffffff // it is not possible for the operation above to overflow, so just check max if( tmp <= IntTraits< T >::maxInt ) { result = (T)tmp; return true; } } return false; } template < typename E > static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW { // lhs is unsigned and < 64-bit, rhs signed __int64 if( rhs < 0 ) { if( lhs >= ~(unsigned __int64)( rhs ) + 1) //negation is safe, since rhs is 64-bit { result = (T)( lhs + rhs ); return; } } else { // now we know that rhs can be safely cast into an unsigned __int64 unsigned __int64 tmp = (unsigned __int64)lhs + (unsigned __int64)rhs; // special case - rhs cannot be larger than 0x7fffffffffffffff, lhs cannot be larger than 0xffffffff // it is not possible for the operation above to overflow, so just check max if( tmp <= IntTraits< T >::maxInt ) { result = (T)tmp; return; } } E::SafeIntOnOverflow(); } }; template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastInt64CheckOverflow> { public: static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW { // lhs is signed __int64, rhs signed __int64 tmp = (__int64)((unsigned __int64)lhs + (unsigned __int64)rhs); if( lhs >= 0 ) { // mixed sign cannot overflow if( rhs >= 0 && tmp < lhs ) return false; } else { // lhs negative if( rhs < 0 && tmp > lhs ) return false; } result = (T)tmp; return true; } template < typename E > static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW { // lhs is signed __int64, rhs signed __int64 tmp = (__int64)((unsigned __int64)lhs + (unsigned __int64)rhs); if( lhs >= 0 ) { // mixed sign cannot overflow if( rhs >= 0 && tmp < lhs ) E::SafeIntOnOverflow(); } else { // lhs negative if( rhs < 0 && tmp > lhs ) E::SafeIntOnOverflow(); } result = (T)tmp; } }; template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastInt64CheckOverflowSafeIntMinMax> { public: static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW { //rhs is signed __int64, lhs signed __int64 tmp; if( AdditionHelper< __int64, __int64, AdditionState_CastInt64CheckOverflow >::Addition( (__int64)lhs, (__int64)rhs, tmp ) && tmp <= IntTraits< T >::maxInt && tmp >= IntTraits< T >::minInt ) { result = (T)tmp; return true; } return false; } template < typename E > static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW { //rhs is signed __int64, lhs signed __int64 tmp; AdditionHelper< __int64, __int64, AdditionState_CastInt64CheckOverflow >::AdditionThrow< E >( (__int64)lhs, (__int64)rhs, tmp ); if( tmp <= IntTraits< T >::maxInt && tmp >= IntTraits< T >::minInt ) { result = (T)tmp; return; } E::SafeIntOnOverflow(); } }; template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastInt64CheckOverflowMax> { public: static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW { //lhs is signed __int64, rhs unsigned < 64-bit unsigned __int64 tmp = (unsigned __int64)lhs + (unsigned __int64)rhs; if( (__int64)tmp >= lhs ) { result = (T)(__int64)tmp; return true; } return false; } template < typename E > static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW { // lhs is signed __int64, rhs unsigned < 64-bit // Some compilers get optimization-happy, let's thwart them unsigned __int64 tmp = (unsigned __int64)lhs + (unsigned __int64)rhs; if( (__int64)tmp >= lhs ) { result = (T)(__int64)tmp; return; } E::SafeIntOnOverflow(); } }; template < typename T, typename U > class AdditionHelper < T, U, AdditionState_ManualCheckInt64Uint64 > { public: static bool Addition( const __int64& lhs, const unsigned __int64& rhs, __int64& result ) SAFEINT_NOTHROW { C_ASSERT( IntTraits< T >::isInt64 && IntTraits< U >::isUint64 ); // rhs is unsigned __int64, lhs __int64 // cast everything to unsigned, perform addition, then // cast back for check - this is done to stop optimizers from removing the code unsigned __int64 tmp = (unsigned __int64)lhs + rhs; if( (__int64)tmp >= lhs ) { result = (__int64)tmp; return true; } return false; } template < typename E > static void AdditionThrow( const __int64& lhs, const unsigned __int64& rhs, T& result ) SAFEINT_CPP_THROW { C_ASSERT( IntTraits< T >::isInt64 && IntTraits< U >::isUint64 ); // rhs is unsigned __int64, lhs __int64 unsigned __int64 tmp = (unsigned __int64)lhs + rhs; if( (__int64)tmp >= lhs ) { result = (__int64)tmp; return; } E::SafeIntOnOverflow(); } }; template < typename T, typename U > class AdditionHelper < T, U, AdditionState_ManualCheck> { public: static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW { // rhs is unsigned __int64, lhs signed, 32-bit or less if( (unsigned __int32)( rhs >> 32 ) == 0 ) { // Now it just happens to work out that the standard behavior does what we want // Adding explicit casts to show exactly what's happening here // Note - this is tweaked to keep optimizers from tossing out the code. unsigned __int32 tmp = (unsigned __int32)rhs + (unsigned __int32)lhs; if( (__int32)tmp >= lhs && SafeCastHelper< T, __int32, GetCastMethod< T, __int32 >::method >::Cast( (__int32)tmp, result ) ) return true; } return false; } template < typename E > static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW { // rhs is unsigned __int64, lhs signed, 32-bit or less if( (unsigned __int32)( rhs >> 32 ) == 0 ) { // Now it just happens to work out that the standard behavior does what we want // Adding explicit casts to show exactly what's happening here unsigned __int32 tmp = (unsigned __int32)rhs + (unsigned __int32)lhs; if( (__int32)tmp >= lhs ) { SafeCastHelper< T, __int32, GetCastMethod< T, __int32 >::method >::template CastThrow< E >( (__int32)tmp, result ); return; } } E::SafeIntOnOverflow(); } }; enum SubtractionState { SubtractionState_BothUnsigned, SubtractionState_CastIntCheckSafeIntMinMax, SubtractionState_CastIntCheckMin, SubtractionState_CastInt64CheckSafeIntMinMax, SubtractionState_CastInt64CheckMin, SubtractionState_Uint64Int, SubtractionState_UintInt64, SubtractionState_Int64Int, SubtractionState_IntInt64, SubtractionState_Int64Uint, SubtractionState_IntUint64, SubtractionState_Int64Uint64, // states for SubtractionMethod2 SubtractionState_BothUnsigned2, SubtractionState_CastIntCheckSafeIntMinMax2, SubtractionState_CastInt64CheckSafeIntMinMax2, SubtractionState_Uint64Int2, SubtractionState_UintInt642, SubtractionState_Int64Int2, SubtractionState_IntInt642, SubtractionState_Int64Uint2, SubtractionState_IntUint642, SubtractionState_Int64Uint642, SubtractionState_Error }; template < typename T, typename U > class SubtractionMethod { public: enum { // unsigned-unsigned method = ((IntRegion< T,U >::IntZone_UintLT32_UintLT32 || (IntRegion< T,U >::IntZone_Uint32_UintLT64) || (IntRegion< T,U >::IntZone_UintLT32_Uint32) || (IntRegion< T,U >::IntZone_Uint64_Uint) || (IntRegion< T,U >::IntZone_UintLT64_Uint64)) ? SubtractionState_BothUnsigned : // unsigned-signed (IntRegion< T,U >::IntZone_UintLT32_IntLT32) ? SubtractionState_CastIntCheckSafeIntMinMax : (IntRegion< T,U >::IntZone_Uint32_IntLT64 || IntRegion< T,U >::IntZone_UintLT32_Int32) ? SubtractionState_CastInt64CheckSafeIntMinMax : (IntRegion< T,U >::IntZone_Uint64_Int || IntRegion< T,U >::IntZone_Uint64_Int64) ? SubtractionState_Uint64Int : (IntRegion< T,U >::IntZone_UintLT64_Int64) ? SubtractionState_UintInt64 : // signed-signed (IntRegion< T,U >::IntZone_IntLT32_IntLT32) ? SubtractionState_CastIntCheckSafeIntMinMax : (IntRegion< T,U >::IntZone_Int32_IntLT64 || IntRegion< T,U >::IntZone_IntLT32_Int32) ? SubtractionState_CastInt64CheckSafeIntMinMax : (IntRegion< T,U >::IntZone_Int64_Int || IntRegion< T,U >::IntZone_Int64_Int64) ? SubtractionState_Int64Int : (IntRegion< T,U >::IntZone_IntLT64_Int64) ? SubtractionState_IntInt64 : // signed-unsigned (IntRegion< T,U >::IntZone_IntLT32_UintLT32) ? SubtractionState_CastIntCheckMin : (IntRegion< T,U >::IntZone_Int32_UintLT32 || IntRegion< T,U >::IntZone_IntLT64_Uint32) ? SubtractionState_CastInt64CheckMin : (IntRegion< T,U >::IntZone_Int64_UintLT64) ? SubtractionState_Int64Uint : (IntRegion< T,U >::IntZone_Int_Uint64) ? SubtractionState_IntUint64 : (IntRegion< T,U >::IntZone_Int64_Uint64) ? SubtractionState_Int64Uint64 : SubtractionState_Error) }; }; // this is for the case of U - SafeInt< T, E > template < typename T, typename U > class SubtractionMethod2 { public: enum { // unsigned-unsigned method = ((IntRegion< T,U >::IntZone_UintLT32_UintLT32 || (IntRegion< T,U >::IntZone_Uint32_UintLT64) || (IntRegion< T,U >::IntZone_UintLT32_Uint32) || (IntRegion< T,U >::IntZone_Uint64_Uint) || (IntRegion< T,U >::IntZone_UintLT64_Uint64)) ? SubtractionState_BothUnsigned2 : // unsigned-signed (IntRegion< T,U >::IntZone_UintLT32_IntLT32) ? SubtractionState_CastIntCheckSafeIntMinMax2 : (IntRegion< T,U >::IntZone_Uint32_IntLT64 || IntRegion< T,U >::IntZone_UintLT32_Int32) ? SubtractionState_CastInt64CheckSafeIntMinMax2 : (IntRegion< T,U >::IntZone_Uint64_Int || IntRegion< T,U >::IntZone_Uint64_Int64) ? SubtractionState_Uint64Int2 : (IntRegion< T,U >::IntZone_UintLT64_Int64) ? SubtractionState_UintInt642 : // signed-signed (IntRegion< T,U >::IntZone_IntLT32_IntLT32) ? SubtractionState_CastIntCheckSafeIntMinMax2 : (IntRegion< T,U >::IntZone_Int32_IntLT64 || IntRegion< T,U >::IntZone_IntLT32_Int32) ? SubtractionState_CastInt64CheckSafeIntMinMax2 : (IntRegion< T,U >::IntZone_Int64_Int || IntRegion< T,U >::IntZone_Int64_Int64) ? SubtractionState_Int64Int2 : (IntRegion< T,U >::IntZone_IntLT64_Int64) ? SubtractionState_IntInt642 : // signed-unsigned (IntRegion< T,U >::IntZone_IntLT32_UintLT32) ? SubtractionState_CastIntCheckSafeIntMinMax2 : (IntRegion< T,U >::IntZone_Int32_UintLT32 || IntRegion< T,U >::IntZone_IntLT64_Uint32) ? SubtractionState_CastInt64CheckSafeIntMinMax2 : (IntRegion< T,U >::IntZone_Int64_UintLT64) ? SubtractionState_Int64Uint2 : (IntRegion< T,U >::IntZone_Int_Uint64) ? SubtractionState_IntUint642 : (IntRegion< T,U >::IntZone_Int64_Uint64) ? SubtractionState_Int64Uint642 : SubtractionState_Error) }; }; template < typename T, typename U, int method > class SubtractionHelper; template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_BothUnsigned > { public: static bool Subtract( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW { // both are unsigned - easy case if( rhs <= lhs ) { result = (T)( lhs - rhs ); return true; } return false; } template < typename E > static void SubtractThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW { // both are unsigned - easy case if( rhs <= lhs ) { result = (T)( lhs - rhs ); return; } E::SafeIntOnOverflow(); } }; template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_BothUnsigned2 > { public: static bool Subtract( const T& lhs, const U& rhs, U& result ) SAFEINT_NOTHROW { // both are unsigned - easy case // Except we do have to check for overflow - lhs could be larger than result can hold if( rhs <= lhs ) { T tmp = (T)(lhs - rhs); return SafeCastHelper< U, T, GetCastMethod::method>::Cast( tmp, result); } return false; } template < typename E > static void SubtractThrow( const T& lhs, const U& rhs, U& result ) SAFEINT_CPP_THROW { // both are unsigned - easy case if( rhs <= lhs ) { T tmp = (T)(lhs - rhs); SafeCastHelper< U, T, GetCastMethod::method >::template CastThrow( tmp, result); return; } E::SafeIntOnOverflow(); } }; template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_CastIntCheckSafeIntMinMax > { public: static bool Subtract( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW { // both values are 16-bit or less // rhs is signed, so could end up increasing or decreasing __int32 tmp = lhs - rhs; if( SafeCastHelper< T, __int32, GetCastMethod< T, __int32 >::method >::Cast( tmp, result ) ) { result = (T)tmp; return true; } return false; } template < typename E > static void SubtractThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW { // both values are 16-bit or less // rhs is signed, so could end up increasing or decreasing __int32 tmp = lhs - rhs; SafeCastHelper< T, __int32, GetCastMethod< T, __int32 >::method >::template CastThrow< E >( tmp, result ); } }; template class SubtractionHelper< U, T, SubtractionState_CastIntCheckSafeIntMinMax2 > { public: static bool Subtract( const U& lhs, const T& rhs, T& result ) SAFEINT_NOTHROW { // both values are 16-bit or less // rhs is signed, so could end up increasing or decreasing __int32 tmp = lhs - rhs; return SafeCastHelper< T, __int32, GetCastMethod< T, __int32 >::method >::Cast( tmp, result ); } template < typename E > static void SubtractThrow( const U& lhs, const T& rhs, T& result ) SAFEINT_CPP_THROW { // both values are 16-bit or less // rhs is signed, so could end up increasing or decreasing __int32 tmp = lhs - rhs; SafeCastHelper< T, __int32, GetCastMethod< T, __int32 >::method >::template CastThrow< E >( tmp, result ); } }; template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_CastIntCheckMin > { public: static bool Subtract( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW { // both values are 16-bit or less // rhs is unsigned - check only minimum __int32 tmp = lhs - rhs; if( tmp >= (__int32)IntTraits< T >::minInt ) { result = (T)tmp; return true; } return false; } template < typename E > static void SubtractThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW { // both values are 16-bit or less // rhs is unsigned - check only minimum __int32 tmp = lhs - rhs; if( tmp >= (__int32)IntTraits< T >::minInt ) { result = (T)tmp; return; } E::SafeIntOnOverflow(); } }; template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_CastInt64CheckSafeIntMinMax > { public: static bool Subtract( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW { // both values are 32-bit or less // rhs is signed, so could end up increasing or decreasing __int64 tmp = (__int64)lhs - (__int64)rhs; return SafeCastHelper< T, __int64, GetCastMethod< T, __int64 >::method >::Cast( tmp, result ); } template < typename E > static void SubtractThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW { // both values are 32-bit or less // rhs is signed, so could end up increasing or decreasing __int64 tmp = (__int64)lhs - (__int64)rhs; SafeCastHelper< T, __int64, GetCastMethod< T, __int64 >::method >::template CastThrow< E >( tmp, result ); } }; template class SubtractionHelper< U, T, SubtractionState_CastInt64CheckSafeIntMinMax2 > { public: static bool Subtract( const U& lhs, const T& rhs, T& result ) SAFEINT_NOTHROW { // both values are 32-bit or less // rhs is signed, so could end up increasing or decreasing __int64 tmp = (__int64)lhs - (__int64)rhs; return SafeCastHelper< T, __int64, GetCastMethod< T, __int64 >::method >::Cast( tmp, result ); } template < typename E > static void SubtractThrow( const U& lhs, const T& rhs, T& result ) SAFEINT_CPP_THROW { // both values are 32-bit or less // rhs is signed, so could end up increasing or decreasing __int64 tmp = (__int64)lhs - (__int64)rhs; SafeCastHelper< T, __int64, GetCastMethod< T, __int64 >::method >::template CastThrow< E >( tmp, result ); } }; template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_CastInt64CheckMin > { public: static bool Subtract( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW { // both values are 32-bit or less // rhs is unsigned - check only minimum __int64 tmp = (__int64)lhs - (__int64)rhs; if( tmp >= (__int64)IntTraits< T >::minInt ) { result = (T)tmp; return true; } return false; } template < typename E > static void SubtractThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW { // both values are 32-bit or less // rhs is unsigned - check only minimum __int64 tmp = (__int64)lhs - (__int64)rhs; if( tmp >= (__int64)IntTraits< T >::minInt ) { result = (T)tmp; return; } E::SafeIntOnOverflow(); } }; template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_Uint64Int > { public: static bool Subtract( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW { // lhs is an unsigned __int64, rhs signed // must first see if rhs is positive or negative if( rhs >= 0 ) { if( (unsigned __int64)rhs <= lhs ) { result = (T)( lhs - (unsigned __int64)rhs ); return true; } } else { T tmp = lhs; // we're now effectively adding result = lhs + AbsValueHelper< U, GetAbsMethod< U >::method >::Abs( rhs ); if(result >= tmp) return true; } return false; } template < typename E > static void SubtractThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW { // lhs is an unsigned __int64, rhs signed // must first see if rhs is positive or negative if( rhs >= 0 ) { if( (unsigned __int64)rhs <= lhs ) { result = (T)( lhs - (unsigned __int64)rhs ); return; } } else { T tmp = lhs; // we're now effectively adding result = lhs + AbsValueHelper< U, GetAbsMethod< U >::method >::Abs( rhs ); if(result >= tmp) return; } E::SafeIntOnOverflow(); } }; template < typename U, typename T > class SubtractionHelper< U, T, SubtractionState_Uint64Int2 > { public: static bool Subtract( const U& lhs, const T& rhs, T& result ) SAFEINT_NOTHROW { // U is unsigned __int64, T is signed if( rhs < 0 ) { // treat this as addition unsigned __int64 tmp; tmp = lhs + (unsigned __int64)AbsValueHelper< T, GetAbsMethod< T >::method >::Abs( rhs ); // must check for addition overflow and max if( tmp >= lhs && tmp <= IntTraits< T >::maxInt ) { result = (T)tmp; return true; } } else if( (unsigned __int64)rhs > lhs ) // now both are positive, so comparison always works { // result is negative // implies that lhs must fit into T, and result cannot overflow // Also allows us to drop to 32-bit math, which is faster on a 32-bit system result = (T)lhs - (T)rhs; return true; } else { // result is positive unsigned __int64 tmp = (unsigned __int64)lhs - (unsigned __int64)rhs; if( tmp <= IntTraits< T >::maxInt ) { result = (T)tmp; return true; } } return false; } template < typename E > static void SubtractThrow( const U& lhs, const T& rhs, T& result ) SAFEINT_CPP_THROW { // U is unsigned __int64, T is signed if( rhs < 0 ) { // treat this as addition unsigned __int64 tmp; tmp = lhs + (unsigned __int64)AbsValueHelper< T, GetAbsMethod< T >::method >::Abs( rhs ); // must check for addition overflow and max if( tmp >= lhs && tmp <= IntTraits< T >::maxInt ) { result = (T)tmp; return; } } else if( (unsigned __int64)rhs > lhs ) // now both are positive, so comparison always works { // result is negative // implies that lhs must fit into T, and result cannot overflow // Also allows us to drop to 32-bit math, which is faster on a 32-bit system result = (T)lhs - (T)rhs; return; } else { // result is positive unsigned __int64 tmp = (unsigned __int64)lhs - (unsigned __int64)rhs; if( tmp <= IntTraits< T >::maxInt ) { result = (T)tmp; return; } } E::SafeIntOnOverflow(); } }; template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_UintInt64 > { public: static bool Subtract( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW { // lhs is an unsigned int32 or smaller, rhs signed __int64 // must first see if rhs is positive or negative if( rhs >= 0 ) { if( (unsigned __int64)rhs <= lhs ) { result = (T)( lhs - (T)rhs ); return true; } } else { // we're now effectively adding // since lhs is 32-bit, and rhs cannot exceed 2^63 // this addition cannot overflow unsigned __int64 tmp = lhs + ~(unsigned __int64)( rhs ) + 1; // negation safe // but we could exceed MaxInt if(tmp <= IntTraits< T >::maxInt) { result = (T)tmp; return true; } } return false; } template < typename E > static void SubtractThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW { // lhs is an unsigned int32 or smaller, rhs signed __int64 // must first see if rhs is positive or negative if( rhs >= 0 ) { if( (unsigned __int64)rhs <= lhs ) { result = (T)( lhs - (T)rhs ); return; } } else { // we're now effectively adding // since lhs is 32-bit, and rhs cannot exceed 2^63 // this addition cannot overflow unsigned __int64 tmp = lhs + ~(unsigned __int64)( rhs ) + 1; // negation safe // but we could exceed MaxInt if(tmp <= IntTraits< T >::maxInt) { result = (T)tmp; return; } } E::SafeIntOnOverflow(); } }; template class SubtractionHelper< U, T, SubtractionState_UintInt642 > { public: static bool Subtract( const U& lhs, const T& rhs, T& result ) SAFEINT_NOTHROW { // U unsigned 32-bit or less, T __int64 if( rhs >= 0 ) { // overflow not possible result = (T)( (__int64)lhs - rhs ); return true; } else { // we effectively have an addition // which cannot overflow internally unsigned __int64 tmp = (unsigned __int64)lhs + (unsigned __int64)( -rhs ); if( tmp <= (unsigned __int64)IntTraits< T >::maxInt ) { result = (T)tmp; return true; } } return false; } template < typename E > static void SubtractThrow( const U& lhs, const T& rhs, T& result ) SAFEINT_CPP_THROW { // U unsigned 32-bit or less, T __int64 if( rhs >= 0 ) { // overflow not possible result = (T)( (__int64)lhs - rhs ); return; } else { // we effectively have an addition // which cannot overflow internally unsigned __int64 tmp = (unsigned __int64)lhs + (unsigned __int64)( -rhs ); if( tmp <= (unsigned __int64)IntTraits< T >::maxInt ) { result = (T)tmp; return; } } E::SafeIntOnOverflow(); } }; template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_Int64Int > { public: static bool Subtract( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW { // lhs is an __int64, rhs signed (up to 64-bit) // we have essentially 4 cases: // // 1) lhs positive, rhs positive - overflow not possible // 2) lhs positive, rhs negative - equivalent to addition - result >= lhs or error // 3) lhs negative, rhs positive - check result <= lhs // 4) lhs negative, rhs negative - overflow not possible __int64 tmp = (__int64)((unsigned __int64)lhs - (unsigned __int64)rhs); // Note - ideally, we can order these so that true conditionals // lead to success, which enables better pipelining // It isn't practical here if( ( lhs >= 0 && rhs < 0 && tmp < lhs ) || // condition 2 ( rhs >= 0 && tmp > lhs ) ) // condition 3 { return false; } result = (T)tmp; return true; } template < typename E > static void SubtractThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW { // lhs is an __int64, rhs signed (up to 64-bit) // we have essentially 4 cases: // // 1) lhs positive, rhs positive - overflow not possible // 2) lhs positive, rhs negative - equivalent to addition - result >= lhs or error // 3) lhs negative, rhs positive - check result <= lhs // 4) lhs negative, rhs negative - overflow not possible __int64 tmp = (__int64)((unsigned __int64)lhs - (unsigned __int64)rhs); // Note - ideally, we can order these so that true conditionals // lead to success, which enables better pipelining // It isn't practical here if( ( lhs >= 0 && rhs < 0 && tmp < lhs ) || // condition 2 ( rhs >= 0 && tmp > lhs ) ) // condition 3 { E::SafeIntOnOverflow(); } result = (T)tmp; } }; template < typename U, typename T > class SubtractionHelper< U, T, SubtractionState_Int64Int2 > { public: static bool Subtract( const U& lhs, const T& rhs, T& result ) SAFEINT_NOTHROW { // lhs __int64, rhs any signed int (including __int64) __int64 tmp = lhs - rhs; // we have essentially 4 cases: // // 1) lhs positive, rhs positive - overflow not possible in tmp // 2) lhs positive, rhs negative - equivalent to addition - result >= lhs or error // 3) lhs negative, rhs positive - check result <= lhs // 4) lhs negative, rhs negative - overflow not possible in tmp if( lhs >= 0 ) { // if both positive, overflow to negative not possible // which is why we'll explicitly check maxInt, and not call SafeCast if( ( IntTraits< T >::isLT64Bit && tmp > IntTraits< T >::maxInt ) || ( rhs < 0 && tmp < lhs ) ) { return false; } } else { // lhs negative if( ( IntTraits< T >::isLT64Bit && tmp < IntTraits< T >::minInt) || ( rhs >=0 && tmp > lhs ) ) { return false; } } result = (T)tmp; return true; } template < typename E > static void SubtractThrow( const U& lhs, const T& rhs, T& result ) SAFEINT_CPP_THROW { // lhs __int64, rhs any signed int (including __int64) __int64 tmp = lhs - rhs; // we have essentially 4 cases: // // 1) lhs positive, rhs positive - overflow not possible in tmp // 2) lhs positive, rhs negative - equivalent to addition - result >= lhs or error // 3) lhs negative, rhs positive - check result <= lhs // 4) lhs negative, rhs negative - overflow not possible in tmp if( lhs >= 0 ) { // if both positive, overflow to negative not possible // which is why we'll explicitly check maxInt, and not call SafeCast if( ( CompileConst< IntTraits< T >::isLT64Bit >::Value() && tmp > IntTraits< T >::maxInt ) || ( rhs < 0 && tmp < lhs ) ) { E::SafeIntOnOverflow(); } } else { // lhs negative if( ( CompileConst< IntTraits< T >::isLT64Bit >::Value() && tmp < IntTraits< T >::minInt) || ( rhs >=0 && tmp > lhs ) ) { E::SafeIntOnOverflow(); } } result = (T)tmp; } }; template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_IntInt64 > { public: static bool Subtract( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW { // lhs is a 32-bit int or less, rhs __int64 // we have essentially 4 cases: // // lhs positive, rhs positive - rhs could be larger than lhs can represent // lhs positive, rhs negative - additive case - check tmp >= lhs and tmp > max int // lhs negative, rhs positive - check tmp <= lhs and tmp < min int // lhs negative, rhs negative - addition cannot internally overflow, check against max __int64 tmp = (__int64)((unsigned __int64)lhs - (unsigned __int64)rhs); if( lhs >= 0 ) { // first case if( rhs >= 0 ) { if( tmp >= IntTraits< T >::minInt ) { result = (T)tmp; return true; } } else { // second case if( tmp >= lhs && tmp <= IntTraits< T >::maxInt ) { result = (T)tmp; return true; } } } else { // lhs < 0 // third case if( rhs >= 0 ) { if( tmp <= lhs && tmp >= IntTraits< T >::minInt ) { result = (T)tmp; return true; } } else { // fourth case if( tmp <= IntTraits< T >::maxInt ) { result = (T)tmp; return true; } } } return false; } template < typename E > static void SubtractThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW { // lhs is a 32-bit int or less, rhs __int64 // we have essentially 4 cases: // // lhs positive, rhs positive - rhs could be larger than lhs can represent // lhs positive, rhs negative - additive case - check tmp >= lhs and tmp > max int // lhs negative, rhs positive - check tmp <= lhs and tmp < min int // lhs negative, rhs negative - addition cannot internally overflow, check against max __int64 tmp = (__int64)((unsigned __int64)lhs - (unsigned __int64)rhs); if( lhs >= 0 ) { // first case if( rhs >= 0 ) { if( tmp >= IntTraits< T >::minInt ) { result = (T)tmp; return; } } else { // second case if( tmp >= lhs && tmp <= IntTraits< T >::maxInt ) { result = (T)tmp; return; } } } else { // lhs < 0 // third case if( rhs >= 0 ) { if( tmp <= lhs && tmp >= IntTraits< T >::minInt ) { result = (T)tmp; return; } } else { // fourth case if( tmp <= IntTraits< T >::maxInt ) { result = (T)tmp; return; } } } E::SafeIntOnOverflow(); } }; template < typename U, typename T > class SubtractionHelper< U, T, SubtractionState_IntInt642 > { public: static bool Subtract( const U& lhs, const T& rhs, T& result ) SAFEINT_NOTHROW { // lhs is any signed int32 or smaller, rhs is int64 __int64 tmp = (__int64)lhs - rhs; if( ( lhs >= 0 && rhs < 0 && tmp < lhs ) || ( rhs > 0 && tmp > lhs ) ) { return false; //else OK } result = (T)tmp; return true; } template < typename E > static void SubtractThrow( const U& lhs, const T& rhs, T& result ) SAFEINT_CPP_THROW { // lhs is any signed int32 or smaller, rhs is int64 __int64 tmp = (__int64)lhs - rhs; if( ( lhs >= 0 && rhs < 0 && tmp < lhs ) || ( rhs > 0 && tmp > lhs ) ) { E::SafeIntOnOverflow(); //else OK } result = (T)tmp; } }; template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_Int64Uint > { public: static bool Subtract( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW { // lhs is a 64-bit int, rhs unsigned int32 or smaller // perform test as unsigned to prevent unwanted optimizations unsigned __int64 tmp = (unsigned __int64)lhs - (unsigned __int64)rhs; if( (__int64)tmp <= lhs ) { result = (T)(__int64)tmp; return true; } return false; } template < typename E > static void SubtractThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW { // lhs is a 64-bit int, rhs unsigned int32 or smaller // perform test as unsigned to prevent unwanted optimizations unsigned __int64 tmp = (unsigned __int64)lhs - (unsigned __int64)rhs; if( (__int64)tmp <= lhs ) { result = (T)tmp; return; } E::SafeIntOnOverflow(); } }; template < typename U, typename T > class SubtractionHelper< U, T, SubtractionState_Int64Uint2 > { public: // lhs is __int64, rhs is unsigned 32-bit or smaller static bool Subtract( const U& lhs, const T& rhs, T& result ) SAFEINT_NOTHROW { // Do this as unsigned to prevent unwanted optimizations unsigned __int64 tmp = (unsigned __int64)lhs - (unsigned __int64)rhs; if( (__int64)tmp <= IntTraits< T >::maxInt && (__int64)tmp >= IntTraits< T >::minInt ) { result = (T)(__int64)tmp; return true; } return false; } template < typename E > static void SubtractThrow( const U& lhs, const T& rhs, T& result ) SAFEINT_CPP_THROW { // Do this as unsigned to prevent unwanted optimizations unsigned __int64 tmp = (unsigned __int64)lhs - (unsigned __int64)rhs; if( (__int64)tmp <= IntTraits< T >::maxInt && (__int64)tmp >= IntTraits< T >::minInt ) { result = (T)(__int64)tmp; return; } E::SafeIntOnOverflow(); } }; template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_IntUint64 > { public: static bool Subtract( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW { // lhs is any signed int, rhs unsigned int64 // check against available range // We need the absolute value of IntTraits< T >::minInt // This will give it to us without extraneous compiler warnings const unsigned __int64 AbsMinIntT = (unsigned __int64)IntTraits< T >::maxInt + 1; if( lhs < 0 ) { if( rhs <= AbsMinIntT - AbsValueHelper< T, GetAbsMethod< T >::method >::Abs( lhs ) ) { result = (T)( lhs - rhs ); return true; } } else { if( rhs <= AbsMinIntT + (unsigned __int64)lhs ) { result = (T)( lhs - rhs ); return true; } } return false; } template < typename E > static void SubtractThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW { // lhs is any signed int, rhs unsigned int64 // check against available range // We need the absolute value of IntTraits< T >::minInt // This will give it to us without extraneous compiler warnings const unsigned __int64 AbsMinIntT = (unsigned __int64)IntTraits< T >::maxInt + 1; if( lhs < 0 ) { if( rhs <= AbsMinIntT - AbsValueHelper< T, GetAbsMethod< T >::method >::Abs( lhs ) ) { result = (T)( lhs - rhs ); return; } } else { if( rhs <= AbsMinIntT + (unsigned __int64)lhs ) { result = (T)( lhs - rhs ); return; } } E::SafeIntOnOverflow(); } }; template < typename U, typename T > class SubtractionHelper< U, T, SubtractionState_IntUint642 > { public: static bool Subtract( const U& lhs, const T& rhs, T& result ) SAFEINT_NOTHROW { // We run into upcasting problems on comparison - needs 2 checks if( lhs >= 0 && (T)lhs >= rhs ) { result = (T)((U)lhs - (U)rhs); return true; } return false; } template < typename E > static void SubtractThrow( const U& lhs, const T& rhs, T& result ) SAFEINT_CPP_THROW { // We run into upcasting problems on comparison - needs 2 checks if( lhs >= 0 && (T)lhs >= rhs ) { result = (T)((U)lhs - (U)rhs); return; } E::SafeIntOnOverflow(); } }; template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_Int64Uint64 > { public: static bool Subtract( const __int64& lhs, const unsigned __int64& rhs, __int64& result ) SAFEINT_NOTHROW { C_ASSERT( IntTraits< T >::isInt64 && IntTraits< U >::isUint64 ); // if we subtract, and it gets larger, there's a problem // Perform test as unsigned to prevent unwanted optimizations unsigned __int64 tmp = (unsigned __int64)lhs - rhs; if( (__int64)tmp <= lhs ) { result = (__int64)tmp; return true; } return false; } template < typename E > static void SubtractThrow( const __int64& lhs, const unsigned __int64& rhs, T& result ) SAFEINT_CPP_THROW { C_ASSERT( IntTraits< T >::isInt64 && IntTraits< U >::isUint64 ); // if we subtract, and it gets larger, there's a problem // Perform test as unsigned to prevent unwanted optimizations unsigned __int64 tmp = (unsigned __int64)lhs - rhs; if( (__int64)tmp <= lhs ) { result = (__int64)tmp; return; } E::SafeIntOnOverflow(); } }; template < typename U, typename T > class SubtractionHelper< U, T, SubtractionState_Int64Uint642 > { public: // If lhs is negative, immediate problem - return must be positive, and subtracting only makes it // get smaller. If rhs > lhs, then it would also go negative, which is the other case static bool Subtract( const __int64& lhs, const unsigned __int64& rhs, T& result ) SAFEINT_NOTHROW { C_ASSERT( IntTraits< T >::isUint64 && IntTraits< U >::isInt64 ); if( lhs >= 0 && (unsigned __int64)lhs >= rhs ) { result = (unsigned __int64)lhs - rhs; return true; } return false; } template < typename E > static void SubtractThrow( const __int64& lhs, const unsigned __int64& rhs, T& result ) SAFEINT_CPP_THROW { C_ASSERT( IntTraits< T >::isUint64 && IntTraits< U >::isInt64 ); if( lhs >= 0 && (unsigned __int64)lhs >= rhs ) { result = (unsigned __int64)lhs - rhs; return; } E::SafeIntOnOverflow(); } }; enum BinaryState { BinaryState_OK, BinaryState_Int8, BinaryState_Int16, BinaryState_Int32 }; template < typename T, typename U > class BinaryMethod { public: enum { // If both operands are unsigned OR // return type is smaller than rhs OR // return type is larger and rhs is unsigned // Then binary operations won't produce unexpected results method = ( sizeof( T ) <= sizeof( U ) || SafeIntCompare< T, U >::isBothUnsigned || !IntTraits< U >::isSigned ) ? BinaryState_OK : IntTraits< U >::isInt8 ? BinaryState_Int8 : IntTraits< U >::isInt16 ? BinaryState_Int16 : BinaryState_Int32 }; }; #ifdef SAFEINT_DISABLE_BINARY_ASSERT #define BinaryAssert(x) #else #define BinaryAssert(x) SAFEINT_ASSERT(x) #endif template < typename T, typename U, int method > class BinaryAndHelper; template < typename T, typename U > class BinaryAndHelper< T, U, BinaryState_OK > { public: static T And( T lhs, U rhs ) SAFEINT_NOTHROW { return (T)( lhs & rhs ); } }; template < typename T, typename U > class BinaryAndHelper< T, U, BinaryState_Int8 > { public: static T And( T lhs, U rhs ) SAFEINT_NOTHROW { // cast forces sign extension to be zeros BinaryAssert( ( lhs & rhs ) == ( lhs & (unsigned __int8)rhs ) ); return (T)( lhs & (unsigned __int8)rhs ); } }; template < typename T, typename U > class BinaryAndHelper< T, U, BinaryState_Int16 > { public: static T And( T lhs, U rhs ) SAFEINT_NOTHROW { //cast forces sign extension to be zeros BinaryAssert( ( lhs & rhs ) == ( lhs & (unsigned __int16)rhs ) ); return (T)( lhs & (unsigned __int16)rhs ); } }; template < typename T, typename U > class BinaryAndHelper< T, U, BinaryState_Int32 > { public: static T And( T lhs, U rhs ) SAFEINT_NOTHROW { //cast forces sign extension to be zeros BinaryAssert( ( lhs & rhs ) == ( lhs & (unsigned __int32)rhs ) ); return (T)( lhs & (unsigned __int32)rhs ); } }; template < typename T, typename U, int method > class BinaryOrHelper; template < typename T, typename U > class BinaryOrHelper< T, U, BinaryState_OK > { public: static T Or( T lhs, U rhs ) SAFEINT_NOTHROW { return (T)( lhs | rhs ); } }; template < typename T, typename U > class BinaryOrHelper< T, U, BinaryState_Int8 > { public: static T Or( T lhs, U rhs ) SAFEINT_NOTHROW { //cast forces sign extension to be zeros BinaryAssert( ( lhs | rhs ) == ( lhs | (unsigned __int8)rhs ) ); return (T)( lhs | (unsigned __int8)rhs ); } }; template < typename T, typename U > class BinaryOrHelper< T, U, BinaryState_Int16 > { public: static T Or( T lhs, U rhs ) SAFEINT_NOTHROW { //cast forces sign extension to be zeros BinaryAssert( ( lhs | rhs ) == ( lhs | (unsigned __int16)rhs ) ); return (T)( lhs | (unsigned __int16)rhs ); } }; template < typename T, typename U > class BinaryOrHelper< T, U, BinaryState_Int32 > { public: static T Or( T lhs, U rhs ) SAFEINT_NOTHROW { //cast forces sign extension to be zeros BinaryAssert( ( lhs | rhs ) == ( lhs | (unsigned __int32)rhs ) ); return (T)( lhs | (unsigned __int32)rhs ); } }; template class BinaryXorHelper; template < typename T, typename U > class BinaryXorHelper< T, U, BinaryState_OK > { public: static T Xor( T lhs, U rhs ) SAFEINT_NOTHROW { return (T)( lhs ^ rhs ); } }; template < typename T, typename U > class BinaryXorHelper< T, U, BinaryState_Int8 > { public: static T Xor( T lhs, U rhs ) SAFEINT_NOTHROW { // cast forces sign extension to be zeros BinaryAssert( ( lhs ^ rhs ) == ( lhs ^ (unsigned __int8)rhs ) ); return (T)( lhs ^ (unsigned __int8)rhs ); } }; template < typename T, typename U > class BinaryXorHelper< T, U, BinaryState_Int16 > { public: static T Xor( T lhs, U rhs ) SAFEINT_NOTHROW { // cast forces sign extension to be zeros BinaryAssert( ( lhs ^ rhs ) == ( lhs ^ (unsigned __int16)rhs ) ); return (T)( lhs ^ (unsigned __int16)rhs ); } }; template < typename T, typename U > class BinaryXorHelper< T, U, BinaryState_Int32 > { public: static T Xor( T lhs, U rhs ) SAFEINT_NOTHROW { // cast forces sign extension to be zeros BinaryAssert( ( lhs ^ rhs ) == ( lhs ^ (unsigned __int32)rhs ) ); return (T)( lhs ^ (unsigned __int32)rhs ); } }; /***************** External functions ****************************************/ // External functions that can be used where you only need to check one operation // non-class helper function so that you can check for a cast's validity // and handle errors how you like template < typename T, typename U > inline bool SafeCast( const T From, U& To ) SAFEINT_NOTHROW { return SafeCastHelper< U, T, GetCastMethod< U, T >::method >::Cast( From, To ); } template < typename T, typename U > inline bool SafeEquals( const T t, const U u ) SAFEINT_NOTHROW { return EqualityTest< T, U, ValidComparison< T, U >::method >::IsEquals( t, u ); } template < typename T, typename U > inline bool SafeNotEquals( const T t, const U u ) SAFEINT_NOTHROW { return !EqualityTest< T, U, ValidComparison< T, U >::method >::IsEquals( t, u ); } template < typename T, typename U > inline bool SafeGreaterThan( const T t, const U u ) SAFEINT_NOTHROW { return GreaterThanTest< T, U, ValidComparison< T, U >::method >::GreaterThan( t, u ); } template < typename T, typename U > inline bool SafeGreaterThanEquals( const T t, const U u ) SAFEINT_NOTHROW { return !GreaterThanTest< U, T, ValidComparison< U, T >::method >::GreaterThan( u, t ); } template < typename T, typename U > inline bool SafeLessThan( const T t, const U u ) SAFEINT_NOTHROW { return GreaterThanTest< U, T, ValidComparison< U, T >::method >::GreaterThan( u, t ); } template < typename T, typename U > inline bool SafeLessThanEquals( const T t, const U u ) SAFEINT_NOTHROW { return !GreaterThanTest< T, U, ValidComparison< T, U >::method >::GreaterThan( t, u ); } template < typename T, typename U > inline bool SafeModulus( const T& t, const U& u, T& result ) SAFEINT_NOTHROW { return ( ModulusHelper< T, U, ValidComparison< T, U >::method >::Modulus( t, u, result ) == SafeIntNoError ); } template < typename T, typename U > inline bool SafeMultiply( T t, U u, T& result ) SAFEINT_NOTHROW { return MultiplicationHelper< T, U, MultiplicationMethod< T, U >::method >::Multiply( t, u, result ); } template < typename T, typename U > inline bool SafeDivide( T t, U u, T& result ) SAFEINT_NOTHROW { return ( DivisionHelper< T, U, DivisionMethod< T, U >::method >::Divide( t, u, result ) == SafeIntNoError ); } template < typename T, typename U > inline bool SafeAdd( T t, U u, T& result ) SAFEINT_NOTHROW { return AdditionHelper< T, U, AdditionMethod< T, U >::method >::Addition( t, u, result ); } template < typename T, typename U > inline bool SafeSubtract( T t, U u, T& result ) SAFEINT_NOTHROW { return SubtractionHelper< T, U, SubtractionMethod< T, U >::method >::Subtract( t, u, result ); } /***************** end external functions ************************************/ // Main SafeInt class // Assumes exceptions can be thrown template < typename T, typename E = SafeIntDefaultExceptionHandler > class SafeInt { public: SafeInt() SAFEINT_NOTHROW { C_ASSERT( NumericType< T >::isInt ); m_int = 0; } // Having a constructor for every type of int // avoids having the compiler evade our checks when doing implicit casts - // e.g., SafeInt s = 0x7fffffff; SafeInt( const T& i ) SAFEINT_NOTHROW { C_ASSERT( NumericType< T >::isInt ); //always safe m_int = i; } // provide explicit boolean converter SafeInt( bool b ) SAFEINT_NOTHROW { C_ASSERT( NumericType< T >::isInt ); m_int = (T)( b ? 1 : 0 ); } template < typename U > SafeInt(const SafeInt< U, E >& u) SAFEINT_CPP_THROW { C_ASSERT( NumericType< T >::isInt ); *this = SafeInt< T, E >( (U)u ); } template < typename U > SafeInt( const U& i ) SAFEINT_CPP_THROW { C_ASSERT( NumericType< T >::isInt ); // SafeCast will throw exceptions if i won't fit in type T SafeCastHelper< T, U, GetCastMethod< T, U >::method >::template CastThrow< E >( i, m_int ); } // The destructor is intentionally commented out - no destructor // vs. a do-nothing destructor makes a huge difference in // inlining characteristics. It wasn't doing anything anyway. // ~SafeInt(){}; // now start overloading operators // assignment operator // constructors exist for all int types and will ensure safety template < typename U > SafeInt< T, E >& operator =( const U& rhs ) SAFEINT_CPP_THROW { // use constructor to test size // constructor is optimized to do minimal checking based // on whether T can contain U // note - do not change this *this = SafeInt< T, E >( rhs ); return *this; } SafeInt< T, E >& operator =( const T& rhs ) SAFEINT_NOTHROW { m_int = rhs; return *this; } template < typename U > SafeInt< T, E >& operator =( const SafeInt< U, E >& rhs ) SAFEINT_CPP_THROW { SafeCastHelper< T, U, GetCastMethod< T, U >::method >::template CastThrow< E >( rhs.Ref(), m_int ); return *this; } SafeInt< T, E >& operator =( const SafeInt< T, E >& rhs ) SAFEINT_NOTHROW { m_int = rhs.m_int; return *this; } // Casting operators operator bool() const SAFEINT_NOTHROW { return !!m_int; } operator char() const SAFEINT_CPP_THROW { char val; SafeCastHelper< char, T, GetCastMethod< char, T >::method >::template CastThrow< E >( m_int, val ); return val; } operator signed char() const SAFEINT_CPP_THROW { signed char val; SafeCastHelper< signed char, T, GetCastMethod< signed char, T >::method >::template CastThrow< E >( m_int, val ); return val; } operator unsigned char() const SAFEINT_CPP_THROW { unsigned char val; SafeCastHelper< unsigned char, T, GetCastMethod< unsigned char, T >::method >::template CastThrow< E >( m_int, val ); return val; } operator __int16() const SAFEINT_CPP_THROW { __int16 val; SafeCastHelper< __int16, T, GetCastMethod< __int16, T >::method >::template CastThrow< E >( m_int, val ); return val; } operator unsigned __int16() const SAFEINT_CPP_THROW { unsigned __int16 val; SafeCastHelper< unsigned __int16, T, GetCastMethod< unsigned __int16, T >::method >::template CastThrow< E >( m_int, val ); return val; } operator __int32() const SAFEINT_CPP_THROW { __int32 val; SafeCastHelper< __int32, T, GetCastMethod< __int32, T >::method >::template CastThrow< E >( m_int, val ); return val; } operator unsigned __int32() const SAFEINT_CPP_THROW { unsigned __int32 val; SafeCastHelper< unsigned __int32, T, GetCastMethod< unsigned __int32, T >::method >::template CastThrow< E >( m_int, val ); return val; } // The compiler knows that int == __int32 // but not that long == __int32 operator long() const SAFEINT_CPP_THROW { long val; SafeCastHelper< long, T, GetCastMethod< long, T >::method >::template CastThrow< E >( m_int, val ); return val; } operator unsigned long() const SAFEINT_CPP_THROW { unsigned long val; SafeCastHelper< unsigned long, T, GetCastMethod< unsigned long, T >::method >::template CastThrow< E >( m_int, val ); return val; } operator __int64() const SAFEINT_CPP_THROW { __int64 val; SafeCastHelper< __int64, T, GetCastMethod< __int64, T >::method >::template CastThrow< E >( m_int, val ); return val; } operator unsigned __int64() const SAFEINT_CPP_THROW { unsigned __int64 val; SafeCastHelper< unsigned __int64, T, GetCastMethod< unsigned __int64, T >::method >::template CastThrow< E >( m_int, val ); return val; } #if defined SAFEINT_USE_WCHAR_T || defined _NATIVE_WCHAR_T_DEFINED operator wchar_t() const SAFEINT_CPP_THROW { wchar_t val; SafeCastHelper< wchar_t, T, GetCastMethod< wchar_t, T >::method >::template CastThrow< E >( m_int, val ); return val; } #endif #ifdef SIZE_T_CAST_NEEDED // We also need an explicit cast to size_t, or the compiler will complain // Apparently, only SOME compilers complain, and cl 14.00.50727.42 isn't one of them // Leave here in case we decide to backport this to an earlier compiler operator size_t() const SAFEINT_CPP_THROW { size_t val; SafeCastHelper< size_t, T, GetCastMethod< size_t, T >::method >::template CastThrow< E >( m_int, val ); return val; } #endif // Also provide a cast operator for floating point types operator float() const SAFEINT_CPP_THROW { float val; SafeCastHelper< float, T, GetCastMethod< float, T >::method >::template CastThrow< E >( m_int, val ); return val; } operator double() const SAFEINT_CPP_THROW { double val; SafeCastHelper< double, T, GetCastMethod< double, T >::method >::template CastThrow< E >( m_int, val ); return val; } operator long double() const SAFEINT_CPP_THROW { long double val; SafeCastHelper< long double, T, GetCastMethod< long double, T >::method >::template CastThrow< E >( m_int, val ); return val; } // If you need a pointer to the data // this could be dangerous, but allows you to correctly pass // instances of this class to APIs that take a pointer to an integer // also see overloaded address-of operator below T* Ptr() SAFEINT_NOTHROW { return &m_int; } const T* Ptr() const SAFEINT_NOTHROW { return &m_int; } const T& Ref() const SAFEINT_NOTHROW { return m_int; } // Or if SafeInt< T, E >::Ptr() is inconvenient, use the overload // operator & // This allows you to do unsafe things! // It is meant to allow you to more easily // pass a SafeInt into things like ReadFile T* operator &() SAFEINT_NOTHROW { return &m_int; } const T* operator &() const SAFEINT_NOTHROW { return &m_int; } // Unary operators bool operator !() const SAFEINT_NOTHROW { return (!m_int) ? true : false; } // operator + (unary) // note - normally, the '+' and '-' operators will upcast to a signed int // for T < 32 bits. This class changes behavior to preserve type const SafeInt< T, E >& operator +() const SAFEINT_NOTHROW { return *this; } //unary - SafeInt< T, E > operator -() const SAFEINT_CPP_THROW { // Note - unsigned still performs the bitwise manipulation // will warn at level 2 or higher if the value is 32-bit or larger return SafeInt(NegationHelper::isSigned>::template NegativeThrow(m_int)); } // prefix increment operator SafeInt< T, E >& operator ++() SAFEINT_CPP_THROW { if( m_int != IntTraits< T >::maxInt ) { ++m_int; return *this; } E::SafeIntOnOverflow(); } // prefix decrement operator SafeInt< T, E >& operator --() SAFEINT_CPP_THROW { if( m_int != IntTraits< T >::minInt ) { --m_int; return *this; } E::SafeIntOnOverflow(); } // note that postfix operators have inherently worse perf // characteristics // postfix increment operator SafeInt< T, E > operator ++( int ) SAFEINT_CPP_THROW // dummy arg to comply with spec { if( m_int != IntTraits< T >::maxInt ) { SafeInt< T, E > tmp( m_int ); m_int++; return tmp; } E::SafeIntOnOverflow(); } // postfix decrement operator SafeInt< T, E > operator --( int ) SAFEINT_CPP_THROW // dummy arg to comply with spec { if( m_int != IntTraits< T >::minInt ) { SafeInt< T, E > tmp( m_int ); m_int--; return tmp; } E::SafeIntOnOverflow(); } // One's complement // Note - this operator will normally change size to an int // cast in return improves perf and maintains type SafeInt< T, E > operator ~() const SAFEINT_NOTHROW { return SafeInt< T, E >( (T)~m_int ); } // Binary operators // // arithmetic binary operators // % modulus // * multiplication // / division // + addition // - subtraction // // For each of the arithmetic operators, you will need to // use them as follows: // // SafeInt c = 2; // SafeInt i = 3; // // SafeInt i2 = i op (char)c; // OR // SafeInt i2 = (int)i op c; // // The base problem is that if the lhs and rhs inputs are different SafeInt types // it is not possible in this implementation to determine what type of SafeInt // should be returned. You have to let the class know which of the two inputs // need to be the return type by forcing the other value to the base integer type. // // Note - as per feedback from Scott Meyers, I'm exploring how to get around this. // 3.0 update - I'm still thinking about this. It can be done with template metaprogramming, // but it is tricky, and there's a perf vs. correctness tradeoff where the right answer // is situational. // // The case of: // // SafeInt< T, E > i, j, k; // i = j op k; // // works just fine and no unboxing is needed because the return type is not ambiguous. // Modulus // Modulus has some convenient properties - // first, the magnitude of the return can never be // larger than the lhs operand, and it must be the same sign // as well. It does, however, suffer from the same promotion // problems as comparisons, division and other operations template < typename U > SafeInt< T, E > operator %( U rhs ) const SAFEINT_CPP_THROW { T result; ModulusHelper< T, U, ValidComparison< T, U >::method >::template ModulusThrow< E >( m_int, rhs, result ); return SafeInt< T, E >( result ); } SafeInt< T, E > operator %( SafeInt< T, E > rhs ) const SAFEINT_CPP_THROW { T result; ModulusHelper< T, T, ValidComparison< T, T >::method >::template ModulusThrow< E >( m_int, rhs, result ); return SafeInt< T, E >( result ); } // Modulus assignment template < typename U > SafeInt< T, E >& operator %=( U rhs ) SAFEINT_CPP_THROW { ModulusHelper< T, U, ValidComparison< T, U >::method >::template ModulusThrow< E >( m_int, rhs, m_int ); return *this; } template < typename U > SafeInt< T, E >& operator %=( SafeInt< U, E > rhs ) SAFEINT_CPP_THROW { ModulusHelper< T, U, ValidComparison< T, U >::method >::template ModulusThrow< E >( m_int, (U)rhs, m_int ); return *this; } // Multiplication template < typename U > SafeInt< T, E > operator *( U rhs ) const SAFEINT_CPP_THROW { T ret( 0 ); MultiplicationHelper< T, U, MultiplicationMethod< T, U >::method >::template MultiplyThrow< E >( m_int, rhs, ret ); return SafeInt< T, E >( ret ); } SafeInt< T, E > operator *( SafeInt< T, E > rhs ) const SAFEINT_CPP_THROW { T ret( 0 ); MultiplicationHelper< T, T, MultiplicationMethod< T, T >::method >::template MultiplyThrow< E >( m_int, (T)rhs, ret ); return SafeInt< T, E >( ret ); } // Multiplication assignment SafeInt< T, E >& operator *=( SafeInt< T, E > rhs ) SAFEINT_CPP_THROW { MultiplicationHelper< T, T, MultiplicationMethod< T, T >::method >::template MultiplyThrow< E >( m_int, (T)rhs, m_int ); return *this; } template < typename U > SafeInt< T, E >& operator *=( U rhs ) SAFEINT_CPP_THROW { MultiplicationHelper< T, U, MultiplicationMethod< T, U >::method >::template MultiplyThrow< E >( m_int, rhs, m_int ); return *this; } template < typename U > SafeInt< T, E >& operator *=( SafeInt< U, E > rhs ) SAFEINT_CPP_THROW { MultiplicationHelper< T, U, MultiplicationMethod< T, U >::method >::template MultiplyThrow< E >( m_int, rhs.Ref(), m_int ); return *this; } // Division template < typename U > SafeInt< T, E > operator /( U rhs ) const SAFEINT_CPP_THROW { T ret( 0 ); DivisionHelper< T, U, DivisionMethod< T, U >::method >::template DivideThrow< E >( m_int, rhs, ret ); return SafeInt< T, E >( ret ); } SafeInt< T, E > operator /( SafeInt< T, E > rhs ) const SAFEINT_CPP_THROW { T ret( 0 ); DivisionHelper< T, T, DivisionMethod< T, T >::method >::template DivideThrow< E >( m_int, (T)rhs, ret ); return SafeInt< T, E >( ret ); } // Division assignment SafeInt< T, E >& operator /=( SafeInt< T, E > i ) SAFEINT_CPP_THROW { DivisionHelper< T, T, DivisionMethod< T, T >::method >::template DivideThrow< E >( m_int, (T)i, m_int ); return *this; } template < typename U > SafeInt< T, E >& operator /=( U i ) SAFEINT_CPP_THROW { DivisionHelper< T, U, DivisionMethod< T, U >::method >::template DivideThrow< E >( m_int, i, m_int ); return *this; } template < typename U > SafeInt< T, E >& operator /=( SafeInt< U, E > i ) { DivisionHelper< T, U, DivisionMethod< T, U >::method >::template DivideThrow< E >( m_int, (U)i, m_int ); return *this; } // For addition and subtraction // Addition SafeInt< T, E > operator +( SafeInt< T, E > rhs ) const SAFEINT_CPP_THROW { T ret( 0 ); AdditionHelper< T, T, AdditionMethod< T, T >::method >::template AdditionThrow< E >( m_int, (T)rhs, ret ); return SafeInt< T, E >( ret ); } template < typename U > SafeInt< T, E > operator +( U rhs ) const SAFEINT_CPP_THROW { T ret( 0 ); AdditionHelper< T, U, AdditionMethod< T, U >::method >::template AdditionThrow< E >( m_int, rhs, ret ); return SafeInt< T, E >( ret ); } //addition assignment SafeInt< T, E >& operator +=( SafeInt< T, E > rhs ) SAFEINT_CPP_THROW { AdditionHelper< T, T, AdditionMethod< T, T >::method >::template AdditionThrow< E >( m_int, (T)rhs, m_int ); return *this; } template < typename U > SafeInt< T, E >& operator +=( U rhs ) SAFEINT_CPP_THROW { AdditionHelper< T, U, AdditionMethod< T, U >::method >::template AdditionThrow< E >( m_int, rhs, m_int ); return *this; } template < typename U > SafeInt< T, E >& operator +=( SafeInt< U, E > rhs ) SAFEINT_CPP_THROW { AdditionHelper< T, U, AdditionMethod< T, U >::method >::template AdditionThrow< E >( m_int, (U)rhs, m_int ); return *this; } // Subtraction template < typename U > SafeInt< T, E > operator -( U rhs ) const SAFEINT_CPP_THROW { T ret( 0 ); SubtractionHelper< T, U, SubtractionMethod< T, U >::method >::template SubtractThrow< E >( m_int, rhs, ret ); return SafeInt< T, E >( ret ); } SafeInt< T, E > operator -(SafeInt< T, E > rhs) const SAFEINT_CPP_THROW { T ret( 0 ); SubtractionHelper< T, T, SubtractionMethod< T, T >::method >::template SubtractThrow< E >( m_int, (T)rhs, ret ); return SafeInt< T, E >( ret ); } // Subtraction assignment SafeInt< T, E >& operator -=( SafeInt< T, E > rhs ) SAFEINT_CPP_THROW { SubtractionHelper< T, T, SubtractionMethod< T, T >::method >::template SubtractThrow< E >( m_int, (T)rhs, m_int ); return *this; } template < typename U > SafeInt< T, E >& operator -=( U rhs ) SAFEINT_CPP_THROW { SubtractionHelper< T, U, SubtractionMethod< T, U >::method >::template SubtractThrow< E >( m_int, rhs, m_int ); return *this; } template < typename U > SafeInt< T, E >& operator -=( SafeInt< U, E > rhs ) SAFEINT_CPP_THROW { SubtractionHelper< T, U, SubtractionMethod< T, U >::method >::template SubtractThrow< E >( m_int, (U)rhs, m_int ); return *this; } // Shift operators // Note - shift operators ALWAYS return the same type as the lhs // specific version for SafeInt< T, E > not needed - // code path is exactly the same as for SafeInt< U, E > as rhs // Left shift // Also, shifting > bitcount is undefined - trap in debug #ifdef SAFEINT_DISABLE_SHIFT_ASSERT #define ShiftAssert(x) #else #define ShiftAssert(x) SAFEINT_ASSERT(x) #endif template < typename U > SafeInt< T, E > operator <<( U bits ) const SAFEINT_NOTHROW { ShiftAssert( !IntTraits< U >::isSigned || bits >= 0 ); ShiftAssert( bits < (int)IntTraits< T >::bitCount ); return SafeInt< T, E >( (T)( m_int << bits ) ); } template < typename U > SafeInt< T, E > operator <<( SafeInt< U, E > bits ) const SAFEINT_NOTHROW { ShiftAssert( !IntTraits< U >::isSigned || (U)bits >= 0 ); ShiftAssert( (U)bits < (int)IntTraits< T >::bitCount ); return SafeInt< T, E >( (T)( m_int << (U)bits ) ); } // Left shift assignment template < typename U > SafeInt< T, E >& operator <<=( U bits ) SAFEINT_NOTHROW { ShiftAssert( !IntTraits< U >::isSigned || bits >= 0 ); ShiftAssert( bits < (int)IntTraits< T >::bitCount ); m_int <<= bits; return *this; } template < typename U > SafeInt< T, E >& operator <<=( SafeInt< U, E > bits ) SAFEINT_NOTHROW { ShiftAssert( !IntTraits< U >::isSigned || (U)bits >= 0 ); ShiftAssert( (U)bits < (int)IntTraits< T >::bitCount ); m_int <<= (U)bits; return *this; } // Right shift template < typename U > SafeInt< T, E > operator >>( U bits ) const SAFEINT_NOTHROW { ShiftAssert( !IntTraits< U >::isSigned || bits >= 0 ); ShiftAssert( bits < (int)IntTraits< T >::bitCount ); return SafeInt< T, E >( (T)( m_int >> bits ) ); } template < typename U > SafeInt< T, E > operator >>( SafeInt< U, E > bits ) const SAFEINT_NOTHROW { ShiftAssert( !IntTraits< U >::isSigned || (U)bits >= 0 ); ShiftAssert( bits < (int)IntTraits< T >::bitCount ); return SafeInt< T, E >( (T)(m_int >> (U)bits) ); } // Right shift assignment template < typename U > SafeInt< T, E >& operator >>=( U bits ) SAFEINT_NOTHROW { ShiftAssert( !IntTraits< U >::isSigned || bits >= 0 ); ShiftAssert( bits < (int)IntTraits< T >::bitCount ); m_int >>= bits; return *this; } template < typename U > SafeInt< T, E >& operator >>=( SafeInt< U, E > bits ) SAFEINT_NOTHROW { ShiftAssert( !IntTraits< U >::isSigned || (U)bits >= 0 ); ShiftAssert( (U)bits < (int)IntTraits< T >::bitCount ); m_int >>= (U)bits; return *this; } // Bitwise operators // This only makes sense if we're dealing with the same type and size // demand a type T, or something that fits into a type T // Bitwise & SafeInt< T, E > operator &( SafeInt< T, E > rhs ) const SAFEINT_NOTHROW { return SafeInt< T, E >( m_int & (T)rhs ); } template < typename U > SafeInt< T, E > operator &( U rhs ) const SAFEINT_NOTHROW { // we want to avoid setting bits by surprise // consider the case of lhs = int, value = 0xffffffff // rhs = char, value = 0xff // // programmer intent is to get only the lower 8 bits // normal behavior is to upcast both sides to an int // which then sign extends rhs, setting all the bits // If you land in the assert, this is because the bitwise operator // was causing unexpected behavior. Fix is to properly cast your inputs // so that it works like you meant, not unexpectedly return SafeInt< T, E >( BinaryAndHelper< T, U, BinaryMethod< T, U >::method >::And( m_int, rhs ) ); } // Bitwise & assignment SafeInt< T, E >& operator &=( SafeInt< T, E > rhs ) SAFEINT_NOTHROW { m_int &= (T)rhs; return *this; } template < typename U > SafeInt< T, E >& operator &=( U rhs ) SAFEINT_NOTHROW { m_int = BinaryAndHelper< T, U, BinaryMethod< T, U >::method >::And( m_int, rhs ); return *this; } template < typename U > SafeInt< T, E >& operator &=( SafeInt< U, E > rhs ) SAFEINT_NOTHROW { m_int = BinaryAndHelper< T, U, BinaryMethod< T, U >::method >::And( m_int, (U)rhs ); return *this; } // XOR SafeInt< T, E > operator ^( SafeInt< T, E > rhs ) const SAFEINT_NOTHROW { return SafeInt< T, E >( (T)( m_int ^ (T)rhs ) ); } template < typename U > SafeInt< T, E > operator ^( U rhs ) const SAFEINT_NOTHROW { // If you land in the assert, this is because the bitwise operator // was causing unexpected behavior. Fix is to properly cast your inputs // so that it works like you meant, not unexpectedly return SafeInt< T, E >( BinaryXorHelper< T, U, BinaryMethod< T, U >::method >::Xor( m_int, rhs ) ); } // XOR assignment SafeInt< T, E >& operator ^=( SafeInt< T, E > rhs ) SAFEINT_NOTHROW { m_int ^= (T)rhs; return *this; } template < typename U > SafeInt< T, E >& operator ^=( U rhs ) SAFEINT_NOTHROW { m_int = BinaryXorHelper< T, U, BinaryMethod< T, U >::method >::Xor( m_int, rhs ); return *this; } template < typename U > SafeInt< T, E >& operator ^=( SafeInt< U, E > rhs ) SAFEINT_NOTHROW { m_int = BinaryXorHelper< T, U, BinaryMethod< T, U >::method >::Xor( m_int, (U)rhs ); return *this; } // bitwise OR SafeInt< T, E > operator |( SafeInt< T, E > rhs ) const SAFEINT_NOTHROW { return SafeInt< T, E >( (T)( m_int | (T)rhs ) ); } template < typename U > SafeInt< T, E > operator |( U rhs ) const SAFEINT_NOTHROW { return SafeInt< T, E >( BinaryOrHelper< T, U, BinaryMethod< T, U >::method >::Or( m_int, rhs ) ); } // bitwise OR assignment SafeInt< T, E >& operator |=( SafeInt< T, E > rhs ) SAFEINT_NOTHROW { m_int |= (T)rhs; return *this; } template < typename U > SafeInt< T, E >& operator |=( U rhs ) SAFEINT_NOTHROW { m_int = BinaryOrHelper< T, U, BinaryMethod< T, U >::method >::Or( m_int, rhs ); return *this; } template < typename U > SafeInt< T, E >& operator |=( SafeInt< U, E > rhs ) SAFEINT_NOTHROW { m_int = BinaryOrHelper< T, U, BinaryMethod< T, U >::method >::Or( m_int, (U)rhs ); return *this; } // Miscellaneous helper functions SafeInt< T, E > Min( SafeInt< T, E > test, const T floor = IntTraits< T >::minInt ) const SAFEINT_NOTHROW { T tmp = test < m_int ? (T)test : m_int; return tmp < floor ? floor : tmp; } SafeInt< T, E > Max( SafeInt< T, E > test, const T upper = IntTraits< T >::maxInt ) const SAFEINT_NOTHROW { T tmp = test > m_int ? (T)test : m_int; return tmp > upper ? upper : tmp; } void Swap( SafeInt< T, E >& with ) SAFEINT_NOTHROW { T temp( m_int ); m_int = with.m_int; with.m_int = temp; } static SafeInt< T, E > SafeAtoI( const char* input ) SAFEINT_CPP_THROW { return SafeTtoI( input ); } static SafeInt< T, E > SafeWtoI( const wchar_t* input ) { return SafeTtoI( input ); } enum alignBits { align2 = 1, align4 = 2, align8 = 3, align16 = 4, align32 = 5, align64 = 6, align128 = 7, align256 = 8 }; template < alignBits bits > const SafeInt< T, E >& Align() SAFEINT_CPP_THROW { // Zero is always aligned if( m_int == 0 ) return *this; // We don't support aligning negative numbers at this time // Can't align unsigned numbers on bitCount (e.g., 8 bits = 256, unsigned char max = 255) // or signed numbers on bitCount-1 (e.g., 7 bits = 128, signed char max = 127). // Also makes no sense to try to align on negative or no bits. ShiftAssert( ( ( IntTraits::isSigned && bits < (int)IntTraits< T >::bitCount - 1 ) || ( !IntTraits::isSigned && bits < (int)IntTraits< T >::bitCount ) ) && bits >= 0 && ( !IntTraits::isSigned || m_int > 0 ) ); const T AlignValue = ( (T)1 << bits ) - 1; m_int = (T)( ( m_int + AlignValue ) & ~AlignValue ); if( m_int <= 0 ) E::SafeIntOnOverflow(); return *this; } // Commonly needed alignments: const SafeInt< T, E >& Align2() { return Align< align2 >(); } const SafeInt< T, E >& Align4() { return Align< align4 >(); } const SafeInt< T, E >& Align8() { return Align< align8 >(); } const SafeInt< T, E >& Align16() { return Align< align16 >(); } const SafeInt< T, E >& Align32() { return Align< align32 >(); } const SafeInt< T, E >& Align64() { return Align< align64 >(); } private: // This is almost certainly not the best optimized version of atoi, // but it does not display a typical bug where it isn't possible to set MinInt // and it won't allow you to overflow your integer. // This is here because it is useful, and it is an example of what // can be done easily with SafeInt. template < typename U > static SafeInt< T, E > SafeTtoI( U* input ) SAFEINT_CPP_THROW { U* tmp = input; SafeInt< T, E > s; bool negative = false; // Bad input, or empty string if( input == nullptr || input[0] == 0 ) E::SafeIntOnOverflow(); switch( *tmp ) { case '-': tmp++; negative = true; break; case '+': tmp++; break; } while( *tmp != 0 ) { if( *tmp < '0' || *tmp > '9' ) break; if( (T)s != 0 ) s *= (T)10; if( !negative ) s += (T)( *tmp - '0' ); else s -= (T)( *tmp - '0' ); tmp++; } return s; } T m_int; }; // Helper function used to subtract pointers. // Used to squelch warnings template SafeInt SafePtrDiff(const P* p1, const P* p2) SAFEINT_CPP_THROW { return SafeInt( p1 - p2 ); } // Comparison operators //Less than template < typename T, typename U, typename E > bool operator <( U lhs, SafeInt< T, E > rhs ) SAFEINT_NOTHROW { return GreaterThanTest< T, U, ValidComparison< T, U >::method >::GreaterThan( (T)rhs, lhs ); } template < typename T, typename U, typename E > bool operator <( SafeInt lhs, U rhs ) SAFEINT_NOTHROW { return GreaterThanTest< U, T, ValidComparison< U, T >::method >::GreaterThan( rhs, (T)lhs ); } template < typename T, typename U, typename E > bool operator <( SafeInt< U, E > lhs, SafeInt< T, E > rhs ) SAFEINT_NOTHROW { return GreaterThanTest< T, U, ValidComparison< T, U >::method >::GreaterThan( (T)rhs, (U)lhs ); } // Greater than template < typename T, typename U, typename E > bool operator >( U lhs, SafeInt< T, E > rhs ) SAFEINT_NOTHROW { return GreaterThanTest< U, T, ValidComparison< U, T >::method >::GreaterThan( lhs, (T)rhs ); } template < typename T, typename U, typename E > bool operator >( SafeInt lhs, U rhs ) SAFEINT_NOTHROW { return GreaterThanTest< T, U, ValidComparison< T, U >::method >::GreaterThan( (T)lhs, rhs ); } template < typename T, typename U, typename E > bool operator >( SafeInt< T, E > lhs, SafeInt< U, E > rhs ) SAFEINT_NOTHROW { return GreaterThanTest< T, U, ValidComparison< T, U >::method >::GreaterThan( (T)lhs, (U)rhs ); } // Greater than or equal template < typename T, typename U, typename E > bool operator >=( U lhs, SafeInt< T, E > rhs ) SAFEINT_NOTHROW { return !GreaterThanTest< T, U, ValidComparison< T, U >::method >::GreaterThan( (T)rhs, lhs ); } template < typename T, typename U, typename E > bool operator >=( SafeInt lhs, U rhs ) SAFEINT_NOTHROW { return !GreaterThanTest< U, T, ValidComparison< U, T >::method >::GreaterThan( rhs, (T)lhs ); } template < typename T, typename U, typename E > bool operator >=( SafeInt< T, E > lhs, SafeInt< U, E > rhs ) SAFEINT_NOTHROW { return !GreaterThanTest< U, T, ValidComparison< U, T >::method >::GreaterThan( (U)rhs, (T)lhs ); } // Less than or equal template < typename T, typename U, typename E > bool operator <=( U lhs, SafeInt< T, E > rhs ) SAFEINT_NOTHROW { return !GreaterThanTest< U, T, ValidComparison< U, T >::method >::GreaterThan( lhs, (T)rhs ); } template < typename T, typename U, typename E > bool operator <=( SafeInt< T, E > lhs, U rhs ) SAFEINT_NOTHROW { return !GreaterThanTest< T, U, ValidComparison< T, U >::method >::GreaterThan( (T)lhs, rhs ); } template < typename T, typename U, typename E > bool operator <=( SafeInt< T, E > lhs, SafeInt< U, E > rhs ) SAFEINT_NOTHROW { return !GreaterThanTest< T, U, ValidComparison< T, U >::method >::GreaterThan( (T)lhs, (U)rhs ); } // equality // explicit overload for bool template < typename T, typename E > bool operator ==( bool lhs, SafeInt< T, E > rhs ) SAFEINT_NOTHROW { return lhs == ( (T)rhs == 0 ? false : true ); } template < typename T, typename E > bool operator ==( SafeInt< T, E > lhs, bool rhs ) SAFEINT_NOTHROW { return rhs == ( (T)lhs == 0 ? false : true ); } template < typename T, typename U, typename E > bool operator ==( U lhs, SafeInt< T, E > rhs ) SAFEINT_NOTHROW { return EqualityTest< T, U, ValidComparison< T, U >::method >::IsEquals((T)rhs, lhs); } template < typename T, typename U, typename E > bool operator ==( SafeInt< T, E > lhs, U rhs ) SAFEINT_NOTHROW { return EqualityTest< T, U, ValidComparison< T, U >::method >::IsEquals( (T)lhs, rhs ); } template < typename T, typename U, typename E > bool operator ==( SafeInt< T, E > lhs, SafeInt< U, E > rhs ) SAFEINT_NOTHROW { return EqualityTest< T, U, ValidComparison< T, U >::method >::IsEquals( (T)lhs, (U)rhs ); } //not equals template < typename T, typename U, typename E > bool operator !=( U lhs, SafeInt< T, E > rhs ) SAFEINT_NOTHROW { return !EqualityTest< T, U, ValidComparison< T, U >::method >::IsEquals( (T)rhs, lhs ); } template < typename T, typename U, typename E > bool operator !=( SafeInt< T, E > lhs, U rhs ) SAFEINT_NOTHROW { return !EqualityTest< T, U, ValidComparison< T, U >::method >::IsEquals( (T)lhs, rhs ); } template < typename T, typename U, typename E > bool operator !=( SafeInt< T, E > lhs, SafeInt< U, E > rhs ) SAFEINT_NOTHROW { return !EqualityTest< T, U, ValidComparison< T, U >::method >::IsEquals( lhs, rhs ); } template < typename T, typename E > bool operator !=( bool lhs, SafeInt< T, E > rhs ) SAFEINT_NOTHROW { return ( (T)rhs == 0 ? false : true ) != lhs; } template < typename T, typename E > bool operator !=( SafeInt< T, E > lhs, bool rhs ) SAFEINT_NOTHROW { return ( (T)lhs == 0 ? false : true ) != rhs; } template < typename T, typename U, typename E, int method > class ModulusSimpleCaseHelper; template < typename T, typename E, int method > class ModulusSignedCaseHelper; template < typename T, typename E > class ModulusSignedCaseHelper < T, E, true > { public: static bool SignedCase( SafeInt< T, E > rhs, SafeInt< T, E >& result ) SAFEINT_NOTHROW { if( (T)rhs == (T)-1 ) { result = 0; return true; } return false; } }; template < typename T, typename E > class ModulusSignedCaseHelper < T, E, false > { public: static bool SignedCase( SafeInt< T, E > /*rhs*/, SafeInt< T, E >& /*result*/ ) SAFEINT_NOTHROW { return false; } }; template < typename T, typename U, typename E > class ModulusSimpleCaseHelper < T, U, E, true > { public: static bool ModulusSimpleCase( U lhs, SafeInt< T, E > rhs, SafeInt< T, E >& result ) SAFEINT_CPP_THROW { if( rhs != 0 ) { if( ModulusSignedCaseHelper< T, E, IntTraits< T >::isSigned >::SignedCase( rhs, result ) ) return true; result = SafeInt< T, E >( (T)( lhs % (T)rhs ) ); return true; } E::SafeIntOnDivZero(); } }; template< typename T, typename U, typename E > class ModulusSimpleCaseHelper < T, U, E, false > { public: static bool ModulusSimpleCase( U /*lhs*/, SafeInt< T, E > /*rhs*/, SafeInt< T, E >& /*result*/ ) SAFEINT_NOTHROW { return false; } }; // Modulus template < typename T, typename U, typename E > SafeInt< T, E > operator %( U lhs, SafeInt< T, E > rhs ) SAFEINT_CPP_THROW { // Value of return depends on sign of lhs // This one may not be safe - bounds check in constructor // if lhs is negative and rhs is unsigned, this will throw an exception. // Fast-track the simple case // same size and same sign SafeInt< T, E > result; if( ModulusSimpleCaseHelper< T, U, E, sizeof(T) == sizeof(U) && (bool)IntTraits< T >::isSigned == (bool)IntTraits< U >::isSigned >::ModulusSimpleCase( lhs, rhs, result ) ) return result; return SafeInt< T, E >( ( SafeInt< U, E >( lhs ) % (T)rhs ) ); } // Multiplication template < typename T, typename U, typename E > SafeInt< T, E > operator *( U lhs, SafeInt< T, E > rhs ) SAFEINT_CPP_THROW { T ret( 0 ); MultiplicationHelper< T, U, MultiplicationMethod< T, U >::method >::template MultiplyThrow< E >( (T)rhs, lhs, ret ); return SafeInt< T, E >(ret); } template < typename T, typename U, typename E, int method > class DivisionNegativeCornerCaseHelper; template < typename T, typename U, typename E > class DivisionNegativeCornerCaseHelper< T, U, E, true > { public: static bool NegativeCornerCase( U lhs, SafeInt< T, E > rhs, SafeInt& result ) SAFEINT_CPP_THROW { // Problem case - normal casting behavior changes meaning // flip rhs to positive // any operator casts now do the right thing U tmp; if( CompileConst< sizeof(T) == 4 >::Value() ) tmp = lhs/(U)( ~(unsigned __int32)(T)rhs + 1 ); else tmp = lhs/(U)( ~(unsigned __int64)(T)rhs + 1 ); if( tmp <= (U)IntTraits< T >::maxInt ) { result = SafeInt< T, E >( (T)(~(unsigned __int64)tmp + 1) ); return true; } // Corner case T maxT = IntTraits< T >::maxInt; if( tmp == (U)maxT + 1 ) { T minT = IntTraits< T >::minInt; result = SafeInt< T, E >( minT ); return true; } E::SafeIntOnOverflow(); } }; template < typename T, typename U, typename E > class DivisionNegativeCornerCaseHelper< T, U, E, false > { public: static bool NegativeCornerCase( U /*lhs*/, SafeInt< T, E > /*rhs*/, SafeInt& /*result*/ ) SAFEINT_NOTHROW { return false; } }; template < typename T, typename U, typename E, int method > class DivisionCornerCaseHelper; template < typename T, typename U, typename E > class DivisionCornerCaseHelper < T, U, E, true > { public: static bool DivisionCornerCase1( U lhs, SafeInt< T, E > rhs, SafeInt& result ) SAFEINT_CPP_THROW { if( (T)rhs > 0 ) { result = SafeInt< T, E >( lhs/(T)rhs ); return true; } // Now rhs is either negative, or zero if( (T)rhs != 0 ) { if( DivisionNegativeCornerCaseHelper< T, U, E, sizeof( U ) >= 4 && sizeof( T ) <= sizeof( U ) >::NegativeCornerCase( lhs, rhs, result ) ) return true; result = SafeInt< T, E >(lhs/(T)rhs); return true; } E::SafeIntOnDivZero(); } }; template < typename T, typename U, typename E > class DivisionCornerCaseHelper < T, U, E, false > { public: static bool DivisionCornerCase1( U /*lhs*/, SafeInt< T, E > /*rhs*/, SafeInt& /*result*/ ) SAFEINT_NOTHROW { return false; } }; template < typename T, typename U, typename E, int method > class DivisionCornerCaseHelper2; template < typename T, typename U, typename E > class DivisionCornerCaseHelper2 < T, U, E, true > { public: static bool DivisionCornerCase2( U lhs, SafeInt< T, E > rhs, SafeInt& result ) SAFEINT_CPP_THROW { if( lhs == IntTraits< U >::minInt && (T)rhs == -1 ) { // corner case of a corner case - lhs = min int, rhs = -1, // but rhs is the return type, so in essence, we can return -lhs // if rhs is a larger type than lhs // If types are wrong, throws #if SAFEINT_COMPILER == VISUAL_STUDIO_COMPILER #pragma warning(push) //cast truncates constant value #pragma warning(disable:4310) #endif if( CompileConst::Value() ) result = SafeInt< T, E >( (T)( -(T)IntTraits< U >::minInt ) ); else E::SafeIntOnOverflow(); #if SAFEINT_COMPILER == VISUAL_STUDIO_COMPILER #pragma warning(pop) #endif return true; } return false; } }; template < typename T, typename U, typename E > class DivisionCornerCaseHelper2 < T, U, E, false > { public: static bool DivisionCornerCase2( U /*lhs*/, SafeInt< T, E > /*rhs*/, SafeInt& /*result*/ ) SAFEINT_NOTHROW { return false; } }; // Division template < typename T, typename U, typename E > SafeInt< T, E > operator /( U lhs, SafeInt< T, E > rhs ) SAFEINT_CPP_THROW { // Corner case - has to be handled seperately SafeInt< T, E > result; if( DivisionCornerCaseHelper< T, U, E, (int)DivisionMethod< U, T >::method == (int)DivisionState_UnsignedSigned >::DivisionCornerCase1( lhs, rhs, result ) ) return result; if( DivisionCornerCaseHelper2< T, U, E, SafeIntCompare< T, U >::isBothSigned >::DivisionCornerCase2( lhs, rhs, result ) ) return result; // Otherwise normal logic works with addition of bounds check when casting from U->T U ret; DivisionHelper< U, T, DivisionMethod< U, T >::method >::template DivideThrow< E >( lhs, (T)rhs, ret ); return SafeInt< T, E >( ret ); } // Addition template < typename T, typename U, typename E > SafeInt< T, E > operator +( U lhs, SafeInt< T, E > rhs ) SAFEINT_CPP_THROW { T ret( 0 ); AdditionHelper< T, U, AdditionMethod< T, U >::method >::template AdditionThrow< E >( (T)rhs, lhs, ret ); return SafeInt< T, E >( ret ); } // Subtraction template < typename T, typename U, typename E > SafeInt< T, E > operator -( U lhs, SafeInt< T, E > rhs ) SAFEINT_CPP_THROW { T ret( 0 ); SubtractionHelper< U, T, SubtractionMethod2< U, T >::method >::template SubtractThrow< E >( lhs, rhs.Ref(), ret ); return SafeInt< T, E >( ret ); } // Overrides designed to deal with cases where a SafeInt is assigned out // to a normal int - this at least makes the last operation safe // += template < typename T, typename U, typename E > T& operator +=( T& lhs, SafeInt< U, E > rhs ) SAFEINT_CPP_THROW { T ret( 0 ); AdditionHelper< T, U, AdditionMethod< T, U >::method >::template AdditionThrow< E >( lhs, (U)rhs, ret ); lhs = ret; return lhs; } template < typename T, typename U, typename E > T& operator -=( T& lhs, SafeInt< U, E > rhs ) SAFEINT_CPP_THROW { T ret( 0 ); SubtractionHelper< T, U, SubtractionMethod< T, U >::method >::template SubtractThrow< E >( lhs, (U)rhs, ret ); lhs = ret; return lhs; } template < typename T, typename U, typename E > T& operator *=( T& lhs, SafeInt< U, E > rhs ) SAFEINT_CPP_THROW { T ret( 0 ); MultiplicationHelper< T, U, MultiplicationMethod< T, U >::method >::template MultiplyThrow< E >( lhs, (U)rhs, ret ); lhs = ret; return lhs; } template < typename T, typename U, typename E > T& operator /=( T& lhs, SafeInt< U, E > rhs ) SAFEINT_CPP_THROW { T ret( 0 ); DivisionHelper< T, U, DivisionMethod< T, U >::method >::template DivideThrow< E >( lhs, (U)rhs, ret ); lhs = ret; return lhs; } template < typename T, typename U, typename E > T& operator %=( T& lhs, SafeInt< U, E > rhs ) SAFEINT_CPP_THROW { T ret( 0 ); ModulusHelper< T, U, ValidComparison< T, U >::method >::template ModulusThrow< E >( lhs, (U)rhs, ret ); lhs = ret; return lhs; } template < typename T, typename U, typename E > T& operator &=( T& lhs, SafeInt< U, E > rhs ) SAFEINT_NOTHROW { lhs = BinaryAndHelper< T, U, BinaryMethod< T, U >::method >::And( lhs, (U)rhs ); return lhs; } template < typename T, typename U, typename E > T& operator ^=( T& lhs, SafeInt< U, E > rhs ) SAFEINT_NOTHROW { lhs = BinaryXorHelper< T, U, BinaryMethod< T, U >::method >::Xor( lhs, (U)rhs ); return lhs; } template < typename T, typename U, typename E > T& operator |=( T& lhs, SafeInt< U, E > rhs ) SAFEINT_NOTHROW { lhs = BinaryOrHelper< T, U, BinaryMethod< T, U >::method >::Or( lhs, (U)rhs ); return lhs; } template < typename T, typename U, typename E > T& operator <<=( T& lhs, SafeInt< U, E > rhs ) SAFEINT_NOTHROW { lhs = (T)( SafeInt< T, E >( lhs ) << (U)rhs ); return lhs; } template < typename T, typename U, typename E > T& operator >>=( T& lhs, SafeInt< U, E > rhs ) SAFEINT_NOTHROW { lhs = (T)( SafeInt< T, E >( lhs ) >> (U)rhs ); return lhs; } // Specific pointer overrides // Note - this function makes no attempt to ensure // that the resulting pointer is still in the buffer, only // that no int overflows happened on the way to getting the new pointer template < typename T, typename U, typename E > T*& operator +=( T*& lhs, SafeInt< U, E > rhs ) SAFEINT_CPP_THROW { // Cast the pointer to a number so we can do arithmetic SafeInt< size_t, E > ptr_val = reinterpret_cast< size_t >( lhs ); // Check first that rhs is valid for the type of ptrdiff_t // and that multiplying by sizeof( T ) doesn't overflow a ptrdiff_t // Next, we need to add 2 SafeInts of different types, so unbox the ptr_diff // Finally, cast the number back to a pointer of the correct type lhs = reinterpret_cast< T* >( (size_t)( ptr_val + (ptrdiff_t)( SafeInt< ptrdiff_t, E >( rhs ) * sizeof( T ) ) ) ); return lhs; } template < typename T, typename U, typename E > T*& operator -=( T*& lhs, SafeInt< U, E > rhs ) SAFEINT_CPP_THROW { // Cast the pointer to a number so we can do arithmetic SafeInt< size_t, E > ptr_val = reinterpret_cast< size_t >( lhs ); // See above for comments lhs = reinterpret_cast< T* >( (size_t)( ptr_val - (ptrdiff_t)( SafeInt< ptrdiff_t, E >( rhs ) * sizeof( T ) ) ) ); return lhs; } template < typename T, typename U, typename E > T*& operator *=( T*& lhs, SafeInt< U, E > ) SAFEINT_NOTHROW { // This operator explicitly not supported C_ASSERT( sizeof(T) == 0 ); return (lhs = NULL); } template < typename T, typename U, typename E > T*& operator /=( T*& lhs, SafeInt< U, E > ) SAFEINT_NOTHROW { // This operator explicitly not supported C_ASSERT( sizeof(T) == 0 ); return (lhs = NULL); } template < typename T, typename U, typename E > T*& operator %=( T*& lhs, SafeInt< U, E > ) SAFEINT_NOTHROW { // This operator explicitly not supported C_ASSERT( sizeof(T) == 0 ); return (lhs = NULL); } template < typename T, typename U, typename E > T*& operator &=( T*& lhs, SafeInt< U, E > ) SAFEINT_NOTHROW { // This operator explicitly not supported C_ASSERT( sizeof(T) == 0 ); return (lhs = NULL); } template < typename T, typename U, typename E > T*& operator ^=( T*& lhs, SafeInt< U, E > ) SAFEINT_NOTHROW { // This operator explicitly not supported C_ASSERT( sizeof(T) == 0 ); return (lhs = NULL); } template < typename T, typename U, typename E > T*& operator |=( T*& lhs, SafeInt< U, E > ) SAFEINT_NOTHROW { // This operator explicitly not supported C_ASSERT( sizeof(T) == 0 ); return (lhs = NULL); } template < typename T, typename U, typename E > T*& operator <<=( T*& lhs, SafeInt< U, E > ) SAFEINT_NOTHROW { // This operator explicitly not supported C_ASSERT( sizeof(T) == 0 ); return (lhs = NULL); } template < typename T, typename U, typename E > T*& operator >>=( T*& lhs, SafeInt< U, E > ) SAFEINT_NOTHROW { // This operator explicitly not supported C_ASSERT( sizeof(T) == 0 ); return (lhs = NULL); } // Shift operators // NOTE - shift operators always return the type of the lhs argument // Left shift template < typename T, typename U, typename E > SafeInt< U, E > operator <<( U lhs, SafeInt< T, E > bits ) SAFEINT_NOTHROW { ShiftAssert( !IntTraits< T >::isSigned || (T)bits >= 0 ); ShiftAssert( (T)bits < (int)IntTraits< U >::bitCount ); return SafeInt< U, E >( (U)( lhs << (T)bits ) ); } // Right shift template < typename T, typename U, typename E > SafeInt< U, E > operator >>( U lhs, SafeInt< T, E > bits ) SAFEINT_NOTHROW { ShiftAssert( !IntTraits< T >::isSigned || (T)bits >= 0 ); ShiftAssert( (T)bits < (int)IntTraits< U >::bitCount ); return SafeInt< U, E >( (U)( lhs >> (T)bits ) ); } // Bitwise operators // This only makes sense if we're dealing with the same type and size // demand a type T, or something that fits into a type T. // Bitwise & template < typename T, typename U, typename E > SafeInt< T, E > operator &( U lhs, SafeInt< T, E > rhs ) SAFEINT_NOTHROW { return SafeInt< T, E >( BinaryAndHelper< T, U, BinaryMethod< T, U >::method >::And( (T)rhs, lhs ) ); } // Bitwise XOR template < typename T, typename U, typename E > SafeInt< T, E > operator ^( U lhs, SafeInt< T, E > rhs ) SAFEINT_NOTHROW { return SafeInt< T, E >(BinaryXorHelper< T, U, BinaryMethod< T, U >::method >::Xor( (T)rhs, lhs ) ); } // Bitwise OR template < typename T, typename U, typename E > SafeInt< T, E > operator |( U lhs, SafeInt< T, E > rhs ) SAFEINT_NOTHROW { return SafeInt< T, E >( BinaryOrHelper< T, U, BinaryMethod< T, U >::method >::Or( (T)rhs, lhs ) ); } #if SAFEINT_COMPILER == GCC_COMPILER #pragma GCC diagnostic pop #endif #if SAFEINT_COMPILER == CLANG_COMPILER #pragma clang diagnostic pop #endif } // utilities } // safeint3 ================================================ FILE: Include/cpprestinclude/cpprest/details/asyncrt_utils.hpp ================================================ /*** * ==++== * * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * ==--== * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ * * Utilities * * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk * * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ****/ #if defined(_WIN32) #if HC_PLATFORM != HC_PLATFORM_XDK #include #endif #else // _WIN32 #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-local-typedef" #endif // TODO 1808 #include // #include #if defined(__clang__) #pragma clang diagnostic pop #endif #endif // _WIN32 #if HC_PLATFORM_IS_MICROSOFT #pragma warning( push ) #pragma warning( disable : 26444 ) // ignore various unnamed objects #pragma warning( disable : 26498 ) // ignore eof warning #pragma warning( disable : 26812 ) // enum instead of enum class #pragma warning( disable : 4365 ) #endif // Could use C++ standard library if not __GLIBCXX__, // For testing purposes we just the handwritten on all platforms. #if defined(CPPREST_STDLIB_UNICODE_CONVERSIONS) #include #endif using namespace web; using namespace utility; using namespace utility::conversions; namespace utility { namespace details { #if !defined(ANDROID) && !defined(__ANDROID__) && !defined(PAVO) std::once_flag g_c_localeFlag; std::unique_ptr g_c_locale(nullptr, [](scoped_c_thread_locale::xplat_locale *){}); scoped_c_thread_locale::xplat_locale scoped_c_thread_locale::c_locale() { std::call_once(g_c_localeFlag, [&]() { scoped_c_thread_locale::xplat_locale *clocale = new scoped_c_thread_locale::xplat_locale(); #ifdef _WIN32 if (clocale == nullptr) { throw std::runtime_error("Unable to create 'C' locale."); } *clocale = _create_locale(LC_ALL, "C"); if (clocale == nullptr || *clocale == nullptr) { throw std::runtime_error("Unable to create 'C' locale."); } auto deleter = [](scoped_c_thread_locale::xplat_locale *clocale) { _free_locale(*clocale); delete clocale; }; #else *clocale = newlocale(LC_ALL, "C", nullptr); if (clocale == nullptr || *clocale == nullptr) { throw std::runtime_error("Unable to create 'C' locale."); } auto deleter = [](scoped_c_thread_locale::xplat_locale *clocale) { freelocale(*clocale); delete clocale; }; #endif g_c_locale = std::unique_ptr(clocale, deleter); }); return *g_c_locale; } #endif #ifdef _WIN32 scoped_c_thread_locale::scoped_c_thread_locale() : m_prevLocale(), m_prevThreadSetting(-1) { char *prevLocale = setlocale(LC_ALL, nullptr); if (prevLocale == nullptr) { throw std::runtime_error("Unable to retrieve current locale."); } if (std::strcmp(prevLocale, "C") != 0) { m_prevLocale = prevLocale; m_prevThreadSetting = _configthreadlocale(_ENABLE_PER_THREAD_LOCALE); if (m_prevThreadSetting == -1) { throw std::runtime_error("Unable to enable per thread locale."); } if (setlocale(LC_ALL, "C") == nullptr) { _configthreadlocale(m_prevThreadSetting); throw std::runtime_error("Unable to set locale"); } } } scoped_c_thread_locale::~scoped_c_thread_locale() { if (m_prevThreadSetting != -1) { setlocale(LC_ALL, m_prevLocale.c_str()); _configthreadlocale(m_prevThreadSetting); } } #elif (defined(ANDROID) || defined(__ANDROID__) || defined(PAVO)) scoped_c_thread_locale::scoped_c_thread_locale() {} scoped_c_thread_locale::~scoped_c_thread_locale() {} #else scoped_c_thread_locale::scoped_c_thread_locale() : m_prevLocale(nullptr) { char *prevLocale = setlocale(LC_ALL, nullptr); if (prevLocale == nullptr) { throw std::runtime_error("Unable to retrieve current locale."); } if (std::strcmp(prevLocale, "C") != 0) { m_prevLocale = uselocale(c_locale()); if (m_prevLocale == nullptr) { throw std::runtime_error("Unable to set locale"); } } } scoped_c_thread_locale::~scoped_c_thread_locale() { if (m_prevLocale != nullptr) { uselocale(m_prevLocale); } } #endif } namespace details { const std::error_category & __cdecl platform_category() { #ifdef _WIN32 return windows_category(); #else return linux_category(); #endif } #ifdef _WIN32 // Remove once VS 2013 is no longer supported. #if _MSC_VER < 1900 static details::windows_category_impl instance; #endif const std::error_category & __cdecl windows_category() { #if _MSC_VER >= 1900 static details::windows_category_impl instance; #endif return instance; } std::string windows_category_impl::message(int errorCode) const CPPREST_NOEXCEPT { const size_t buffer_size = 4096; DWORD dwFlags = FORMAT_MESSAGE_FROM_SYSTEM; LPCVOID lpSource = NULL; #if !defined(__cplusplus_winrt) if (errorCode >= 12000) { dwFlags = FORMAT_MESSAGE_FROM_HMODULE; lpSource = GetModuleHandleA("winhttp.dll"); // this handle DOES NOT need to be freed } #endif std::wstring buffer; buffer.resize(buffer_size); const auto result = ::FormatMessageW( dwFlags, lpSource, errorCode, 0, &buffer[0], buffer_size, NULL); if (result == 0) { std::ostringstream os; os << "Unable to get an error message for error code: " << errorCode << "."; return os.str(); } return utility::conversions::to_utf8string(buffer); } std::error_condition windows_category_impl::default_error_condition(int errorCode) const CPPREST_NOEXCEPT { // First see if the STL implementation can handle the mapping for common cases. const std::error_condition errCondition = std::system_category().default_error_condition(errorCode); const std::string errConditionMsg = errCondition.message(); if(_stricmp(errConditionMsg.c_str(), "unknown error") != 0) { return errCondition; } switch(errorCode) { #ifndef __cplusplus_winrt case ERROR_WINHTTP_TIMEOUT: return std::errc::timed_out; case ERROR_WINHTTP_CANNOT_CONNECT: return std::errc::host_unreachable; case ERROR_WINHTTP_CONNECTION_ERROR: return std::errc::connection_aborted; #endif case INET_E_RESOURCE_NOT_FOUND: case INET_E_CANNOT_CONNECT: return std::errc::host_unreachable; case INET_E_CONNECTION_TIMEOUT: return std::errc::timed_out; case INET_E_DOWNLOAD_FAILURE: return std::errc::connection_aborted; default: break; } return std::error_condition(errorCode, *this); } #else const std::error_category & __cdecl linux_category() { // On Linux we are using boost error codes which have the exact same // mapping and are equivalent with std::generic_category error codes. return std::generic_category(); } #endif } #define LOW_3BITS 0x7 #define LOW_4BITS 0xF #define LOW_5BITS 0x1F #define LOW_6BITS 0x3F #define BIT4 0x8 #define BIT5 0x10 #define BIT6 0x20 #define BIT7 0x40 #define BIT8 0x80 #define L_SURROGATE_START 0xDC00 #define L_SURROGATE_END 0xDFFF #define H_SURROGATE_START 0xD800 #define H_SURROGATE_END 0xDBFF #define SURROGATE_PAIR_START 0x10000 utf16string __cdecl conversions::utf8_to_utf16(const std::string &s) { #if defined(CPPREST_STDLIB_UNICODE_CONVERSIONS) std::wstring_convert, utf16char> conversion; return conversion.from_bytes(s); #else utf16string dest; // Save repeated heap allocations, use less than source string size assuming some // of the characters are not just ASCII and collapse. dest.reserve(static_cast(static_cast(s.size()) * .70)); for (auto src = s.begin(); src != s.end(); ++src) { if ((*src & BIT8) == 0) // single byte character, 0x0 to 0x7F { dest.push_back(utf16string::value_type(*src)); } else { unsigned char numContBytes = 0; uint32_t codePoint; if ((*src & BIT7) == 0) { throw std::range_error("UTF-8 string character can never start with 10xxxxxx"); } else if ((*src & BIT6) == 0) // 2 byte character, 0x80 to 0x7FF { codePoint = *src & LOW_5BITS; numContBytes = 1; } else if ((*src & BIT5) == 0) // 3 byte character, 0x800 to 0xFFFF { codePoint = *src & LOW_4BITS; numContBytes = 2; } else if ((*src & BIT4) == 0) // 4 byte character, 0x10000 to 0x10FFFF { codePoint = *src & LOW_3BITS; numContBytes = 3; } else { throw std::range_error("UTF-8 string has invalid Unicode code point"); } for (unsigned char i = 0; i < numContBytes; ++i) { if (++src == s.end()) { throw std::range_error("UTF-8 string is missing bytes in character"); } if ((*src & BIT8) == 0 || (*src & BIT7) != 0) { throw std::range_error("UTF-8 continuation byte is missing leading byte"); } codePoint <<= 6; codePoint |= *src & LOW_6BITS; } if (codePoint >= SURROGATE_PAIR_START) { // In UTF-16 U+10000 to U+10FFFF are represented as two 16-bit code units, surrogate pairs. // - 0x10000 is subtracted from the code point // - high surrogate is 0xD800 added to the top ten bits // - low surrogate is 0xDC00 added to the low ten bits codePoint -= SURROGATE_PAIR_START; dest.push_back(utf16string::value_type((codePoint >> 10) | H_SURROGATE_START)); dest.push_back(utf16string::value_type((codePoint & 0x3FF) | L_SURROGATE_START)); } else { // In UTF-16 U+0000 to U+D7FF and U+E000 to U+FFFF are represented exactly as the Unicode code point value. // U+D800 to U+DFFF are not valid characters, for simplicity we assume they are not present but will encode // them if encountered. dest.push_back(utf16string::value_type(codePoint)); } } } return dest; #endif } std::string __cdecl conversions::utf16_to_utf8(const utf16string &w) { #if defined(CPPREST_STDLIB_UNICODE_CONVERSIONS) std::wstring_convert, utf16char> conversion; return conversion.to_bytes(w); #else std::string dest; dest.reserve(w.size()); for (auto src = w.begin(); src != w.end(); ++src) { // Check for high surrogate. if (*src >= H_SURROGATE_START && *src <= H_SURROGATE_END) { const auto highSurrogate = *src++; if (src == w.end()) { throw std::range_error("UTF-16 string is missing low surrogate"); } const auto lowSurrogate = *src; if (lowSurrogate < L_SURROGATE_START || lowSurrogate > L_SURROGATE_END) { throw std::range_error("UTF-16 string has invalid low surrogate"); } // To get from surrogate pair to Unicode code point: // - subract 0xD800 from high surrogate, this forms top ten bits // - subract 0xDC00 from low surrogate, this forms low ten bits // - add 0x10000 // Leaves a code point in U+10000 to U+10FFFF range. uint32_t codePoint = highSurrogate - H_SURROGATE_START; codePoint <<= 10; codePoint |= lowSurrogate - L_SURROGATE_START; codePoint += SURROGATE_PAIR_START; // 4 bytes need using 21 bits dest.push_back(char((codePoint >> 18) | 0xF0)); // leading 3 bits dest.push_back(char(((codePoint >> 12) & LOW_6BITS) | BIT8)); // next 6 bits dest.push_back(char(((codePoint >> 6) & LOW_6BITS) | BIT8)); // next 6 bits dest.push_back(char((codePoint & LOW_6BITS) | BIT8)); // trailing 6 bits } else { if (*src <= 0x7F) // single byte character { dest.push_back(static_cast(*src)); } else if (*src <= 0x7FF) // 2 bytes needed (11 bits used) { dest.push_back(char((*src >> 6) | 0xC0)); // leading 5 bits dest.push_back(char((*src & LOW_6BITS) | BIT8)); // trailing 6 bits } else // 3 bytes needed (16 bits used) { dest.push_back(char((*src >> 12) | 0xE0)); // leading 4 bits dest.push_back(char(((*src >> 6) & LOW_6BITS) | BIT8)); // middle 6 bits dest.push_back(char((*src & LOW_6BITS) | BIT8)); // trailing 6 bits } } } return dest; #endif } utf16string __cdecl conversions::usascii_to_utf16(const std::string &s) { // Ascii is a subset of UTF-8 so just convert to UTF-16 return utf8_to_utf16(s); } utf16string __cdecl conversions::latin1_to_utf16(const std::string &s) { // Latin1 is the first 256 code points in Unicode. // In UTF-16 encoding each of these is represented as exactly the numeric code point. utf16string dest; dest.resize(s.size()); for (size_t i = 0; i < s.size(); ++i) { dest[i] = utf16char(static_cast(s[i])); } return dest; } utf8string __cdecl conversions::latin1_to_utf8(const std::string &s) { return utf16_to_utf8(latin1_to_utf16(s)); } utility::string_t __cdecl conversions::to_string_t(utf16string &&s) { #ifdef _UTF16_STRINGS return std::move(s); #else return utf16_to_utf8(std::move(s)); #endif } utility::string_t __cdecl conversions::to_string_t(std::string &&s) { #ifdef _UTF16_STRINGS return utf8_to_utf16(std::move(s)); #else return std::move(s); #endif } utility::string_t __cdecl conversions::to_string_t(const utf16string &s) { #ifdef _UTF16_STRINGS return s; #else return utf16_to_utf8(s); #endif } utility::string_t __cdecl conversions::to_string_t(const std::string &s) { #ifdef _UTF16_STRINGS return utf8_to_utf16(s); #else return s; #endif } std::string __cdecl conversions::to_utf8string(std::string value) { return value; } std::string __cdecl conversions::to_utf8string(const utf16string &value) { return utf16_to_utf8(value); } utf16string __cdecl conversions::to_utf16string(const std::string &value) { return utf8_to_utf16(value); } utf16string __cdecl conversions::to_utf16string(utf16string value) { return value; } #ifndef _WIN32 datetime datetime::timeval_to_datetime(const timeval &time) { const uint64_t epoch_offset = 11644473600LL; // diff between windows and unix epochs (seconds) uint64_t result = epoch_offset + time.tv_sec; result *= _secondTicks; // convert to 10e-7 result += time.tv_usec * 10; // convert and add microseconds, 10e-6 to 10e-7 return datetime(result); } #endif static bool is_digit(utility::char_t c) { return c >= _XPLATSTR('0') && c <= _XPLATSTR('9'); } datetime __cdecl datetime::utc_now() { #ifdef _WIN32 ULARGE_INTEGER largeInt; FILETIME fileTime; GetSystemTimeAsFileTime(&fileTime); largeInt.LowPart = fileTime.dwLowDateTime; largeInt.HighPart = fileTime.dwHighDateTime; return datetime(largeInt.QuadPart); #else //LINUX timeval time {}; gettimeofday(&time, nullptr); return timeval_to_datetime(time); #endif } utility::string_t datetime::to_string(date_format format) const { #ifdef _WIN32 int status; ULARGE_INTEGER largeInt; largeInt.QuadPart = m_interval; FILETIME ft; ft.dwHighDateTime = largeInt.HighPart; ft.dwLowDateTime = largeInt.LowPart; SYSTEMTIME systemTime; if (!FileTimeToSystemTime((const FILETIME *)&ft, &systemTime)) { throw utility::details::create_system_error(GetLastError()); } std::wostringstream outStream; outStream.imbue(std::locale::classic()); if (format == RFC_1123) { #if _WIN32_WINNT < _WIN32_WINNT_VISTA TCHAR dateStr[18] = {0}; status = GetDateFormat(LOCALE_INVARIANT, 0, &systemTime, __TEXT("ddd',' dd MMM yyyy"), dateStr, sizeof(dateStr) / sizeof(TCHAR)); #else wchar_t dateStr[18] = {0}; status = GetDateFormatEx(LOCALE_NAME_INVARIANT, 0, &systemTime, L"ddd',' dd MMM yyyy", dateStr, sizeof(dateStr) / sizeof(wchar_t), NULL); #endif // _WIN32_WINNT < _WIN32_WINNT_VISTA if (status == 0) { throw utility::details::create_system_error(GetLastError()); } #if _WIN32_WINNT < _WIN32_WINNT_VISTA TCHAR timeStr[10] = {0}; status = GetTimeFormat(LOCALE_INVARIANT, TIME_NOTIMEMARKER | TIME_FORCE24HOURFORMAT, &systemTime, __TEXT("HH':'mm':'ss"), timeStr, sizeof(timeStr) / sizeof(TCHAR)); #else wchar_t timeStr[10] = {0}; status = GetTimeFormatEx(LOCALE_NAME_INVARIANT, TIME_NOTIMEMARKER | TIME_FORCE24HOURFORMAT, &systemTime, L"HH':'mm':'ss", timeStr, sizeof(timeStr) / sizeof(wchar_t)); #endif // _WIN32_WINNT < _WIN32_WINNT_VISTA if (status == 0) { throw utility::details::create_system_error(GetLastError()); } outStream << dateStr << " " << timeStr << " " << "GMT"; } else if (format == ISO_8601) { const size_t buffSize = 64; #if _WIN32_WINNT < _WIN32_WINNT_VISTA TCHAR dateStr[buffSize] = {0}; status = GetDateFormat(LOCALE_INVARIANT, 0, &systemTime, __TEXT("yyyy-MM-dd"), dateStr, buffSize); #else wchar_t dateStr[buffSize] = {0}; status = GetDateFormatEx(LOCALE_NAME_INVARIANT, 0, &systemTime, L"yyyy-MM-dd", dateStr, buffSize, NULL); #endif // _WIN32_WINNT < _WIN32_WINNT_VISTA if (status == 0) { throw utility::details::create_system_error(GetLastError()); } #if _WIN32_WINNT < _WIN32_WINNT_VISTA TCHAR timeStr[buffSize] = {0}; status = GetTimeFormat(LOCALE_INVARIANT, TIME_NOTIMEMARKER | TIME_FORCE24HOURFORMAT, &systemTime, __TEXT("HH':'mm':'ss"), timeStr, buffSize); #else wchar_t timeStr[buffSize] = {0}; status = GetTimeFormatEx(LOCALE_NAME_INVARIANT, TIME_NOTIMEMARKER | TIME_FORCE24HOURFORMAT, &systemTime, L"HH':'mm':'ss", timeStr, buffSize); #endif // _WIN32_WINNT < _WIN32_WINNT_VISTA if (status == 0) { throw utility::details::create_system_error(GetLastError()); } outStream << dateStr << "T" << timeStr; uint64_t frac_sec = largeInt.QuadPart % _secondTicks; if (frac_sec > 0) { // Append fractional second, which is a 7-digit value with no trailing zeros // This way, '1200' becomes '00012' char buf[9] = { 0 }; sprintf_s(buf, sizeof(buf), ".%07ld", (long int)frac_sec); // trim trailing zeros for (int i = 7; buf[i] == '0'; i--) buf[i] = '\0'; outStream << buf; } outStream << "Z"; } return outStream.str(); #else //LINUX uint64_t input = m_interval; uint64_t frac_sec = input % _secondTicks; input /= _secondTicks; // convert to seconds time_t time = (time_t)input - (time_t)11644473600LL;// diff between windows and unix epochs (seconds) struct tm datetime; #if defined(PAVO) gmtime_s(&time, &datetime); #else gmtime_r(&time, &datetime); #endif // PAVO const int max_dt_length = 64; char output[max_dt_length+1] = {0}; if (format != RFC_1123 && frac_sec > 0) { // Append fractional second, which is a 7-digit value with no trailing zeros // This way, '1200' becomes '00012' char buf[9] = { 0 }; snprintf(buf, sizeof(buf), ".%07ld", (long int)frac_sec); // trim trailing zeros for (int i = 7; buf[i] == '0'; i--) buf[i] = '\0'; // format the datetime into a separate buffer char datetime_str[max_dt_length+1] = {0}; strftime(datetime_str, sizeof(datetime_str), "%Y-%m-%dT%H:%M:%S", &datetime); // now print this buffer into the output buffer snprintf(output, sizeof(output), "%s%sZ", datetime_str, buf); } else { strftime(output, sizeof(output), format == RFC_1123 ? "%a, %d %b %Y %H:%M:%S GMT" : "%Y-%m-%dT%H:%M:%SZ", &datetime); } return std::string(output); #endif } #ifdef _WIN32 bool __cdecl datetime::system_type_to_datetime(void* pvsysTime, uint64_t seconds, datetime * pdt) { SYSTEMTIME* psysTime = (SYSTEMTIME*)pvsysTime; FILETIME fileTime; if (SystemTimeToFileTime(psysTime, &fileTime)) { ULARGE_INTEGER largeInt; largeInt.LowPart = fileTime.dwLowDateTime; largeInt.HighPart = fileTime.dwHighDateTime; // Add hundredths of nanoseconds largeInt.QuadPart += seconds; *pdt = datetime(largeInt.QuadPart); return true; } return false; } #endif // Take a string that represents a fractional second and return the number of ticks // This is equivalent to doing atof on the string and multiplying by 10000000, // but does not lose precision template uint64_t timeticks_from_second(StringIterator begin, StringIterator end) { int size = (int)(end - begin); _ASSERTE(begin[0] == _T('.')); uint64_t ufrac_second = 0; for (int i = 1; i <= 7; ++i) { ufrac_second *= 10; int add = i < size ? begin[i] - _T('0') : 0; ufrac_second += add; } return ufrac_second; } void extract_fractional_second(const utility::string_t& dateString, utility::string_t& resultString, uint64_t& ufrac_second) { resultString = dateString; // First, the string must be strictly longer than 2 characters, and the trailing character must be 'Z' if (resultString.size() > 2 && resultString[resultString.size() - 1] == _T('Z')) { // Second, find the last non-digit by scanning the string backwards auto last_non_digit = std::find_if_not(resultString.rbegin() + 1, resultString.rend(), is_digit); if (last_non_digit < resultString.rend() - 1) { // Finally, make sure the last non-digit is a dot: auto last_dot = last_non_digit.base() - 1; if (*last_dot == _T('.')) { // Got it! Now extract the fractional second auto last_before_Z = std::end(resultString) - 1; ufrac_second = timeticks_from_second(last_dot, last_before_Z); // And erase it from the string resultString.erase(last_dot, last_before_Z); } } } } datetime __cdecl datetime::from_string(const utility::string_t& dateString, date_format format) { // avoid floating point math to preserve precision uint64_t ufrac_second = 0; #ifdef _WIN32 datetime result; if (format == RFC_1123) { SYSTEMTIME sysTime = {0}; std::wstring month(3, L'\0'); std::wstring unused(3, L'\0'); const wchar_t * formatString = L"%3c, %2d %3c %4d %2d:%2d:%2d %3c"; auto n = swscanf_s(dateString.c_str(), formatString, unused.data(), unused.size(), &sysTime.wDay, month.data(), month.size(), &sysTime.wYear, &sysTime.wHour, &sysTime.wMinute, &sysTime.wSecond, unused.data(), unused.size()); if (n == 8) { std::wstring monthnames[12] = {L"Jan", L"Feb", L"Mar", L"Apr", L"May", L"Jun", L"Jul", L"Aug", L"Sep", L"Oct", L"Nov", L"Dec"}; auto loc = std::find_if(monthnames, monthnames+12, [&month](const std::wstring& m) { return m == month;}); if (loc != monthnames+12) { sysTime.wMonth = (short) ((loc - monthnames) + 1); if (system_type_to_datetime(&sysTime, ufrac_second, &result)) { return result; } } } } else if (format == ISO_8601) { // Unlike FILETIME, SYSTEMTIME does not have enough precision to hold seconds in 100 nanosecond // increments. Therefore, start with seconds and milliseconds set to 0, then add them separately // Try to extract the fractional second from the timestamp utility::string_t input; extract_fractional_second(dateString, input, ufrac_second); { SYSTEMTIME sysTime = { 0 }; const wchar_t * formatString = L"%4d-%2d-%2dT%2d:%2d:%2dZ"; auto n = swscanf_s(input.c_str(), formatString, &sysTime.wYear, &sysTime.wMonth, &sysTime.wDay, &sysTime.wHour, &sysTime.wMinute, &sysTime.wSecond); if (n == 3 || n == 6) { if (system_type_to_datetime(&sysTime, ufrac_second, &result)) { return result; } } } { SYSTEMTIME sysTime = {0}; DWORD date = 0; const wchar_t * formatString = L"%8dT%2d:%2d:%2dZ"; auto n = swscanf_s(input.c_str(), formatString, &date, &sysTime.wHour, &sysTime.wMinute, &sysTime.wSecond); if (n == 1 || n == 4) { sysTime.wDay = date % 100; date /= 100; sysTime.wMonth = date % 100; date /= 100; sysTime.wYear = (WORD)date; if (system_type_to_datetime(&sysTime, ufrac_second, &result)) { return result; } } } { SYSTEMTIME sysTime = {0}; GetSystemTime(&sysTime); // Fill date portion with today's information sysTime.wSecond = 0; sysTime.wMilliseconds = 0; const wchar_t * formatString = L"%2d:%2d:%2dZ"; auto n = swscanf_s(input.c_str(), formatString, &sysTime.wHour, &sysTime.wMinute, &sysTime.wSecond); if (n == 3) { if (system_type_to_datetime(&sysTime, ufrac_second, &result)) { return result; } } } } return datetime(); #else std::string input(dateString); struct tm output = tm(); if (format == RFC_1123) { strptime(input.data(), "%a, %d %b %Y %H:%M:%S GMT", &output); } else { // Try to extract the fractional second from the timestamp utility::string_t input; extract_fractional_second(dateString, input, ufrac_second); auto result = strptime(input.data(), "%Y-%m-%dT%H:%M:%SZ", &output); if (result == nullptr) { result = strptime(input.data(), "%Y%m%dT%H:%M:%SZ", &output); } if (result == nullptr) { // Fill the date portion with the epoch, // strptime will do the rest memset(&output, 0, sizeof(struct tm)); output.tm_year = 70; output.tm_mon = 1; output.tm_mday = 1; result = strptime(input.data(), "%H:%M:%SZ", &output); } if (result == nullptr) { result = strptime(input.data(), "%Y-%m-%d", &output); } if (result == nullptr) { result = strptime(input.data(), "%Y%m%d", &output); } if (result == nullptr) { return datetime(); } } #if (defined(ANDROID) || defined(__ANDROID__)) // HACK: The (nonportable?) POSIX function timegm is not available in // bionic. As a workaround[1][2], we set the C library timezone to // UTC, call mktime, then set the timezone back. However, the C // environment is fundamentally a shared global resource and thread- // unsafe. We can protect our usage here, however any other code might // manipulate the environment at the same time. // // [1] http://linux.die.net/man/3/timegm // [2] http://www.gnu.org/software/libc/manual/html_node/Broken_002ddown-Time.html time_t time; static std::mutex env_var_lock; { std::lock_guard lock(env_var_lock); std::string prev_env; auto prev_env_cstr = getenv("TZ"); if (prev_env_cstr != nullptr) { prev_env = prev_env_cstr; } setenv("TZ", "UTC", 1); time = mktime(&output); if (prev_env_cstr) { setenv("TZ", prev_env.c_str(), 1); } else { unsetenv("TZ"); } tzset(); } #else time_t time = timegm(&output); #endif struct timeval tv = timeval(); tv.tv_sec = time; auto result = timeval_to_datetime(tv); // fractional seconds are already in correct format so just add them. result = result + ufrac_second; return result; #endif } /// /// Converts a timespan/interval in seconds to xml duration string as specified by /// http://www.w3.org/TR/xmlschema-2/#duration /// utility::string_t __cdecl timespan::seconds_to_xml_duration(utility::seconds durationSecs) { auto numSecs = durationSecs.count(); // Find the number of minutes auto numMins = numSecs / 60; if (numMins > 0) { numSecs = numSecs % 60; } // Hours auto numHours = numMins / 60; if (numHours > 0) { numMins = numMins % 60; } // Days auto numDays = numHours / 24; if (numDays > 0) { numHours = numHours % 24; } // The format is: // PdaysDThoursHminutesMsecondsS utility::ostringstream_t oss; oss.imbue(std::locale::classic()); oss << _XPLATSTR("P"); if (numDays > 0) { oss << numDays << _XPLATSTR("D"); } oss << _XPLATSTR("T"); if (numHours > 0) { oss << numHours << _XPLATSTR("H"); } if (numMins > 0) { oss << numMins << _XPLATSTR("M"); } if (numSecs > 0) { oss << numSecs << _XPLATSTR("S"); } return oss.str(); } utility::seconds __cdecl timespan::xml_duration_to_seconds(const utility::string_t ×panString) { // The format is: // PnDTnHnMnS // if n == 0 then the field could be omitted // The final S could be omitted int64_t numSecs = 0; utility::istringstream_t is(timespanString); is.imbue(std::locale::classic()); auto eof = std::char_traits::eof(); std::basic_istream::int_type c; c = is.get(); // P while (c != eof) { int val = 0; c = is.get(); while (is_digit((utility::char_t)c)) { val = val * 10 + (c - L'0'); c = is.get(); if (c == '.') { // decimal point is not handled do { c = is.get(); } while(is_digit((utility::char_t)c)); } } if (c == L'D') numSecs += static_cast(val) * 24 * 3600; // days if (c == L'H') numSecs += static_cast(val) * 3600; // Hours if (c == L'M') numSecs += static_cast(val) * 60; // Minutes if (c == L'S' || c == eof) { numSecs += val; // seconds break; } } return utility::seconds(numSecs); } const utility::char_t * nonce_generator::c_allowed_chars = _XPLATSTR("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); utility::string_t nonce_generator::generate() { std::uniform_int_distribution<> distr(0, static_cast(ustrlen(c_allowed_chars) - 1)); utility::string_t result; result.reserve(length()); std::generate_n(std::back_inserter(result), length(), [&]() { return c_allowed_chars[distr(m_random)]; } ); return result; } } #if HC_PLATFORM_IS_MICROSOFT #pragma warning( pop ) #endif ================================================ FILE: Include/cpprestinclude/cpprest/details/base64.hpp ================================================ /*** * ==++== * * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * ==--== * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ * * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk * * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ****/ #pragma once using namespace web; using namespace utility; #if HC_PLATFORM_IS_MICROSOFT #pragma warning( push ) #pragma warning( disable : 28020 ) // ignore expression validation #pragma warning( disable : 4365 ) #endif std::vector _from_base64(const utility::string_t& str); utility::string_t _to_base64(const unsigned char *ptr, size_t size); std::vector __cdecl conversions::from_base64(const utility::string_t& str) { return _from_base64(str); } utility::string_t __cdecl conversions::to_base64(const unsigned char* data, size_t dataSize) { return _to_base64(data, dataSize); } utility::string_t __cdecl conversions::to_base64(const std::vector& input) { if (input.size() == 0) { // return empty string return utility::string_t(); } return _to_base64(&input[0], input.size()); } utility::string_t __cdecl conversions::to_base64(uint64_t input) { return _to_base64(reinterpret_cast(&input), sizeof(input)); } static const char* _base64_enctbl = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; const std::array _base64_dectbl = {{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, 255, 254, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255, 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 255, 255, 255, 255, 255 }}; struct _triple_byte { unsigned char _1_1 : 2; unsigned char _0 : 6; unsigned char _2_1 : 4; unsigned char _1_2 : 4; unsigned char _3 : 6; unsigned char _2_2 : 2; }; // // A note on the implementation of BASE64 encoding and decoding: // // This is a fairly basic and naive implementation; there is probably a lot of room for // performance improvement, as well as for adding options such as support for URI-safe base64, // ignoring CRLF, relaxed validation rules, etc. The decoder is currently pretty strict. // #ifdef __GNUC__ // gcc is concerned about the bitfield uses in the code, something we simply need to ignore. #pragma GCC diagnostic ignored "-Wconversion" #endif std::vector _from_base64(const utility::string_t& input) { std::vector result; if ( input.empty() ) return result; size_t padding = 0; // Validation { auto size = input.size(); if ( (size % 4) != 0 ) { throw std::runtime_error("length of base64 string is not an even multiple of 4"); } for (auto iter = input.begin(); iter != input.end(); ++iter,--size) { const size_t ch_sz = static_cast(*iter); if ( ch_sz >= _base64_dectbl.size() || _base64_dectbl[ch_sz] == 255 ) { throw std::runtime_error("invalid character found in base64 string"); } if ( _base64_dectbl[ch_sz] == 254 ) { padding++; // padding only at the end if ( size > 2 ) { throw std::runtime_error("invalid padding character found in base64 string"); } if ( size == 2 ) { const size_t ch2_sz = static_cast(*(iter+1)); if ( ch2_sz >= _base64_dectbl.size() || _base64_dectbl[ch2_sz] != 254 ) { throw std::runtime_error("invalid padding character found in base64 string"); } } } } } auto size = input.size(); const utility::char_t* ptr = &input[0]; auto outsz = (size / 4)*3; outsz -= padding; result.resize(outsz); size_t idx = 0; for (; size > 4; ++idx ) { unsigned char target[3]; memset(target, 0, sizeof(target)); _triple_byte* record = reinterpret_cast<_triple_byte*>(target); unsigned char val0 = _base64_dectbl[ptr[0]]; unsigned char val1 = _base64_dectbl[ptr[1]]; unsigned char val2 = _base64_dectbl[ptr[2]]; unsigned char val3 = _base64_dectbl[ptr[3]]; record->_0 = val0; record->_1_1 = val1 >> 4; result[idx] = target[0]; record->_1_2 = val1 & 0xF; record->_2_1 = val2 >> 2; result[++idx] = target[1]; record->_2_2 = val2 & 0x3; record->_3 = val3 & 0x3F; result[++idx] = target[2]; ptr += 4; size -= 4; } // Handle the last four bytes separately, to avoid having the conditional statements // in all the iterations (a performance issue). { unsigned char target[3]; memset(target, 0, sizeof(target)); _triple_byte* record = reinterpret_cast<_triple_byte*>(target); unsigned char val0 = _base64_dectbl[ptr[0]]; unsigned char val1 = _base64_dectbl[ptr[1]]; unsigned char val2 = _base64_dectbl[ptr[2]]; unsigned char val3 = _base64_dectbl[ptr[3]]; record->_0 = val0; record->_1_1 = val1 >> 4; result[idx] = target[0]; record->_1_2 = val1 & 0xF; if ( val2 != 254 ) { record->_2_1 = val2 >> 2; result[++idx] = target[1]; } else { // There shouldn't be any information (ones) in the unused bits, if ( record->_1_2 != 0 ) { throw std::runtime_error("Invalid end of base64 string"); } return result; } record->_2_2 = val2 & 0x3; if ( val3 != 254 ) { record->_3 = val3 & 0x3F; result[++idx] = target[2]; } else { // There shouldn't be any information (ones) in the unused bits. if ( record->_2_2 != 0 ) { throw std::runtime_error("Invalid end of base64 string"); } return result; } } return result; } utility::string_t _to_base64(const unsigned char *ptr, size_t size) { utility::string_t result; for (; size >= 3; ) { const _triple_byte* record = reinterpret_cast(ptr); unsigned char idx0 = record->_0; unsigned char idx1 = (record->_1_1 << 4) | record->_1_2; unsigned char idx2 = (record->_2_1 << 2) | record->_2_2; unsigned char idx3 = record->_3; result.push_back(utility::char_t(_base64_enctbl[idx0])); result.push_back(utility::char_t(_base64_enctbl[idx1])); result.push_back(utility::char_t(_base64_enctbl[idx2])); result.push_back(utility::char_t(_base64_enctbl[idx3])); size -= 3; ptr += 3; } switch(size) { case 1: { const _triple_byte* record = reinterpret_cast(ptr); unsigned char idx0 = record->_0; unsigned char idx1 = (record->_1_1 << 4); result.push_back(utility::char_t(_base64_enctbl[idx0])); result.push_back(utility::char_t(_base64_enctbl[idx1])); result.push_back('='); result.push_back('='); break; } case 2: { const _triple_byte* record = reinterpret_cast(ptr); unsigned char idx0 = record->_0; unsigned char idx1 = (record->_1_1 << 4) | record->_1_2; unsigned char idx2 = (record->_2_1 << 2); result.push_back(utility::char_t(_base64_enctbl[idx0])); result.push_back(utility::char_t(_base64_enctbl[idx1])); result.push_back(utility::char_t(_base64_enctbl[idx2])); result.push_back('='); break; } } return result; } #if HC_PLATFORM_IS_MICROSOFT #pragma warning( pop ) #endif ================================================ FILE: Include/cpprestinclude/cpprest/details/basic_types.h ================================================ /*** * Copyright (C) Microsoft. All rights reserved. * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. * * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ * * Platform-dependent type definitions * * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk * * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ****/ #pragma once #include #include #include #include #include "cpprest/details/cpprest_compat.h" #ifndef _WIN32 # define __STDC_LIMIT_MACROS # include #else #include #endif #include "cpprest/details/SafeInt3.hpp" namespace utility { #ifdef _WIN32 #define _UTF16_STRINGS #endif // We should be using a 64-bit size type for most situations that do // not involve specifying the size of a memory allocation or buffer. typedef uint64_t size64_t; #ifdef _UTF16_STRINGS // // On Windows, all strings are wide // typedef wchar_t char_t ; typedef std::wstring string_t; #define _XPLATSTR(x) L ## x typedef std::wostringstream ostringstream_t; typedef std::wofstream ofstream_t; typedef std::wostream ostream_t; typedef std::wistream istream_t; typedef std::wifstream ifstream_t; typedef std::wistringstream istringstream_t; typedef std::wstringstream stringstream_t; #define ucout std::wcout #define ucin std::wcin #define ucerr std::wcerr #define ustrlen wcslen #else // // On POSIX platforms, all strings are narrow // typedef char char_t; typedef std::string string_t; #define _XPLATSTR(x) x typedef std::ostringstream ostringstream_t; typedef std::ofstream ofstream_t; typedef std::ostream ostream_t; typedef std::istream istream_t; typedef std::ifstream ifstream_t; typedef std::istringstream istringstream_t; typedef std::stringstream stringstream_t; #define ucout std::cout #define ucin std::cin #define ucerr std::cerr #define ustrlen strlen #endif // endif _UTF16_STRINGS #ifndef _TURN_OFF_PLATFORM_STRING #define U(x) _XPLATSTR(x) #endif // !_TURN_OFF_PLATFORM_STRING }// namespace utility typedef char utf8char; typedef std::string utf8string; typedef std::stringstream utf8stringstream; typedef std::ostringstream utf8ostringstream; typedef std::ostream utf8ostream; typedef std::istream utf8istream; typedef std::istringstream utf8istringstream; #ifdef _UTF16_STRINGS typedef wchar_t utf16char; typedef std::wstring utf16string; typedef std::wstringstream utf16stringstream; typedef std::wostringstream utf16ostringstream; typedef std::wostream utf16ostream; typedef std::wistream utf16istream; typedef std::wistringstream utf16istringstream; #else typedef char16_t utf16char; typedef std::u16string utf16string; typedef std::basic_stringstream utf16stringstream; typedef std::basic_ostringstream utf16ostringstream; typedef std::basic_ostream utf16ostream; typedef std::basic_istream utf16istream; typedef std::basic_istringstream utf16istringstream; #endif #if defined(_WIN32) // Include on everything except Windows Desktop ARM, unless explicitly excluded. #if !defined(CPPREST_EXCLUDE_WEBSOCKETS) #if defined(WINAPI_FAMILY) #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && defined(_M_ARM) #define CPPREST_EXCLUDE_WEBSOCKETS #endif #else #if defined(_M_ARM) #define CPPREST_EXCLUDE_WEBSOCKETS #endif #endif #endif #endif ================================================ FILE: Include/cpprestinclude/cpprest/details/cpprest_compat.h ================================================ /*** * ==++== * * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * ==--== * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ * * Standard macros and definitions. * This header has minimal dependency on windows headers and is safe for use in the public API * * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk * * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ****/ #pragma once #if defined(_WIN32) // Settings specific to Windows #if _MSC_VER >= 1900 #define CPPREST_NOEXCEPT noexcept #else #define CPPREST_NOEXCEPT #endif #define CASABLANCA_UNREFERENCED_PARAMETER(x) (x) #include #else // End settings specific to Windows // Settings common to all but Windows #define __declspec(x) __attribute__ ((x)) #define dllimport #define novtable /* no novtable equivalent */ #define __assume(x) do { if (!(x)) __builtin_unreachable(); } while (false) #define CASABLANCA_UNREFERENCED_PARAMETER(x) (void)x #define CPPREST_NOEXCEPT noexcept #include #define _ASSERTE(x) assert(x) // No SAL on non Windows platforms #include "cpprest/details/nosal.h" #if not defined __cdecl #if defined cdecl #define __cdecl __attribute__ ((cdecl)) #else #define __cdecl #endif #if defined(__ANDROID__) // This is needed to disable the use of __thread inside the boost library. // Android does not support thread local storage -- if boost is included // without this macro defined, it will create references to __tls_get_addr // which (while able to link) will not be available at runtime and prevent // the .so from loading. #define BOOST_ASIO_DISABLE_THREAD_KEYWORD_EXTENSION #endif #ifdef __clang__ #include #endif #endif // defined(__APPLE__) #endif #ifdef _NO_ASYNCRTIMP #define _ASYNCRTIMP #else #ifdef _ASYNCRT_EXPORT #define _ASYNCRTIMP __declspec(dllexport) #else #define _ASYNCRTIMP __declspec(dllimport) #endif #endif #ifdef CASABLANCA_DEPRECATION_NO_WARNINGS #define CASABLANCA_DEPRECATED(x) #else #define CASABLANCA_DEPRECATED(x) __declspec(deprecated(x)) #endif ================================================ FILE: Include/cpprestinclude/cpprest/details/http_client_msg.hpp ================================================ #if !XSAPI_NO_PPL /*** * ==++== * * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * ==--== * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ * * HTTP Library: Request and reply message definitions (client side). * * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk * * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ****/ #pragma once #if HC_PLATFORM_IS_MICROSOFT #pragma warning( push ) #pragma warning( disable : 26444 ) // ignore various unnamed objects #endif namespace web { namespace http { uri details::_http_request::relative_uri() const { // If the listener path is empty, then just return the request URI. if(m_listener_path.empty() || m_listener_path == _XPLATSTR("/")) { return m_uri.resource(); } utility::string_t prefix = uri::decode(m_listener_path); utility::string_t path = uri::decode(m_uri.resource().to_string()); if(path.empty()) { path = _XPLATSTR("/"); } auto pos = path.find(prefix); if (pos == 0) { return uri(uri::encode_uri(path.erase(0, prefix.length()))); } else { throw http_exception(_XPLATSTR("Error: request was not prefixed with listener uri")); } } uri details::_http_request::absolute_uri() const { if (m_base_uri.is_empty()) { return m_uri; } else { return uri_builder(m_base_uri).append(m_uri).to_uri(); } } void details::_http_request::set_request_uri(const uri& relative) { m_uri = relative; } utility::string_t details::_http_request::to_string() const { utility::ostringstream_t buffer; buffer.imbue(std::locale::classic()); buffer << m_method << _XPLATSTR(" ") << (this->m_uri.is_empty() ? _XPLATSTR("/") : this->m_uri.to_string()) << _XPLATSTR(" HTTP/1.1\r\n"); buffer << http_msg_base::to_string(); return buffer.str(); } void details::_http_request::_record_body_data_for_retry(const concurrency::streams::istream &stream) { CASABLANCA_UNREFERENCED_PARAMETER(stream); if (!m_bodyTextRecorded && !m_bodyVectorRecorded) { m_onlySetBodyUsingStream = true; } } void details::_http_request::_record_body_data_for_retry(const std::vector &body_data) { m_bodyVector = body_data; m_bodyVectorRecorded = true; } void details::_http_request::_record_body_data_for_retry(const utf8string &body_text, const utf8string &content_type) { m_bodyText = body_text; m_contentType = content_type; m_bodyTextRecorded = true; } bool details::_http_request::_reset_body_for_retry() { if (m_onlySetBodyUsingStream) { return false; } if (m_bodyTextRecorded) { set_body(concurrency::streams::bytestream::open_istream(m_bodyText), m_bodyText.size(), m_contentType); } else if (m_bodyVectorRecorded) { set_body(concurrency::streams::bytestream::open_istream(m_bodyVector), m_bodyVector.size(), "application/octet-stream"); } return true; } utility::string_t details::_http_response::to_string() const { // If the user didn't explicitly set a reason phrase then we should have it default // if they used one of the standard known status codes. auto reason_phrase = m_reason_phrase; if(reason_phrase.empty()) { static http_status_to_phrase idToPhraseMap[] = { #define _PHRASES #define DAT(a,b,c) {status_codes::a, c}, #include "cpprest/details/http_constants.dat" #undef _PHRASES #undef DAT }; for( auto iter = std::begin(idToPhraseMap); iter != std::end(idToPhraseMap); ++iter) { if( iter->id == status_code() ) { reason_phrase = iter->phrase; break; } } } utility::ostringstream_t buffer; buffer.imbue(std::locale::classic()); buffer << _XPLATSTR("HTTP/1.1 ") << m_status_code << _XPLATSTR(" ") << reason_phrase << _XPLATSTR("\r\n"); buffer << http_msg_base::to_string(); return buffer.str(); } }} // namespace web::http #if HC_PLATFORM_IS_MICROSOFT #pragma warning( pop ) #endif #endif // !XSAPI_NO_PPL ================================================ FILE: Include/cpprestinclude/cpprest/details/http_helpers.h ================================================ /*** * ==++== * * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * ==--== * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ * * Implementation Details of the http.h layer of messaging * * Functions and types for interoperating with http.h from modern C++ * This file includes windows definitions and should not be included in a public header * * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk * * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ****/ #pragma once #include "cpprest/details/basic_types.h" namespace web { namespace http { namespace details { /// /// Helper function to get the default HTTP reason phrase for a status code. /// utility::string_t get_default_reason_phrase(status_code code); // simple helper functions to trim whitespace. _ASYNCRTIMP void __cdecl trim_whitespace(utility::string_t &str); bool validate_method(const utility::string_t& method); namespace chunked_encoding { // Transfer-Encoding: chunked support static const size_t additional_encoding_space = 12; static const size_t data_offset = additional_encoding_space-2; // Add the data necessary for properly sending data with transfer-encoding: chunked. // // There are up to 12 additional bytes needed for each chunk: // // The last chunk requires 5 bytes, and is fixed. // All other chunks require up to 8 bytes for the length, and four for the two CRLF // delimiters. // _ASYNCRTIMP size_t __cdecl add_chunked_delimiters(_Out_writes_(buffer_size) uint8_t *data, _In_ size_t buffer_size, size_t bytes_read); } }}} ================================================ FILE: Include/cpprestinclude/cpprest/details/http_helpers.hpp ================================================ #if !XSAPI_NO_PPL /*** * ==++== * * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * ==--== * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ * * Implementation Details of the http.h layer of messaging * * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk * * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ****/ #pragma once using namespace web; using namespace utility::conversions; namespace web { namespace http { namespace details { // Remove once VS 2013 is no longer supported. #if defined(_WIN32) && _MSC_VER < 1900 static const http_status_to_phrase idToPhraseMap [] = { #define _PHRASES #define DAT(a,b,c) {status_codes::a, c}, #include "cpprest/details/http_constants.dat" #undef _PHRASES #undef DAT }; #endif utility::string_t get_default_reason_phrase(status_code code) { #if !defined(_WIN32) || _MSC_VER >= 1900 // Future improvement: why is this stored as an array of structs instead of a map // indexed on the status code for faster lookup? // Not a big deal because it is uncommon to not include a reason phrase. static const http_status_to_phrase idToPhraseMap [] = { #define _PHRASES #define DAT(a,b,c) {status_codes::a, c}, #include "cpprest/details/http_constants.dat" #undef _PHRASES #undef DAT }; #endif utility::string_t phrase; for (const auto &elm : idToPhraseMap) { if (elm.id == code) { phrase = elm.phrase; break; } } return phrase; } static void ltrim_whitespace(utility::string_t &str) { size_t index; for (index = 0; index < str.size() && isspace(str[index]); ++index); str.erase(0, index); } static void rtrim_whitespace(utility::string_t &str) { size_t index; for (index = str.size(); index > 0 && isspace(str[index - 1]); --index); str.erase(index); } void trim_whitespace(utility::string_t &str) { ltrim_whitespace(str); rtrim_whitespace(str); } size_t chunked_encoding::add_chunked_delimiters(_Out_writes_(buffer_size) uint8_t *data, _In_ size_t buffer_size, size_t bytes_read) { size_t offset = 0; if (buffer_size < bytes_read + http::details::chunked_encoding::additional_encoding_space) { throw http_exception(_XPLATSTR("Insufficient buffer size.")); } if (bytes_read == 0) { offset = 7; data[7] = '0'; data[8] = '\r'; data[9] = '\n'; // The end of the size. data[10] = '\r'; data[11] = '\n'; // The end of the message. } else { char buffer[9]; #ifdef _WIN32 #pragma warning(suppress: 4777) sprintf_s(buffer, sizeof(buffer), "%8IX", bytes_read); #else snprintf(buffer, sizeof(buffer), "%8zX", bytes_read); #endif memcpy(&data[0], buffer, 8); while (data[offset] == ' ') ++offset; data[8] = '\r'; data[9] = '\n'; // The end of the size. data[10 + bytes_read] = '\r'; data[11 + bytes_read] = '\n'; // The end of the chunk. } return offset; } #if (!defined(_WIN32) || defined(__cplusplus_winrt)) const std::array valid_chars = {{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0-15 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //16-31 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, //32-47 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, //48-63 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, //64-79 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, //80-95 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, //96-111 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0 //112-127 }}; // Checks if the method contains any invalid characters bool validate_method(const utility::string_t& method) { for (const auto &ch : method) { size_t ch_sz = static_cast(ch); if (ch_sz >= 128) return false; if (!valid_chars[ch_sz]) return false; } return true; } #endif } // namespace details }} // namespace web::http #endif // !XSAPI_NO_PPL ================================================ FILE: Include/cpprestinclude/cpprest/details/http_msg.hpp ================================================ #if !XSAPI_NO_PPL /*** * ==++== * * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * ==--== * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ * * HTTP Library: Request and reply message definitions. * * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk * * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ****/ #pragma once #include "cpprest/details/http_helpers.h" #include "cpprest/producerconsumerstream.h" #include using namespace web; using namespace concurrency; using namespace utility::conversions; using namespace http::details; #if HC_PLATFORM_IS_MICROSOFT #pragma warning( push ) #pragma warning( disable : 26444 ) // ignore various unnamed objects #endif namespace web { namespace http { #define CRLF _XPLATSTR("\r\n") utility::string_t http_headers::content_type() const { utility::string_t result; match(http::header_names::content_type, result); return result; } /// Helper functions to convert a series of bytes from a charset to utf-8 or utf-16. /// These APIs deal with checking for and handling byte order marker (BOM). namespace { enum endianness { little_endian, big_endian, unknown }; endianness check_byte_order_mark(const utf16string &str) { if (str.empty()) { return unknown; } const unsigned char *src = reinterpret_cast(str.data()); // little endian if (src[0] == 0xFF && src[1] == 0xFE) { return little_endian; } // big endian else if (src[0] == 0xFE && src[1] == 0xFF) { return big_endian; } return unknown; } std::string convert_utf16le_to_utf8(utf16string src, bool erase_bom) { if (erase_bom && !src.empty()) { src.erase(0, 1); } return utf16_to_utf8(std::move(src)); } utility::string_t convert_utf16le_to_string_t(utf16string src, bool erase_bom) { if (erase_bom && !src.empty()) { src.erase(0, 1); } #ifdef _UTF16_STRINGS return src; #else return utf16_to_utf8(std::move(src)); #endif } // Helper function to change endian ness from big endian to little endian utf16string big_endian_to_little_endian(utf16string src, bool erase_bom) { if (erase_bom && !src.empty()) { src.erase(0, 1); } if (src.empty()) { return src; } const size_t size = src.size(); for (size_t i = 0; i < size; ++i) { utf16char ch = src[i]; src[i] = static_cast(ch << 8); src[i] = static_cast(src[i] | ch >> 8); } return src; } std::string convert_utf16be_to_utf8(utf16string src, bool erase_bom) { return utf16_to_utf8(big_endian_to_little_endian(std::move(src), erase_bom)); } utf16string convert_utf16be_to_utf16le(utf16string src, bool erase_bom) { return big_endian_to_little_endian(std::move(src), erase_bom); } utility::string_t convert_utf16be_to_string_t(utf16string src, bool erase_bom) { #ifdef _UTF16_STRINGS return convert_utf16be_to_utf16le(std::move(src), erase_bom); #else return convert_utf16be_to_utf8(std::move(src), erase_bom); #endif } std::string convert_utf16_to_utf8(utf16string src) { const endianness endian = check_byte_order_mark(src); switch (endian) { case little_endian: return convert_utf16le_to_utf8(std::move(src), true); case big_endian: return convert_utf16be_to_utf8(std::move(src), true); case unknown: // unknown defaults to big endian. return convert_utf16be_to_utf8(std::move(src), false); } __assume(0); } utf16string convert_utf16_to_utf16(utf16string src) { const endianness endian = check_byte_order_mark(src); switch (endian) { case little_endian: src.erase(0, 1); return src; case big_endian: return convert_utf16be_to_utf16le(std::move(src), true); case unknown: // unknown defaults to big endian. return convert_utf16be_to_utf16le(std::move(src), false); } __assume(0); } utility::string_t convert_utf16_to_string_t(utf16string src) { #ifdef _UTF16_STRINGS return convert_utf16_to_utf16(std::move(src)); #else return convert_utf16_to_utf8(std::move(src)); #endif } } void http_headers::set_content_type(utility::string_t type) { m_headers[http::header_names::content_type] = std::move(type); } utility::string_t http_headers::cache_control() const { utility::string_t result; match(http::header_names::cache_control, result); return result; } void http_headers::set_cache_control(utility::string_t control) { add(http::header_names::cache_control, std::move(control)); } utility::string_t http_headers::date() const { utility::string_t result; match(http::header_names::date, result); return result; } void http_headers::set_date(const utility::datetime& date) { m_headers[http::header_names::date] = date.to_string(utility::datetime::RFC_1123); } utility::size64_t http_headers::content_length() const { utility::size64_t length = 0; match(http::header_names::content_length, length); return length; } void http_headers::set_content_length(utility::size64_t length) { m_headers[http::header_names::content_length] = utility::conversions::print_string(length, std::locale::classic()); } namespace details { utility::string_t flatten_http_headers(const http_headers &headers) { utility::string_t flattened_headers; for (auto iter = headers.begin(); iter != headers.end(); ++iter) { flattened_headers.append(iter->first); flattened_headers.push_back(':'); flattened_headers.append(iter->second); flattened_headers.append(CRLF); } return flattened_headers; } #if defined(_WIN32) void parse_headers_string(_Inout_z_ utf16char *headersStr, http_headers &headers) { utf16char *context = nullptr; utf16char *line = wcstok_s(headersStr, CRLF, &context); while (line != nullptr) { const utility::string_t header_line(line); const size_t colonIndex = header_line.find_first_of(_XPLATSTR(":")); if (colonIndex != utility::string_t::npos) { utility::string_t key = header_line.substr(0, colonIndex); utility::string_t value = header_line.substr(colonIndex + 1, header_line.length() - colonIndex - 1); http::details::trim_whitespace(key); http::details::trim_whitespace(value); headers.add(key, value); } line = wcstok_s(nullptr, CRLF, &context); } } #endif } static const utility::char_t * stream_was_set_explicitly = _XPLATSTR("A stream was set on the message and extraction is not possible"); static const utility::char_t * unsupported_charset = _XPLATSTR("Charset must be iso-8859-1, utf-8, utf-16, utf-16le, or utf-16be to be extracted."); http_msg_base::http_msg_base() : m_headers(), m_default_outstream(false) { } void http_msg_base::_prepare_to_receive_data() { // See if the user specified an outstream if (!outstream()) { // The user did not specify an outstream. // We will create one... concurrency::streams::producer_consumer_buffer buf; set_outstream(buf.create_ostream(), true); // Since we are creating the streambuffer, set the input stream // so that the user can retrieve the data. set_instream(buf.create_istream()); } // If the user did specify an outstream we leave the instream // as invalid. It is assumed that user either has a read head // to the out streambuffer or the data is streamed into a container // or media (like file) that the user can read from... } size_t http_msg_base::_get_content_length() { // An invalid response_stream indicates that there is no body if ((bool)instream()) { size_t content_length = 0; utility::string_t transfer_encoding; bool has_cnt_length = headers().match(header_names::content_length, content_length); bool has_xfr_encode = headers().match(header_names::transfer_encoding, transfer_encoding); if (has_xfr_encode) { return SIZE_MAX; } if (has_cnt_length) { return content_length; } // Neither is set. Assume transfer-encoding for now (until we have the ability to determine // the length of the stream). headers().add(header_names::transfer_encoding, _XPLATSTR("chunked")); return SIZE_MAX; } return 0; } // Helper function to inline continuation if possible. struct inline_continuation { inline_continuation(pplx::task &prev, const std::function)> &next) : m_prev(prev), m_next(next) {} ~inline_continuation() { if (m_prev.is_done()) { m_next(m_prev); } else { m_prev.then(m_next); } } pplx::task & m_prev; std::function)> m_next; private: inline_continuation(const inline_continuation &); inline_continuation &operator=(const inline_continuation &); }; void http_msg_base::_complete(utility::size64_t body_size, const std::exception_ptr &exceptionPtr) { const auto &completionEvent = _get_data_available(); auto closeTask = pplx::task_from_result(); if (exceptionPtr == std::exception_ptr()) { if (m_default_outstream) { closeTask = outstream().close(); } inline_continuation(closeTask, [completionEvent, body_size](pplx::task t) { try { t.get(); completionEvent.set(body_size); } catch (...) { // If close throws an exception report back to user. completionEvent.set_exception(std::current_exception()); pplx::create_task(completionEvent).then([](pplx::task t) { try { t.get(); } catch (...) {} }); } }); } else { if (outstream().is_valid()) { closeTask = outstream().close(exceptionPtr); } inline_continuation(closeTask, [completionEvent, exceptionPtr](pplx::task t) { // If closing stream throws an exception ignore since we already have an error. try { t.get(); } catch (...) {} completionEvent.set_exception(exceptionPtr); pplx::create_task(completionEvent).then([](pplx::task t) { try { t.get(); } catch (...) {} }); }); } } static bool is_content_type_one_of(const utility::string_t *first, const utility::string_t *last, const utility::string_t &value) { while (first != last) { if (utility::details::str_icmp(*first, value)) { return true; } ++first; } return false; } // Remove once VS 2013 is no longer supported. #if defined(_WIN32) && _MSC_VER < 1900 // Not referring to mime_types to avoid static initialization order fiasco. static const utility::string_t textual_types [] = { U("message/http"), U("application/json"), U("application/xml"), U("application/atom+xml"), U("application/http"), U("application/x-www-form-urlencoded") }; #endif /// /// Determines whether or not the given content type is 'textual' according the feature specifications. /// static bool is_content_type_textual(const utility::string_t &content_type) { #if !defined(_WIN32) || _MSC_VER >= 1900 static const utility::string_t textual_types [] = { mime_types::message_http, mime_types::application_json, mime_types::application_xml, mime_types::application_atom_xml, mime_types::application_http, mime_types::application_x_www_form_urlencoded }; #endif if (content_type.size() >= 4 && utility::details::str_icmp(content_type.substr(0, 4), _XPLATSTR("text"))) { return true; } return (is_content_type_one_of(std::begin(textual_types), std::end(textual_types), content_type)); } // Remove once VS 2013 is no longer supported. #if defined(_WIN32) && _MSC_VER < 1900 // Not referring to mime_types to avoid static initialization order fiasco. static const utility::string_t json_types [] = { U("application/json"), U("application/x-json"), U("text/json"), U("text/x-json"), U("text/javascript"), U("text/x-javascript"), U("application/javascript"), U("application/x-javascript") }; #endif /// /// Determines whether or not the given content type is JSON according the feature specifications. /// static bool is_content_type_json(const utility::string_t &content_type) { #if !defined(_WIN32) || _MSC_VER >= 1900 static const utility::string_t json_types [] = { mime_types::application_json, mime_types::application_xjson, mime_types::text_json, mime_types::text_xjson, mime_types::text_javascript, mime_types::text_xjavascript, mime_types::application_javascript, mime_types::application_xjavascript }; #endif return (is_content_type_one_of(std::begin(json_types), std::end(json_types), content_type)); } /// /// Gets the default charset for given content type. If the MIME type is not textual or recognized Latin1 will be returned. /// static utility::string_t get_default_charset(const utility::string_t &content_type) { // We are defaulting everything to Latin1 except JSON which is utf-8. if (is_content_type_json(content_type)) { return charset_types::utf8; } else { return charset_types::latin1; } } /// /// Parses the given Content-Type header value to get out actual content type and charset. /// If the charset isn't specified the default charset for the content type will be set. /// static void parse_content_type_and_charset(const utility::string_t &content_type, utility::string_t &content, utility::string_t &charset) { const size_t semi_colon_index = content_type.find_first_of(_XPLATSTR(";")); // No charset specified. if (semi_colon_index == utility::string_t::npos) { content = content_type; trim_whitespace(content); charset = get_default_charset(content); return; } // Split into content type and second part which could be charset. content = content_type.substr(0, semi_colon_index); trim_whitespace(content); utility::string_t possible_charset = content_type.substr(semi_colon_index + 1); trim_whitespace(possible_charset); const size_t equals_index = possible_charset.find_first_of(_XPLATSTR("=")); // No charset specified. if (equals_index == utility::string_t::npos) { charset = get_default_charset(content); return; } // Split and make sure 'charset' utility::string_t charset_key = possible_charset.substr(0, equals_index); trim_whitespace(charset_key); if (!utility::details::str_icmp(charset_key, _XPLATSTR("charset"))) { charset = get_default_charset(content); return; } charset = possible_charset.substr(equals_index + 1); // Remove the redundant ';' at the end of charset. while (charset.back() == ';') { charset.pop_back(); } trim_whitespace(charset); if (charset.front() == _XPLATSTR('"') && charset.back() == _XPLATSTR('"')) { charset = charset.substr(1, charset.size() - 2); trim_whitespace(charset); } } utility::string_t details::http_msg_base::parse_and_check_content_type(bool ignore_content_type, const std::function &check_content_type) { if (!instream()) { throw http_exception(stream_was_set_explicitly); } utility::string_t content, charset = charset_types::utf8; if (!ignore_content_type) { parse_content_type_and_charset(headers().content_type(), content, charset); // If no Content-Type or empty body then just return an empty string. if (content.empty() || instream().streambuf().in_avail() == 0) { return utility::string_t(); } if (!check_content_type(content)) { return utility::string_t(); } } return charset; } utf8string details::http_msg_base::extract_utf8string(bool ignore_content_type) { const auto &charset = parse_and_check_content_type(ignore_content_type, is_content_type_textual); if (charset.empty()) { return utf8string(); } auto buf_r = instream().streambuf(); // Perform the correct character set conversion if one is necessary. if (utility::details::str_icmp(charset, charset_types::utf8) || utility::details::str_icmp(charset, charset_types::usascii) || utility::details::str_icmp(charset, charset_types::ascii)) { std::string body; body.resize((std::string::size_type)buf_r.in_avail()); buf_r.getn(const_cast(reinterpret_cast(body.data())), body.size()).get(); // There is no risk of blocking. return body; } // Latin1 else if (utility::details::str_icmp(charset, charset_types::latin1)) { std::string body; body.resize((std::string::size_type)buf_r.in_avail()); buf_r.getn(const_cast(reinterpret_cast(body.data())), body.size()).get(); // There is no risk of blocking. return latin1_to_utf8(std::move(body)); } // utf-16 else if (utility::details::str_icmp(charset, charset_types::utf16)) { utf16string body; body.resize(buf_r.in_avail() / sizeof(utf16string::value_type)); buf_r.getn(const_cast(reinterpret_cast(body.data())), body.size() * sizeof(utf16string::value_type)); // There is no risk of blocking. return convert_utf16_to_utf8(std::move(body)); } // utf-16le else if (utility::details::str_icmp(charset, charset_types::utf16le)) { utf16string body; body.resize(buf_r.in_avail() / sizeof(utf16string::value_type)); buf_r.getn(const_cast(reinterpret_cast(body.data())), body.size() * sizeof(utf16string::value_type)); // There is no risk of blocking. return utility::conversions::utf16_to_utf8(std::move(body)); } // utf-16be else if (utility::details::str_icmp(charset, charset_types::utf16be)) { utf16string body; body.resize(buf_r.in_avail() / sizeof(utf16string::value_type)); buf_r.getn(const_cast(reinterpret_cast(body.data())), body.size() * sizeof(utf16string::value_type)); // There is no risk of blocking. return convert_utf16be_to_utf8(std::move(body), false); } else { throw http_exception(unsupported_charset); } } utf16string details::http_msg_base::extract_utf16string(bool ignore_content_type) { const auto &charset = parse_and_check_content_type(ignore_content_type, is_content_type_textual); if (charset.empty()) { return utf16string(); } auto buf_r = instream().streambuf(); // Perform the correct character set conversion if one is necessary. if (utility::details::str_icmp(charset, charset_types::utf16le)) { utf16string body; body.resize(buf_r.in_avail() / sizeof(utf16string::value_type)); buf_r.getn(const_cast(reinterpret_cast(body.data())), body.size() * sizeof(utf16string::value_type)); // There is no risk of blocking. return body; } // utf-8, ascii else if (utility::details::str_icmp(charset, charset_types::utf8) || utility::details::str_icmp(charset, charset_types::usascii) || utility::details::str_icmp(charset, charset_types::ascii)) { std::string body; body.resize((std::string::size_type)buf_r.in_avail()); buf_r.getn(const_cast(reinterpret_cast(body.data())), body.size()).get(); // There is no risk of blocking. return utility::conversions::utf8_to_utf16(std::move(body)); } // Latin1 else if (utility::details::str_icmp(charset, charset_types::latin1)) { std::string body; body.resize((std::string::size_type)buf_r.in_avail()); buf_r.getn(const_cast(reinterpret_cast(body.data())), body.size()).get(); // There is no risk of blocking. return latin1_to_utf16(std::move(body)); } // utf-16 else if (utility::details::str_icmp(charset, charset_types::utf16)) { utf16string body; body.resize(buf_r.in_avail() / sizeof(utf16string::value_type)); buf_r.getn(const_cast(reinterpret_cast(body.data())), body.size() * sizeof(utf16string::value_type)); // There is no risk of blocking. return convert_utf16_to_utf16(std::move(body)); } // utf-16be else if (utility::details::str_icmp(charset, charset_types::utf16be)) { utf16string body; body.resize(buf_r.in_avail() / sizeof(utf16string::value_type)); buf_r.getn(const_cast(reinterpret_cast(body.data())), body.size() * sizeof(utf16string::value_type)); // There is no risk of blocking. return convert_utf16be_to_utf16le(std::move(body), false); } else { throw http_exception(unsupported_charset); } } utility::string_t details::http_msg_base::extract_string(bool ignore_content_type) { const auto &charset = parse_and_check_content_type(ignore_content_type, is_content_type_textual); if (charset.empty()) { return utility::string_t(); } auto buf_r = instream().streambuf(); // Perform the correct character set conversion if one is necessary. if (utility::details::str_icmp(charset, charset_types::usascii) || utility::details::str_icmp(charset, charset_types::ascii)) { std::string body; body.resize((std::string::size_type)buf_r.in_avail()); buf_r.getn(const_cast(reinterpret_cast(body.data())), body.size()).get(); // There is no risk of blocking. return to_string_t(std::move(body)); } // Latin1 if(utility::details::str_icmp(charset, charset_types::latin1)) { std::string body; body.resize((std::string::size_type)buf_r.in_avail()); buf_r.getn(const_cast(reinterpret_cast(body.data())), body.size()).get(); // There is no risk of blocking. // Could optimize for linux in the future if a latin1_to_utf8 function was written. return to_string_t(latin1_to_utf16(std::move(body))); } // utf-8. else if(utility::details::str_icmp(charset, charset_types::utf8)) { std::string body; body.resize((std::string::size_type)buf_r.in_avail()); buf_r.getn(const_cast(reinterpret_cast(body.data())), body.size()).get(); // There is no risk of blocking. return to_string_t(std::move(body)); } // utf-16. else if(utility::details::str_icmp(charset, charset_types::utf16)) { utf16string body; body.resize(buf_r.in_avail() / sizeof(utf16string::value_type)); buf_r.getn(const_cast(reinterpret_cast(body.data())), body.size() * sizeof(utf16string::value_type)); // There is no risk of blocking. return convert_utf16_to_string_t(std::move(body)); } // utf-16le else if(utility::details::str_icmp(charset, charset_types::utf16le)) { utf16string body; body.resize(buf_r.in_avail() / sizeof(utf16string::value_type)); buf_r.getn(const_cast(reinterpret_cast(body.data())), body.size() * sizeof(utf16string::value_type)); // There is no risk of blocking. return convert_utf16le_to_string_t(std::move(body), false); } // utf-16be else if(utility::details::str_icmp(charset, charset_types::utf16be)) { utf16string body; body.resize(buf_r.in_avail() / sizeof(utf16string::value_type)); buf_r.getn(const_cast(reinterpret_cast(body.data())), body.size() * sizeof(utf16string::value_type)); // There is no risk of blocking. return convert_utf16be_to_string_t(std::move(body), false); } else { throw http_exception(unsupported_charset); } } json::value details::http_msg_base::_extract_json(bool ignore_content_type) { const auto &charset = parse_and_check_content_type(ignore_content_type, is_content_type_json); if (charset.empty()) { return json::value(extract_string(ignore_content_type)); } auto buf_r = instream().streambuf(); // Latin1 if(utility::details::str_icmp(charset, charset_types::latin1)) { std::string body; body.resize(buf_r.in_avail()); buf_r.getn(const_cast(reinterpret_cast(body.data())), body.size()).get(); // There is no risk of blocking. // On Linux could optimize in the future if a latin1_to_utf8 function is written. return json::value::parse(to_string_t(latin1_to_utf16(std::move(body)))); } // utf-8, usascii and ascii else if(utility::details::str_icmp(charset, charset_types::utf8) || utility::details::str_icmp(charset, charset_types::usascii) || utility::details::str_icmp(charset, charset_types::ascii)) { std::string body; body.resize(buf_r.in_avail()); buf_r.getn(const_cast(reinterpret_cast(body.data())), body.size()).get(); // There is no risk of blocking. return json::value::parse(to_string_t(std::move(body))); } // utf-16. else if(utility::details::str_icmp(charset, charset_types::utf16)) { utf16string body; body.resize(buf_r.in_avail() / sizeof(utf16string::value_type)); buf_r.getn(const_cast(reinterpret_cast(body.data())), body.size() * sizeof(utf16string::value_type)); // There is no risk of blocking. return json::value::parse(convert_utf16_to_string_t(std::move(body))); } // utf-16le else if(utility::details::str_icmp(charset, charset_types::utf16le)) { utf16string body; body.resize(buf_r.in_avail() / sizeof(utf16string::value_type)); buf_r.getn(const_cast(reinterpret_cast(body.data())), body.size() * sizeof(utf16string::value_type)); // There is no risk of blocking. return json::value::parse(convert_utf16le_to_string_t(std::move(body), false)); } // utf-16be else if(utility::details::str_icmp(charset, charset_types::utf16be)) { utf16string body; body.resize(buf_r.in_avail() / sizeof(utf16string::value_type)); buf_r.getn(const_cast(reinterpret_cast(body.data())), body.size() * sizeof(utf16string::value_type)); // There is no risk of blocking. return json::value::parse(convert_utf16be_to_string_t(std::move(body), false)); } else { throw http_exception(unsupported_charset); } } std::vector details::http_msg_base::_extract_vector() { if (!instream()) { throw http_exception(stream_was_set_explicitly); } std::vector body; auto buf_r = instream().streambuf(); const size_t size = buf_r.in_avail(); body.resize(size); buf_r.getn(const_cast(reinterpret_cast(body.data())), size).get(); // There is no risk of blocking. return body; } // Helper function to convert message body without extracting. static utility::string_t convert_body_to_string_t(const utility::string_t &content_type, concurrency::streams::istream instream) { if (!instream) { // The instream is not yet set return utility::string_t(); } concurrency::streams::streambuf streambuf = instream.streambuf(); _ASSERTE((bool)streambuf); _ASSERTE(streambuf.is_open()); _ASSERTE(streambuf.can_read()); utility::string_t content, charset; parse_content_type_and_charset(content_type, content, charset); // Content-Type must have textual type. if(!is_content_type_textual(content) || streambuf.in_avail() == 0) { return utility::string_t(); } // Latin1 if(utility::details::str_icmp(charset, charset_types::latin1)) { std::string body; body.resize(streambuf.in_avail()); if(streambuf.scopy((unsigned char *)&body[0], body.size()) == 0) return utility::string_t(); return to_string_t(latin1_to_utf16(std::move(body))); } // utf-8. else if(utility::details::str_icmp(charset, charset_types::utf8)) { std::string body; body.resize(streambuf.in_avail()); if(streambuf.scopy((unsigned char *)&body[0], body.size()) == 0) return utility::string_t(); return to_string_t(std::move(body)); } // utf-16. else if(utility::details::str_icmp(charset, charset_types::utf16)) { utf16string body; body.resize(streambuf.in_avail() / sizeof(utf16string::value_type)); if(streambuf.scopy((unsigned char *)&body[0], body.size() * sizeof(utf16string::value_type)) == 0) return utility::string_t(); return convert_utf16_to_string_t(std::move(body)); } // utf-16le else if(utility::details::str_icmp(charset, charset_types::utf16le)) { utf16string body; body.resize(streambuf.in_avail() / sizeof(utf16string::value_type)); if(streambuf.scopy((unsigned char *)&body[0], body.size() * sizeof(utf16string::value_type)) == 0) return utility::string_t(); return convert_utf16le_to_string_t(std::move(body), false); } // utf-16be else if(utility::details::str_icmp(charset, charset_types::utf16be)) { utf16string body; body.resize(streambuf.in_avail() / sizeof(utf16string::value_type)); if(streambuf.scopy((unsigned char *)&body[0], body.size() * sizeof(utf16string::value_type)) == 0) return utility::string_t(); return convert_utf16be_to_string_t(std::move(body), false); } else { return utility::string_t(); } } // // Helper function to generate a wstring from given http_headers and message body. // static utility::string_t http_headers_body_to_string(const http_headers &headers, concurrency::streams::istream instream) { utility::ostringstream_t buffer; buffer.imbue(std::locale::classic()); for (const auto &header : headers) { buffer << header.first << _XPLATSTR(": ") << header.second << CRLF; } buffer << CRLF; utility::string_t content_type; if(headers.match(http::header_names::content_type, content_type)) { buffer << convert_body_to_string_t(content_type, instream); } return buffer.str(); } utility::string_t details::http_msg_base::to_string() const { return http_headers_body_to_string(m_headers, instream()); } static void set_content_type_if_not_present(http::http_headers &headers, const utility::string_t &content_type) { utility::string_t temp; if(!headers.match(http::header_names::content_type, temp)) { headers.add(http::header_names::content_type, content_type); } } void details::http_msg_base::set_body(const streams::istream &instream, const utf8string &contentType) { set_content_type_if_not_present( headers(), #ifdef _UTF16_STRINGS utility::conversions::utf8_to_utf16(contentType)); #else contentType); #endif set_instream(instream); } void details::http_msg_base::set_body(const streams::istream &instream, const utf16string &contentType) { set_content_type_if_not_present( headers(), #ifdef _UTF16_STRINGS contentType); #else utility::conversions::utf16_to_utf8(contentType)); #endif set_instream(instream); } void details::http_msg_base::set_body(const streams::istream &instream, utility::size64_t contentLength, const utf8string &contentType) { headers().set_content_length(contentLength); set_body(instream, contentType); m_data_available.set(contentLength); } void details::http_msg_base::set_body(const concurrency::streams::istream &instream, utility::size64_t contentLength, const utf16string &contentType) { headers().set_content_length(contentLength); set_body(instream, contentType); m_data_available.set(contentLength); } details::_http_request::_http_request(http::method mtd) : m_method(std::move(mtd)), m_initiated_response(0), m_server_context(), m_cancellationToken(pplx::cancellation_token::none()), m_bodyTextRecorded(false), m_bodyVectorRecorded(false), m_onlySetBodyUsingStream(false) { if(m_method.empty()) { throw std::invalid_argument("Invalid HTTP method specified. Method can't be an empty string."); } } details::_http_request::_http_request(std::unique_ptr server_context) : m_initiated_response(0), m_server_context(std::move(server_context)), m_cancellationToken(pplx::cancellation_token::none()), m_bodyTextRecorded(false), m_bodyVectorRecorded(false), m_onlySetBodyUsingStream(false) { } #define _METHODS #define DAT(a,b) const method methods::a = b; #include "cpprest/details/http_constants.dat" #undef _METHODS #undef DAT #define _HEADER_NAMES #define DAT(a,b) const utility::string_t header_names::a = _XPLATSTR(b); #include "cpprest/details/http_constants.dat" #undef _HEADER_NAMES #undef DAT #define _MIME_TYPES #define DAT(a,b) const utility::string_t mime_types::a = _XPLATSTR(b); #include "cpprest/details/http_constants.dat" #undef _MIME_TYPES #undef DAT #define _CHARSET_TYPES #define DAT(a,b) const utility::string_t charset_types::a = _XPLATSTR(b); #include "cpprest/details/http_constants.dat" #undef _CHARSET_TYPES #undef DAT // This is necessary for Linux because of a bug in GCC 4.7 #ifndef _WIN32 #define _PHRASES #define DAT(a,b,c) const status_code status_codes::a; #include "cpprest/details/http_constants.dat" #undef _PHRASES #undef DAT #endif }} // namespace web::http #if HC_PLATFORM_IS_MICROSOFT #pragma warning( pop ) #endif #endif // !XSAPI_NO_PPL ================================================ FILE: Include/cpprestinclude/cpprest/details/json.hpp ================================================ /*** * Copyright (C) Microsoft. All rights reserved. * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. * * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ * * HTTP Library: JSON parser and writer * * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk * * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ****/ #pragma once using namespace web; bool json::details::g_keep_json_object_unsorted = false; void json::keep_object_element_order(bool keep_order) { json::details::g_keep_json_object_unsorted = keep_order; } utility::ostream_t& web::json::operator << (utility::ostream_t &os, const web::json::value &val) { val.serialize(os); return os; } utility::istream_t& web::json::operator >> (utility::istream_t &is, json::value &val) { val = json::value::parse(is); return is; } web::json::value::value() : m_value(utility::details::make_unique()) #ifdef ENABLE_JSON_VALUE_VISUALIZER ,m_kind(value::Null) #endif { } web::json::value::value(int32_t value) : m_value(utility::details::make_unique(value)) #ifdef ENABLE_JSON_VALUE_VISUALIZER ,m_kind(value::Number) #endif { } web::json::value::value(uint32_t value) : m_value(utility::details::make_unique(value)) #ifdef ENABLE_JSON_VALUE_VISUALIZER ,m_kind(value::Number) #endif { } web::json::value::value(int64_t value) : m_value(utility::details::make_unique(value)) #ifdef ENABLE_JSON_VALUE_VISUALIZER ,m_kind(value::Number) #endif { } web::json::value::value(uint64_t value) : m_value(utility::details::make_unique(value)) #ifdef ENABLE_JSON_VALUE_VISUALIZER ,m_kind(value::Number) #endif { } web::json::value::value(double value) : m_value(utility::details::make_unique(value)) #ifdef ENABLE_JSON_VALUE_VISUALIZER ,m_kind(value::Number) #endif { } web::json::value::value(bool value) : m_value(utility::details::make_unique(value)) #ifdef ENABLE_JSON_VALUE_VISUALIZER ,m_kind(value::Boolean) #endif { } web::json::value::value(utility::string_t value) : m_value(utility::details::make_unique(std::move(value))) #ifdef ENABLE_JSON_VALUE_VISUALIZER ,m_kind(value::String) #endif { } web::json::value::value(utility::string_t value, bool has_escape_chars) : m_value(utility::details::make_unique(std::move(value), has_escape_chars)) #ifdef ENABLE_JSON_VALUE_VISUALIZER , m_kind(value::String) #endif { } web::json::value::value(const utility::char_t* value) : m_value(utility::details::make_unique(value)) #ifdef ENABLE_JSON_VALUE_VISUALIZER ,m_kind(value::String) #endif { } web::json::value::value(const utility::char_t* value, bool has_escape_chars) : m_value(utility::details::make_unique(utility::string_t(value), has_escape_chars)) #ifdef ENABLE_JSON_VALUE_VISUALIZER , m_kind(value::String) #endif { } web::json::value::value(const value &other) : m_value(other.m_value->_copy_value()) #ifdef ENABLE_JSON_VALUE_VISUALIZER ,m_kind(other.m_kind) #endif { } web::json::value &web::json::value::operator=(const value &other) { if(this != &other) { m_value = std::unique_ptr(other.m_value->_copy_value()); #ifdef ENABLE_JSON_VALUE_VISUALIZER m_kind = other.m_kind; #endif } return *this; } web::json::value::value(value &&other) CPPREST_NOEXCEPT : m_value(std::move(other.m_value)) #ifdef ENABLE_JSON_VALUE_VISUALIZER ,m_kind(other.m_kind) #endif {} web::json::value &web::json::value::operator=(web::json::value &&other) CPPREST_NOEXCEPT { if(this != &other) { m_value.swap(other.m_value); #ifdef ENABLE_JSON_VALUE_VISUALIZER m_kind = other.m_kind; #endif } return *this; } web::json::value web::json::value::null() { return web::json::value(); } web::json::value web::json::value::number(double value) { return web::json::value(value); } web::json::value web::json::value::number(int32_t value) { return web::json::value(value); } web::json::value web::json::value::number(uint32_t value) { return web::json::value(value); } web::json::value web::json::value::number(int64_t value) { return web::json::value(value); } web::json::value web::json::value::number(uint64_t value) { return web::json::value(value); } web::json::value web::json::value::boolean(bool value) { return web::json::value(value); } web::json::value web::json::value::string(utility::string_t value) { std::unique_ptr ptr = utility::details::make_unique(std::move(value)); return web::json::value(std::move(ptr) #ifdef ENABLE_JSON_VALUE_VISUALIZER ,value::String #endif ); } web::json::value web::json::value::string(utility::string_t value, bool has_escape_chars) { std::unique_ptr ptr = utility::details::make_unique(std::move(value), has_escape_chars); return web::json::value(std::move(ptr) #ifdef ENABLE_JSON_VALUE_VISUALIZER ,value::String #endif ); } #ifdef _WIN32 web::json::value web::json::value::string(const std::string &value) { std::unique_ptr ptr = utility::details::make_unique(utility::conversions::to_utf16string(value)); return web::json::value(std::move(ptr) #ifdef ENABLE_JSON_VALUE_VISUALIZER ,value::String #endif ); } #endif web::json::value web::json::value::object(bool keep_order) { std::unique_ptr ptr = utility::details::make_unique(keep_order); return web::json::value(std::move(ptr) #ifdef ENABLE_JSON_VALUE_VISUALIZER ,value::Object #endif ); } web::json::value web::json::value::object(std::vector> fields, bool keep_order) { std::unique_ptr ptr = utility::details::make_unique(std::move(fields), keep_order); return web::json::value(std::move(ptr) #ifdef ENABLE_JSON_VALUE_VISUALIZER ,value::Object #endif ); } web::json::value web::json::value::array() { std::unique_ptr ptr = utility::details::make_unique(); return web::json::value(std::move(ptr) #ifdef ENABLE_JSON_VALUE_VISUALIZER ,value::Array #endif ); } web::json::value web::json::value::array(size_t size) { std::unique_ptr ptr = utility::details::make_unique(size); return web::json::value(std::move(ptr) #ifdef ENABLE_JSON_VALUE_VISUALIZER ,value::Array #endif ); } web::json::value web::json::value::array(std::vector elements) { std::unique_ptr ptr = utility::details::make_unique(std::move(elements)); return web::json::value(std::move(ptr) #ifdef ENABLE_JSON_VALUE_VISUALIZER ,value::Array #endif ); } const web::json::number& web::json::value::as_number() const { return m_value->as_number(); } double web::json::value::as_double() const { return m_value->as_double(); } int web::json::value::as_integer() const { return m_value->as_integer(); } bool web::json::value::as_bool() const { return m_value->as_bool(); } json::array& web::json::value::as_array() { return m_value->as_array(); } const json::array& web::json::value::as_array() const { return m_value->as_array(); } json::object& web::json::value::as_object() { return m_value->as_object(); } const json::object& web::json::value::as_object() const { return m_value->as_object(); } bool web::json::number::is_int32() const { switch (m_type) { case signed_type : return m_intval >= (-2147483647-1) && m_intval <= 2147483647; case unsigned_type : return m_uintval <= 0x7FFFFFFF; case double_type : default : return false; } } bool web::json::number::is_uint32() const { switch (m_type) { case signed_type : return m_intval >= 0 && m_intval <= 0xFFFFFFFF; case unsigned_type : return m_uintval <= 0xFFFFFFFF; case double_type : default : return false; } } bool web::json::number::is_int64() const { switch (m_type) { case signed_type : return true; case unsigned_type : return m_uintval <= 0xffffffffffffffff; case double_type : default : return false; } } bool web::json::details::_String::has_escape_chars(const _String &str) { return std::any_of(std::begin(str.m_string), std::end(str.m_string), [](utility::string_t::value_type const x) { if (x <= 31) { return true; } if (x == '"') { return true; } if (x == '\\') { return true; } return false; }); } web::json::value::value_type json::value::type() const { return m_value->type(); } bool json::value::is_integer() const { if(!is_number()) { return false; } return m_value->is_integer(); } bool json::value::is_double() const { if(!is_number()) { return false; } return m_value->is_double(); } json::value& web::json::details::_Object::index(const utility::string_t &key) { return m_object[key]; } bool web::json::details::_Object::has_field(const utility::string_t &key) const { return m_object.find(key) != m_object.end(); } utility::string_t json::value::to_string() const { #ifndef _WIN32 utility::details::scoped_c_thread_locale locale; #endif return m_value->to_string(); } bool json::value::operator==(const json::value &other) const { if (this->m_value.get() == other.m_value.get()) return true; if (this->type() != other.type()) return false; switch(this->type()) { case Null: return true; case Number: return this->as_number() == other.as_number(); case Boolean: return this->as_bool() == other.as_bool(); case String: return this->as_string() == other.as_string(); case Object: return static_cast(this->m_value.get())->is_equal(static_cast(other.m_value.get())); case Array: return static_cast(this->m_value.get())->is_equal(static_cast(other.m_value.get())); } __assume(0); } void web::json::value::erase(size_t index) { return this->as_array().erase(index); } void web::json::value::erase(const utility::string_t &key) { return this->as_object().erase(key); } // at() overloads web::json::value& web::json::value::at(size_t index) { return this->as_array().at(index); } const web::json::value& web::json::value::at(size_t index) const { return this->as_array().at(index); } web::json::value& web::json::value::at(const utility::string_t& key) { return this->as_object().at(key); } const web::json::value& web::json::value::at(const utility::string_t& key) const { return this->as_object().at(key); } web::json::value& web::json::value::operator [] (const utility::string_t &key) { if ( this->is_null() ) { m_value.reset(new web::json::details::_Object(details::g_keep_json_object_unsorted)); #ifdef ENABLE_JSON_VALUE_VISUALIZER m_kind = value::Object; #endif } return m_value->index(key); } web::json::value& web::json::value::operator[](size_t index) { if ( this->is_null() ) { m_value.reset(new web::json::details::_Array()); #ifdef ENABLE_JSON_VALUE_VISUALIZER m_kind = value::Array; #endif } return m_value->index(index); } // Remove once VS 2013 is no longer supported. #if defined(_WIN32) && _MSC_VER < 1900 static web::json::details::json_error_category_impl instance; #endif const web::json::details::json_error_category_impl& web::json::details::json_error_category() { #if !defined(_WIN32) || _MSC_VER >= 1900 static web::json::details::json_error_category_impl instance; #endif return instance; } ================================================ FILE: Include/cpprestinclude/cpprest/details/json_parsing.hpp ================================================ /*** * Copyright (C) Microsoft. All rights reserved. * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. * * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ * * HTTP Library: JSON parser * * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk * * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ****/ #pragma once #include #include #if defined(_MSC_VER) #pragma warning(disable : 4127) // allow expressions like while(true) pass #pragma warning( disable : 4365 ) #pragma warning( disable : 4061 ) #endif using namespace web; using namespace web::json; using namespace utility::conversions; std::array _hexval = {{ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }}; namespace web { namespace json { namespace details { // // JSON Parsing // template #if defined(_WIN32) __declspec(noreturn) #else __attribute__((noreturn)) #endif void CreateException(const Token &tk, const utility::string_t &message) { utility::ostringstream_t os; os << _XPLATSTR("* Line ") << tk.start.m_line << _XPLATSTR(", Column ") << tk.start.m_column << _XPLATSTR(" Syntax error: ") << message; utility::string_t osStr = os.str(); throw web::json::json_exception(osStr.c_str()); } template void SetErrorCode(Token &tk, json_error jsonErrorCode) { tk.m_error = std::error_code(jsonErrorCode, json_error_category()); } template class JSON_Parser { public: JSON_Parser() : m_currentLine(1), m_currentColumn(1), m_currentParsingDepth(0) { } struct Location { size_t m_line; size_t m_column; }; struct Token { enum Kind { TKN_EOF, TKN_OpenBrace, TKN_CloseBrace, TKN_OpenBracket, TKN_CloseBracket, TKN_Comma, TKN_Colon, TKN_StringLiteral, TKN_NumberLiteral, TKN_IntegerLiteral, TKN_BooleanLiteral, TKN_NullLiteral, TKN_Comment }; Token() : kind(TKN_EOF) {} Kind kind; std::basic_string string_val; typename JSON_Parser::Location start{}; union { double double_val; int64_t int64_val; uint64_t uint64_val; bool boolean_val; bool has_unescape_symbol; }; bool signed_number{ false }; std::error_code m_error; }; void GetNextToken(Token &); web::json::value ParseValue(typename JSON_Parser::Token &first) { #ifndef _WIN32 utility::details::scoped_c_thread_locale locale; #endif #ifdef ENABLE_JSON_VALUE_VISUALIZER auto _value = _ParseValue(first); auto type = _value->type(); return web::json::value(std::move(_value), type); #else return web::json::value(_ParseValue(first)); #endif } protected: typedef typename std::char_traits::int_type int_type; virtual int_type NextCharacter() = 0; virtual int_type PeekCharacter() = 0; virtual bool CompleteComment(Token &token); virtual bool CompleteStringLiteral(Token &token); bool handle_unescape_char(Token &token); private: bool CompleteNumberLiteral(CharType first, Token &token); bool ParseInt64(CharType first, uint64_t& value); bool CompleteKeywordTrue(Token &token); bool CompleteKeywordFalse(Token &token); bool CompleteKeywordNull(Token &token); std::unique_ptr _ParseValue(typename JSON_Parser::Token &first); std::unique_ptr _ParseObject(typename JSON_Parser::Token &tkn); std::unique_ptr _ParseArray(typename JSON_Parser::Token &tkn); JSON_Parser& operator=(const JSON_Parser&); int_type EatWhitespace(); void CreateToken(typename JSON_Parser::Token& tk, typename Token::Kind kind, Location &start) { tk.kind = kind; tk.start = start; tk.string_val.clear(); } void CreateToken(typename JSON_Parser::Token& tk, typename Token::Kind kind) { tk.kind = kind; tk.start.m_line = m_currentLine; tk.start.m_column = m_currentColumn; tk.string_val.clear(); } protected: size_t m_currentLine; size_t m_currentColumn; size_t m_currentParsingDepth; // The DEBUG macro is defined in XCode but we don't in our CMakeList // so for now we will keep the same on debug and release. In the future // this can be increase on release if necessary. #if defined(__APPLE__) static const size_t maxParsingDepth = 32; #else static const size_t maxParsingDepth = 128; #endif }; // Replace with template alias once VS 2012 support is removed. template typename std::char_traits::int_type eof() { return std::char_traits::eof(); } template class JSON_StreamParser : public JSON_Parser { public: JSON_StreamParser(std::basic_istream &stream) : m_streambuf(stream.rdbuf()) { } protected: virtual typename JSON_Parser::int_type NextCharacter(); virtual typename JSON_Parser::int_type PeekCharacter(); private: typename std::basic_streambuf>* m_streambuf; }; template class JSON_StringParser : public JSON_Parser { public: JSON_StringParser(const std::basic_string& string) : m_position(&string[0]) { m_startpos = m_position; m_endpos = m_position+string.size(); } protected: virtual typename JSON_Parser::int_type NextCharacter(); virtual typename JSON_Parser::int_type PeekCharacter(); virtual bool CompleteComment(typename JSON_Parser::Token &token); virtual bool CompleteStringLiteral(typename JSON_Parser::Token &token); private: bool finish_parsing_string_with_unescape_char(typename JSON_Parser::Token &token); const CharType* m_position; const CharType* m_startpos; const CharType* m_endpos; }; template typename JSON_Parser::int_type JSON_StreamParser::NextCharacter() { auto ch = m_streambuf->sbumpc(); if (ch == '\n') { this->m_currentLine += 1; this->m_currentColumn = 0; } else { this->m_currentColumn += 1; } return ch; } template typename JSON_Parser::int_type JSON_StreamParser::PeekCharacter() { return m_streambuf->sgetc(); } template typename JSON_Parser::int_type JSON_StringParser::NextCharacter() { if (m_position == m_endpos) return eof(); CharType ch = *m_position; m_position += 1; if ( ch == '\n' ) { this->m_currentLine += 1; this->m_currentColumn = 0; } else { this->m_currentColumn += 1; } return ch; } template typename JSON_Parser::int_type JSON_StringParser::PeekCharacter() { if ( m_position == m_endpos ) return eof(); return *m_position; } // // Consume whitespace characters and return the first non-space character or EOF // template typename JSON_Parser::int_type JSON_Parser::EatWhitespace() { auto ch = NextCharacter(); while ( ch != eof() && iswspace(static_cast(ch))) { ch = NextCharacter(); } return ch; } template bool JSON_Parser::CompleteKeywordTrue(Token &token) { if (NextCharacter() != 'r') return false; if (NextCharacter() != 'u') return false; if (NextCharacter() != 'e') return false; token.kind = Token::TKN_BooleanLiteral; token.boolean_val = true; return true; } template bool JSON_Parser::CompleteKeywordFalse(Token &token) { if (NextCharacter() != 'a') return false; if (NextCharacter() != 'l') return false; if (NextCharacter() != 's') return false; if (NextCharacter() != 'e') return false; token.kind = Token::TKN_BooleanLiteral; token.boolean_val = false; return true; } template bool JSON_Parser::CompleteKeywordNull(Token &token) { if (NextCharacter() != 'u') return false; if (NextCharacter() != 'l') return false; if (NextCharacter() != 'l') return false; token.kind = Token::TKN_NullLiteral; return true; } // Returns false only on overflow template inline bool JSON_Parser::ParseInt64(CharType first, uint64_t& value) { value = static_cast(first) - '0'; auto ch = PeekCharacter(); while (ch >= '0' && ch <= '9') { unsigned int next_digit = (unsigned int)(ch - '0'); if (value > (ULLONG_MAX / 10) || (value == ULLONG_MAX/10 && next_digit > ULLONG_MAX%10)) return false; NextCharacter(); value *= 10; value += next_digit; ch = PeekCharacter(); } return true; } // This namespace hides the x-plat helper functions namespace { #if defined(_WIN32) static int print_llu(char* ptr, size_t n, uint64_t val64) { return _snprintf_s_l(ptr, n, _TRUNCATE, "%I64u", utility::details::scoped_c_thread_locale::c_locale(), val64); } static int print_llu(wchar_t* ptr, size_t n, uint64_t val64) { return _snwprintf_s_l(ptr, n, _TRUNCATE, L"%I64u", utility::details::scoped_c_thread_locale::c_locale(), val64); } static double anystod(const char* str) { return _strtod_l(str, nullptr, utility::details::scoped_c_thread_locale::c_locale()); } static double anystod(const wchar_t* str) { return _wcstod_l(str, nullptr, utility::details::scoped_c_thread_locale::c_locale()); } #else static int __attribute__((__unused__)) print_llu(char* ptr, size_t n, unsigned long long val64) { return snprintf(ptr, n, "%llu", val64); } static int __attribute__((__unused__)) print_llu(char* ptr, size_t n, unsigned long val64) { return snprintf(ptr, n, "%lu", val64); } static double __attribute__((__unused__)) anystod(const char* str) { return strtod(str, nullptr); } static double __attribute__((__unused__)) anystod(const wchar_t* str) { return wcstod(str, nullptr); } #endif } template bool JSON_Parser::CompleteNumberLiteral(CharType first, Token &token) { bool minus_sign; if (first == '-') { minus_sign = true; // Safe to cast because the check after this if/else statement will cover EOF. first = static_cast(NextCharacter()); } else { minus_sign = false; } if (first < '0' || first > '9') return false; auto ch = PeekCharacter(); //Check for two (or more) zeros at the beginning if (first == '0' && ch == '0') return false; // Parse the number assuming its integer uint64_t val64; bool complete = ParseInt64(first, val64); ch = PeekCharacter(); if (complete && ch!='.' && ch!='E' && ch!='e') { if (minus_sign) { if (val64 > static_cast(1) << 63 ) { // It is negative and cannot be represented in int64, so we resort to double token.double_val = 0 - static_cast(val64); token.signed_number = true; token.kind = JSON_Parser::Token::TKN_NumberLiteral; return true; } // It is negative, but fits into int64 token.int64_val = 0 - static_cast(val64); token.kind = JSON_Parser::Token::TKN_IntegerLiteral; token.signed_number = true; return true; } // It is positive so we use unsigned int64 token.uint64_val = val64; token.kind = JSON_Parser::Token::TKN_IntegerLiteral; token.signed_number = false; return true; } // Magic number 5 leaves room for decimal point, null terminator, etc (in most cases) ::std::vector buf(::std::numeric_limits::digits10 + 5); int count = print_llu(buf.data(), buf.size(), val64); _ASSERTE(count >= 0); _ASSERTE((size_t)count < buf.size()); // Resize to cut off the null terminator buf.resize(count); bool decimal = false; while (ch != eof()) { // Digit encountered? if (ch >= '0' && ch <= '9') { buf.push_back(static_cast(ch)); NextCharacter(); ch = PeekCharacter(); } // Decimal dot? else if (ch == '.') { if (decimal) return false; decimal = true; buf.push_back(static_cast(ch)); NextCharacter(); ch = PeekCharacter(); // Check that the following char is a digit if (ch < '0' || ch > '9') return false; buf.push_back(static_cast(ch)); NextCharacter(); ch = PeekCharacter(); } // Exponent? else if (ch == 'E' || ch == 'e') { buf.push_back(static_cast(ch)); NextCharacter(); ch = PeekCharacter(); // Check for the exponent sign if (ch == '+') { buf.push_back(static_cast(ch)); NextCharacter(); ch = PeekCharacter(); } else if (ch == '-') { buf.push_back(static_cast(ch)); NextCharacter(); ch = PeekCharacter(); } // First number of the exponent if (ch >= '0' && ch <= '9') { buf.push_back(static_cast(ch)); NextCharacter(); ch = PeekCharacter(); } else return false; // The rest of the exponent while (ch >= '0' && ch <= '9') { buf.push_back(static_cast(ch)); NextCharacter(); ch = PeekCharacter(); } // The peeked character is not a number, so we can break from the loop and construct the number break; } else { // Not expected number character? break; } }; buf.push_back('\0'); token.double_val = anystod(buf.data()); if (minus_sign) { token.double_val = -token.double_val; } token.kind = (JSON_Parser::Token::TKN_NumberLiteral); return true; } template bool JSON_Parser::CompleteComment(Token &token) { // We already found a '/' character as the first of a token -- what kind of comment is it? auto ch = NextCharacter(); if ( ch == eof() || (ch != '/' && ch != '*') ) return false; if ( ch == '/' ) { // Line comment -- look for a newline or EOF to terminate. ch = NextCharacter(); while ( ch != eof() && ch != '\n') { ch = NextCharacter(); } } else { // Block comment -- look for a terminating "*/" sequence. ch = NextCharacter(); while ( true ) { if ( ch == eof()) return false; if ( ch == '*' ) { auto ch1 = PeekCharacter(); if ( ch1 == eof()) return false; if ( ch1 == '/' ) { // Consume the character NextCharacter(); break; } ch = ch1; } ch = NextCharacter(); } } token.kind = Token::TKN_Comment; return true; } template bool JSON_StringParser::CompleteComment(typename JSON_Parser::Token &token) { // This function is specialized for the string parser, since we can be slightly more // efficient in copying data from the input to the token: do a memcpy() rather than // one character at a time. auto ch = JSON_StringParser::NextCharacter(); if ( ch == eof() || (ch != '/' && ch != '*') ) return false; if ( ch == '/' ) { // Line comment -- look for a newline or EOF to terminate. ch = JSON_StringParser::NextCharacter(); while ( ch != eof() && ch != '\n') { ch = JSON_StringParser::NextCharacter(); } } else { // Block comment -- look for a terminating "*/" sequence. ch = JSON_StringParser::NextCharacter(); while ( true ) { if ( ch == eof()) return false; if ( ch == '*' ) { ch = JSON_StringParser::PeekCharacter(); if ( ch == eof()) return false; if ( ch == '/' ) { // Consume the character JSON_StringParser::NextCharacter(); break; } } ch = JSON_StringParser::NextCharacter(); } } token.kind = JSON_Parser::Token::TKN_Comment; return true; } void convert_append_unicode_code_unit(JSON_Parser::Token &token, utf16char value) { token.string_val.push_back(value); } void convert_append_unicode_code_unit(JSON_Parser::Token &token, utf16char value) { utf16string utf16(reinterpret_cast(&value), 1); token.string_val.append(::utility::conversions::utf16_to_utf8(utf16)); } template inline bool JSON_Parser::handle_unescape_char(Token &token) { token.has_unescape_symbol = true; // This function converts unescaped character pairs (e.g. "\t") into their ASCII or Unicode representations (e.g. tab sign) // Also it handles \u + 4 hexadecimal digits auto ch = NextCharacter(); switch (ch) { case '\"': token.string_val.push_back('\"'); return true; case '\\': token.string_val.push_back('\\'); return true; case '/': token.string_val.push_back('/'); return true; case 'b': token.string_val.push_back('\b'); return true; case 'f': token.string_val.push_back('\f'); return true; case 'r': token.string_val.push_back('\r'); return true; case 'n': token.string_val.push_back('\n'); return true; case 't': token.string_val.push_back('\t'); return true; case 'u': { // A four-hexdigit Unicode character. // Transform into a 16 bit code point. int decoded = 0; for (int i = 0; i < 4; ++i) { ch = NextCharacter(); int ch_int = static_cast(ch); if (ch_int < 0 || ch_int > 127) return false; #ifdef _WIN32 const int isxdigitResult = _isxdigit_l(ch_int, utility::details::scoped_c_thread_locale::c_locale()); #else const int isxdigitResult = isxdigit(ch_int); #endif if (!isxdigitResult) return false; int val = _hexval[static_cast(ch_int)]; _ASSERTE(val != -1); // Add the input char to the decoded number decoded |= (val << (4 * (3 - i))); } // Construct the character based on the decoded number convert_append_unicode_code_unit(token, static_cast(decoded)); return true; } default: return false; } } template bool JSON_Parser::CompleteStringLiteral(Token &token) { token.has_unescape_symbol = false; auto ch = NextCharacter(); while ( ch != '"' ) { if ( ch == '\\' ) { handle_unescape_char(token); } else if (ch >= CharType(0x0) && ch < CharType(0x20)) { return false; } else { if (ch == eof()) return false; token.string_val.push_back(static_cast(ch)); } ch = NextCharacter(); } if ( ch == '"' ) { token.kind = Token::TKN_StringLiteral; } else { return false; } return true; } template bool JSON_StringParser::CompleteStringLiteral(typename JSON_Parser::Token &token) { // This function is specialized for the string parser, since we can be slightly more // efficient in copying data from the input to the token: do a memcpy() rather than // one character at a time. auto start = m_position; token.has_unescape_symbol = false; auto ch = JSON_StringParser::NextCharacter(); while (ch != '"') { if (ch == eof()) return false; if (ch == '\\') { const size_t numChars = m_position - start - 1; const size_t prevSize = token.string_val.size(); token.string_val.resize(prevSize + numChars); memcpy(const_cast(token.string_val.c_str() + prevSize), start, numChars * sizeof(CharType)); if (!JSON_StringParser::handle_unescape_char(token)) { return false; } // Reset start position and continue. start = m_position; } else if (ch >= CharType(0x0) && ch < CharType(0x20)) { return false; } ch = JSON_StringParser::NextCharacter(); } const size_t numChars = m_position - start - 1; const size_t prevSize = token.string_val.size(); token.string_val.resize(prevSize + numChars); memcpy(const_cast(token.string_val.c_str() + prevSize), start, numChars * sizeof(CharType)); token.kind = JSON_Parser::Token::TKN_StringLiteral; return true; } template void JSON_Parser::GetNextToken(typename JSON_Parser::Token& result) { try_again: auto ch = EatWhitespace(); CreateToken(result, Token::TKN_EOF); if (ch == eof()) return; switch (ch) { case '{': case '[': { if(++m_currentParsingDepth > JSON_Parser::maxParsingDepth) { SetErrorCode(result, json_error::nesting); break; } typename JSON_Parser::Token::Kind tk = ch == '{' ? Token::TKN_OpenBrace : Token::TKN_OpenBracket; CreateToken(result, tk, result.start); break; } case '}': case ']': { if((signed int)(--m_currentParsingDepth) < 0) { SetErrorCode(result, json_error::mismatched_brances); break; } typename JSON_Parser::Token::Kind tk = ch == '}' ? Token::TKN_CloseBrace : Token::TKN_CloseBracket; CreateToken(result, tk, result.start); break; } case ',': CreateToken(result, Token::TKN_Comma, result.start); break; case ':': CreateToken(result, Token::TKN_Colon, result.start); break; case 't': if (!CompleteKeywordTrue(result)) { SetErrorCode(result, json_error::malformed_literal); } break; case 'f': if (!CompleteKeywordFalse(result)) { SetErrorCode(result, json_error::malformed_literal); } break; case 'n': if (!CompleteKeywordNull(result)) { SetErrorCode(result, json_error::malformed_literal); } break; case '/': if (!CompleteComment(result)) { SetErrorCode(result, json_error::malformed_comment); break; } // For now, we're ignoring comments. goto try_again; case '"': if (!CompleteStringLiteral(result)) { SetErrorCode(result, json_error::malformed_string_literal); } break; case '-': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (!CompleteNumberLiteral(static_cast(ch), result)) { SetErrorCode(result, json_error::malformed_numeric_literal); } break; default: SetErrorCode(result, json_error::malformed_token); break; } } template std::unique_ptr JSON_Parser::_ParseObject(typename JSON_Parser::Token &tkn) { auto obj = utility::details::make_unique(g_keep_json_object_unsorted); auto& elems = obj->m_object.m_elements; GetNextToken(tkn); if (tkn.m_error) goto error; if (tkn.kind != JSON_Parser::Token::TKN_CloseBrace) { while (true) { // State 1: New field or end of object, looking for field name or closing brace std::basic_string fieldName; switch (tkn.kind) { case JSON_Parser::Token::TKN_StringLiteral: fieldName = std::move(tkn.string_val); break; default: goto error; } GetNextToken(tkn); if (tkn.m_error) goto error; // State 2: Looking for a colon. if (tkn.kind != JSON_Parser::Token::TKN_Colon) goto done; GetNextToken(tkn); if (tkn.m_error) goto error; // State 3: Looking for an expression. #ifdef ENABLE_JSON_VALUE_VISUALIZER auto fieldValue = _ParseValue(tkn); auto type = fieldValue->type(); elems.emplace_back(utility::conversions::to_string_t(std::move(fieldName)), json::value(std::move(fieldValue), type)); #else elems.emplace_back(utility::conversions::to_string_t(std::move(fieldName)), json::value(_ParseValue(tkn))); #endif if (tkn.m_error) goto error; // State 4: Looking for a comma or a closing brace switch (tkn.kind) { case JSON_Parser::Token::TKN_Comma: GetNextToken(tkn); if (tkn.m_error) goto error; break; case JSON_Parser::Token::TKN_CloseBrace: goto done; default: goto error; } } } done: GetNextToken(tkn); if (tkn.m_error) return utility::details::make_unique(); if (!g_keep_json_object_unsorted) { ::std::sort(elems.begin(), elems.end(), json::object::compare_pairs); } return std::move(obj); error: if (!tkn.m_error) { SetErrorCode(tkn, json_error::malformed_object_literal); } return utility::details::make_unique(); } template std::unique_ptr JSON_Parser::_ParseArray(typename JSON_Parser::Token &tkn) { GetNextToken(tkn); if (tkn.m_error) return utility::details::make_unique(); auto result = utility::details::make_unique(); if (tkn.kind != JSON_Parser::Token::TKN_CloseBracket) { while (true) { // State 1: Looking for an expression. result->m_array.m_elements.emplace_back(ParseValue(tkn)); if (tkn.m_error) return utility::details::make_unique(); // State 4: Looking for a comma or a closing bracket switch (tkn.kind) { case JSON_Parser::Token::TKN_Comma: GetNextToken(tkn); if (tkn.m_error) return utility::details::make_unique(); break; case JSON_Parser::Token::TKN_CloseBracket: GetNextToken(tkn); if (tkn.m_error) return utility::details::make_unique(); return std::move(result); default: SetErrorCode(tkn, json_error::malformed_array_literal); return utility::details::make_unique(); } } } GetNextToken(tkn); if (tkn.m_error) return utility::details::make_unique(); return std::move(result); } template std::unique_ptr JSON_Parser::_ParseValue(typename JSON_Parser::Token &tkn) { switch (tkn.kind) { case JSON_Parser::Token::TKN_OpenBrace: { return _ParseObject(tkn); } case JSON_Parser::Token::TKN_OpenBracket: { return _ParseArray(tkn); } case JSON_Parser::Token::TKN_StringLiteral: { auto value = utility::details::make_unique(std::move(tkn.string_val), tkn.has_unescape_symbol); GetNextToken(tkn); if (tkn.m_error) return utility::details::make_unique(); return std::move(value); } case JSON_Parser::Token::TKN_IntegerLiteral: { std::unique_ptr value; if (tkn.signed_number) value = utility::details::make_unique(tkn.int64_val); else value = utility::details::make_unique(tkn.uint64_val); GetNextToken(tkn); if (tkn.m_error) return utility::details::make_unique(); return std::move(value); } case JSON_Parser::Token::TKN_NumberLiteral: { auto value = utility::details::make_unique(tkn.double_val); GetNextToken(tkn); if (tkn.m_error) return utility::details::make_unique(); return std::move(value); } case JSON_Parser::Token::TKN_BooleanLiteral: { auto value = utility::details::make_unique(tkn.boolean_val); GetNextToken(tkn); if (tkn.m_error) return utility::details::make_unique(); return std::move(value); } case JSON_Parser::Token::TKN_NullLiteral: { GetNextToken(tkn); // Returning a null value whether or not an error occurred. return utility::details::make_unique(); } default: { SetErrorCode(tkn, json_error::malformed_token); return utility::details::make_unique(); } } } }}} static web::json::value _parse_stream(utility::istream_t &stream) { web::json::details::JSON_StreamParser parser(stream); web::json::details::JSON_Parser::Token tkn; parser.GetNextToken(tkn); if (tkn.m_error) { web::json::details::CreateException(tkn, utility::conversions::to_string_t(tkn.m_error.message())); } auto value = parser.ParseValue(tkn); if (tkn.m_error) { web::json::details::CreateException(tkn, utility::conversions::to_string_t(tkn.m_error.message())); } else if (tkn.kind != web::json::details::JSON_Parser::Token::TKN_EOF) { web::json::details::CreateException(tkn, _XPLATSTR("Left-over characters in stream after parsing a JSON value")); } return value; } static web::json::value _parse_stream(utility::istream_t &stream, std::error_code& error) { web::json::details::JSON_StreamParser parser(stream); web::json::details::JSON_Parser::Token tkn; parser.GetNextToken(tkn); if (tkn.m_error) { error = std::move(tkn.m_error); return web::json::value(); } auto returnObject = parser.ParseValue(tkn); if (tkn.kind != web::json::details::JSON_Parser::Token::TKN_EOF) { web::json::details::SetErrorCode(tkn, web::json::details::json_error::left_over_character_in_stream); } error = std::move(tkn.m_error); return returnObject; } #ifdef _WIN32 static web::json::value _parse_narrow_stream(std::istream &stream) { web::json::details::JSON_StreamParser parser(stream); web::json::details::JSON_StreamParser::Token tkn; parser.GetNextToken(tkn); if (tkn.m_error) { web::json::details::CreateException(tkn, utility::conversions::to_string_t(tkn.m_error.message())); } auto value = parser.ParseValue(tkn); if (tkn.m_error) { web::json::details::CreateException(tkn, utility::conversions::to_string_t(tkn.m_error.message())); } else if (tkn.kind != web::json::details::JSON_Parser::Token::TKN_EOF) { web::json::details::CreateException(tkn, _XPLATSTR("Left-over characters in stream after parsing a JSON value")); } return value; } static web::json::value _parse_narrow_stream(std::istream &stream, std::error_code& error) { web::json::details::JSON_StreamParser parser(stream); web::json::details::JSON_StreamParser::Token tkn; parser.GetNextToken(tkn); if (tkn.m_error) { error = std::move(tkn.m_error); return web::json::value(); } auto returnObject = parser.ParseValue(tkn); if (tkn.kind != web::json::details::JSON_Parser::Token::TKN_EOF) { returnObject = web::json::value(); web::json::details::SetErrorCode(tkn, web::json::details::json_error::left_over_character_in_stream); } error = std::move(tkn.m_error); return returnObject; } #endif web::json::value web::json::value::parse(const utility::string_t& str) { web::json::details::JSON_StringParser parser(str); web::json::details::JSON_Parser::Token tkn; parser.GetNextToken(tkn); if (tkn.m_error) { web::json::details::CreateException(tkn, utility::conversions::to_string_t(tkn.m_error.message())); } auto value = parser.ParseValue(tkn); if (tkn.m_error) { web::json::details::CreateException(tkn, utility::conversions::to_string_t(tkn.m_error.message())); } else if (tkn.kind != web::json::details::JSON_Parser::Token::TKN_EOF) { web::json::details::CreateException(tkn, _XPLATSTR("Left-over characters in stream after parsing a JSON value")); } return value; } web::json::value web::json::value::parse(const utility::string_t& str, std::error_code& error) { web::json::details::JSON_StringParser parser(str); web::json::details::JSON_Parser::Token tkn; parser.GetNextToken(tkn); if (tkn.m_error) { error = std::move(tkn.m_error); return web::json::value(); } auto returnObject = parser.ParseValue(tkn); if (tkn.kind != web::json::details::JSON_Parser::Token::TKN_EOF) { returnObject = web::json::value(); web::json::details::SetErrorCode(tkn, web::json::details::json_error::left_over_character_in_stream); } error = std::move(tkn.m_error); return returnObject; } web::json::value web::json::value::parse(utility::istream_t &stream) { return _parse_stream(stream); } web::json::value web::json::value::parse(utility::istream_t &stream, std::error_code& error) { return _parse_stream(stream, error); } #ifdef _WIN32 web::json::value web::json::value::parse(std::istream& stream) { return _parse_narrow_stream(stream); } web::json::value web::json::value::parse(std::istream& stream, std::error_code& error) { return _parse_narrow_stream(stream, error); } #endif ================================================ FILE: Include/cpprestinclude/cpprest/details/json_serialization.hpp ================================================ /*** * Copyright (C) Microsoft. All rights reserved. * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. * * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ * * HTTP Library: JSON parser and writer * * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk * * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ****/ #pragma once #include #include #ifndef _WIN32 #define __STDC_FORMAT_MACROS #include #endif using namespace web; using namespace web::json; using namespace utility::conversions; #if HC_PLATFORM_IS_MICROSOFT #pragma warning( push ) #pragma warning( disable : 4365 ) #endif // // JSON Serialization // #ifdef _WIN32 void web::json::value::serialize(std::ostream& stream) const { // This has better performance than writing directly to stream. std::string str; m_value->serialize_impl(str); stream << str; } void web::json::value::format(std::basic_string &string) const { m_value->format(string); } #endif void web::json::value::serialize(utility::ostream_t &stream) const { #ifndef _WIN32 utility::details::scoped_c_thread_locale locale; #endif // This has better performance than writing directly to stream. utility::string_t str; m_value->serialize_impl(str); stream << str; } void web::json::value::format(std::basic_string& string) const { m_value->format(string); } template void web::json::details::append_escape_string(std::basic_string& str, const std::basic_string& escaped) { for (const auto &ch : escaped) { switch (ch) { case '\"': str += '\\'; str += '\"'; break; case '\\': str += '\\'; str += '\\'; break; case '\b': str += '\\'; str += 'b'; break; case '\f': str += '\\'; str += 'f'; break; case '\r': str += '\\'; str += 'r'; break; case '\n': str += '\\'; str += 'n'; break; case '\t': str += '\\'; str += 't'; break; default: // If a control character then must unicode escaped. if (ch >= 0 && ch <= 0x1F) { static const std::array intToHex = { { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' } }; str += '\\'; str += 'u'; str += '0'; str += '0'; str += intToHex[(ch & 0xF0) >> 4]; str += intToHex[ch & 0x0F]; } else { str += ch; } } } } void web::json::details::format_string(const utility::string_t& key, utility::string_t& str) { str.push_back('"'); append_escape_string(str, key); str.push_back('"'); } #ifdef _WIN32 void web::json::details::format_string(const utility::string_t& key, std::string& str) { str.push_back('"'); append_escape_string(str, utility::conversions::to_utf8string(key)); str.push_back('"'); } #endif void web::json::details::_String::format(std::basic_string& str) const { str.push_back('"'); if(m_has_escape_char) { append_escape_string(str, utility::conversions::to_utf8string(m_string)); } else { str.append(utility::conversions::to_utf8string(m_string)); } str.push_back('"'); } void web::json::details::_Number::format(std::basic_string& stream) const { if(m_number.m_type != number::type::double_type) { // #digits + 1 to avoid loss + 1 for the sign + 1 for null terminator. const size_t tempSize = std::numeric_limits::digits10 + 3; char tempBuffer[tempSize]; #ifdef _WIN32 // This can be improved performance-wise if we implement our own routine if (m_number.m_type == number::type::signed_type) _i64toa_s(m_number.m_intval, tempBuffer, tempSize, 10); else _ui64toa_s(m_number.m_uintval, tempBuffer, tempSize, 10); const auto numChars = strnlen_s(tempBuffer, tempSize); #else int numChars; if (m_number.m_type == number::type::signed_type) numChars = snprintf(tempBuffer, tempSize, "%" PRId64, m_number.m_intval); else numChars = snprintf(tempBuffer, tempSize, "%" PRIu64, m_number.m_uintval); #endif stream.append(tempBuffer, numChars); } else { // #digits + 2 to avoid loss + 1 for the sign + 1 for decimal point + 5 for exponent (e+xxx) + 1 for null terminator const size_t tempSize = std::numeric_limits::digits10 + 10; char tempBuffer[tempSize]; #ifdef _WIN32 const auto numChars = _sprintf_s_l( tempBuffer, tempSize, "%.*g", utility::details::scoped_c_thread_locale::c_locale(), std::numeric_limits::digits10 + 2, m_number.m_value); #else const auto numChars = snprintf(tempBuffer, tempSize, "%.*g", std::numeric_limits::digits10 + 2, m_number.m_value); #endif stream.append(tempBuffer, numChars); } } #ifdef _WIN32 void web::json::details::_String::format(std::basic_string& str) const { str.push_back(L'"'); if(m_has_escape_char) { append_escape_string(str, m_string); } else { str.append(m_string); } str.push_back(L'"'); } void web::json::details::_Number::format(std::basic_string& stream) const { if(m_number.m_type != number::type::double_type) { // #digits + 1 to avoid loss + 1 for the sign + 1 for null terminator. const size_t tempSize = std::numeric_limits::digits10 + 3; wchar_t tempBuffer[tempSize]; if (m_number.m_type == number::type::signed_type) _i64tow_s(m_number.m_intval, tempBuffer, tempSize, 10); else _ui64tow_s(m_number.m_uintval, tempBuffer, tempSize, 10); stream.append(tempBuffer, wcsnlen_s(tempBuffer, tempSize)); } else { // #digits + 2 to avoid loss + 1 for the sign + 1 for decimal point + 5 for exponent (e+xxx) + 1 for null terminator const size_t tempSize = std::numeric_limits::digits10 + 10; wchar_t tempBuffer[tempSize]; const int numChars = _swprintf_s_l( tempBuffer, tempSize, L"%.*g", utility::details::scoped_c_thread_locale::c_locale(), std::numeric_limits::digits10 + 2, m_number.m_value); stream.append(tempBuffer, numChars); } } #endif const utility::string_t & web::json::details::_String::as_string() const { return m_string; } const utility::string_t & web::json::value::as_string() const { return m_value->as_string(); } utility::string_t json::value::serialize() const { #ifndef _WIN32 utility::details::scoped_c_thread_locale locale; #endif return m_value->to_string(); } #if HC_PLATFORM_IS_MICROSOFT #pragma warning( pop ) #endif ================================================ FILE: Include/cpprestinclude/cpprest/details/nosal.h ================================================ /*** * ==++== * * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * ==--== * * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk * * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ ***/ #pragma once // selected MS SAL annotations #ifdef _In_ #undef _In_ #endif #define _In_ #ifdef _Inout_ #undef _Inout_ #endif #define _Inout_ #ifdef _Out_ #undef _Out_ #endif #define _Out_ #ifdef _In_z_ #undef _In_z_ #endif #define _In_z_ #ifdef _Out_z_ #undef _Out_z_ #endif #define _Out_z_ #ifdef _Inout_z_ #undef _Inout_z_ #endif #define _Inout_z_ #ifdef _In_opt_ #undef _In_opt_ #endif #define _In_opt_ #ifdef _Out_opt_ #undef _Out_opt_ #endif #define _Out_opt_ #ifdef _Inout_opt_ #undef _Inout_opt_ #endif #define _Inout_opt_ #ifdef _Out_writes_ #undef _Out_writes_ #endif #define _Out_writes_(x) #ifdef _Out_writes_opt_ #undef _Out_writes_opt_ #endif #define _Out_writes_opt_(x) #ifdef _In_reads_ #undef _In_reads_ #endif #define _In_reads_(x) #ifdef _Inout_updates_bytes_ #undef _Inout_updates_bytes_ #endif #define _Inout_updates_bytes_(x) ================================================ FILE: Include/cpprestinclude/cpprest/details/uri.hpp ================================================ /*** * ==++== * * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * ==--== * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ * * Protocol independent support for URIs. * * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk * * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ****/ #pragma once using namespace utility::conversions; #if HC_PLATFORM_IS_MICROSOFT #pragma warning( push ) #pragma warning( disable : 26444 ) // ignore various unnamed objects #pragma warning( disable : 26812 ) // enum instead of enum class #pragma warning( disable : 4365 ) #endif namespace web { namespace details { utility::string_t uri_components::join() { // canonicalize components first // convert scheme to lowercase std::transform(m_scheme.begin(), m_scheme.end(), m_scheme.begin(), [](utility::char_t c) { return (utility::char_t)tolower(c); }); // convert host to lowercase std::transform(m_host.begin(), m_host.end(), m_host.begin(), [](utility::char_t c) { return (utility::char_t)tolower(c); }); // canonicalize the path to have a leading slash if it's a full uri if (!m_host.empty() && m_path.empty()) { m_path = _XPLATSTR("/"); } else if (!m_host.empty() && m_path[0] != _XPLATSTR('/')) { m_path.insert(m_path.begin(), 1, _XPLATSTR('/')); } utility::string_t ret; #if (defined(_MSC_VER) && (_MSC_VER >= 1800)) if (!m_scheme.empty()) { ret.append(m_scheme).append({ _XPLATSTR(':') }); } if (!m_host.empty()) { ret.append(_XPLATSTR("//")); if (!m_user_info.empty()) { ret.append(m_user_info).append({ _XPLATSTR('@') }); } ret.append(m_host); if (m_port > 0) { ret.append({ _XPLATSTR(':') }).append(utility::conversions::print_string(m_port, std::locale::classic())); } } if (!m_path.empty()) { // only add the leading slash when the host is present if (!m_host.empty() && m_path.front() != _XPLATSTR('/')) { ret.append({ _XPLATSTR('/') }); } ret.append(m_path); } if (!m_query.empty()) { ret.append({ _XPLATSTR('?') }).append(m_query); } if (!m_fragment.empty()) { ret.append({ _XPLATSTR('#') }).append(m_fragment); } return ret; #else utility::ostringstream_t os; os.imbue(std::locale::classic()); if (!m_scheme.empty()) { os << m_scheme << _XPLATSTR(':'); } if (!m_host.empty()) { os << _XPLATSTR("//"); if (!m_user_info.empty()) { os << m_user_info << _XPLATSTR('@'); } os << m_host; if (m_port > 0) { os << _XPLATSTR(':') << m_port; } } if (!m_path.empty()) { // only add the leading slash when the host is present if (!m_host.empty() && m_path.front() != _XPLATSTR('/')) { os << _XPLATSTR('/'); } os << m_path; } if (!m_query.empty()) { os << _XPLATSTR('?') << m_query; } if (!m_fragment.empty()) { os << _XPLATSTR('#') << m_fragment; } return os.str(); #endif } } using namespace details; uri::uri(const details::uri_components &components) : m_components(components) { m_uri = m_components.join(); if (!details::uri_parser::validate(m_uri)) { throw uri_exception("provided uri is invalid: " + utility::conversions::to_utf8string(m_uri)); } } uri::uri(const utility::string_t &uri_string) { if (!details::uri_parser::parse(uri_string, m_components)) { throw uri_exception("provided uri is invalid: " + utility::conversions::to_utf8string(uri_string)); } m_uri = m_components.join(); } uri::uri(const utility::char_t *uri_string): m_uri(uri_string) { if (!details::uri_parser::parse(uri_string, m_components)) { throw uri_exception("provided uri is invalid: " + utility::conversions::to_utf8string(uri_string)); } m_uri = m_components.join(); } utility::string_t uri::encode_impl(const utility::string_t &raw, const std::function& should_encode) { const utility::char_t * const hex = _XPLATSTR("0123456789ABCDEF"); utility::string_t encoded; std::string utf8raw = to_utf8string(raw); for (auto iter = utf8raw.begin(); iter != utf8raw.end(); ++iter) { // for utf8 encoded string, char ASCII can be greater than 127. int ch = static_cast(*iter); // ch should be same under both utf8 and utf16. if(should_encode(ch)) { encoded.push_back(_XPLATSTR('%')); encoded.push_back(hex[(ch >> 4) & 0xF]); encoded.push_back(hex[ch & 0xF]); } else { // ASCII don't need to be encoded, which should be same on both utf8 and utf16. encoded.push_back((utility::char_t)ch); } } return encoded; } /// /// Encodes a string by converting all characters except for RFC 3986 unreserved characters to their /// hexadecimal representation. /// utility::string_t uri::encode_data_string(const utility::string_t &raw) { return uri::encode_impl(raw, [](int ch) -> bool { return !uri_parser::is_unreserved(ch); }); } utility::string_t uri::encode_uri(const utility::string_t &raw, uri::components::component component) { // Note: we also encode the '+' character because some non-standard implementations // encode the space character as a '+' instead of %20. To better interoperate we encode // '+' to avoid any confusion and be mistaken as a space. switch(component) { case components::user_info: return uri::encode_impl(raw, [](int ch) -> bool { return !uri_parser::is_user_info_character(ch) || ch == '%' || ch == '+'; }); case components::host: return uri::encode_impl(raw, [](int ch) -> bool { // No encoding of ASCII characters in host name (RFC 3986 3.2.2) return ch > 127; }); case components::path: return uri::encode_impl(raw, [](int ch) -> bool { return !uri_parser::is_path_character(ch) || ch == '%' || ch == '+'; }); case components::query: return uri::encode_impl(raw, [](int ch) -> bool { return !uri_parser::is_query_character(ch) || ch == '%' || ch == '+'; }); case components::fragment: return uri::encode_impl(raw, [](int ch) -> bool { return !uri_parser::is_fragment_character(ch) || ch == '%' || ch == '+'; }); case components::full_uri: default: return uri::encode_impl(raw, [](int ch) -> bool { return !uri_parser::is_unreserved(ch) && !uri_parser::is_reserved(ch); }); }; } /// /// Helper function to convert a hex character digit to a decimal character value. /// Throws an exception if not a valid hex digit. /// static int hex_char_digit_to_decimal_char(int hex) { int decimal; if(hex >= '0' && hex <= '9') { decimal = hex - '0'; } else if(hex >= 'A' && hex <= 'F') { decimal = 10 + (hex - 'A'); } else if(hex >= 'a' && hex <= 'f') { decimal = 10 + (hex - 'a'); } else { throw uri_exception("Invalid hexadecimal digit"); } return decimal; } utility::string_t uri::decode(const utility::string_t &encoded) { std::string utf8raw; for(auto iter = encoded.begin(); iter != encoded.end(); ++iter) { if(*iter == _XPLATSTR('%')) { if(++iter == encoded.end()) { throw uri_exception("Invalid URI string, two hexadecimal digits must follow '%'"); } int decimal_value = hex_char_digit_to_decimal_char(static_cast(*iter)) << 4; if(++iter == encoded.end()) { throw uri_exception("Invalid URI string, two hexadecimal digits must follow '%'"); } decimal_value += hex_char_digit_to_decimal_char(static_cast(*iter)); utf8raw.push_back(static_cast(decimal_value)); } else { // encoded string has to be ASCII. utf8raw.push_back(reinterpret_cast(*iter)); } } return to_string_t(utf8raw); } std::vector uri::split_path(const utility::string_t &path) { std::vector results; utility::istringstream_t iss(path); iss.imbue(std::locale::classic()); utility::string_t s; while (std::getline(iss, s, _XPLATSTR('/'))) { if (!s.empty()) { results.push_back(s); } } return results; } std::map uri::split_query(const utility::string_t &query) { std::map results; // Split into key value pairs separated by '&'. size_t prev_amp_index = 0; while(prev_amp_index != utility::string_t::npos) { size_t amp_index = query.find_first_of(_XPLATSTR('&'), prev_amp_index); if (amp_index == utility::string_t::npos) amp_index = query.find_first_of(_XPLATSTR(';'), prev_amp_index); utility::string_t key_value_pair = query.substr( prev_amp_index, amp_index == utility::string_t::npos ? query.size() - prev_amp_index : amp_index - prev_amp_index); prev_amp_index = amp_index == utility::string_t::npos ? utility::string_t::npos : amp_index + 1; size_t equals_index = key_value_pair.find_first_of(_XPLATSTR('=')); if(equals_index == utility::string_t::npos) { continue; } else if (equals_index == 0) { utility::string_t value(key_value_pair.begin() + equals_index + 1, key_value_pair.end()); results[_XPLATSTR("")] = value; } else { utility::string_t key(key_value_pair.begin(), key_value_pair.begin() + equals_index); utility::string_t value(key_value_pair.begin() + equals_index + 1, key_value_pair.end()); results[key] = value; } } return results; } bool uri::validate(const utility::string_t &uri_string) { return uri_parser::validate(uri_string); } uri uri::authority() const { return uri_builder().set_scheme(this->scheme()).set_host(this->host()).set_port(this->port()).set_user_info(this->user_info()).to_uri(); } uri uri::resource() const { return uri_builder().set_path(this->path()).set_query(this->query()).set_fragment(this->fragment()).to_uri(); } bool uri::operator == (const uri &other) const { // Each individual URI component must be decoded before performing comparison. // TFS # 375865 if (this->is_empty() && other.is_empty()) { return true; } else if (this->is_empty() || other.is_empty()) { return false; } else if (this->scheme() != other.scheme()) { // scheme is canonicalized to lowercase return false; } else if(uri::decode(this->user_info()) != uri::decode(other.user_info())) { return false; } else if (uri::decode(this->host()) != uri::decode(other.host())) { // host is canonicalized to lowercase return false; } else if (this->port() != other.port()) { return false; } else if (uri::decode(this->path()) != uri::decode(other.path())) { return false; } else if (uri::decode(this->query()) != uri::decode(other.query())) { return false; } else if (uri::decode(this->fragment()) != uri::decode(other.fragment())) { return false; } return true; } } #if HC_PLATFORM_IS_MICROSOFT #pragma warning( pop ) #endif ================================================ FILE: Include/cpprestinclude/cpprest/details/uri_builder.hpp ================================================ /*** * ==++== * * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * ==--== * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ * * Builder for constructing URIs. * * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk * * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ****/ #pragma once namespace web { namespace http { // URI class has been moved from web::http namespace to web namespace. // The below using declarations ensure we don't break existing code. // Please use the web::uri class going forward. using web::uri; using web::uri_builder; } uri_builder &uri_builder::append_path(const utility::string_t &path, bool is_encode) { if(path.empty() || path == _XPLATSTR("/")) { return *this; } auto encoded_path = is_encode ? uri::encode_uri(path, uri::components::path) : path; auto thisPath = this->path(); if(thisPath.empty() || thisPath == _XPLATSTR("/")) { if(encoded_path.front() != _XPLATSTR('/')) { set_path(_XPLATSTR("/") + encoded_path); } else { set_path(encoded_path); } } else if(thisPath.back() == _XPLATSTR('/') && encoded_path.front() == _XPLATSTR('/')) { thisPath.pop_back(); set_path(thisPath + encoded_path); } else if(thisPath.back() != _XPLATSTR('/') && encoded_path.front() != _XPLATSTR('/')) { set_path(thisPath + _XPLATSTR("/") + encoded_path); } else { // Only one slash. set_path(thisPath + encoded_path); } return *this; } uri_builder &uri_builder::append_query(const utility::string_t &query, bool is_encode) { if(query.empty()) { return *this; } auto encoded_query = is_encode ? uri::encode_uri(query, uri::components::query) : query; auto thisQuery = this->query(); if (thisQuery.empty()) { this->set_query(encoded_query); } else if(thisQuery.back() == _XPLATSTR('&') && encoded_query.front() == _XPLATSTR('&')) { thisQuery.pop_back(); this->set_query(thisQuery + encoded_query); } else if(thisQuery.back() != _XPLATSTR('&') && encoded_query.front() != _XPLATSTR('&')) { this->set_query(thisQuery + _XPLATSTR("&") + encoded_query); } else { // Only one ampersand. this->set_query(thisQuery + encoded_query); } return *this; } uri_builder &uri_builder::append(const http::uri &relative_uri) { append_path(relative_uri.path()); append_query(relative_uri.query()); this->set_fragment(this->fragment() + relative_uri.fragment()); return *this; } utility::string_t uri_builder::to_string() { return to_uri().to_string(); } uri uri_builder::to_uri() { return uri(m_uri); } bool uri_builder::is_valid() { return uri::validate(m_uri.join()); } } // namespace web ================================================ FILE: Include/cpprestinclude/cpprest/details/uri_parser.h ================================================ /*** * ==++== * * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * ==--== * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ * * URI parsing implementation * * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk * * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ****/ #pragma once #include namespace web { namespace details { namespace uri_parser { /// /// Parses the uri, attempting to determine its validity. /// /// This function accepts both uris ('http://msn.com') and uri relative-references ('path1/path2?query') /// bool validate(const utility::string_t &encoded_string); /// /// Parses the uri, setting each provided string to the value of that component. Components /// that are not part of the provided text are set to the empty string. Component strings /// DO NOT contain their beginning or ending delimiters. /// /// This function accepts both uris ('http://msn.com') and uri relative-references ('path1/path2?query') /// bool parse(const utility::string_t &encoded_string, uri_components &components); /// /// Unreserved characters are those that are allowed in a URI but do not have a reserved purpose. They include: /// - A-Z /// - a-z /// - 0-9 /// - '-' (hyphen) /// - '.' (period) /// - '_' (underscore) /// - '~' (tilde) /// inline bool is_unreserved(int c) { return ::utility::details::is_alnum((char)c) || c == '-' || c == '.' || c == '_' || c == '~'; } /// /// General delimiters serve as the delimiters between different uri components. /// General delimiters include: /// - All of these :/?#[]@ /// inline bool is_gen_delim(int c) { return c == ':' || c == '/' || c == '?' || c == '#' || c == '[' || c == ']' || c == '@'; } /// /// Subdelimiters are those characters that may have a defined meaning within component /// of a uri for a particular scheme. They do not serve as delimiters in any case between /// uri segments. sub_delimiters include: /// - All of these !$&'()*+,;= /// inline bool is_sub_delim(int c) { switch (c) { case '!': case '$': case '&': case '\'': case '(': case ')': case '*': case '+': case ',': case ';': case '=': return true; default: return false; } } /// /// Reserved characters includes the general delimiters and sub delimiters. Some characters /// are neither reserved nor unreserved, and must be percent-encoded. /// inline bool is_reserved(int c) { return is_gen_delim(c) || is_sub_delim(c); } /// /// Legal characters in the scheme portion include: /// - Any alphanumeric character /// - '+' (plus) /// - '-' (hyphen) /// - '.' (period) /// /// Note that the scheme must BEGIN with an alpha character. /// inline bool is_scheme_character(int c) { return ::utility::details::is_alnum((char)c) || c == '+' || c == '-' || c == '.'; } /// /// Legal characters in the user information portion include: /// - Any unreserved character /// - The percent character ('%'), and thus any percent-endcoded octet /// - The sub-delimiters /// - ':' (colon) /// inline bool is_user_info_character(int c) { return is_unreserved(c) || is_sub_delim(c) || c == '%' || c == ':'; } /// /// Legal characters in the host portion include: /// - Any unreserved character /// - The percent character ('%'), and thus any percent-endcoded octet /// - The sub-delimiters /// - ':' (colon) /// - '[' (open bracket) /// - ']' (close bracket) /// inline bool is_host_character(int c) { return is_unreserved(c) || is_sub_delim(c) || c == '%' || c == ':' || c == '[' || c == ']'; } /// /// Legal characters in the authority portion include: /// - Any unreserved character /// - The percent character ('%'), and thus any percent-endcoded octet /// - The sub-delimiters /// - ':' (colon) /// /// Note that we don't currently support: /// - IPv6 addresses (requires '[]') /// inline bool is_authority_character(int c) { return is_unreserved(c) || is_sub_delim(c) || c == '%' || c == '@' || c == ':'; } /// /// Legal characters in the path portion include: /// - Any unreserved character /// - The percent character ('%'), and thus any percent-endcoded octet /// - The sub-delimiters /// - ':' (colon) /// - '@' (ampersand) /// inline bool is_path_character(int c) { return is_unreserved(c) || is_sub_delim(c) || c == '%' || c == '/' || c == ':' || c == '@'; } /// /// Legal characters in the query portion include: /// - Any path character /// - '?' (question mark) /// inline bool is_query_character(int c) { return is_path_character(c) || c == '?'; } /// /// Legal characters in the fragment portion include: /// - Any path character /// - '?' (question mark) /// inline bool is_fragment_character(int c) { // this is intentional, they have the same set of legal characters return is_query_character(c); } /// /// Parses the uri, setting the given pointers to locations inside the given buffer. /// 'encoded' is expected to point to an encoded zero-terminated string containing a uri /// bool inner_parse( const utility::char_t *encoded, const utility::char_t **scheme_begin, const utility::char_t **scheme_end, const utility::char_t **uinfo_begin, const utility::char_t **uinfo_end, const utility::char_t **host_begin, const utility::char_t **host_end, _Out_ int *port, const utility::char_t **path_begin, const utility::char_t **path_end, const utility::char_t **query_begin, const utility::char_t **query_end, const utility::char_t **fragment_begin, const utility::char_t **fragment_end); } }} ================================================ FILE: Include/cpprestinclude/cpprest/details/uri_parser.hpp ================================================ /*** * ==++== * * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * ==--== * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ * * URI parsing implementation * * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk * * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ****/ #pragma once #include namespace web { namespace details { namespace uri_parser { bool validate(const utility::string_t &encoded_string) { const utility::char_t *scheme_begin = nullptr; const utility::char_t *scheme_end = nullptr; const utility::char_t *uinfo_begin = nullptr; const utility::char_t *uinfo_end = nullptr; const utility::char_t *host_begin = nullptr; const utility::char_t *host_end = nullptr; int port_ptr = 0; const utility::char_t *path_begin = nullptr; const utility::char_t *path_end = nullptr; const utility::char_t *query_begin = nullptr; const utility::char_t *query_end = nullptr; const utility::char_t *fragment_begin = nullptr; const utility::char_t *fragment_end = nullptr; // perform a parse, but don't copy out the data return inner_parse( encoded_string.c_str(), &scheme_begin, &scheme_end, &uinfo_begin, &uinfo_end, &host_begin, &host_end, &port_ptr, &path_begin, &path_end, &query_begin, &query_end, &fragment_begin, &fragment_end); } bool parse(const utility::string_t &encoded_string, uri_components &components) { const utility::char_t *scheme_begin = nullptr; const utility::char_t *scheme_end = nullptr; const utility::char_t *host_begin = nullptr; const utility::char_t *host_end = nullptr; const utility::char_t *uinfo_begin = nullptr; const utility::char_t *uinfo_end = nullptr; int port_ptr = 0; const utility::char_t *path_begin = nullptr; const utility::char_t *path_end = nullptr; const utility::char_t *query_begin = nullptr; const utility::char_t *query_end = nullptr; const utility::char_t *fragment_begin = nullptr; const utility::char_t *fragment_end = nullptr; if (inner_parse( encoded_string.c_str(), &scheme_begin, &scheme_end, &uinfo_begin, &uinfo_end, &host_begin, &host_end, &port_ptr, &path_begin, &path_end, &query_begin, &query_end, &fragment_begin, &fragment_end)) { if (scheme_begin) { components.m_scheme.assign(scheme_begin, scheme_end); // convert scheme to lowercase std::transform(components.m_scheme.begin(), components.m_scheme.end(), components.m_scheme.begin(), [](utility::char_t c) { return (utility::char_t)tolower(c); }); } else { components.m_scheme.clear(); } if (uinfo_begin) { components.m_user_info.assign(uinfo_begin, uinfo_end); } if (host_begin) { components.m_host.assign(host_begin, host_end); // convert host to lowercase std::transform(components.m_host.begin(), components.m_host.end(), components.m_host.begin(), [](utility::char_t c) { return (utility::char_t)tolower(c); }); } else { components.m_host.clear(); } if (port_ptr) { components.m_port = port_ptr; } else { components.m_port = 0; } if (path_begin) { components.m_path.assign(path_begin, path_end); } else { // default path to begin with a slash for easy comparison components.m_path = _XPLATSTR("/"); } if (query_begin) { components.m_query.assign(query_begin, query_end); } else { components.m_query.clear(); } if (fragment_begin) { components.m_fragment.assign(fragment_begin, fragment_end); } else { components.m_fragment.clear(); } return true; } else { return false; } } bool inner_parse( const utility::char_t *encoded, const utility::char_t **scheme_begin, const utility::char_t **scheme_end, const utility::char_t **uinfo_begin, const utility::char_t **uinfo_end, const utility::char_t **host_begin, const utility::char_t **host_end, _Out_ int *port, const utility::char_t **path_begin, const utility::char_t **path_end, const utility::char_t **query_begin, const utility::char_t **query_end, const utility::char_t **fragment_begin, const utility::char_t **fragment_end) { *scheme_begin = nullptr; *scheme_end = nullptr; *uinfo_begin = nullptr; *uinfo_end = nullptr; *host_begin = nullptr; *host_end = nullptr; *port = 0; *path_begin = nullptr; *path_end = nullptr; *query_begin = nullptr; *query_end = nullptr; *fragment_begin = nullptr; *fragment_end = nullptr; const utility::char_t *p = encoded; // IMPORTANT -- A uri may either be an absolute uri, or an relative-reference // Absolute: 'http://host.com' // Relative-Reference: '//:host.com', '/path1/path2?query', './path1:path2' // A Relative-Reference can be disambiguated by parsing for a ':' before the first slash bool is_relative_reference = true; const utility::char_t *p2 = p; for (;*p2 != _XPLATSTR('/') && *p2 != _XPLATSTR('\0'); p2++) { if (*p2 == _XPLATSTR(':')) { // found a colon, the first portion is a scheme is_relative_reference = false; break; } } if (!is_relative_reference) { // the first character of a scheme must be a letter if (!isalpha(*p)) { return false; } // start parsing the scheme, it's always delimited by a colon (must be present) *scheme_begin = p++; for (;*p != ':'; p++) { if (!is_scheme_character(*p)) { return false; } } *scheme_end = p; // skip over the colon p++; } // if we see two slashes next, then we're going to parse the authority portion // later on we'll break up the authority into the port and host const utility::char_t *authority_begin = nullptr; const utility::char_t *authority_end = nullptr; if (*p == _XPLATSTR('/') && p[1] == _XPLATSTR('/')) { // skip over the slashes p += 2; authority_begin = p; // the authority is delimited by a slash (resource), question-mark (query) or octothorpe (fragment) // or by EOS. The authority could be empty ('file:///C:\file_name.txt') for (;*p != _XPLATSTR('/') && *p != _XPLATSTR('?') && *p != _XPLATSTR('#') && *p != _XPLATSTR('\0'); p++) { // We're NOT currently supporting IPv6, IPvFuture or username/password in authority if (!is_authority_character(*p)) { return false; } } authority_end = p; // now lets see if we have a port specified -- by working back from the end if (authority_begin != authority_end) { // the port is made up of all digits const utility::char_t *port_begin = authority_end - 1; for (;isdigit(*port_begin) && port_begin != authority_begin; port_begin--) { } if (*port_begin == _XPLATSTR(':')) { // has a port *host_begin = authority_begin; *host_end = port_begin; //skip the colon port_begin++; *port = utility::conversions::scan_string(utility::string_t(port_begin, authority_end), std::locale::classic()); } else { // no port *host_begin = authority_begin; *host_end = authority_end; } // look for a user_info component const utility::char_t *u_end = *host_begin; for (;is_user_info_character(*u_end) && u_end != *host_end; u_end++) { } if (*u_end == _XPLATSTR('@')) { *host_begin = u_end+1; *uinfo_begin = authority_begin; *uinfo_end = u_end; } else { uinfo_end = uinfo_begin = nullptr; } } } // if we see a path character or a slash, then the // if we see a slash, or any other legal path character, parse the path next if (*p == _XPLATSTR('/') || is_path_character(*p)) { *path_begin = p; // the path is delimited by a question-mark (query) or octothorpe (fragment) or by EOS for (;*p != _XPLATSTR('?') && *p != _XPLATSTR('#') && *p != _XPLATSTR('\0'); p++) { if (!is_path_character(*p)) { return false; } } *path_end = p; } // if we see a ?, then the query is next if (*p == _XPLATSTR('?')) { // skip over the question mark p++; *query_begin = p; // the query is delimited by a '#' (fragment) or EOS for (;*p != _XPLATSTR('#') && *p != _XPLATSTR('\0'); p++) { if (!is_query_character(*p)) { return false; } } *query_end = p; } // if we see a #, then the fragment is next if (*p == _XPLATSTR('#')) { // skip over the hash mark p++; *fragment_begin = p; // the fragment is delimited by EOS for (;*p != _XPLATSTR('\0'); p++) { if (!is_fragment_character(*p)) { return false; } } *fragment_end = p; } return true; } }}} ================================================ FILE: Include/cpprestinclude/cpprest/details/web_utilities.h ================================================ /*** * ==++== * * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * ==--== * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ * * utility classes used by the different web:: clients * * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ****/ #pragma once #include "cpprest/asyncrt_utils.h" namespace web { namespace http { namespace client { namespace details { class winhttp_client; class winrt_client; class asio_context; }}} namespace websockets { namespace client { namespace details { class winrt_callback_client; class wspp_callback_client; }}} namespace details { class zero_memory_deleter { public: _ASYNCRTIMP void operator()(::utility::string_t *data) const; }; typedef std::unique_ptr<::utility::string_t, zero_memory_deleter> plaintext_string; #if defined(_WIN32) && !defined(CPPREST_TARGET_XP) #if defined(__cplusplus_winrt) class winrt_encryption { public: winrt_encryption() {} _ASYNCRTIMP winrt_encryption(const std::wstring &data); _ASYNCRTIMP plaintext_string decrypt() const; private: ::pplx::task m_buffer; }; #else class win32_encryption { public: win32_encryption() {} _ASYNCRTIMP win32_encryption(const std::wstring &data); _ASYNCRTIMP ~win32_encryption(); _ASYNCRTIMP plaintext_string decrypt() const; private: std::vector m_buffer; size_t m_numCharacters; }; #endif #endif } /// /// Represents a set of user credentials (user name and password) to be used /// for authentication. /// class credentials { public: /// /// Constructs an empty set of credentials without a user name or password. /// credentials() {} /// /// Constructs credentials from given user name and password. /// /// User name as a string. /// Password as a string. credentials(utility::string_t username, const utility::string_t & password) : m_username(std::move(username)), m_password(password) {} /// /// The user name associated with the credentials. /// /// A string containing the user name. const utility::string_t &username() const { return m_username; } /// /// The password for the user name associated with the credentials. /// /// A string containing the password. CASABLANCA_DEPRECATED("This API is deprecated for security reasons to avoid unnecessary password copies stored in plaintext.") utility::string_t password() const { #if defined(_WIN32) && !defined(CPPREST_TARGET_XP) return utility::string_t(*m_password.decrypt()); #else return m_password; #endif } /// /// Checks if credentials have been set /// /// true if user name and password is set, false otherwise. bool is_set() const { return !m_username.empty(); } private: friend class http::client::details::winhttp_client; friend class http::client::details::winrt_client; friend class http::client::details::asio_context; friend class websockets::client::details::winrt_callback_client; friend class websockets::client::details::wspp_callback_client; details::plaintext_string decrypt() const { // Encryption APIs not supported on XP #if defined(_WIN32) && !defined(CPPREST_TARGET_XP) return m_password.decrypt(); #else return details::plaintext_string(new ::utility::string_t(m_password)); #endif } ::utility::string_t m_username; #if defined(_WIN32) && !defined(CPPREST_TARGET_XP) #if defined(__cplusplus_winrt) details::winrt_encryption m_password; #else details::win32_encryption m_password; #endif #else ::utility::string_t m_password; #endif }; /// /// web_proxy represents the concept of the web proxy, which can be auto-discovered, /// disabled, or specified explicitly by the user. /// class web_proxy { enum web_proxy_mode_internal{ use_default_, use_auto_discovery_, disabled_, user_provided_ }; public: enum web_proxy_mode{ use_default = use_default_, use_auto_discovery = use_auto_discovery_, disabled = disabled_}; /// /// Constructs a proxy with the default settings. /// web_proxy() : m_address(_XPLATSTR("")), m_mode(use_default_) {} /// /// Creates a proxy with specified mode. /// /// Mode to use. web_proxy( web_proxy_mode mode ) : m_address(_XPLATSTR("")), m_mode(static_cast(mode)) {} /// /// Creates a proxy explicitly with provided address. /// /// Proxy URI to use. web_proxy( uri address ) : m_address(address), m_mode(user_provided_) {} /// /// Gets this proxy's URI address. Returns an empty URI if not explicitly set by user. /// /// A reference to this proxy's URI. const uri& address() const { return m_address; } /// /// Gets the credentials used for authentication with this proxy. /// /// Credentials to for this proxy. const web::credentials& credentials() const { return m_credentials; } /// /// Sets the credentials to use for authentication with this proxy. /// /// Credentials to use for this proxy. void set_credentials(web::credentials cred) { if( m_mode == disabled_ ) { throw std::invalid_argument("Cannot attach credentials to a disabled proxy"); } m_credentials = std::move(cred); } /// /// Checks if this proxy was constructed with default settings. /// /// True if default, false otherwise. bool is_default() const { return m_mode == use_default_; } /// /// Checks if using a proxy is disabled. /// /// True if disabled, false otherwise. bool is_disabled() const { return m_mode == disabled_; } /// /// Checks if the auto discovery protocol, WPAD, is to be used. /// /// True if auto discovery enabled, false otherwise. bool is_auto_discovery() const { return m_mode == use_auto_discovery_; } /// /// Checks if a proxy address is explicitly specified by the user. /// /// True if a proxy address was explicitly specified, false otherwise. bool is_specified() const { return m_mode == user_provided_; } private: web::uri m_address; web_proxy_mode_internal m_mode; web::credentials m_credentials; }; } ================================================ FILE: Include/cpprestinclude/cpprest/http_headers.h ================================================ /*** * Copyright (C) Microsoft. All rights reserved. * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. * * ==--== * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ * * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk * * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ****/ #pragma once #include #include #include #include #include #if HC_PLATFORM_IS_MICROSOFT #pragma warning( push ) #pragma warning( disable : 26444 ) // ignore various unnamed objects #endif namespace web { namespace http { /// /// Represents HTTP headers, acts like a map. /// class http_headers { public: struct _case_insensitive_cmp { bool operator()(const utility::string_t &str1, const utility::string_t &str2) const { #ifdef _WIN32 return _wcsicmp(str1.c_str(), str2.c_str()) < 0; #else return utility::cmp::icmp(str1, str2) < 0; #endif } }; /// /// STL-style typedefs /// typedef std::map::key_type key_type; typedef std::map::key_compare key_compare; typedef std::map::allocator_type allocator_type; typedef std::map::size_type size_type; typedef std::map::difference_type difference_type; typedef std::map::pointer pointer; typedef std::map::const_pointer const_pointer; typedef std::map::reference reference; typedef std::map::const_reference const_reference; typedef std::map::iterator iterator; typedef std::map::const_iterator const_iterator; typedef std::map::reverse_iterator reverse_iterator; typedef std::map::const_reverse_iterator const_reverse_iterator; /// /// Constructs an empty set of HTTP headers. /// http_headers() {} /// /// Copy constructor. /// /// An http_headers object to copy from. http_headers(const http_headers &other) : m_headers(other.m_headers) {} /// /// Assignment operator. /// /// An http_headers object to copy from. http_headers &operator=(const http_headers &other) { if(this != &other) { m_headers = other.m_headers; } return *this; } /// /// Move constructor. /// /// An http_headers object to move. http_headers(http_headers &&other) noexcept : m_headers(std::move(other.m_headers)) {} /// /// Move assignment operator. /// /// An http_headers object to move. http_headers &operator=(http_headers &&other) noexcept { if(this != &other) { m_headers = std::move(other.m_headers); } return *this; } /// /// Adds a header field using the '<<' operator. /// /// The name of the header field. /// The value of the header field. /// If the header field exists, the value will be combined as comma separated string. template void add(const key_type& name, const _t1& value) { if (has(name)) { m_headers[name].append(_XPLATSTR(", ")).append(utility::conversions::print_string(value)); } else { m_headers[name] = utility::conversions::print_string(value); } } /// /// Removes a header field. /// /// The name of the header field. void remove(const key_type& name) { m_headers.erase(name); } /// /// Removes all elements from the headers. /// void clear() { m_headers.clear(); } /// /// Checks if there is a header with the given key. /// /// The name of the header field. /// true if there is a header with the given name, false otherwise. bool has(const key_type& name) const { return m_headers.find(name) != m_headers.end(); } /// /// Returns the number of header fields. /// /// Number of header fields. size_type size() const { return m_headers.size(); } /// /// Tests to see if there are any header fields. /// /// true if there are no headers, false otherwise. bool empty() const { return m_headers.empty(); } /// /// Returns a reference to header field with given name, if there is no header field one is inserted. /// utility::string_t & operator[](const key_type &name) { return m_headers[name]; } /// /// Checks if a header field exists with given name and returns an iterator if found. Otherwise /// and iterator to end is returned. /// /// The name of the header field. /// An iterator to where the HTTP header is found. iterator find(const key_type &name) { return m_headers.find(name); } const_iterator find(const key_type &name) const { return m_headers.find(name); } /// /// Attempts to match a header field with the given name using the '>>' operator. /// /// The name of the header field. /// The value of the header field. /// true if header field was found and successfully stored in value parameter. template bool match(const key_type &name, _t1 &value) const { auto iter = m_headers.find(name); if (iter != m_headers.end()) { // Check to see if doesn't have a value. if(iter->second.empty()) { bind_impl(iter->second, value); return true; } return bind_impl(iter->second, value); } else { return false; } } /// /// Returns an iterator referring to the first header field. /// /// An iterator to the beginning of the HTTP headers iterator begin() { return m_headers.begin(); } const_iterator begin() const { return m_headers.begin(); } /// /// Returns an iterator referring to the past-the-end header field. /// /// An iterator to the element past the end of the HTTP headers. iterator end() { return m_headers.end(); } const_iterator end() const { return m_headers.end(); } /// /// Gets the content length of the message. /// /// The length of the content. _ASYNCRTIMP utility::size64_t content_length() const; /// /// Sets the content length of the message. /// /// The length of the content. _ASYNCRTIMP void set_content_length(utility::size64_t length); /// /// Gets the content type of the message. /// /// The content type of the body. _ASYNCRTIMP utility::string_t content_type() const; /// /// Sets the content type of the message. /// /// The content type of the body. _ASYNCRTIMP void set_content_type(utility::string_t type); /// /// Gets the cache control header of the message. /// /// The cache control header value. _ASYNCRTIMP utility::string_t cache_control() const; /// /// Sets the cache control header of the message. /// /// The cache control header value. _ASYNCRTIMP void set_cache_control(utility::string_t control); /// /// Gets the date header of the message. /// /// The date header value. _ASYNCRTIMP utility::string_t date() const; /// /// Sets the date header of the message. /// /// The date header value. _ASYNCRTIMP void set_date(const utility::datetime& date); private: template bool bind_impl(const key_type &text, _t &ref) const { utility::istringstream_t iss(text); iss.imbue(std::locale::classic()); iss >> ref; if (iss.fail() || !iss.eof()) { return false; } return true; } bool bind_impl(const key_type &text, ::utility::string_t &ref) const { ref = text; return true; } // Headers are stored in a map with case insensitive key. std::map m_headers; }; namespace details { /// /// Serialize the http_headers into name:value pairs separated by a carriage return and line feed. /// utility::string_t flatten_http_headers(const http_headers &headers); #if defined(_WIN32) /// /// Parses a string containing Http headers. /// void parse_headers_string(_Inout_z_ utf16char *headersStr, http_headers &headers); #endif } }} #if HC_PLATFORM_IS_MICROSOFT #pragma warning( pop ) #endif ================================================ FILE: Include/cpprestinclude/cpprest/http_msg.h ================================================ /*** * ==++== * * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * ==--== * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ * * HTTP Library: Request and reply message definitions. * * For the latest on this and related APIs, please see http://casablanca.codeplex.com. * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk * * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ****/ #pragma once #include #include #include #include #include #if !XSAPI_NO_PPL #include "pplx/pplxtasks.h" #endif // !XSAPI_NO_PPL #include "cpprest/json.h" #include "cpprest/uri.h" #include "cpprest/http_headers.h" #include "cpprest/details/cpprest_compat.h" #include "cpprest/asyncrt_utils.h" #include "cpprest/streams.h" #include "cpprest/containerstream.h" #if HC_PLATFORM_IS_MICROSOFT #pragma warning( push ) #pragma warning( disable : 26444 ) // ignore various unnamed objects #endif #if HC_PLATFORM == HC_PLATFORM_GDK #define HTTPMSG_UTF16(x) L##x // avoids'to_utf16string' identifier not found build error #else #define HTTPMSG_UTF16(x) utility::conversions::to_utf16string(x) #endif namespace web { namespace http { #if !XSAPI_NO_PPL // URI class has been moved from web::http namespace to web namespace. // The below using declarations ensure we don't break existing code. // Please use the web::uri class going forward. using web::uri; using web::uri_builder; namespace client { class http_client; } /// /// Predefined method strings for the standard HTTP methods mentioned in the /// HTTP 1.1 specification. /// typedef utility::string_t method; /// /// Common HTTP methods. /// class methods { public: #define _METHODS #define DAT(a,b) _ASYNCRTIMP const static method a; #include "cpprest/details/http_constants.dat" #undef _METHODS #undef DAT }; #endif // !XSAPI_NO_PPL typedef unsigned short status_code; /// /// Predefined values for all of the standard HTTP 1.1 response status codes. /// class status_codes { public: #define _PHRASES #define DAT(a,b,c) const static status_code a=b; #include "cpprest/details/http_constants.dat" #undef _PHRASES #undef DAT }; #if !XSAPI_NO_PPL namespace details { /// /// Constants for MIME types. /// class mime_types { public: #define _MIME_TYPES #define DAT(a,b) _ASYNCRTIMP const static utility::string_t a; #include "cpprest/details/http_constants.dat" #undef _MIME_TYPES #undef DAT }; /// /// Constants for charset types. /// class charset_types { public: #define _CHARSET_TYPES #define DAT(a,b) _ASYNCRTIMP const static utility::string_t a; #include "cpprest/details/http_constants.dat" #undef _CHARSET_TYPES #undef DAT }; } /// Message direction namespace message_direction { /// /// Enumeration used to denote the direction of a message: a request with a body is /// an upload, a response with a body is a download. /// enum direction { upload, download }; } typedef utility::string_t reason_phrase; typedef std::function progress_handler; struct http_status_to_phrase { unsigned short id; reason_phrase phrase; }; /// /// Constants for the HTTP headers mentioned in RFC 2616. /// class header_names { public: #define _HEADER_NAMES #define DAT(a,b) _ASYNCRTIMP const static utility::string_t a; #include "cpprest/details/http_constants.dat" #undef _HEADER_NAMES #undef DAT }; /// /// Represents an HTTP error. This class holds an error message and an optional error code. /// class http_exception : public std::exception { public: /// /// Creates an http_exception with just a string message and no error code. /// /// Error message string. http_exception(const utility::string_t &whatArg) : m_msg(utility::conversions::to_utf8string(whatArg)) {} #ifdef _WIN32 /// /// Creates an http_exception with just a string message and no error code. /// /// Error message string. http_exception(std::string whatArg) : m_msg(std::move(whatArg)) {} #endif /// /// Creates an http_exception with from a error code using the current platform error category. /// The message of the error code will be used as the what() string message. /// /// Error code value. http_exception(int errorCode) : m_errorCode(utility::details::create_error_code(errorCode)) { m_msg = m_errorCode.message(); } /// /// Creates an http_exception with from a error code using the current platform error category. /// /// Error code value. /// Message to use in what() string. http_exception(int errorCode, const utility::string_t &whatArg) : m_errorCode(utility::details::create_error_code(errorCode)), m_msg(utility::conversions::to_utf8string(whatArg)) {} #ifdef _WIN32 /// /// Creates an http_exception with from a error code using the current platform error category. /// /// Error code value. /// Message to use in what() string. http_exception(int errorCode, std::string whatArg) : m_errorCode(utility::details::create_error_code(errorCode)), m_msg(std::move(whatArg)) {} #endif /// /// Creates an http_exception with from a error code and category. The message of the error code will be used /// as the what string message. /// /// Error code value. /// Error category for the code. http_exception(int errorCode, const std::error_category &cat) : m_errorCode(std::error_code(errorCode, cat)) { m_msg = m_errorCode.message(); } /// /// Gets a string identifying the cause of the exception. /// /// A null terminated character string. const char* what() const CPPREST_NOEXCEPT { return m_msg.c_str(); } /// /// Retrieves the underlying error code causing this exception. /// /// A std::error_code. const std::error_code & error_code() const { return m_errorCode; } private: std::error_code m_errorCode; std::string m_msg; }; namespace details { /// /// Base class for HTTP messages. /// This class is to store common functionality so it isn't duplicated on /// both the request and response side. /// class http_msg_base { public: friend class http::client::http_client; _ASYNCRTIMP http_msg_base(); virtual ~http_msg_base() {} http_headers &headers() { return m_headers; } _ASYNCRTIMP void set_body(const concurrency::streams::istream &instream, const utf8string &contentType); _ASYNCRTIMP void set_body(const concurrency::streams::istream &instream, const utf16string &contentType); _ASYNCRTIMP void set_body(const concurrency::streams::istream &instream, utility::size64_t contentLength, const utf8string &contentType); _ASYNCRTIMP void set_body(const concurrency::streams::istream &instream, utility::size64_t contentLength, const utf16string &contentType); /// /// Helper function for extract functions. Parses the Content-Type header and check to make sure it matches, /// throws an exception if not. /// /// If true ignores the Content-Type header value. /// Function to verify additional information on Content-Type. /// A string containing the charset, an empty string if no Content-Type header is empty. utility::string_t parse_and_check_content_type(bool ignore_content_type, const std::function &check_content_type); _ASYNCRTIMP utf8string extract_utf8string(bool ignore_content_type = false); _ASYNCRTIMP utf16string extract_utf16string(bool ignore_content_type = false); _ASYNCRTIMP utility::string_t extract_string(bool ignore_content_type = false); _ASYNCRTIMP json::value _extract_json(bool ignore_content_type = false); _ASYNCRTIMP std::vector _extract_vector(); virtual _ASYNCRTIMP utility::string_t to_string() const; /// /// Completes this message /// virtual _ASYNCRTIMP void _complete(utility::size64_t bodySize, const std::exception_ptr &exceptionPtr = std::exception_ptr()); /// /// Set the stream through which the message body could be read /// void set_instream(const concurrency::streams::istream &instream) { m_inStream = instream; } /// /// Get the stream through which the message body could be read /// const concurrency::streams::istream & instream() const { return m_inStream; } /// /// Set the stream through which the message body could be written /// void set_outstream(const concurrency::streams::ostream &outstream, bool is_default) { m_outStream = outstream; m_default_outstream = is_default; } /// /// Get the stream through which the message body could be written /// const concurrency::streams::ostream & outstream() const { return m_outStream; } const pplx::task_completion_event & _get_data_available() const { return m_data_available; } /// /// Prepare the message with an output stream to receive network data /// _ASYNCRTIMP void _prepare_to_receive_data(); /// /// Determine the content length /// /// /// size_t::max if there is content with unknown length (transfer_encoding:chunked) /// 0 if there is no content /// length if there is content with known length /// /// /// This routine should only be called after a msg (request/response) has been /// completely constructed. /// _ASYNCRTIMP size_t _get_content_length(); protected: /// /// Stream to read the message body. /// By default this is an invalid stream. The user could set the instream on /// a request by calling set_request_stream(...). This would also be set when /// set_body() is called - a stream from the body is constructed and set. /// Even in the presense of msg body this stream could be invalid. An example /// would be when the user sets an ostream for the response. With that API the /// user does not provide the ability to read the msg body. /// Thus m_instream is valid when there is a msg body and it can actually be read /// concurrency::streams::istream m_inStream; /// /// stream to write the msg body /// By default this is an invalid stream. The user could set this on the response /// (for http_client). In all the other cases we would construct one to transfer /// the data from the network into the message body. /// concurrency::streams::ostream m_outStream; http_headers m_headers; bool m_default_outstream; /// The TCE is used to signal the availability of the message body. pplx::task_completion_event m_data_available; }; /// /// Base structure for associating internal server information /// with an HTTP request/response. /// class _http_server_context { public: _http_server_context() {} virtual ~_http_server_context() {} private: }; /// /// Internal representation of an HTTP response. /// class _http_response final : public http::details::http_msg_base { public: _http_response() : m_status_code((std::numeric_limits::max)()) { } _http_response(http::status_code code) : m_status_code(code) {} http::status_code status_code() const { return m_status_code; } void set_status_code(http::status_code code) { m_status_code = code; } const http::reason_phrase & reason_phrase() const { return m_reason_phrase; } void set_reason_phrase(const http::reason_phrase &reason) { m_reason_phrase = reason; } _ASYNCRTIMP utility::string_t to_string() const; _http_server_context * _get_server_context() const { return m_server_context.get(); } void _set_server_context(std::unique_ptr server_context) { m_server_context = std::move(server_context); } private: std::unique_ptr<_http_server_context> m_server_context; http::status_code m_status_code; http::reason_phrase m_reason_phrase; }; } // namespace details /// /// Represents an HTTP response. /// class http_response { public: /// /// Constructs a response with an empty status code, no headers, and no body. /// /// A new HTTP response. http_response() : _m_impl(std::make_shared()) { } /// /// Constructs a response with given status code, no headers, and no body. /// /// HTTP status code to use in response. /// A new HTTP response. http_response(http::status_code code) : _m_impl(std::make_shared(code)) { } /// /// Gets the status code of the response message. /// /// status code. http::status_code status_code() const { return _m_impl->status_code(); } /// /// Sets the status code of the response message. /// /// Status code to set. /// /// This will overwrite any previously set status code. /// void set_status_code(http::status_code code) const { _m_impl->set_status_code(code); } /// /// Gets the reason phrase of the response message. /// If no reason phrase is set it will default to the standard one corresponding to the status code. /// /// Reason phrase. const http::reason_phrase & reason_phrase() const { return _m_impl->reason_phrase(); } /// /// Sets the reason phrase of the response message. /// If no reason phrase is set it will default to the standard one corresponding to the status code. /// /// The reason phrase to set. void set_reason_phrase(const http::reason_phrase &reason) const { _m_impl->set_reason_phrase(reason); } /// /// Gets the headers of the response message. /// /// HTTP headers for this response. /// /// Use the http_headers::add method to fill in desired headers. /// http_headers &headers() { return _m_impl->headers(); } /// /// Gets a const reference to the headers of the response message. /// /// HTTP headers for this response. const http_headers &headers() const { return _m_impl->headers(); } /// /// Generates a string representation of the message, including the body when possible. /// Mainly this should be used for debugging purposes as it has to copy the /// message body and doesn't have excellent performance. /// /// A string representation of this HTTP request. /// Note this function is synchronous and doesn't wait for the /// entire message body to arrive. If the message body has arrived by the time this /// function is called and it is has a textual Content-Type it will be included. /// Otherwise just the headers will be present. utility::string_t to_string() const { return _m_impl->to_string(); } /// /// Extracts the body of the response message as a string value, checking that the content type is a MIME text type. /// A body can only be extracted once because in some cases an optimization is made where the data is 'moved' out. /// /// If true, ignores the Content-Type header and assumes text. /// String containing body of the message. pplx::task extract_string(bool ignore_content_type = false) const { auto impl = _m_impl; return pplx::create_task(_m_impl->_get_data_available()).then([impl, ignore_content_type](utility::size64_t) { return impl->extract_string(ignore_content_type); }); } /// /// Extracts the body of the response message as a UTF-8 string value, checking that the content type is a MIME text type. /// A body can only be extracted once because in some cases an optimization is made where the data is 'moved' out. /// /// If true, ignores the Content-Type header and assumes text. /// String containing body of the message. pplx::task extract_utf8string(bool ignore_content_type = false) const { auto impl = _m_impl; return pplx::create_task(_m_impl->_get_data_available()).then([impl, ignore_content_type](utility::size64_t) { return impl->extract_utf8string(ignore_content_type); }); } /// /// Extracts the body of the response message as a UTF-16 string value, checking that the content type is a MIME text type. /// A body can only be extracted once because in some cases an optimization is made where the data is 'moved' out. /// /// If true, ignores the Content-Type header and assumes text. /// String containing body of the message. pplx::task extract_utf16string(bool ignore_content_type = false) const { auto impl = _m_impl; return pplx::create_task(_m_impl->_get_data_available()).then([impl, ignore_content_type](utility::size64_t) { return impl->extract_utf16string(ignore_content_type); }); } /// /// Extracts the body of the response message into a json value, checking that the content type is application/json. /// A body can only be extracted once because in some cases an optimization is made where the data is 'moved' out. /// /// If true, ignores the Content-Type header and assumes json. /// JSON value from the body of this message. pplx::task extract_json(bool ignore_content_type = false) const { auto impl = _m_impl; return pplx::create_task(_m_impl->_get_data_available()).then([impl, ignore_content_type](utility::size64_t) { return impl->_extract_json(ignore_content_type); }); } /// /// Extracts the body of the response message into a vector of bytes. /// /// The body of the message as a vector of bytes. pplx::task> extract_vector() const { auto impl = _m_impl; return pplx::create_task(_m_impl->_get_data_available()).then([impl](utility::size64_t) { return impl->_extract_vector(); }); } /// /// Sets the body of the message to a textual string and set the "Content-Type" header. Assumes /// the character encoding of the string is UTF-8. /// /// String containing body text. /// MIME type to set the "Content-Type" header to. Default to "text/plain; charset=utf-8". /// /// This will overwrite any previously set body data and "Content-Type" header. /// void set_body(utf8string &&body_text, const utf8string &content_type = utf8string("text/plain; charset=utf-8")) { const auto length = body_text.size(); _m_impl->set_body(concurrency::streams::bytestream::open_istream(std::move(body_text)), length, content_type); } /// /// Sets the body of the message to a textual string and set the "Content-Type" header. Assumes /// the character encoding of the string is UTF-8. /// /// String containing body text. /// MIME type to set the "Content-Type" header to. Default to "text/plain; charset=utf-8". /// /// This will overwrite any previously set body data and "Content-Type" header. /// void set_body(const utf8string &body_text, const utf8string &content_type = utf8string("text/plain; charset=utf-8")) { _m_impl->set_body(concurrency::streams::bytestream::open_istream(body_text), body_text.size(), content_type); } /// /// Sets the body of the message to a textual string and set the "Content-Type" header. Assumes /// the character encoding of the string is UTF-16 will perform conversion to UTF-8. /// /// String containing body text. /// MIME type to set the "Content-Type" header to. Default to "text/plain". /// /// This will overwrite any previously set body data and "Content-Type" header. /// void set_body(const utf16string &body_text, utf16string content_type = HTTPMSG_UTF16("text/plain")) { if (content_type.find(::utility::conversions::to_utf16string("charset=")) != content_type.npos) { throw std::invalid_argument("content_type can't contain a 'charset'."); } auto utf8body = utility::conversions::utf16_to_utf8(body_text); auto length = utf8body.size(); _m_impl->set_body(concurrency::streams::bytestream::open_istream( std::move(utf8body)), length, std::move(content_type.append(::utility::conversions::to_utf16string("; charset=utf-8")))); } /// /// Sets the body of the message to contain json value. If the 'Content-Type' /// header hasn't already been set it will be set to 'application/json'. /// /// json value. /// /// This will overwrite any previously set body data. /// void set_body(const json::value &body_data) { auto body_text = utility::conversions::to_utf8string(body_data.serialize()); auto length = body_text.size(); set_body(concurrency::streams::bytestream::open_istream(std::move(body_text)), length, _XPLATSTR("application/json")); } /// /// Sets the body of the message to the contents of a byte vector. If the 'Content-Type' /// header hasn't already been set it will be set to 'application/octet-stream'. /// /// Vector containing body data. /// /// This will overwrite any previously set body data. /// void set_body(std::vector &&body_data) { auto length = body_data.size(); set_body(concurrency::streams::bytestream::open_istream(std::move(body_data)), length); } /// /// Sets the body of the message to the contents of a byte vector. If the 'Content-Type' /// header hasn't already been set it will be set to 'application/octet-stream'. /// /// Vector containing body data. /// /// This will overwrite any previously set body data. /// void set_body(const std::vector &body_data) { set_body(concurrency::streams::bytestream::open_istream(body_data), body_data.size()); } /// /// Defines a stream that will be relied on to provide the body of the HTTP message when it is /// sent. /// /// A readable, open asynchronous stream. /// A string holding the MIME type of the message body. /// /// This cannot be used in conjunction with any other means of setting the body of the request. /// The stream will not be read until the message is sent. /// void set_body(const concurrency::streams::istream &stream, const utility::string_t &content_type = _XPLATSTR("application/octet-stream")) { _m_impl->set_body(stream, content_type); } /// /// Defines a stream that will be relied on to provide the body of the HTTP message when it is /// sent. /// /// A readable, open asynchronous stream. /// The size of the data to be sent in the body. /// A string holding the MIME type of the message body. /// /// This cannot be used in conjunction with any other means of setting the body of the request. /// The stream will not be read until the message is sent. /// void set_body(const concurrency::streams::istream &stream, utility::size64_t content_length, const utility::string_t &content_type = _XPLATSTR("application/octet-stream")) { _m_impl->set_body(stream, content_length, content_type); } /// /// Produces a stream which the caller may use to retrieve data from an incoming request. /// /// A readable, open asynchronous stream. /// /// This cannot be used in conjunction with any other means of getting the body of the request. /// It is not necessary to wait until the message has been sent before starting to write to the /// stream, but it is advisable to do so, since it will allow the network I/O to start earlier /// and the work of sending data can be overlapped with the production of more data. /// concurrency::streams::istream body() const { return _m_impl->instream(); } /// /// Signals the user (client) when all the data for this response message has been received. /// /// A task which is completed when all of the response body has been received. pplx::task content_ready() const { http_response resp = *this; return pplx::create_task(_m_impl->_get_data_available()).then([resp](utility::size64_t) mutable { return resp; }); } std::shared_ptr _get_impl() const { return _m_impl; } http::details::_http_server_context * _get_server_context() const { return _m_impl->_get_server_context(); } void _set_server_context(std::unique_ptr server_context) { _m_impl->_set_server_context(std::move(server_context)); } private: std::shared_ptr _m_impl; }; namespace details { /// /// Internal representation of an HTTP request message. /// class _http_request final : public http::details::http_msg_base, public std::enable_shared_from_this<_http_request> { public: _ASYNCRTIMP _http_request(http::method mtd); _ASYNCRTIMP _http_request(std::unique_ptr server_context); virtual ~_http_request() {} http::method &method() { return m_method; } uri &request_uri() { return m_uri; } _ASYNCRTIMP uri absolute_uri() const; _ASYNCRTIMP uri relative_uri() const; _ASYNCRTIMP void set_request_uri(const uri&); const pplx::cancellation_token &cancellation_token() const { return m_cancellationToken; } void set_cancellation_token(const pplx::cancellation_token &token) { m_cancellationToken = token; } _ASYNCRTIMP utility::string_t to_string() const; _ASYNCRTIMP pplx::task reply(const http_response &response); pplx::task get_response() { return pplx::task(m_response); } _ASYNCRTIMP pplx::task _reply_if_not_already(http::status_code status); void set_response_stream(const concurrency::streams::ostream &stream) { m_response_stream = stream; } void set_progress_handler(const progress_handler &handler) { m_progress_handler = std::make_shared(handler); } const concurrency::streams::ostream & _response_stream() const { return m_response_stream; } const std::shared_ptr & _progress_handler() const { return m_progress_handler; } http::details::_http_server_context * _get_server_context() const { return m_server_context.get(); } void _set_server_context(std::unique_ptr server_context) { m_server_context = std::move(server_context); } void _set_listener_path(const utility::string_t &path) { m_listener_path = path; } void _set_base_uri(const http::uri &base_uri) { m_base_uri = base_uri; } _ASYNCRTIMP void _record_body_data_for_retry(const concurrency::streams::istream &stream); _ASYNCRTIMP void _record_body_data_for_retry(const std::vector &body_data); _ASYNCRTIMP void _record_body_data_for_retry(const utf8string &body_text, const utf8string &content_type); _ASYNCRTIMP bool _reset_body_for_retry(); private: // Actual initiates sending the response, without checking if a response has already been sent. pplx::task _reply_impl(http_response response); http::method m_method; // Tracks whether or not a response has already been started for this message. pplx::details::atomic_long m_initiated_response; std::unique_ptr m_server_context; pplx::cancellation_token m_cancellationToken; http::uri m_base_uri; http::uri m_uri; utility::string_t m_listener_path; concurrency::streams::ostream m_response_stream; std::shared_ptr m_progress_handler; pplx::task_completion_event m_response; bool m_bodyTextRecorded; bool m_bodyVectorRecorded; bool m_onlySetBodyUsingStream; utf8string m_bodyText; utf8string m_contentType; std::vector m_bodyVector; }; } // namespace details /// /// Represents an HTTP request. /// class http_request { public: /// /// Constructs a new HTTP request with the 'GET' method. /// http_request() : _m_impl(std::make_shared(methods::GET)) {} /// /// Constructs a new HTTP request with the given request method. /// /// Request method. http_request(http::method mtd) : _m_impl(std::make_shared(std::move(mtd))) {} /// /// Destructor frees any held resources. /// ~http_request() {} /// /// Get the method (GET/PUT/POST/DELETE) of the request message. /// /// Request method of this HTTP request. const http::method &method() const { return _m_impl->method(); } /// /// Get the method (GET/PUT/POST/DELETE) of the request message. /// /// Request method of this HTTP request. void set_method(const http::method &method) const { _m_impl->method() = method; } /// /// Get the underling URI of the request message. /// /// The uri of this message. const uri & request_uri() const { return _m_impl->request_uri(); } /// /// Set the underling URI of the request message. /// /// The uri for this message. void set_request_uri(const uri& uri) { return _m_impl->set_request_uri(uri); } /// /// Gets a reference the URI path, query, and fragment part of this request message. /// This will be appended to the base URI specified at construction of the http_client. /// /// A string. /// When the request is the one passed to a listener's handler, the /// relative URI is the request URI less the listener's path. In all other circumstances, /// request_uri() and relative_uri() will return the same value. /// uri relative_uri() const { return _m_impl->relative_uri(); } /// /// Get an absolute URI with scheme, host, port, path, query, and fragment part of /// the request message. /// /// Absolute URI is only valid after this http_request object has been passed /// to http_client::request(). /// uri absolute_uri() const { return _m_impl->absolute_uri(); } /// /// Gets a reference to the headers of the response message. /// /// HTTP headers for this response. /// /// Use the http_headers::add to fill in desired headers. /// http_headers &headers() { return _m_impl->headers(); } /// /// Gets a const reference to the headers of the response message. /// /// HTTP headers for this response. /// /// Use the http_headers::add to fill in desired headers. /// const http_headers &headers() const { return _m_impl->headers(); } /// /// Extract the body of the request message as a string value, checking that the content type is a MIME text type. /// A body can only be extracted once because in some cases an optimization is made where the data is 'moved' out. /// /// If true, ignores the Content-Type header and assumes UTF-8. /// String containing body of the message. pplx::task extract_string(bool ignore_content_type = false) { auto impl = _m_impl; return pplx::create_task(_m_impl->_get_data_available()).then([impl, ignore_content_type](utility::size64_t) { return impl->extract_string(ignore_content_type); }); } /// /// Extract the body of the request message as a UTF-8 string value, checking that the content type is a MIME text type. /// A body can only be extracted once because in some cases an optimization is made where the data is 'moved' out. /// /// If true, ignores the Content-Type header and assumes UTF-8. /// String containing body of the message. pplx::task extract_utf8string(bool ignore_content_type = false) { auto impl = _m_impl; return pplx::create_task(_m_impl->_get_data_available()).then([impl, ignore_content_type](utility::size64_t) { return impl->extract_utf8string(ignore_content_type); }); } /// /// Extract the body of the request message as a UTF-16 string value, checking that the content type is a MIME text type. /// A body can only be extracted once because in some cases an optimization is made where the data is 'moved' out. /// /// If true, ignores the Content-Type header and assumes UTF-16. /// String containing body of the message. pplx::task extract_utf16string(bool ignore_content_type = false) { auto impl = _m_impl; return pplx::create_task(_m_impl->_get_data_available()).then([impl, ignore_content_type](utility::size64_t) { return impl->extract_utf16string(ignore_content_type); }); } /// /// Extracts the body of the request message into a json value, checking that the content type is application/json. /// A body can only be extracted once because in some cases an optimization is made where the data is 'moved' out. /// /// If true, ignores the Content-Type header and assumes UTF-8. /// JSON value from the body of this message. pplx::task extract_json(bool ignore_content_type = false) const { auto impl = _m_impl; return pplx::create_task(_m_impl->_get_data_available()).then([impl, ignore_content_type](utility::size64_t) { return impl->_extract_json(ignore_content_type); }); } /// /// Extract the body of the response message into a vector of bytes. Extracting a vector can be done on /// /// The body of the message as a vector of bytes. pplx::task> extract_vector() const { auto impl = _m_impl; return pplx::create_task(_m_impl->_get_data_available()).then([impl](utility::size64_t) { return impl->_extract_vector(); }); } /// /// Sets the body of the message to a textual string and set the "Content-Type" header. Assumes /// the character encoding of the string is UTF-8. /// /// String containing body text. /// MIME type to set the "Content-Type" header to. Default to "text/plain; charset=utf-8". /// /// This will overwrite any previously set body data and "Content-Type" header. /// void set_body(utf8string &&body_text, const utf8string &content_type = utf8string("text/plain; charset=utf-8")) { const auto length = body_text.size(); _m_impl->_record_body_data_for_retry(body_text, content_type); _m_impl->set_body(concurrency::streams::bytestream::open_istream(std::move(body_text)), length, content_type); } /// /// Sets the body of the message to a textual string and set the "Content-Type" header. Assumes /// the character encoding of the string is UTF-8. /// /// String containing body text. /// MIME type to set the "Content-Type" header to. Default to "text/plain; charset=utf-8". /// /// This will overwrite any previously set body data and "Content-Type" header. /// void set_body(const utf8string &body_text, const utf8string &content_type = utf8string("text/plain; charset=utf-8")) { _m_impl->_record_body_data_for_retry(body_text, content_type); _m_impl->set_body(concurrency::streams::bytestream::open_istream(body_text), body_text.size(), content_type); } /// /// Sets the body of the message to a textual string and set the "Content-Type" header. Assumes /// the character encoding of the string is UTF-16 will perform conversion to UTF-8. /// /// String containing body text. /// MIME type to set the "Content-Type" header to. Default to "text/plain". /// /// This will overwrite any previously set body data and "Content-Type" header. /// void set_body(const utf16string &body_text, utf16string content_type = HTTPMSG_UTF16("text/plain")) { if(content_type.find(::utility::conversions::to_utf16string("charset=")) != content_type.npos) { throw std::invalid_argument("content_type can't contain a 'charset'."); } auto utf8body = utility::conversions::utf16_to_utf8(body_text); auto length = utf8body.size(); auto contextTypeAppended = content_type.append(::utility::conversions::to_utf16string("; charset=utf-8")); _m_impl->_record_body_data_for_retry(utf8body, utility::conversions::utf16_to_utf8(contextTypeAppended)); _m_impl->set_body(concurrency::streams::bytestream::open_istream( std::move(utf8body)), length, std::move(contextTypeAppended)); } /// /// Sets the body of the message to contain json value. If the 'Content-Type' /// header hasn't already been set it will be set to 'application/json'. /// /// json value. /// /// This will overwrite any previously set body data. /// void set_body(const json::value &body_data) { auto body_text = utility::conversions::to_utf8string(body_data.serialize()); auto length = body_text.size(); utf8string contentType = "application/json"; _m_impl->_record_body_data_for_retry(body_text, contentType); _m_impl->set_body(concurrency::streams::bytestream::open_istream(std::move(body_text)), length, contentType); } /// /// Sets the body of the message to the contents of a byte vector. If the 'Content-Type' /// header hasn't already been set it will be set to 'application/octet-stream'. /// /// Vector containing body data. /// /// This will overwrite any previously set body data. /// void set_body(std::vector &&body_data) { auto length = body_data.size(); _m_impl->_record_body_data_for_retry(body_data); _m_impl->set_body(concurrency::streams::bytestream::open_istream(std::move(body_data)), length, _XPLATSTR("application/octet-stream")); } /// /// Sets the body of the message to the contents of a byte vector. If the 'Content-Type' /// header hasn't already been set it will be set to 'application/octet-stream'. /// /// Vector containing body data. /// /// This will overwrite any previously set body data. /// void set_body(const std::vector &body_data) { _m_impl->_record_body_data_for_retry(body_data); set_body(concurrency::streams::bytestream::open_istream(body_data), body_data.size()); } /// /// Defines a stream that will be relied on to provide the body of the HTTP message when it is /// sent. /// /// A readable, open asynchronous stream. /// A string holding the MIME type of the message body. /// /// This cannot be used in conjunction with any other means of setting the body of the request. /// The stream will not be read until the message is sent. /// void set_body(const concurrency::streams::istream &stream, const utility::string_t &content_type = _XPLATSTR("application/octet-stream")) { _m_impl->_record_body_data_for_retry(stream); _m_impl->set_body(stream, content_type); } /// /// Defines a stream that will be relied on to provide the body of the HTTP message when it is /// sent. /// /// A readable, open asynchronous stream. /// The size of the data to be sent in the body. /// A string holding the MIME type of the message body. /// /// This cannot be used in conjunction with any other means of setting the body of the request. /// The stream will not be read until the message is sent. /// void set_body(const concurrency::streams::istream &stream, utility::size64_t content_length, const utility::string_t &content_type = _XPLATSTR("application/octet-stream")) { _m_impl->_record_body_data_for_retry(stream); _m_impl->set_body(stream, content_length, content_type); } /// /// Produces a stream which the caller may use to retrieve data from an incoming request. /// /// A readable, open asynchronous stream. /// /// This cannot be used in conjunction with any other means of getting the body of the request. /// It is not necessary to wait until the message has been sent before starting to write to the /// stream, but it is advisable to do so, since it will allow the network I/O to start earlier /// and the work of sending data can be overlapped with the production of more data. /// concurrency::streams::istream body() const { return _m_impl->instream(); } /// /// Defines a stream that will be relied on to hold the body of the HTTP response message that /// results from the request. /// /// A writable, open asynchronous stream. /// /// If this function is called, the body of the response should not be accessed in any other /// way. /// void set_response_stream(const concurrency::streams::ostream &stream) { return _m_impl->set_response_stream(stream); } /// /// Defines a callback function that will be invoked for every chunk of data uploaded or downloaded /// as part of the request. /// /// A function representing the progress handler. It's parameters are: /// up: a message_direction::direction value indicating the direction of the message /// that is being reported. /// progress: the number of bytes that have been processed so far. /// /// /// This function will be called at least once for upload and at least once for /// the download body, unless there is some exception generated. An HTTP message with an error /// code is not an exception. This means, that even if there is no body, the progress handler /// will be called. /// /// Setting the chunk size on the http_client does not guarantee that the client will be using /// exactly that increment for uploading and downloading data. /// /// The handler will be called only once for each combination of argument values, in order. Depending /// on how a service responds, some download values may come before all upload values have been /// reported. /// /// The progress handler will be called on the thread processing the request. This means that /// the implementation of the handler must take care not to block the thread or do anything /// that takes significant amounts of time. In particular, do not do any kind of I/O from within /// the handler, do not update user interfaces, and to not acquire any locks. If such activities /// are necessary, it is the handler's responsibility to execute that work on a separate thread. /// void set_progress_handler(const progress_handler &handler) { return _m_impl->set_progress_handler(handler); } /// /// Asynchronously responses to this HTTP request. /// /// Response to send. /// An asynchronous operation that is completed once response is sent. pplx::task reply(const http_response &response) const { return _m_impl->reply(response); } /// /// Asynchronously responses to this HTTP request. /// /// Response status code. /// An asynchronous operation that is completed once response is sent. pplx::task reply(http::status_code status) const { return reply(http_response(status)); } /// /// Responds to this HTTP request. /// /// Response status code. /// Json value to use in the response body. /// An asynchronous operation that is completed once response is sent. pplx::task reply(http::status_code status, const json::value &body_data) const { http_response response(status); response.set_body(body_data); return reply(response); } /// /// Responds to this HTTP request with a string. /// Assumes the character encoding of the string is UTF-8. /// /// Response status code. /// UTF-8 string containing the text to use in the response body. /// Content type of the body. /// An asynchronous operation that is completed once response is sent. /// /// Callers of this function do NOT need to block waiting for the response to be /// sent to before the body data is destroyed or goes out of scope. /// pplx::task reply(http::status_code status, utf8string &&body_data, const utf8string &content_type = "text/plain; charset=utf-8") const { http_response response(status); response.set_body(std::move(body_data), content_type); return reply(response); } /// /// Responds to this HTTP request with a string. /// Assumes the character encoding of the string is UTF-8. /// /// Response status code. /// UTF-8 string containing the text to use in the response body. /// Content type of the body. /// An asynchronous operation that is completed once response is sent. /// // Callers of this function do NOT need to block waiting for the response to be /// sent to before the body data is destroyed or goes out of scope. /// pplx::task reply(http::status_code status, const utf8string &body_data, const utf8string &content_type = "text/plain; charset=utf-8") const { http_response response(status); response.set_body(body_data, content_type); return reply(response); } /// /// Responds to this HTTP request with a string. Assumes the character encoding /// of the string is UTF-16 will perform conversion to UTF-8. /// /// Response status code. /// UTF-16 string containing the text to use in the response body. /// Content type of the body. /// An asynchronous operation that is completed once response is sent. /// // Callers of this function do NOT need to block waiting for the response to be /// sent to before the body data is destroyed or goes out of scope. /// pplx::task reply(http::status_code status, const utf16string &body_data, const utf16string &content_type = HTTPMSG_UTF16("text/plain")) const { http_response response(status); response.set_body(body_data, content_type); return reply(response); } /// /// Responds to this HTTP request. /// /// Response status code. /// A string holding the MIME type of the message body. /// An asynchronous stream representing the body data. /// A task that is completed once a response from the request is received. pplx::task reply(status_code status, const concurrency::streams::istream &body, const utility::string_t &content_type = _XPLATSTR("application/octet-stream")) const { http_response response(status); response.set_body(body, content_type); return reply(response); } /// /// Responds to this HTTP request. /// /// Response status code. /// The size of the data to be sent in the body.. /// A string holding the MIME type of the message body. /// An asynchronous stream representing the body data. /// A task that is completed once a response from the request is received. pplx::task reply(status_code status, const concurrency::streams::istream &body, utility::size64_t content_length, const utility::string_t &content_type = _XPLATSTR("application/octet-stream")) const { http_response response(status); response.set_body(body, content_length, content_type); return reply(response); } /// /// Signals the user (listener) when all the data for this request message has been received. /// /// A task which is completed when all of the response body has been received pplx::task content_ready() const { http_request req = *this; return pplx::create_task(_m_impl->_get_data_available()).then([req](utility::size64_t) mutable { return req; }); } /// /// Gets a task representing the response that will eventually be sent. /// /// A task that is completed once response is sent. pplx::task get_response() const { return _m_impl->get_response(); } /// /// Generates a string representation of the message, including the body when possible. /// Mainly this should be used for debugging purposes as it has to copy the /// message body and doesn't have excellent performance. /// /// A string representation of this HTTP request. /// Note this function is synchronous and doesn't wait for the /// entire message body to arrive. If the message body has arrived by the time this /// function is called and it is has a textual Content-Type it will be included. /// Otherwise just the headers will be present. utility::string_t to_string() const { return _m_impl->to_string(); } /// /// Sends a response if one has not already been sent. /// pplx::task _reply_if_not_already(status_code status) { return _m_impl->_reply_if_not_already(status); } /// /// Gets the server context associated with this HTTP message. /// http::details::_http_server_context * _get_server_context() const { return _m_impl->_get_server_context(); } /// /// These are used for the initial creation of the HTTP request. /// static http_request _create_request(std::unique_ptr server_context) { return http_request(std::move(server_context)); } void _set_server_context(std::unique_ptr server_context) { _m_impl->_set_server_context(std::move(server_context)); } void _set_listener_path(const utility::string_t &path) { _m_impl->_set_listener_path(path); } const std::shared_ptr & _get_impl() const { return _m_impl; } void _set_cancellation_token(const pplx::cancellation_token &token) { _m_impl->set_cancellation_token(token); } const pplx::cancellation_token & _cancellation_token() const { return _m_impl->cancellation_token(); } void _set_base_uri(const http::uri &base_uri) { _m_impl->_set_base_uri(base_uri); } void _set_body_internal(const concurrency::streams::istream &stream, const utility::string_t &content_type = _XPLATSTR("application/octet-stream")) { _m_impl->set_body(stream, content_type); } bool _reset_body_for_retry() { return _m_impl->_reset_body_for_retry(); } private: friend class http::details::_http_request; friend class http::client::http_client; http_request(std::unique_ptr server_context) : _m_impl(std::make_shared(std::move(server_context))) {} std::shared_ptr _m_impl; }; namespace client { class http_pipeline; } /// /// HTTP client handler class, used to represent an HTTP pipeline stage. /// /// /// When a request goes out, it passes through a series of stages, customizable by /// the application and/or libraries. The default stage will interact with lower-level /// communication layers to actually send the message on the network. When creating a client /// instance, an application may add pipeline stages in front of the already existing /// stages. Each stage has a reference to the next stage available in the /// value. /// class http_pipeline_stage : public std::enable_shared_from_this { public: http_pipeline_stage() {}; #if (defined(_MSC_VER) && (_MSC_VER >= 1800)) http_pipeline_stage & operator=(const http_pipeline_stage &) = delete; http_pipeline_stage(const http_pipeline_stage &) = delete; #endif virtual ~http_pipeline_stage() {}; /// /// Runs this stage against the given request and passes onto the next stage. /// /// The HTTP request. /// A task of the HTTP response. virtual pplx::task propagate(http_request request) = 0; protected: /// /// Gets the next stage in the pipeline. /// /// A shared pointer to a pipeline stage. const std::shared_ptr & next_stage() const { return m_next_stage; } /// /// Gets a shared pointer to this pipeline stage. /// /// A shared pointer to a pipeline stage. CASABLANCA_DEPRECATED("This api is redundant. Use 'shared_from_this()' directly instead.") std::shared_ptr current_stage() { return this->shared_from_this(); } private: friend class ::web::http::client::http_pipeline; void set_next_stage(const std::shared_ptr &next) { m_next_stage = next; } std::shared_ptr m_next_stage; }; #endif // !XSAPI_NO_PPL }} #if HC_PLATFORM_IS_MICROSOFT #pragma warning( pop ) #endif ================================================ FILE: Include/cpprestinclude/cpprest/json.h ================================================ /*** * ==++== * * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * ==--== * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ * * HTTP Library: JSON parser and writer * * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk * * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ****/ #pragma once #ifndef _CASA_JSON_H #define _CASA_JSON_H #include #include #include #include #include #include #include "cpprest/details/basic_types.h" #include "cpprest/asyncrt_utils.h" #if HC_PLATFORM != HC_PLATFORM_ANDROID #pragma warning (push) #pragma warning(disable : 4266) #pragma warning(disable : 26812) // enum instead of enum class #endif namespace web { /// Library for parsing and serializing JSON values to and from C++ types. namespace json { // Various forward declarations. namespace details { class _Value; class _Number; class _Null; class _Boolean; class _String; class _Object; class _Array; template class JSON_Parser; } namespace details { extern bool g_keep_json_object_unsorted; } /// /// Preserve the order of the name/value pairs when parsing a JSON object. /// The default is false, which can yield better performance. /// /// true if ordering should be preserved when parsing, false otherwise. /// Note this is a global setting and affects all JSON parsing done. void _ASYNCRTIMP __cdecl keep_object_element_order(bool keep_order); #ifdef _WIN32 #ifdef _DEBUG #define ENABLE_JSON_VALUE_VISUALIZER #endif #endif class number; class array; class object; /// /// A JSON value represented as a C++ class. /// class value { public: /// /// This enumeration represents the various kinds of JSON values. /// enum value_type { /// Number value Number, /// Boolean value Boolean, /// String value String, /// Object value Object, /// Array value Array, /// Null value Null }; /// /// Constructor creating a null value /// _ASYNCRTIMP value(); /// /// Constructor creating a JSON number value /// /// The C++ value to create a JSON value from _ASYNCRTIMP value(int32_t value); /// /// Constructor creating a JSON number value /// /// The C++ value to create a JSON value from _ASYNCRTIMP value(uint32_t value); /// /// Constructor creating a JSON number value /// /// The C++ value to create a JSON value from _ASYNCRTIMP value(int64_t value); /// /// Constructor creating a JSON number value /// /// The C++ value to create a JSON value from _ASYNCRTIMP value(uint64_t value); /// /// Constructor creating a JSON number value /// /// The C++ value to create a JSON value from _ASYNCRTIMP value(double value); /// /// Constructor creating a JSON Boolean value /// /// The C++ value to create a JSON value from _ASYNCRTIMP explicit value(bool value); /// /// Constructor creating a JSON string value /// /// The C++ value to create a JSON value from, a C++ STL string of the platform-native character width /// /// This constructor has O(n) performance because it tries to determine if /// specified string has characters that should be properly escaped in JSON. /// _ASYNCRTIMP explicit value(utility::string_t value); /// /// Constructor creating a JSON string value specifying if the string contains characters to escape /// /// The C++ value to create a JSON value from, a C++ STL string of the platform-native character width /// Whether contains characters /// that should be escaped in JSON value /// /// This constructor has O(1) performance. /// _ASYNCRTIMP explicit value(utility::string_t value, bool has_escape_chars); /// /// Constructor creating a JSON string value /// /// The C++ value to create a JSON value from, a C++ STL string of the platform-native character width /// /// /// This constructor has O(n) performance because it tries to determine if /// specified string has characters that should be properly escaped in JSON. /// /// /// This constructor exists in order to avoid string literals matching another constructor, /// as is very likely. For example, conversion to bool does not require a user-defined conversion, /// and will therefore match first, which means that the JSON value turns up as a boolean. /// /// _ASYNCRTIMP explicit value(const utility::char_t* value); /// /// Constructor creating a JSON string value /// /// The C++ value to create a JSON value from, a C++ STL string of the platform-native character width /// Whether contains characters /// /// /// This overload has O(1) performance. /// /// /// This constructor exists in order to avoid string literals matching another constructor, /// as is very likely. For example, conversion to bool does not require a user-defined conversion, /// and will therefore match first, which means that the JSON value turns up as a boolean. /// /// _ASYNCRTIMP explicit value(const utility::char_t* value, bool has_escape_chars); /// /// Copy constructor /// _ASYNCRTIMP value(const value &); /// /// Move constructor /// _ASYNCRTIMP value(value &&) CPPREST_NOEXCEPT ; /// /// Assignment operator. /// /// The JSON value object that contains the result of the assignment. _ASYNCRTIMP value &operator=(const value &); /// /// Move assignment operator. /// /// The JSON value object that contains the result of the assignment. _ASYNCRTIMP value &operator=(value &&) CPPREST_NOEXCEPT ; // Static factories /// /// Creates a null value /// /// A JSON null value static _ASYNCRTIMP value __cdecl null(); /// /// Creates a number value /// /// The C++ value to create a JSON value from /// A JSON number value static _ASYNCRTIMP value __cdecl number(double value); /// /// Creates a number value /// /// The C++ value to create a JSON value from /// A JSON number value static _ASYNCRTIMP value __cdecl number(int32_t value); /// /// Creates a number value /// /// The C++ value to create a JSON value from /// A JSON number value static _ASYNCRTIMP value __cdecl number(uint32_t value); /// /// Creates a number value /// /// The C++ value to create a JSON value from /// A JSON number value static _ASYNCRTIMP value __cdecl number(int64_t value); /// /// Creates a number value /// /// The C++ value to create a JSON value from /// A JSON number value static _ASYNCRTIMP value __cdecl number(uint64_t value); /// /// Creates a Boolean value /// /// The C++ value to create a JSON value from /// A JSON Boolean value static _ASYNCRTIMP value __cdecl boolean(bool value); /// /// Creates a string value /// /// The C++ value to create a JSON value from /// A JSON string value /// /// This overload has O(n) performance because it tries to determine if /// specified string has characters that should be properly escaped in JSON. /// static _ASYNCRTIMP value __cdecl string(utility::string_t value); /// /// Creates a string value specifying if the string contains characters to escape /// /// The C++ value to create a JSON value from /// Whether contains characters /// that should be escaped in JSON value /// A JSON string value /// /// This overload has O(1) performance. /// static _ASYNCRTIMP value __cdecl string(utility::string_t value, bool has_escape_chars); #ifdef _WIN32 private: // Only used internally by JSON parser. static _ASYNCRTIMP value __cdecl string(const std::string &value); public: #endif /// /// Creates an object value /// /// Whether to preserve the original order of the fields /// An empty JSON object value static _ASYNCRTIMP json::value __cdecl object(bool keep_order = false); /// /// Creates an object value from a collection of field/values /// /// Field names associated with JSON values /// Whether to preserve the original order of the fields /// A non-empty JSON object value static _ASYNCRTIMP json::value __cdecl object(std::vector> fields, bool keep_order = false); /// /// Creates an empty JSON array /// /// An empty JSON array value static _ASYNCRTIMP json::value __cdecl array(); /// /// Creates a JSON array /// /// The initial number of elements of the JSON value /// A JSON array value static _ASYNCRTIMP json::value __cdecl array(size_t size); /// /// Creates a JSON array /// /// A vector of JSON values /// A JSON array value static _ASYNCRTIMP json::value __cdecl array(std::vector elements); /// /// Accesses the type of JSON value the current value instance is /// /// The value's type _ASYNCRTIMP json::value::value_type type() const; /// /// Is the current value a null value? /// /// true if the value is a null value, false otherwise bool is_null() const { return type() == Null; }; /// /// Is the current value a number value? /// /// true if the value is a number value, false otherwise bool is_number() const { return type() == Number; } /// /// Is the current value represented as an integer number value? /// /// /// Note that if a json value is a number but represented as a double it can still /// be retrieved as a integer using as_integer(), however the value will be truncated. /// /// true if the value is an integer value, false otherwise. _ASYNCRTIMP bool is_integer() const; /// /// Is the current value represented as an double number value? /// /// /// Note that if a json value is a number but represented as a int it can still /// be retrieved as a double using as_double(). /// /// true if the value is an double value, false otherwise. _ASYNCRTIMP bool is_double() const; /// /// Is the current value a Boolean value? /// /// true if the value is a Boolean value, false otherwise bool is_boolean() const { return type() == Boolean; } /// /// Is the current value a string value? /// /// true if the value is a string value, false otherwise bool is_string() const { return type() == String; } /// /// Is the current value an array? /// /// true if the value is an array, false otherwise bool is_array() const { return type() == Array; } /// /// Is the current value an object? /// /// true if the value is an object, false otherwise bool is_object() const { return type() == Object; } /// /// Gets the number of children of the value. /// /// The number of children. 0 for all non-composites. size_t size() const; /// /// Parses a string and construct a JSON value. /// /// The C++ value to create a JSON value from, a C++ STL double-byte string _ASYNCRTIMP static value __cdecl parse(const utility::string_t &value); /// /// Attempts to parse a string and construct a JSON value. /// /// The C++ value to create a JSON value from, a C++ STL double-byte string /// If parsing fails, the error code is greater than 0 /// The parsed object. Returns web::json::value::null if failed _ASYNCRTIMP static value __cdecl parse(const utility::string_t &value, std::error_code &errorCode); /// /// Serializes the current JSON value to a C++ string. /// /// A string representation of the value _ASYNCRTIMP utility::string_t serialize() const; /// /// Serializes the current JSON value to a C++ string. /// /// A string representation of the value CASABLANCA_DEPRECATED("This API is deprecated and has been renamed to avoid confusion with as_string(), use ::web::json::value::serialize() instead.") _ASYNCRTIMP utility::string_t to_string() const; /// /// Parses a JSON value from the contents of an input stream using the native platform character width. /// /// The stream to read the JSON value from /// The JSON value object created from the input stream. _ASYNCRTIMP static value __cdecl parse(utility::istream_t &input); /// /// Parses a JSON value from the contents of an input stream using the native platform character width. /// /// The stream to read the JSON value from /// If parsing fails, the error code is greater than 0 /// The parsed object. Returns web::json::value::null if failed _ASYNCRTIMP static value __cdecl parse(utility::istream_t &input, std::error_code &errorCode); /// /// Writes the current JSON value to a stream with the native platform character width. /// /// The stream that the JSON string representation should be written to. _ASYNCRTIMP void serialize(utility::ostream_t &stream) const; #ifdef _WIN32 /// /// Parses a JSON value from the contents of a single-byte (UTF8) stream. /// /// The stream to read the JSON value from _ASYNCRTIMP static value __cdecl parse(std::istream& stream); /// /// Parses a JSON value from the contents of a single-byte (UTF8) stream. /// /// The stream to read the JSON value from /// If parsing fails, the error code is greater than 0 /// The parsed object. Returns web::json::value::null if failed _ASYNCRTIMP static value __cdecl parse(std::istream& stream, std::error_code& error); /// /// Serializes the content of the value into a single-byte (UTF8) stream. /// /// The stream that the JSON string representation should be written to. _ASYNCRTIMP void serialize(std::ostream& stream) const; #endif /// /// Converts the JSON value to a C++ double, if and only if it is a number value. /// Throws json_exception if the value is not a number /// /// A double representation of the value _ASYNCRTIMP double as_double() const; /// /// Converts the JSON value to a C++ integer, if and only if it is a number value. /// Throws json_exception if the value is not a number /// /// An integer representation of the value _ASYNCRTIMP int as_integer() const; /// /// Converts the JSON value to a number class, if and only if it is a number value. /// Throws json_exception if the value is not a number /// /// An instance of number class _ASYNCRTIMP const json::number& as_number() const; /// /// Converts the JSON value to a C++ bool, if and only if it is a Boolean value. /// /// A C++ bool representation of the value _ASYNCRTIMP bool as_bool() const; /// /// Converts the JSON value to a json array, if and only if it is an array value. /// /// The returned json::array should have the same or shorter lifetime as this /// An array representation of the value _ASYNCRTIMP json::array& as_array(); /// /// Converts the JSON value to a json array, if and only if it is an array value. /// /// The returned json::array should have the same or shorter lifetime as this /// An array representation of the value _ASYNCRTIMP const json::array& as_array() const; /// /// Converts the JSON value to a json object, if and only if it is an object value. /// /// An object representation of the value _ASYNCRTIMP json::object& as_object(); /// /// Converts the JSON value to a json object, if and only if it is an object value. /// /// An object representation of the value _ASYNCRTIMP const json::object& as_object() const; /// /// Converts the JSON value to a C++ STL string, if and only if it is a string value. /// /// A C++ STL string representation of the value _ASYNCRTIMP const utility::string_t& as_string() const; /// /// Compares two JSON values for equality. /// /// The JSON value to compare with. /// True iff the values are equal. _ASYNCRTIMP bool operator==(const value& other) const; /// /// Compares two JSON values for inequality. /// /// The JSON value to compare with. /// True iff the values are unequal. bool operator!=(const value& other) const { return !((*this) == other); } /// /// Tests for the presence of a field. /// /// The name of the field /// True if the field exists, false otherwise. bool has_field(const utility::string_t &key) const; /// /// Accesses a field of a JSON object. /// /// The name of the field /// The value kept in the field; null if the field does not exist CASABLANCA_DEPRECATED("This API is deprecated and will be removed in a future release, use json::value::at() instead.") value get(const utility::string_t &key) const; /// /// Erases an element of a JSON array. Throws if index is out of bounds. /// /// The index of the element to erase in the JSON array. _ASYNCRTIMP void erase(size_t index); /// /// Erases an element of a JSON object. Throws if the key doesn't exist. /// /// The key of the element to erase in the JSON object. _ASYNCRTIMP void erase(const utility::string_t &key); /// /// Accesses an element of a JSON array. Throws when index out of bounds. /// /// The index of an element in the JSON array. /// A reference to the value. _ASYNCRTIMP json::value& at(size_t index); /// /// Accesses an element of a JSON array. Throws when index out of bounds. /// /// The index of an element in the JSON array. /// A reference to the value. _ASYNCRTIMP const json::value& at(size_t index) const; /// /// Accesses an element of a JSON object. If the key doesn't exist, this method throws. /// /// The key of an element in the JSON object. /// If the key exists, a reference to the value. _ASYNCRTIMP json::value& at(const utility::string_t& key); /// /// Accesses an element of a JSON object. If the key doesn't exist, this method throws. /// /// The key of an element in the JSON object. /// If the key exists, a reference to the value. _ASYNCRTIMP const json::value& at(const utility::string_t& key) const; /// /// Accesses a field of a JSON object. /// /// The name of the field /// A reference to the value kept in the field. _ASYNCRTIMP value & operator [] (const utility::string_t &key); #ifdef _WIN32 private: // Only used internally by JSON parser _ASYNCRTIMP value & operator [] (const std::string &key) { // JSON object stores its field map as a unordered_map of string_t, so this conversion is hard to avoid return operator[](utility::conversions::to_string_t(key)); } public: #endif /// /// Accesses an element of a JSON array. /// /// The index of an element in the JSON array /// The value kept at the array index; null if outside the boundaries of the array CASABLANCA_DEPRECATED("This API is deprecated and will be removed in a future release, use json::value::at() instead.") value get(size_t index) const; /// /// Accesses an element of a JSON array. /// /// The index of an element in the JSON array. /// A reference to the value kept in the field. _ASYNCRTIMP value & operator [] (size_t index); private: friend class web::json::details::_Object; friend class web::json::details::_Array; template friend class web::json::details::JSON_Parser; #ifdef _WIN32 /// /// Writes the current JSON value as a double-byte string to a string instance. /// /// The string that the JSON representation should be written to. _ASYNCRTIMP void format(std::basic_string &string) const; #endif /// /// Serializes the content of the value into a string instance in UTF8 format /// /// The string that the JSON representation should be written to _ASYNCRTIMP void format(std::basic_string& string) const; #ifdef ENABLE_JSON_VALUE_VISUALIZER explicit value(std::unique_ptr v, value_type kind) : m_value(std::move(v)), m_kind(kind) #else explicit value(std::unique_ptr v) : m_value(std::move(v)) #endif {} std::unique_ptr m_value; #ifdef ENABLE_JSON_VALUE_VISUALIZER value_type m_kind; #endif }; /// /// A single exception type to represent errors in parsing, converting, and accessing /// elements of JSON values. /// class json_exception : public std::exception { private: std::string _message; public: json_exception(const utility::char_t * const &message) : _message(utility::conversions::to_utf8string(message)) { } // Must be narrow string because it derives from std::exception const char* what() const CPPREST_NOEXCEPT { return _message.c_str(); } }; namespace details { enum json_error { left_over_character_in_stream = 1, malformed_array_literal, malformed_comment, malformed_literal, malformed_object_literal, malformed_numeric_literal, malformed_string_literal, malformed_token, mismatched_brances, nesting, unexpected_token }; class json_error_category_impl : public std::error_category { public: virtual const char* name() const CPPREST_NOEXCEPT override { return "json"; } virtual std::string message(int ev) const override { switch (ev) { case json_error::left_over_character_in_stream: return "Left-over characters in stream after parsing a JSON value"; case json_error::malformed_array_literal: return "Malformed array literal"; case json_error::malformed_comment: return "Malformed comment"; case json_error::malformed_literal: return "Malformed literal"; case json_error::malformed_object_literal: return "Malformed object literal"; case json_error::malformed_numeric_literal: return "Malformed numeric literal"; case json_error::malformed_string_literal: return "Malformed string literal"; case json_error::malformed_token: return "Malformed token"; case json_error::mismatched_brances: return "Mismatched braces"; case json_error::nesting: return "Nesting too deep"; case json_error::unexpected_token: return "Unexpected token"; default: return "Unknown json error"; } } }; const json_error_category_impl& json_error_category(); } /// /// A JSON array represented as a C++ class. /// class array { typedef std::vector storage_type; public: typedef storage_type::iterator iterator; typedef storage_type::const_iterator const_iterator; typedef storage_type::reverse_iterator reverse_iterator; typedef storage_type::const_reverse_iterator const_reverse_iterator; typedef storage_type::size_type size_type; private: array() : m_elements() { } array(size_type size) : m_elements(size) { } array(storage_type elements) : m_elements(std::move(elements)) { } public: /// /// Gets the beginning iterator element of the array /// /// An iterator to the beginning of the JSON array. iterator begin() { return m_elements.begin(); } /// /// Gets the beginning const iterator element of the array. /// /// A const_iterator to the beginning of the JSON array. const_iterator begin() const { return m_elements.cbegin(); } /// /// Gets the end iterator element of the array /// /// An iterator to the end of the JSON array. iterator end() { return m_elements.end(); } /// /// Gets the end const iterator element of the array. /// /// A const_iterator to the end of the JSON array. const_iterator end() const { return m_elements.cend(); } /// /// Gets the beginning reverse iterator element of the array /// /// An reverse_iterator to the beginning of the JSON array. reverse_iterator rbegin() { return m_elements.rbegin(); } /// /// Gets the beginning const reverse iterator element of the array /// /// An const_reverse_iterator to the beginning of the JSON array. const_reverse_iterator rbegin() const { return m_elements.rbegin(); } /// /// Gets the end reverse iterator element of the array /// /// An reverse_iterator to the end of the JSON array. reverse_iterator rend() { return m_elements.rend(); } /// /// Gets the end const reverse iterator element of the array /// /// An const_reverse_iterator to the end of the JSON array. const_reverse_iterator rend() const { return m_elements.crend(); } /// /// Gets the beginning const iterator element of the array. /// /// A const_iterator to the beginning of the JSON array. const_iterator cbegin() const { return m_elements.cbegin(); } /// /// Gets the end const iterator element of the array. /// /// A const_iterator to the end of the JSON array. const_iterator cend() const { return m_elements.cend(); } /// /// Gets the beginning const reverse iterator element of the array. /// /// A const_reverse_iterator to the beginning of the JSON array. const_reverse_iterator crbegin() const { return m_elements.crbegin(); } /// /// Gets the end const reverse iterator element of the array. /// /// A const_reverse_iterator to the end of the JSON array. const_reverse_iterator crend() const { return m_elements.crend(); } /// /// Deletes an element of the JSON array. /// /// A const_iterator to the element to delete. /// Iterator to the new location of the element following the erased element. /// GCC doesn't support erase with const_iterator on vector yet. In the future this should be changed. iterator erase(iterator position) { return m_elements.erase(position); } /// /// Deletes the element at an index of the JSON array. /// /// The index of the element to delete. void erase(size_type index) { if (index >= m_elements.size()) { throw json_exception(_XPLATSTR("index out of bounds")); } #if HC_PLATFORM != HC_PLATFORM_ANDROID #pragma warning(push) #pragma warning(disable: 4365) #endif m_elements.erase(m_elements.begin() + static_cast::size_type>(index)); #if HC_PLATFORM != HC_PLATFORM_ANDROID #pragma warning( pop ) #endif } /// /// Accesses an element of a JSON array. Throws when index out of bounds. /// /// The index of an element in the JSON array. /// A reference to the value kept in the field. json::value& at(size_type index) { if (index >= m_elements.size()) throw json_exception(_XPLATSTR("index out of bounds")); return m_elements[index]; } /// /// Accesses an element of a JSON array. Throws when index out of bounds. /// /// The index of an element in the JSON array. /// A reference to the value kept in the field. const json::value& at(size_type index) const { if (index >= m_elements.size()) throw json_exception(_XPLATSTR("index out of bounds")); return m_elements[index]; } /// /// Accesses an element of a JSON array. /// /// The index of an element in the JSON array. /// A reference to the value kept in the field. json::value& operator[](size_type index) { msl::safeint3::SafeInt nMinSize(index); nMinSize += 1; msl::safeint3::SafeInt nlastSize(m_elements.size()); if (nlastSize < nMinSize) m_elements.resize(nMinSize); return m_elements[index]; } /// /// Gets the number of elements of the array. /// /// The number of elements. size_type size() const { return m_elements.size(); } private: storage_type m_elements; friend class details::_Array; template friend class json::details::JSON_Parser; }; /// /// A JSON object represented as a C++ class. /// class object { typedef std::vector> storage_type; public: typedef storage_type::iterator iterator; typedef storage_type::const_iterator const_iterator; typedef storage_type::reverse_iterator reverse_iterator; typedef storage_type::const_reverse_iterator const_reverse_iterator; typedef storage_type::size_type size_type; private: object(bool keep_order = false) : m_elements(), m_keep_order(keep_order) { } object(storage_type elements, bool keep_order = false) : m_elements(std::move(elements)), m_keep_order(keep_order) { if (!keep_order) { sort(m_elements.begin(), m_elements.end(), compare_pairs); } } public: /// /// Gets the beginning iterator element of the object /// /// An iterator to the beginning of the JSON object. iterator begin() { return m_elements.begin(); } /// /// Gets the beginning const iterator element of the object. /// /// A const_iterator to the beginning of the JSON object. const_iterator begin() const { return m_elements.cbegin(); } /// /// Gets the end iterator element of the object /// /// An iterator to the end of the JSON object. iterator end() { return m_elements.end(); } /// /// Gets the end const iterator element of the object. /// /// A const_iterator to the end of the JSON object. const_iterator end() const { return m_elements.cend(); } /// /// Gets the beginning reverse iterator element of the object /// /// An reverse_iterator to the beginning of the JSON object. reverse_iterator rbegin() { return m_elements.rbegin(); } /// /// Gets the beginning const reverse iterator element of the object /// /// An const_reverse_iterator to the beginning of the JSON object. const_reverse_iterator rbegin() const { return m_elements.rbegin(); } /// /// Gets the end reverse iterator element of the object /// /// An reverse_iterator to the end of the JSON object. reverse_iterator rend() { return m_elements.rend(); } /// /// Gets the end const reverse iterator element of the object /// /// An const_reverse_iterator to the end of the JSON object. const_reverse_iterator rend() const { return m_elements.crend(); } /// /// Gets the beginning const iterator element of the object. /// /// A const_iterator to the beginning of the JSON object. const_iterator cbegin() const { return m_elements.cbegin(); } /// /// Gets the end const iterator element of the object. /// /// A const_iterator to the end of the JSON object. const_iterator cend() const { return m_elements.cend(); } /// /// Gets the beginning const reverse iterator element of the object. /// /// A const_reverse_iterator to the beginning of the JSON object. const_reverse_iterator crbegin() const { return m_elements.crbegin(); } /// /// Gets the end const reverse iterator element of the object. /// /// A const_reverse_iterator to the end of the JSON object. const_reverse_iterator crend() const { return m_elements.crend(); } /// /// Deletes an element of the JSON object. /// /// A const_iterator to the element to delete. /// Iterator to the new location of the element following the erased element. /// GCC doesn't support erase with const_iterator on vector yet. In the future this should be changed. iterator erase(iterator position) { return m_elements.erase(position); } /// /// Deletes an element of the JSON object. If the key doesn't exist, this method throws. /// /// The key of an element in the JSON object. void erase(const utility::string_t &key) { auto iter = find_by_key(key); if (iter == m_elements.end()) { throw web::json::json_exception(_XPLATSTR("Key not found")); } m_elements.erase(iter); } /// /// Accesses an element of a JSON object. If the key doesn't exist, this method throws. /// /// The key of an element in the JSON object. /// If the key exists, a reference to the value kept in the field. json::value& at(const utility::string_t& key) { auto iter = find_by_key(key); if (iter == m_elements.end()) { throw web::json::json_exception(_XPLATSTR("Key not found")); } return iter->second; } /// /// Accesses an element of a JSON object. If the key doesn't exist, this method throws. /// /// The key of an element in the JSON object. /// If the key exists, a reference to the value kept in the field. const json::value& at(const utility::string_t& key) const { auto iter = find_by_key(key); if (iter == m_elements.end()) { throw web::json::json_exception(_XPLATSTR("Key not found")); } return iter->second; } /// /// Accesses an element of a JSON object. /// /// The key of an element in the JSON object. /// If the key exists, a reference to the value kept in the field, otherwise a newly created null value that will be stored for the given key. json::value& operator[](const utility::string_t& key) { auto iter = find_insert_location(key); if (iter == m_elements.end() || key != iter->first) { return m_elements.insert(iter, std::pair(key, value()))->second; } return iter->second; } /// /// Gets an iterator to an element of a JSON object. /// /// The key of an element in the JSON object. /// A const iterator to the value kept in the field. const_iterator find(const utility::string_t& key) const { return find_by_key(key); } /// /// Gets the number of elements of the object. /// /// The number of elements. size_type size() const { return m_elements.size(); } /// /// Checks if there are any elements in the JSON object. /// /// True iff empty. bool empty() const { return m_elements.empty(); } private: static bool compare_pairs(const std::pair& p1, const std::pair& p2) { return p1.first < p2.first; } static bool compare_with_key(const std::pair& p1, const utility::string_t& key) { return p1.first < key; } storage_type::iterator find_insert_location(const utility::string_t &key) { if (m_keep_order) { return std::find_if(m_elements.begin(), m_elements.end(), [&key](const std::pair& p) { return p.first == key; }); } else { return std::lower_bound(m_elements.begin(), m_elements.end(), key, compare_with_key); } } storage_type::const_iterator find_by_key(const utility::string_t& key) const { if (m_keep_order) { return std::find_if(m_elements.begin(), m_elements.end(), [&key](const std::pair& p) { return p.first == key; }); } else { auto iter = std::lower_bound(m_elements.begin(), m_elements.end(), key, compare_with_key); if (iter != m_elements.end() && key != iter->first) { return m_elements.end(); } return iter; } } storage_type::iterator find_by_key(const utility::string_t& key) { auto iter = find_insert_location(key); if (iter != m_elements.end() && key != iter->first) { return m_elements.end(); } return iter; } storage_type m_elements; bool m_keep_order; friend class details::_Object; template friend class json::details::JSON_Parser; }; /// /// A JSON number represented as a C++ class. /// class number { // Note that these constructors make sure that only negative integers are stored as signed int64 (while others convert to unsigned int64). // This helps handling number objects e.g. comparing two numbers. number(double value) : m_value(value), m_type(double_type) { } number(int32_t value) : m_intval(value), m_type(value < 0 ? signed_type : unsigned_type) { } number(uint32_t value) : m_intval(value), m_type(unsigned_type) { } number(int64_t value) : m_intval(value), m_type(value < 0 ? signed_type : unsigned_type) { } number(uint64_t value) : m_uintval(value), m_type(unsigned_type) { } public: /// /// Does the number fit into int32? /// /// true if the number fits into int32, false otherwise _ASYNCRTIMP bool is_int32() const; /// /// Does the number fit into unsigned int32? /// /// true if the number fits into unsigned int32, false otherwise _ASYNCRTIMP bool is_uint32() const; /// /// Does the number fit into int64? /// /// true if the number fits into int64, false otherwise _ASYNCRTIMP bool is_int64() const; /// /// Does the number fit into unsigned int64? /// /// true if the number fits into unsigned int64, false otherwise bool is_uint64() const { switch (m_type) { case signed_type : return m_intval >= 0; case unsigned_type : return true; case double_type : default : return false; } } /// /// Converts the JSON number to a C++ double. /// /// A double representation of the number double to_double() const { switch (m_type) { case double_type : return m_value; case signed_type : return static_cast(m_intval); case unsigned_type : return static_cast(m_uintval); default : return false; } } /// /// Converts the JSON number to int32. /// /// An int32 representation of the number int32_t to_int32() const { if (m_type == double_type) return static_cast(m_value); else return static_cast(m_intval); } /// /// Converts the JSON number to unsigned int32. /// /// An usigned int32 representation of the number uint32_t to_uint32() const { if (m_type == double_type) return static_cast(m_value); else return static_cast(m_intval); } /// /// Converts the JSON number to int64. /// /// An int64 representation of the number int64_t to_int64() const { if (m_type == double_type) return static_cast(m_value); else return static_cast(m_intval); } /// /// Converts the JSON number to unsigned int64. /// /// An unsigned int64 representation of the number uint64_t to_uint64() const { if (m_type == double_type) return static_cast(m_value); else return static_cast(m_intval); } /// /// Is the number represented internally as an integral type? /// /// true if the number is represented as an integral type, false otherwise bool is_integral() const { return m_type != double_type; } /// /// Compares two JSON numbers for equality. /// /// The JSON number to compare with. /// True iff the numbers are equal. bool operator==(const number &other) const { if (m_type != other.m_type) return false; switch (m_type) { case json::number::type::signed_type : return m_intval == other.m_intval; case json::number::type::unsigned_type : return m_uintval == other.m_uintval; case json::number::type::double_type : return m_value == other.m_value; } __assume(0); } private: union { int64_t m_intval; uint64_t m_uintval; double m_value; }; enum type { signed_type=0, unsigned_type, double_type } m_type; friend class details::_Number; }; namespace details { class _Value { public: virtual std::unique_ptr<_Value> _copy_value() = 0; virtual bool has_field(const utility::string_t &) const { return false; } virtual value get_field(const utility::string_t &) const { throw json_exception(_XPLATSTR("not an object")); } virtual value get_element(array::size_type) const { throw json_exception(_XPLATSTR("not an array")); } virtual value &index(const utility::string_t &) { throw json_exception(_XPLATSTR("not an object")); } virtual value &index(array::size_type) { throw json_exception(_XPLATSTR("not an array")); } virtual const value &cnst_index(const utility::string_t &) const { throw json_exception(_XPLATSTR("not an object")); } virtual const value &cnst_index(array::size_type) const { throw json_exception(_XPLATSTR("not an array")); } // Common function used for serialization to strings and streams. virtual void serialize_impl(std::string& str) const { format(str); } #ifdef _WIN32 virtual void serialize_impl(std::wstring& str) const { format(str); } #endif virtual utility::string_t to_string() const { utility::string_t str; serialize_impl(str); return str; } virtual json::value::value_type type() const { return json::value::Null; } virtual bool is_integer() const { throw json_exception(_XPLATSTR("not a number")); } virtual bool is_double() const { throw json_exception(_XPLATSTR("not a number")); } virtual const json::number& as_number() { throw json_exception(_XPLATSTR("not a number")); } virtual double as_double() const { throw json_exception(_XPLATSTR("not a number")); } virtual int as_integer() const { throw json_exception(_XPLATSTR("not a number")); } virtual bool as_bool() const { throw json_exception(_XPLATSTR("not a boolean")); } virtual json::array& as_array() { throw json_exception(_XPLATSTR("not an array")); } virtual const json::array& as_array() const { throw json_exception(_XPLATSTR("not an array")); } virtual json::object& as_object() { throw json_exception(_XPLATSTR("not an object")); } virtual const json::object& as_object() const { throw json_exception(_XPLATSTR("not an object")); } virtual const utility::string_t& as_string() const { throw json_exception(_XPLATSTR("not a string")); } virtual size_t size() const { return 0; } virtual ~_Value() {} protected: _Value() {} virtual void format(std::basic_string& stream) const { stream.append("null"); } #ifdef _WIN32 virtual void format(std::basic_string& stream) const { stream.append(L"null"); } #endif private: friend class web::json::value; }; class _Null : public _Value { public: virtual std::unique_ptr<_Value> _copy_value() { return utility::details::make_unique<_Null>(); } virtual json::value::value_type type() const { return json::value::Null; } }; class _Number : public _Value { public: _Number(double value) : m_number(value) { } _Number(int32_t value) : m_number(value) { } _Number(uint32_t value) : m_number(value) { } _Number(int64_t value) : m_number(value) { } _Number(uint64_t value) : m_number(value) { } virtual std::unique_ptr<_Value> _copy_value() { return utility::details::make_unique<_Number>(*this); } virtual json::value::value_type type() const { return json::value::Number; } virtual bool is_integer() const { return m_number.is_integral(); } virtual bool is_double() const { return !m_number.is_integral(); } virtual double as_double() const { return m_number.to_double(); } virtual int as_integer() const { return m_number.to_int32(); } virtual const number& as_number() { return m_number; } protected: virtual void format(std::basic_string& stream) const ; #ifdef _WIN32 virtual void format(std::basic_string& stream) const; #endif private: template friend class json::details::JSON_Parser; json::number m_number; }; class _Boolean : public _Value { public: _Boolean(bool value) : m_value(value) { } virtual std::unique_ptr<_Value> _copy_value() { return utility::details::make_unique<_Boolean>(*this); } virtual json::value::value_type type() const { return json::value::Boolean; } virtual bool as_bool() const { return m_value; } protected: virtual void format(std::basic_string& stream) const { stream.append(m_value ? "true" : "false"); } #ifdef _WIN32 virtual void format(std::basic_string& stream) const { stream.append(m_value ? L"true" : L"false"); } #endif private: template friend class json::details::JSON_Parser; bool m_value; }; class _String : public _Value { public: _String(utility::string_t value) : m_string(std::move(value)) { m_has_escape_char = has_escape_chars(*this); } _String(utility::string_t value, bool escaped_chars) : m_string(std::move(value)), m_has_escape_char(escaped_chars) { } #ifdef _WIN32 _String(std::string &&value) : m_string(utility::conversions::to_utf16string(std::move(value))) { m_has_escape_char = has_escape_chars(*this); } _String(std::string &&value, bool escape_chars) : m_string(utility::conversions::to_utf16string(std::move(value))), m_has_escape_char(escape_chars) { } #endif virtual std::unique_ptr<_Value> _copy_value() { return utility::details::make_unique<_String>(*this); } virtual json::value::value_type type() const { return json::value::String; } virtual const utility::string_t & as_string() const; virtual void serialize_impl(std::string& str) const { serialize_impl_char_type(str); } #ifdef _WIN32 virtual void serialize_impl(std::wstring& str) const { serialize_impl_char_type(str); } #endif protected: virtual void format(std::basic_string& str) const; #ifdef _WIN32 virtual void format(std::basic_string& str) const; #endif private: friend class _Object; friend class _Array; size_t get_reserve_size() const { return m_string.size() + 2; } template void serialize_impl_char_type(std::basic_string& str) const { // To avoid repeated allocations reserve some space all up front. // size of string + 2 for quotes str.reserve(get_reserve_size()); format(str); } std::string as_utf8_string() const; utf16string as_utf16_string() const; utility::string_t m_string; // There are significant performance gains that can be made by knowning whether // or not a character that requires escaping is present. bool m_has_escape_char; static bool has_escape_chars(const _String &str); }; template _ASYNCRTIMP void append_escape_string(std::basic_string& str, const std::basic_string& escaped); void format_string(const utility::string_t& key, utility::string_t& str); #ifdef _WIN32 void format_string(const utility::string_t& key, std::string& str); #endif class _Object : public _Value { public: _Object(bool keep_order) : m_object(keep_order) { } _Object(object::storage_type fields, bool keep_order) : m_object(std::move(fields), keep_order) { } virtual std::unique_ptr<_Value> _copy_value() { return utility::details::make_unique<_Object>(*this); } virtual json::object& as_object() { return m_object; } virtual const json::object& as_object() const { return m_object; } virtual json::value::value_type type() const { return json::value::Object; } virtual bool has_field(const utility::string_t &) const; virtual json::value &index(const utility::string_t &key); bool is_equal(const _Object* other) const { if (m_object.size() != other->m_object.size()) return false; return std::equal(std::begin(m_object), std::end(m_object), std::begin(other->m_object)); } virtual void serialize_impl(std::string& str) const { // To avoid repeated allocations reserve some space all up front. str.reserve(get_reserve_size()); format(str); } #ifdef _WIN32 virtual void serialize_impl(std::wstring& str) const { // To avoid repeated allocations reserve some space all up front. str.reserve(get_reserve_size()); format(str); } #endif size_t size() const { return m_object.size(); } protected: virtual void format(std::basic_string& str) const { format_impl(str); } #ifdef _WIN32 virtual void format(std::basic_string& str) const { format_impl(str); } #endif private: json::object m_object; template friend class json::details::JSON_Parser; template void format_impl(std::basic_string& str) const { str.push_back('{'); if(!m_object.empty()) { auto lastElement = m_object.end() - 1; for (auto iter = m_object.begin(); iter != lastElement; ++iter) { format_string(iter->first, str); str.push_back(':'); iter->second.format(str); str.push_back(','); } format_string(lastElement->first, str); str.push_back(':'); lastElement->second.format(str); } str.push_back('}'); } size_t get_reserve_size() const { // This is a heuristic we can tune more in the future: // Basically size of string plus // sum size of value if an object, array, or string. size_t reserveSize = 2; // For brackets {} for(auto iter = m_object.begin(); iter != m_object.end(); ++iter) { reserveSize += iter->first.length() + 2; // 2 for quotes size_t valueSize = iter->second.size() * 20; // Multipler by each object/array element if(valueSize == 0) { if(iter->second.type() == json::value::String) { valueSize = static_cast<_String *>(iter->second.m_value.get())->get_reserve_size(); } else { valueSize = 5; // true, false, or null } } reserveSize += valueSize; } return reserveSize; } }; class _Array : public _Value { public: _Array() {} _Array(array::size_type size) : m_array(size) {} _Array(array::storage_type elements) : m_array(std::move(elements)) { } virtual std::unique_ptr<_Value> _copy_value() { return utility::details::make_unique<_Array>(*this); } virtual json::value::value_type type() const { return json::value::Array; } virtual json::array& as_array() { return m_array; } virtual const json::array& as_array() const { return m_array; } virtual json::value &index(json::array::size_type index) { return m_array[index]; } bool is_equal(const _Array* other) const { if ( m_array.size() != other->m_array.size()) return false; auto iterT = m_array.cbegin(); auto iterO = other->m_array.cbegin(); auto iterTe = m_array.cend(); auto iterOe = other->m_array.cend(); for (; iterT != iterTe && iterO != iterOe; ++iterT, ++iterO) { if ( *iterT != *iterO ) return false; } return true; } virtual void serialize_impl(std::string& str) const { // To avoid repeated allocations reserve some space all up front. str.reserve(get_reserve_size()); format(str); } #ifdef _WIN32 virtual void serialize_impl(std::wstring& str) const { // To avoid repeated allocations reserve some space all up front. str.reserve(get_reserve_size()); format(str); } #endif size_t size() const { return m_array.size(); } protected: virtual void format(std::basic_string& str) const { format_impl(str); } #ifdef _WIN32 virtual void format(std::basic_string& str) const { format_impl(str); } #endif private: json::array m_array; template friend class json::details::JSON_Parser; template void format_impl(std::basic_string& str) const { str.push_back('['); if(!m_array.m_elements.empty()) { auto lastElement = m_array.m_elements.end() - 1; for (auto iter = m_array.m_elements.begin(); iter != lastElement; ++iter) { iter->format(str); str.push_back(','); } lastElement->format(str); } str.push_back(']'); } size_t get_reserve_size() const { // This is a heuristic we can tune more in the future: // Basically sum size of each value if an object, array, or string by a multiplier. size_t reserveSize = 2; // For brackets [] for(auto iter = m_array.cbegin(); iter != m_array.cend(); ++iter) { size_t valueSize = iter->size() * 20; // Per each nested array/object if(valueSize == 0) valueSize = 5; // true, false, or null reserveSize += valueSize; } return reserveSize; } }; } // namespace details /// /// Gets the number of children of the value. /// /// The number of children. 0 for all non-composites. inline size_t json::value::size() const { return m_value->size(); } /// /// Test for the presence of a field. /// /// The name of the field /// True if the field exists, false otherwise. inline bool json::value::has_field(const utility::string_t& key) const { return m_value->has_field(key); } /// /// Access a field of a JSON object. /// /// The name of the field /// The value kept in the field; null if the field does not exist inline json::value json::value::get(const utility::string_t& key) const { return m_value->get_field(key); } /// /// Access an element of a JSON array. /// /// The index of an element in the JSON array /// The value kept at the array index; null if outside the boundaries of the array inline json::value json::value::get(size_t index) const { return m_value->get_element(index); } /// /// A standard std::ostream operator to facilitate writing JSON values to streams. /// /// The output stream to write the JSON value to. /// The JSON value to be written to the stream. /// The output stream object _ASYNCRTIMP utility::ostream_t& __cdecl operator << (utility::ostream_t &os, const json::value &val); /// /// A standard std::istream operator to facilitate reading JSON values from streams. /// /// The input stream to read the JSON value from. /// The JSON value object read from the stream. /// The input stream object. _ASYNCRTIMP utility::istream_t& __cdecl operator >> (utility::istream_t &is, json::value &val); }} #if HC_PLATFORM != HC_PLATFORM_ANDROID #pragma warning (pop) #endif #endif ================================================ FILE: Include/cpprestinclude/cpprest/producerconsumerstream.h ================================================ /*** * ==++== * * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * ==--== * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ * * This file defines a basic memory-based stream buffer, which allows consumer / producer pairs to communicate * data via a buffer. * * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk * * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ****/ #pragma once #ifndef _CASA_PRODUCER_CONSUMER_STREAMS_H #define _CASA_PRODUCER_CONSUMER_STREAMS_H #include #include #include #include #include "pplx/pplxtasks.h" #include "cpprest/astreambuf.h" namespace Concurrency { namespace streams { namespace details { /// /// The basic_producer_consumer_buffer class serves as a memory-based steam buffer that supports both writing and reading /// sequences of characters. It can be used as a consumer/producer buffer. /// template class basic_producer_consumer_buffer : public streams::details::streambuf_state_manager<_CharType> { public: typedef typename ::concurrency::streams::char_traits<_CharType> traits; typedef typename basic_streambuf<_CharType>::int_type int_type; typedef typename basic_streambuf<_CharType>::pos_type pos_type; typedef typename basic_streambuf<_CharType>::off_type off_type; /// /// Constructor /// basic_producer_consumer_buffer(size_t alloc_size) : streambuf_state_manager<_CharType>(std::ios_base::out | std::ios_base::in), m_alloc_size(alloc_size), m_allocBlock(nullptr), m_total(0), m_total_read(0), m_total_written(0), m_synced(0) { } /// /// Destructor /// virtual ~basic_producer_consumer_buffer() { // Note: there is no need to call 'wait()' on the result of close(), // since we happen to know that close() will return without actually // doing anything asynchronously. Should the implementation of _close_write() // change in that regard, this logic may also have to change. (void)this->_close_read(); (void)this->_close_write(); _ASSERTE(m_requests.empty()); m_blocks.clear(); } /// /// can_seek is used to determine whether a stream buffer supports seeking. /// virtual bool can_seek() const { return false; } /// /// has_size is used to determine whether a stream buffer supports size(). /// virtual bool has_size() const { return false; } /// /// Get the stream buffer size, if one has been set. /// /// The direction of buffering (in or out) /// An implementation that does not support buffering will always return '0'. virtual size_t buffer_size(std::ios_base::openmode = std::ios_base::in) const { return 0; } /// /// Sets the stream buffer implementation to buffer or not buffer. /// /// The size to use for internal buffering, 0 if no buffering should be done. /// The direction of buffering (in or out) /// An implementation that does not support buffering will silently ignore calls to this function and it will not have any effect on what is returned by subsequent calls to . virtual void set_buffer_size(size_t , std::ios_base::openmode = std::ios_base::in) { return; } /// /// For any input stream, in_avail returns the number of characters that are immediately available /// to be consumed without blocking. May be used in conjunction with to read data without /// incurring the overhead of using tasks. /// virtual size_t in_avail() const { return m_total; } /// /// Gets the current read or write position in the stream. /// /// The I/O direction to seek (see remarks) /// The current position. EOF if the operation fails. /// Some streams may have separate write and read cursors. /// For such streams, the direction parameter defines whether to move the read or the write cursor. virtual pos_type getpos(std::ios_base::openmode mode) const { if ( ((mode & std::ios_base::in) && !this->can_read()) || ((mode & std::ios_base::out) && !this->can_write())) return static_cast(traits::eof()); if (mode == std::ios_base::in) return (pos_type)m_total_read; else if (mode == std::ios_base::out) return (pos_type)m_total_written; else return (pos_type)traits::eof(); } // Seeking is not supported virtual pos_type seekpos(pos_type, std::ios_base::openmode) { return (pos_type)traits::eof(); } virtual pos_type seekoff(off_type , std::ios_base::seekdir , std::ios_base::openmode ) { return (pos_type)traits::eof(); } /// /// Allocates a contiguous memory block and returns it. /// /// The number of characters to allocate. /// A pointer to a block to write to, null if the stream buffer implementation does not support alloc/commit. virtual _CharType* _alloc(size_t count) { if (!this->can_write()) { return nullptr; } // We always allocate a new block even if the count could be satisfied by // the current write block. While this does lead to wasted space it allows for // easier book keeping _ASSERTE(!m_allocBlock); m_allocBlock = std::make_shared<_block>(count); return m_allocBlock->wbegin(); } /// /// Submits a block already allocated by the stream buffer. /// /// The number of characters to be committed. virtual void _commit(size_t count) { pplx::extensibility::scoped_critical_section_t l(m_lock); // The count does not reflect the actual size of the block. // Since we do not allow any more writes to this block it would suffice. // If we ever change the algorithm to reuse blocks then this needs to be revisited. _ASSERTE((bool)m_allocBlock); m_allocBlock->update_write_head(count); m_blocks.push_back(m_allocBlock); m_allocBlock = nullptr; update_write_head(count); } /// /// Gets a pointer to the next already allocated contiguous block of data. /// /// A reference to a pointer variable that will hold the address of the block on success. /// The number of contiguous characters available at the address in 'ptr.' /// true if the operation succeeded, false otherwise. /// /// A return of false does not necessarily indicate that a subsequent read operation would fail, only that /// there is no block to return immediately or that the stream buffer does not support the operation. /// The stream buffer may not de-allocate the block until is called. /// If the end of the stream is reached, the function will return true, a null pointer, and a count of zero; /// a subsequent read will not succeed. /// virtual bool acquire(_Out_ _CharType*& ptr, _Out_ size_t& count) { count = 0; ptr = nullptr; if (!this->can_read()) return false; pplx::extensibility::scoped_critical_section_t l(m_lock); if (m_blocks.empty()) { // If the write head has been closed then have reached the end of the // stream (return true), otherwise more data could be written later (return false). return !this->can_write(); } else { auto block = m_blocks.front(); count = block->rd_chars_left(); ptr = block->rbegin(); _ASSERTE(ptr != nullptr); return true; } } /// /// Releases a block of data acquired using . This frees the stream buffer to de-allocate the /// memory, if it so desires. Move the read position ahead by the count. /// /// A pointer to the block of data to be released. /// The number of characters that were read. virtual void release(_Out_writes_opt_ (count) _CharType *ptr, _In_ size_t count) { if (ptr == nullptr) return; pplx::extensibility::scoped_critical_section_t l(m_lock); auto block = m_blocks.front(); _ASSERTE(block->rd_chars_left() >= count); block->m_read += count; update_read_head(count); } protected: virtual pplx::task _sync() { pplx::extensibility::scoped_critical_section_t l(m_lock); m_synced = in_avail(); fulfill_outstanding(); return pplx::task_from_result(true); } virtual pplx::task _putc(_CharType ch) { return pplx::task_from_result((this->write(&ch, 1) == 1) ? static_cast(ch) : traits::eof()); } virtual pplx::task _putn(const _CharType *ptr, size_t count) { return pplx::task_from_result(this->write(ptr, count)); } virtual pplx::task _getn(_Out_writes_ (count) _CharType *ptr, _In_ size_t count) { pplx::task_completion_event tce; enqueue_request(_request(count, [this, ptr, count, tce]() { // VS 2010 resolves read to a global function. Explicit // invocation through the "this" pointer fixes the issue. tce.set(this->read(ptr, count)); })); return pplx::create_task(tce); } virtual size_t _sgetn(_Out_writes_ (count) _CharType *ptr, _In_ size_t count) { pplx::extensibility::scoped_critical_section_t l(m_lock); return can_satisfy(count) ? this->read(ptr, count) : (size_t)traits::requires_async(); } virtual size_t _scopy(_Out_writes_ (count) _CharType *ptr, _In_ size_t count) { pplx::extensibility::scoped_critical_section_t l(m_lock); return can_satisfy(count) ? this->read(ptr, count, false) : (size_t)traits::requires_async(); } virtual pplx::task _bumpc() { pplx::task_completion_event tce; enqueue_request(_request(1, [this, tce]() { tce.set(this->read_byte(true)); })); return pplx::create_task(tce); } virtual int_type _sbumpc() { pplx::extensibility::scoped_critical_section_t l(m_lock); return can_satisfy(1) ? this->read_byte(true) : traits::requires_async(); } virtual pplx::task _getc() { pplx::task_completion_event tce; enqueue_request(_request(1, [this, tce]() { tce.set(this->read_byte(false)); })); return pplx::create_task(tce); } int_type _sgetc() { pplx::extensibility::scoped_critical_section_t l(m_lock); return can_satisfy(1) ? this->read_byte(false) : traits::requires_async(); } virtual pplx::task _nextc() { pplx::task_completion_event tce; enqueue_request(_request(1, [this, tce]() { this->read_byte(true); tce.set(this->read_byte(false)); })); return pplx::create_task(tce); } virtual pplx::task _ungetc() { return pplx::task_from_result(traits::eof()); } private: /// /// Close the stream buffer for writing /// pplx::task _close_write() { // First indicate that there could be no more writes. // Fulfill outstanding relies on that to flush all the // read requests. this->m_stream_can_write = false; { pplx::extensibility::scoped_critical_section_t l(this->m_lock); // This runs on the thread that called close. this->fulfill_outstanding(); } return pplx::task_from_result(); } /// /// Updates the write head by an offset specified by count /// /// This should be called with the lock held void update_write_head(size_t count) { m_total += count; m_total_written += count; fulfill_outstanding(); } /// /// Writes count characters from ptr into the stream buffer /// size_t write(const _CharType *ptr, size_t count) { if (!this->can_write() || (count == 0)) return 0; // If no one is going to read, why bother? // Just pretend to be writing! if (!this->can_read()) return count; pplx::extensibility::scoped_critical_section_t l(m_lock); // Allocate a new block if necessary if ( m_blocks.empty() || m_blocks.back()->wr_chars_left() < count ) { msl::safeint3::SafeInt alloc = m_alloc_size.Max(count); m_blocks.push_back(std::make_shared<_block>(alloc)); } // The block at the back is always the write head auto last = m_blocks.back(); auto countWritten = last->write(ptr, count); _ASSERTE(countWritten == count); update_write_head(countWritten); return countWritten; } /// /// Fulfill pending requests /// /// This should be called with the lock held void fulfill_outstanding() { while ( !m_requests.empty() ) { auto req = m_requests.front(); // If we cannot satisfy the request then we need // to wait for the producer to write data if (!can_satisfy(req.size())) return; // We have enough data to satisfy this request req.complete(); // Remove it from the request queue m_requests.pop(); } } /// /// Represents a memory block /// class _block { public: _block(size_t size) : m_read(0), m_pos(0), m_size(size), m_data(new _CharType[size]) { } ~_block() { delete [] m_data; } // Read head size_t m_read; // Write head size_t m_pos; // Allocation size (of m_data) size_t m_size; // The data store _CharType * m_data; // Pointer to the read head _CharType * rbegin() { return m_data + m_read; } // Pointer to the write head _CharType * wbegin() { return m_data + m_pos; } // Read up to count characters from the block size_t read(_Out_writes_ (count) _CharType * dest, _In_ size_t count, bool advance = true) { msl::safeint3::SafeInt avail(rd_chars_left()); auto countRead = static_cast(avail.Min(count)); _CharType * beg = rbegin(); _CharType * end = rbegin() + countRead; #ifdef _WIN32 // Avoid warning C4996: Use checked iterators under SECURE_SCL std::copy(beg, end, stdext::checked_array_iterator<_CharType *>(dest, count)); #else std::copy(beg, end, dest); #endif // _WIN32 if (advance) { m_read += countRead; } return countRead; } // Write count characters into the block size_t write(const _CharType * src, size_t count) { msl::safeint3::SafeInt avail(wr_chars_left()); auto countWritten = static_cast(avail.Min(count)); const _CharType * srcEnd = src + countWritten; #ifdef _WIN32 // Avoid warning C4996: Use checked iterators under SECURE_SCL std::copy(src, srcEnd, stdext::checked_array_iterator<_CharType *>(wbegin(), static_cast(avail))); #else std::copy(src, srcEnd, wbegin()); #endif // _WIN32 update_write_head(countWritten); return countWritten; } void update_write_head(size_t count) { m_pos += count; } size_t rd_chars_left() const { return m_pos-m_read; } size_t wr_chars_left() const { return m_size-m_pos; } private: // Copy is not supported _block(const _block&); _block& operator=(const _block&); }; /// /// Represents a request on the stream buffer - typically reads /// class _request { public: typedef std::function func_type; _request(size_t count, const func_type& func) : m_func(func), m_count(count) { } void complete() { m_func(); } size_t size() const { return m_count; } private: func_type m_func; size_t m_count; }; void enqueue_request(_request req) { pplx::extensibility::scoped_critical_section_t l(m_lock); if (can_satisfy(req.size())) { // We can immediately fulfill the request. req.complete(); } else { // We must wait for data to arrive. m_requests.push(req); } } /// /// Determine if the request can be satisfied. /// bool can_satisfy(size_t count) { return (m_synced > 0) || (this->in_avail() >= count) || !this->can_write(); } /// /// Reads a byte from the stream and returns it as int_type. /// Note: This routine shall only be called if can_satisfy() returned true. /// /// This should be called with the lock held int_type read_byte(bool advance = true) { _CharType value; auto read_size = this->read(&value, 1, advance); return read_size == 1 ? static_cast(value) : traits::eof(); } /// /// Reads up to count characters into ptr and returns the count of characters copied. /// The return value (actual characters copied) could be <= count. /// Note: This routine shall only be called if can_satisfy() returned true. /// /// This should be called with the lock held size_t read(_Out_writes_ (count) _CharType *ptr, _In_ size_t count, bool advance = true) { _ASSERTE(can_satisfy(count)); size_t read = 0; for (auto iter = begin(m_blocks); iter != std::end(m_blocks); ++iter) { auto block = *iter; auto read_from_block = block->read(ptr + read, count - read, advance); read += read_from_block; _ASSERTE(count >= read); if (read == count) break; } if (advance) { update_read_head(read); } return read; } /// /// Updates the read head by the specified offset /// /// This should be called with the lock held void update_read_head(size_t count) { m_total -= count; m_total_read += count; if ( m_synced > 0 ) m_synced = (m_synced > count) ? (m_synced-count) : 0; // The block at the front is always the read head. // Purge empty blocks so that the block at the front reflects the read head while (!m_blocks.empty()) { // If front block is not empty - we are done if (m_blocks.front()->rd_chars_left() > 0) break; // The block has no more data to be read. Relase the block m_blocks.pop_front(); } } // The in/out mode for the buffer std::ios_base::openmode m_mode{}; // Default block size msl::safeint3::SafeInt m_alloc_size; // Block used for alloc/commit std::shared_ptr<_block> m_allocBlock; // Total available data size_t m_total; size_t m_total_read; size_t m_total_written; // Keeps track of the number of chars that have been flushed but still // remain to be consumed by a read operation. size_t m_synced; // The producer-consumer buffer is intended to be used concurrently by a reader // and a writer, who are not coordinating their accesses to the buffer (coordination // being what the buffer is for in the first place). Thus, we have to protect // against some of the internal data elements against concurrent accesses // and the possibility of inconsistent states. A simple non-recursive lock // should be sufficient for those purposes. pplx::extensibility::critical_section_t m_lock; // Memory blocks std::deque> m_blocks; // Queue of requests std::queue<_request> m_requests; }; } // namespace details /// /// The producer_consumer_buffer class serves as a memory-based steam buffer that supports both writing and reading /// sequences of bytes. It can be used as a consumer/producer buffer. /// /// /// The data type of the basic element of the producer_consumer_buffer. /// /// /// This is a reference-counted version of basic_producer_consumer_buffer. template class producer_consumer_buffer : public streambuf<_CharType> { public: typedef _CharType char_type; /// /// Create a producer_consumer_buffer. /// /// The internal default block size. producer_consumer_buffer(size_t alloc_size = 512) : streambuf<_CharType>(std::make_shared>(alloc_size)) { } }; }} // namespaces #endif ================================================ FILE: Include/cpprestinclude/cpprest/streams.h ================================================ #if !XSAPI_NO_PPL /*** * Copyright (C) Microsoft. All rights reserved. * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. * * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ * * Asynchronous I/O: streams API, used for formatted input and output, based on unformatted I/O using stream buffers * * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk * * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ****/ #pragma once #ifndef CASA_STREAMS_H #define CASA_STREAMS_H #include "cpprest/astreambuf.h" #include #include namespace Concurrency { namespace streams { template class basic_ostream; template class basic_istream; namespace details { template class basic_ostream_helper { public: basic_ostream_helper(streams::streambuf buffer) : m_buffer(buffer) {} ~basic_ostream_helper() {} private: template friend class streams::basic_ostream; concurrency::streams::streambuf m_buffer; }; template class basic_istream_helper { public: basic_istream_helper(streams::streambuf buffer) : m_buffer(buffer) {} ~basic_istream_helper() {} private: template friend class streams::basic_istream; concurrency::streams::streambuf m_buffer; }; template struct Value2StringFormatter { template static std::basic_string format(const T& val) { std::basic_ostringstream ss; ss << val; return ss.str(); } }; template<> struct Value2StringFormatter { template static std::basic_string format(const T& val) { std::basic_ostringstream ss; ss << val; return reinterpret_cast(ss.str().c_str()); } static std::basic_string format(const utf16string& val) { return format(utility::conversions::utf16_to_utf8(val)); } }; static const char* _in_stream_msg = "stream not set up for input of data"; static const char* _in_streambuf_msg = "stream buffer not set up for input of data"; static const char* _out_stream_msg = "stream not set up for output of data"; static const char* _out_streambuf_msg = "stream buffer not set up for output of data"; } // namespace details /// /// Base interface for all asynchronous output streams. /// template class basic_ostream { public: typedef char_traits traits; typedef typename traits::int_type int_type; typedef typename traits::pos_type pos_type; typedef typename traits::off_type off_type; /// /// Default constructor /// basic_ostream() {} /// /// Copy constructor /// /// The source object basic_ostream(const basic_ostream& other) : m_helper(other.m_helper) {} /// /// Assignment operator /// /// The source object /// A reference to the stream object that contains the result of the assignment. basic_ostream& operator=(const basic_ostream& other) { m_helper = other.m_helper; return *this; } /// /// Constructor /// /// A stream buffer. basic_ostream(streams::streambuf buffer) : m_helper(std::make_shared>(buffer)) { _verify_and_throw(details::_out_streambuf_msg); } /// /// Close the stream, preventing further write operations. /// pplx::task close() const { return is_valid() ? helper()->m_buffer.close(std::ios_base::out) : pplx::task_from_result(); } /// /// Close the stream with exception, preventing further write operations. /// /// Pointer to the exception. pplx::task close(std::exception_ptr eptr) const { return is_valid() ? helper()->m_buffer.close(std::ios_base::out, eptr) : pplx::task_from_result(); } /// /// Put a single character into the stream. /// /// A character pplx::task write(CharType ch) const { pplx::task result; if (!_verify_and_return_task(details::_out_stream_msg, result)) return result; return helper()->m_buffer.putc(ch); } /// /// Write a single value of "blittable" type T into the stream. /// /// A value of type T. /// /// This is not a replacement for a proper binary serialization solution, but it may /// form the foundation for one. Writing data bit-wise to a stream is a primitive /// operation of binary serialization. /// Currently, no attention is paid to byte order. All data is written in the platform's /// native byte order, which means little-endian on all platforms that have been tested. /// This function is only available for streams using a single-byte character size. /// template CASABLANCA_DEPRECATED( "Unsafe API that will be removed in future releases, use one of the other write overloads instead.") pplx::task write(T value) const { static_assert(sizeof(CharType) == 1, "binary write is only supported for single-byte streams"); static_assert(std::is_trivial::value, "unsafe to use with non-trivial types"); pplx::task result; if (!_verify_and_return_task(details::_out_stream_msg, result)) return result; auto copy = std::make_shared(std::move(value)); return helper() ->m_buffer.putn_nocopy((CharType*)copy.get(), sizeof(T)) .then([copy](pplx::task op) -> size_t { return op.get(); }); } /// /// Write a number of characters from a given stream buffer into the stream. /// /// A source stream buffer. /// The number of characters to write. pplx::task write(streams::streambuf source, size_t count) const { pplx::task result; if (!_verify_and_return_task(details::_out_stream_msg, result)) return result; if (!source.can_read()) return pplx::task_from_exception( std::make_exception_ptr(std::runtime_error("source buffer not set up for input of data"))); if (count == 0) return pplx::task_from_result((size_t)0); auto buffer = helper()->m_buffer; auto data = buffer.alloc(count); if (data != nullptr) { auto post_read = [buffer](pplx::task op) -> pplx::task { auto b = buffer; b.commit(op.get()); return op; }; return source.getn(data, count).then(post_read); } else { size_t available = 0; const bool acquired = source.acquire(data, available); if (available >= count) { auto post_write = [source, data](pplx::task op) -> pplx::task { auto s = source; s.release(data, op.get()); return op; }; return buffer.putn_nocopy(data, count).then(post_write); } else { // Always have to release if acquire returned true. if (acquired) { source.release(data, 0); } std::shared_ptr buf(new CharType[count], [](CharType* buf) { delete[] buf; }); auto post_write = [buf](pplx::task op) -> pplx::task { return op; }; auto post_read = [buf, post_write, buffer](pplx::task op) -> pplx::task { auto b = buffer; return b.putn_nocopy(buf.get(), op.get()).then(post_write); }; return source.getn(buf.get(), count).then(post_read); } } } /// /// Write the specified string to the output stream. /// /// Input string. pplx::task print(const std::basic_string& str) const { pplx::task result; if (!_verify_and_return_task(details::_out_stream_msg, result)) return result; if (str.empty()) { return pplx::task_from_result(0); } else { auto sharedStr = std::make_shared>(str); return helper()->m_buffer.putn_nocopy(sharedStr->c_str(), sharedStr->size()).then([sharedStr](size_t size) { return size; }); } } /// /// Write a value of type T to the output stream. /// /// /// The data type of the object to be written to the stream /// /// Input object. template pplx::task print(const T& val) const { pplx::task result; if (!_verify_and_return_task(details::_out_stream_msg, result)) return result; // TODO in the future this could be improved to have Value2StringFormatter avoid another unnecessary copy // by putting the string on the heap before calling the print string overload. return print(details::Value2StringFormatter::format(val)); } /// /// Write a value of type T to the output stream and append a newline character. /// /// /// The data type of the object to be written to the stream /// /// Input object. template pplx::task print_line(const T& val) const { pplx::task result; if (!_verify_and_return_task(details::_out_stream_msg, result)) return result; auto str = details::Value2StringFormatter::format(val); str.push_back(CharType('\n')); return print(str); } /// /// Flush any buffered output data. /// pplx::task flush() const { pplx::task result; if (!_verify_and_return_task(details::_out_stream_msg, result)) return result; return helper()->m_buffer.sync(); } /// /// Seeks to the specified write position. /// /// An offset relative to the beginning of the stream. /// The new position in the stream. pos_type seek(pos_type pos) const { _verify_and_throw(details::_out_stream_msg); return helper()->m_buffer.seekpos(pos, std::ios_base::out); } /// /// Seeks to the specified write position. /// /// An offset relative to the beginning, current write position, or the end of the stream. /// The starting point (beginning, current, end) for the seek. /// The new position in the stream. pos_type seek(off_type off, std::ios_base::seekdir way) const { _verify_and_throw(details::_out_stream_msg); return helper()->m_buffer.seekoff(off, way, std::ios_base::out); } /// /// Get the current write position, i.e. the offset from the beginning of the stream. /// /// The current write position. pos_type tell() const { _verify_and_throw(details::_out_stream_msg); return helper()->m_buffer.getpos(std::ios_base::out); } /// /// can_seek is used to determine whether the stream supports seeking. /// /// true if the stream supports seeking, false otherwise. bool can_seek() const { return is_valid() && m_helper->m_buffer.can_seek(); } /// /// Test whether the stream has been initialized with a valid stream buffer. /// /// true if the stream has been initialized with a valid stream buffer, false /// otherwise. bool is_valid() const { return (m_helper != nullptr) && ((bool)m_helper->m_buffer); } /// /// Test whether the stream has been initialized or not. /// operator bool() const { return is_valid(); } /// /// Test whether the stream is open for writing. /// /// true if the stream is open for writing, false otherwise. bool is_open() const { return is_valid() && m_helper->m_buffer.can_write(); } /// /// Get the underlying stream buffer. /// /// The underlying stream buffer. concurrency::streams::streambuf streambuf() const { return helper()->m_buffer; } protected: void set_helper(std::shared_ptr> helper) { m_helper = helper; } private: template bool _verify_and_return_task(const char* msg, pplx::task& tsk) const { auto buffer = helper()->m_buffer; if (!(buffer.exception() == nullptr)) { tsk = pplx::task_from_exception(buffer.exception()); return false; } if (!buffer.can_write()) { tsk = pplx::task_from_exception(std::make_exception_ptr(std::runtime_error(msg))); return false; } return true; } void _verify_and_throw(const char* msg) const { auto buffer = helper()->m_buffer; if (!(buffer.exception() == nullptr)) std::rethrow_exception(buffer.exception()); if (!buffer.can_write()) throw std::runtime_error(msg); } std::shared_ptr> helper() const { if (!m_helper) throw std::logic_error("uninitialized stream object"); return m_helper; } std::shared_ptr> m_helper; }; template struct _type_parser_integral_traits { typedef std::false_type _is_integral; typedef std::false_type _is_unsigned; }; #ifdef _WIN32 #define _INT_TRAIT(_t, _low, _high) \ template<> \ struct _type_parser_integral_traits<_t> \ { \ typedef std::true_type _is_integral; \ typedef std::false_type _is_unsigned; \ static const int64_t _min = _low; \ static const int64_t _max = _high; \ }; #define _UINT_TRAIT(_t, _low, _high) \ template<> \ struct _type_parser_integral_traits<_t> \ { \ typedef std::true_type _is_integral; \ typedef std::true_type _is_unsigned; \ static const uint64_t _max = _high; \ }; _INT_TRAIT(char, INT8_MIN, INT8_MAX) _INT_TRAIT(signed char, INT8_MIN, INT8_MAX) _INT_TRAIT(short, INT16_MIN, INT16_MAX) #if defined(_NATIVE_WCHAR_T_DEFINED) _INT_TRAIT(wchar_t, WCHAR_MIN, WCHAR_MAX) #endif _INT_TRAIT(int, INT32_MIN, INT32_MAX) _INT_TRAIT(long, LONG_MIN, LONG_MAX) _INT_TRAIT(long long, LLONG_MIN, LLONG_MAX) _UINT_TRAIT(unsigned char, UINT8_MIN, UINT8_MAX) _UINT_TRAIT(unsigned short, UINT16_MIN, UINT16_MAX) _UINT_TRAIT(unsigned int, UINT32_MIN, UINT32_MAX) _UINT_TRAIT(unsigned long, ULONG_MIN, ULONG_MAX) _UINT_TRAIT(unsigned long long, ULLONG_MIN, ULLONG_MAX) #else #define _INT_TRAIT(_t) \ template<> \ struct _type_parser_integral_traits<_t> \ { \ typedef std::true_type _is_integral; \ typedef std::false_type _is_unsigned; \ static const int64_t _min = (std::numeric_limits<_t>::min)(); \ static const int64_t _max = (std::numeric_limits<_t>::max)(); \ }; #define _UINT_TRAIT(_t) \ template<> \ struct _type_parser_integral_traits<_t> \ { \ typedef std::true_type _is_integral; \ typedef std::true_type _is_unsigned; \ static const uint64_t _max = (std::numeric_limits<_t>::max)(); \ }; _INT_TRAIT(char) _INT_TRAIT(signed char) _INT_TRAIT(short) _INT_TRAIT(utf16char) _INT_TRAIT(int) _INT_TRAIT(long) _INT_TRAIT(long long) _UINT_TRAIT(unsigned char) _UINT_TRAIT(unsigned short) _UINT_TRAIT(unsigned int) _UINT_TRAIT(unsigned long) _UINT_TRAIT(unsigned long long) #endif template class _type_parser_base { public: typedef char_traits traits; typedef typename traits::int_type int_type; _type_parser_base() {} protected: // Aid in parsing input: skipping whitespace characters. static pplx::task _skip_whitespace(streams::streambuf buffer); // Aid in parsing input: peek at a character at a time, call type-specific code to examine, extract value when done. // AcceptFunctor should model std::function, int_type)> // ExtractFunctor should model std::function(std::shared_ptr)> template static pplx::task _parse_input(streams::streambuf buffer, AcceptFunctor accept_character, ExtractFunctor extract); }; /// /// Class used to handle asynchronous parsing for basic_istream::extract. To support new /// types create a new template specialization and implement the parse function. /// template class type_parser { public: static pplx::task parse(streams::streambuf buffer) { typedef typename _type_parser_integral_traits::_is_integral ii; typedef typename _type_parser_integral_traits::_is_unsigned ui; static_assert(ii::value || !ui::value, "type is not supported for extraction from a stream"); return _parse(buffer, ii {}, ui {}); } private: static pplx::task _parse(streams::streambuf buffer, std::false_type, std::false_type) { _parse_floating_point(buffer); } static pplx::task _parse(streams::streambuf buffer, std::true_type, std::false_type) { return type_parser::parse(buffer).then([](pplx::task op) -> T { int64_t val = op.get(); if (val <= _type_parser_integral_traits::_max && val >= _type_parser_integral_traits::_min) return (T)val; else throw std::range_error("input out of range for target type"); }); } static pplx::task _parse(streams::streambuf buffer, std::true_type, std::true_type) { return type_parser::parse(buffer).then([](pplx::task op) -> T { uint64_t val = op.get(); if (val <= _type_parser_integral_traits::_max) return (T)val; else throw std::range_error("input out of range for target type"); }); } }; /// /// Base interface for all asynchronous input streams. /// template class basic_istream { public: typedef char_traits traits; typedef typename traits::int_type int_type; typedef typename traits::pos_type pos_type; typedef typename traits::off_type off_type; /// /// Default constructor /// basic_istream() {} /// /// Constructor /// /// /// The data type of the basic element of the stream. /// /// A stream buffer. template basic_istream(streams::streambuf buffer) : m_helper(std::make_shared>(std::move(buffer))) { _verify_and_throw(details::_in_streambuf_msg); } /// /// Copy constructor /// /// The source object basic_istream(const basic_istream& other) : m_helper(other.m_helper) {} /// /// Assignment operator /// /// The source object /// A reference to the stream object that contains the result of the assignment. basic_istream& operator=(const basic_istream& other) { m_helper = other.m_helper; return *this; } /// /// Close the stream, preventing further read operations. /// pplx::task close() const { return is_valid() ? helper()->m_buffer.close(std::ios_base::in) : pplx::task_from_result(); } /// /// Close the stream with exception, preventing further read operations. /// /// Pointer to the exception. pplx::task close(std::exception_ptr eptr) const { return is_valid() ? m_helper->m_buffer.close(std::ios_base::in, eptr) : pplx::task_from_result(); } /// /// Tests whether last read cause the stream reach EOF. /// /// True if the read head has reached the end of the stream, false otherwise. bool is_eof() const { return is_valid() ? m_helper->m_buffer.is_eof() : false; } /// /// Get the next character and return it as an int_type. Advance the read position. /// /// A task that holds the next character as an int_type on successful completion. pplx::task read() const { pplx::task result; if (!_verify_and_return_task(details::_in_stream_msg, result)) return result; return helper()->m_buffer.bumpc(); } /// /// Read a single value of "blittable" type T from the stream. /// /// A value of type T. /// /// This is not a replacement for a proper binary serialization solution, but it may /// form the foundation for one. Reading data bit-wise to a stream is a primitive /// operation of binary serialization. /// Currently, no attention is paid to byte order. All data is read in the platform's /// native byte order, which means little-endian on all platforms that have been tested. /// This function is only available for streams using a single-byte character size. /// template CASABLANCA_DEPRECATED( "Unsafe API that will be removed in future releases, use one of the other read overloads instead.") pplx::task read() const { static_assert(sizeof(CharType) == 1, "binary read is only supported for single-byte streams"); static_assert(std::is_trivial::value, "unsafe to use with non-trivial types"); pplx::task result; if (!_verify_and_return_task(details::_in_stream_msg, result)) return result; auto copy = std::make_shared(); return helper()->m_buffer.getn((CharType*)copy.get(), sizeof(T)).then([copy](pplx::task) -> T { return std::move(*copy); }); } /// /// Reads up to count characters and place into the provided buffer. /// /// An async stream buffer supporting write operations. /// The maximum number of characters to read /// A task that holds the number of characters read. This number is 0 if the end of the stream is /// reached. pplx::task read(streams::streambuf target, size_t count) const { pplx::task result; if (!_verify_and_return_task(details::_in_stream_msg, result)) return result; if (!target.can_write()) return pplx::task_from_exception( std::make_exception_ptr(std::runtime_error("target not set up for output of data"))); // Capture 'buffer' rather than 'helper' here due to VC++ 2010 limitations. auto buffer = helper()->m_buffer; auto data = target.alloc(count); if (data != nullptr) { auto post_read = [target](pplx::task op) -> pplx::task { auto t = target; t.commit(op.get()); return op; }; return buffer.getn(data, count).then(post_read); } else { size_t available = 0; const bool acquired = buffer.acquire(data, available); if (available >= count) { auto post_write = [buffer, data](pplx::task op) -> pplx::task { auto b = buffer; b.release(data, op.get()); return op; }; return target.putn_nocopy(data, count).then(post_write); } else { // Always have to release if acquire returned true. if (acquired) { buffer.release(data, 0); } std::shared_ptr buf(new CharType[count], [](CharType* buf) { delete[] buf; }); auto post_write = [buf](pplx::task op) -> pplx::task { return op; }; auto post_read = [buf, target, post_write](pplx::task op) -> pplx::task { auto trg = target; return trg.putn_nocopy(buf.get(), op.get()).then(post_write); }; return helper()->m_buffer.getn(buf.get(), count).then(post_read); } } } /// /// Get the next character and return it as an int_type. Do not advance the read position. /// /// A task that holds the character, widened to an integer. This character is EOF when the peek /// operation fails. pplx::task peek() const { pplx::task result; if (!_verify_and_return_task(details::_in_stream_msg, result)) return result; return helper()->m_buffer.getc(); } /// /// Read characters until a delimiter or EOF is found, and place them into the target. /// Proceed past the delimiter, but don't include it in the target buffer. /// /// An async stream buffer supporting write operations. /// The delimiting character to stop the read at. /// A task that holds the number of characters read. pplx::task read_to_delim(streams::streambuf target, int_type delim) const { pplx::task result; if (!_verify_and_return_task(details::_in_stream_msg, result)) return result; if (!target.can_write()) return pplx::task_from_exception( std::make_exception_ptr(std::runtime_error("target not set up for output of data"))); // Capture 'buffer' rather than 'helper' here due to VC++ 2010 limitations. auto buffer = helper()->m_buffer; int_type req_async = traits::requires_async(); std::shared_ptr<_read_helper> _locals = std::make_shared<_read_helper>(); auto flush = [=]() mutable { return target.putn_nocopy(_locals->outbuf, _locals->write_pos).then([=](size_t wrote) mutable { _locals->total += wrote; _locals->write_pos = 0; return target.sync(); }); }; auto update = [=](int_type ch) mutable { if (ch == traits::eof()) return false; if (ch == delim) return false; _locals->outbuf[_locals->write_pos] = static_cast(ch); _locals->write_pos += 1; if (_locals->is_full()) { // Flushing synchronously because performance is terrible if we // schedule an empty task. This isn't on a user's thread. flush().get(); } return true; }; auto loop = pplx::details::_do_while([=]() mutable -> pplx::task { while (buffer.in_avail() > 0) { int_type ch = buffer.sbumpc(); if (ch == req_async) { break; } if (!update(ch)) { return pplx::task_from_result(false); } } return buffer.bumpc().then(update); }); return loop.then([=](bool) mutable { return flush().then([=] { return _locals->total; }); }); } /// /// Read until reaching a newline character. The newline is not included in the target. /// /// An asynchronous stream buffer supporting write operations. /// A task that holds the number of characters read. This number is 0 if the end of the stream is /// reached. pplx::task read_line(streams::streambuf target) const { pplx::task result; if (!_verify_and_return_task(details::_in_stream_msg, result)) return result; if (!target.can_write()) return pplx::task_from_exception( std::make_exception_ptr(std::runtime_error("target not set up for receiving data"))); // Capture 'buffer' rather than 'helper' here due to VC++ 2010 limitations. concurrency::streams::streambuf buffer = helper()->m_buffer; int_type req_async = traits::requires_async(); std::shared_ptr<_read_helper> _locals = std::make_shared<_read_helper>(); auto flush = [=]() mutable { return target.putn_nocopy(_locals->outbuf, _locals->write_pos).then([=](size_t wrote) mutable { _locals->total += wrote; _locals->write_pos = 0; return target.sync(); }); }; auto update = [=](int_type ch) mutable { if (ch == traits::eof()) return false; if (ch == '\n') return false; if (ch == '\r') { _locals->saw_CR = true; return true; } _locals->outbuf[_locals->write_pos] = static_cast(ch); _locals->write_pos += 1; if (_locals->is_full()) { // Flushing synchronously because performance is terrible if we // schedule an empty task. This isn't on a user's thread. flush().wait(); } return true; }; auto update_after_cr = [=](int_type ch) mutable -> pplx::task { if (ch == traits::eof()) return pplx::task_from_result(false); if (ch == '\n') { return buffer.bumpc().then([](int_type) { return false; }); } return pplx::task_from_result(false); }; auto loop = pplx::details::_do_while([=]() mutable -> pplx::task { while (buffer.in_avail() > 0) { int_type ch; if (_locals->saw_CR) { ch = buffer.sgetc(); if (ch == '\n') buffer.sbumpc(); return pplx::task_from_result(false); } ch = buffer.sbumpc(); if (ch == req_async) break; if (!update(ch)) { return pplx::task_from_result(false); } } if (_locals->saw_CR) { return buffer.getc().then(update_after_cr); } return buffer.bumpc().then(update); }); return loop.then([=](bool) mutable { return flush().then([=] { return _locals->total; }); }); } /// /// Read until reaching the end of the stream. /// /// An asynchronous stream buffer supporting write operations. /// The number of characters read. pplx::task read_to_end(streams::streambuf target) const { pplx::task result; if (!_verify_and_return_task("stream not set up for output of data", result)) return result; if (!target.can_write()) return pplx::task_from_exception( std::make_exception_ptr(std::runtime_error("source buffer not set up for input of data"))); auto l_buffer = helper()->m_buffer; auto l_buf_size = this->buf_size; std::shared_ptr<_read_helper> l_locals = std::make_shared<_read_helper>(); auto copy_to_target = [l_locals, target, l_buffer, l_buf_size]() mutable -> pplx::task { // We need to capture these, because the object itself may go away // before we're done processing the data. // auto locs = _locals; // auto trg = target; return l_buffer.getn(l_locals->outbuf, l_buf_size).then([=](size_t rd) mutable -> pplx::task { if (rd == 0) return pplx::task_from_result(false); // Must be nested to capture rd return target.putn_nocopy(l_locals->outbuf, rd) .then([target, l_locals, rd](size_t wr) mutable -> pplx::task { l_locals->total += wr; if (rd != wr) // Number of bytes written is less than number of bytes received. throw std::runtime_error("failed to write all bytes"); return target.sync().then([]() { return true; }); }); }); }; auto loop = pplx::details::_do_while(copy_to_target); return loop.then([=](bool) mutable -> size_t { return l_locals->total; }); } /// /// Seeks to the specified write position. /// /// An offset relative to the beginning of the stream. /// The new position in the stream. pos_type seek(pos_type pos) const { _verify_and_throw(details::_in_stream_msg); return helper()->m_buffer.seekpos(pos, std::ios_base::in); } /// /// Seeks to the specified write position. /// /// An offset relative to the beginning, current write position, or the end of the stream. /// The starting point (beginning, current, end) for the seek. /// The new position in the stream. pos_type seek(off_type off, std::ios_base::seekdir way) const { _verify_and_throw(details::_in_stream_msg); return helper()->m_buffer.seekoff(off, way, std::ios_base::in); } /// /// Get the current write position, i.e. the offset from the beginning of the stream. /// /// The current write position. pos_type tell() const { _verify_and_throw(details::_in_stream_msg); return helper()->m_buffer.getpos(std::ios_base::in); } /// /// can_seek is used to determine whether the stream supports seeking. /// /// true if the stream supports seeking, false otherwise. bool can_seek() const { return is_valid() && m_helper->m_buffer.can_seek(); } /// /// Test whether the stream has been initialized with a valid stream buffer. /// bool is_valid() const { return (m_helper != nullptr) && ((bool)m_helper->m_buffer); } /// /// Test whether the stream has been initialized or not. /// operator bool() const { return is_valid(); } /// /// Test whether the stream is open for writing. /// /// true if the stream is open for writing, false otherwise. bool is_open() const { return is_valid() && m_helper->m_buffer.can_read(); } /// /// Get the underlying stream buffer. /// concurrency::streams::streambuf streambuf() const { return helper()->m_buffer; } /// /// Read a value of type T from the stream. /// /// /// Supports the C++ primitive types. Can be expanded to additional types /// by adding template specializations for type_parser. /// /// /// The data type of the element to be read from the stream. /// /// A task that holds the element read from the stream. template pplx::task extract() const { pplx::task result; if (!_verify_and_return_task(details::_in_stream_msg, result)) return result; return type_parser::parse(helper()->m_buffer); } private: template bool _verify_and_return_task(const char* msg, pplx::task& tsk) const { auto buffer = helper()->m_buffer; if (!(buffer.exception() == nullptr)) { tsk = pplx::task_from_exception(buffer.exception()); return false; } if (!buffer.can_read()) { tsk = pplx::task_from_exception(std::make_exception_ptr(std::runtime_error(msg))); return false; } return true; } void _verify_and_throw(const char* msg) const { auto buffer = helper()->m_buffer; if (!(buffer.exception() == nullptr)) std::rethrow_exception(buffer.exception()); if (!buffer.can_read()) throw std::runtime_error(msg); } std::shared_ptr> helper() const { if (!m_helper) throw std::logic_error("uninitialized stream object"); return m_helper; } static const size_t buf_size = 16 * 1024; struct _read_helper { size_t total; CharType outbuf[buf_size]; size_t write_pos; bool saw_CR; bool is_full() const { return write_pos == buf_size; } _read_helper() : total(0), write_pos(0), saw_CR(false) {} }; std::shared_ptr> m_helper; }; typedef basic_ostream ostream; typedef basic_istream istream; typedef basic_ostream wostream; typedef basic_istream wistream; template pplx::task _type_parser_base::_skip_whitespace(streams::streambuf buffer) { int_type req_async = traits::requires_async(); auto update = [=](int_type ch) mutable { if (isspace(ch)) { if (buffer.sbumpc() == req_async) { // Synchronously because performance is terrible if we // schedule an empty task. This isn't on a user's thread. buffer.nextc().wait(); } return true; } return false; }; auto loop = pplx::details::_do_while([=]() mutable -> pplx::task { while (buffer.in_avail() > 0) { int_type ch = buffer.sgetc(); if (ch == req_async) break; if (!update(ch)) { return pplx::task_from_result(false); } } return buffer.getc().then(update); }); return loop.then([=](pplx::task op) { op.wait(); }); } template template pplx::task _type_parser_base::_parse_input(concurrency::streams::streambuf buffer, AcceptFunctor accept_character, ExtractFunctor extract) { std::shared_ptr state = std::make_shared(); auto update = [=](pplx::task op) -> pplx::task { int_type ch = op.get(); if (ch == traits::eof()) return pplx::task_from_result(false); bool accepted = accept_character(state, ch); if (!accepted) return pplx::task_from_result(false); // We peeked earlier, so now we must advance the position. concurrency::streams::streambuf buf = buffer; return buf.bumpc().then([](int_type) { return true; }); }; auto peek_char = [=]() -> pplx::task { concurrency::streams::streambuf buf = buffer; // If task results are immediately available, there's little need to use ".then()," // so optimize for prompt values. auto get_op = buf.getc(); while (get_op.is_done()) { auto condition = update(get_op); if (!condition.is_done() || !condition.get()) return condition; get_op = buf.getc(); } return get_op.then(update); }; auto finish = [=](pplx::task op) -> pplx::task { op.wait(); pplx::task result = extract(state); return result; }; return _skip_whitespace(buffer).then([=](pplx::task op) -> pplx::task { op.wait(); return pplx::details::_do_while(peek_char).then(finish); }); } template class type_parser> : public _type_parser_base { typedef _type_parser_base base; public: typedef typename base::traits traits; typedef typename base::int_type int_type; static pplx::task parse(streams::streambuf buffer) { return base::template _parse_input, std::string>( buffer, _accept_char, _extract_result); } private: static bool _accept_char(std::shared_ptr> state, int_type ch) { if (ch == traits::eof() || isspace(ch)) return false; state->push_back(CharType(ch)); return true; } static pplx::task> _extract_result(std::shared_ptr> state) { return pplx::task_from_result(*state); } }; template class type_parser : public _type_parser_base { typedef _type_parser_base base; public: typedef typename base::traits traits; typedef typename base::int_type int_type; static pplx::task parse(streams::streambuf buffer) { return base::template _parse_input<_int64_state, int64_t>(buffer, _accept_char, _extract_result); } private: struct _int64_state { _int64_state() : result(0), correct(false), minus(0) {} int64_t result; bool correct; char minus; // 0 -- no sign, 1 -- plus, 2 -- minus }; static bool _accept_char(std::shared_ptr<_int64_state> state, int_type ch) { if (ch == traits::eof()) return false; if (state->minus == 0) { // OK to find a sign. if (!::isdigit(ch) && ch != int_type('+') && ch != int_type('-')) return false; } else { if (!::isdigit(ch)) return false; } // At least one digit was found. state->correct = true; if (ch == int_type('+')) { state->minus = 1; } else if (ch == int_type('-')) { state->minus = 2; } else { if (state->minus == 0) state->minus = 1; // Shift the existing value by 10, then add the new value. bool positive = state->result >= 0; state->result *= 10; state->result += int64_t(ch - int_type('0')); if ((state->result >= 0) != positive) { state->correct = false; return false; } } return true; } static pplx::task _extract_result(std::shared_ptr<_int64_state> state) { if (!state->correct) throw std::range_error("integer value is too large to fit in 64 bits"); int64_t result = (state->minus == 2) ? -state->result : state->result; return pplx::task_from_result(result); } }; template struct _double_state { _double_state() : result(0) , minus(0) , after_comma(0) , exponent(false) , exponent_number(0) , exponent_minus(0) , complete(false) , p_exception_string() { } FloatingPoint result; char minus; // 0 -- no sign, 1 -- plus, 2 -- minus int after_comma; bool exponent; int exponent_number; char exponent_minus; // 0 -- no sign, 1 -- plus, 2 -- minus bool complete; std::string p_exception_string; }; template static std::string create_exception_message(int_type ch, bool exponent) { std::string result; if (exponent) { result.assign("Invalid character 'X' in exponent"); } else { result.assign("Invalid character 'X'"); } result[19] = static_cast(ch); return result; } template static bool _accept_char(std::shared_ptr<_double_state> state, int_type ch) { if (state->minus == 0) { if (!::isdigit(ch) && ch != int_type('.') && ch != int_type('+') && ch != int_type('-')) { if (!state->complete) state->p_exception_string = create_exception_message(ch, false); return false; } } else { if (!state->exponent && !::isdigit(ch) && ch != int_type('.') && ch != int_type('E') && ch != int_type('e')) { if (!state->complete) state->p_exception_string = create_exception_message(ch, false); return false; } if (state->exponent && !::isdigit(ch) && ch != int_type('+') && ch != int_type('-')) { if (!state->complete) state->p_exception_string = create_exception_message(ch, true); return false; } } switch (ch) { case int_type('+'): state->complete = false; if (state->exponent) { if (state->exponent_minus != 0) { state->p_exception_string = "The exponent sign already set"; return false; } state->exponent_minus = 1; } else { state->minus = 1; } break; case int_type('-'): state->complete = false; if (state->exponent) { if (state->exponent_minus != 0) { state->p_exception_string = "The exponent sign already set"; return false; } state->exponent_minus = 2; } else { state->minus = 2; } break; case int_type('.'): state->complete = false; if (state->after_comma > 0) return false; state->after_comma = 1; break; case int_type('E'): case int_type('e'): state->complete = false; if (state->exponent) return false; state->exponent_number = 0; state->exponent = true; break; default: state->complete = true; if (!state->exponent) { if (state->minus == 0) state->minus = 1; state->result *= 10; state->result += int64_t(ch - int_type('0')); if (state->after_comma > 0) state->after_comma++; } else { if (state->exponent_minus == 0) state->exponent_minus = 1; state->exponent_number *= 10; state->exponent_number += int64_t(ch - int_type('0')); } } return true; } template static pplx::task _extract_result(std::shared_ptr<_double_state> state) { if (state->p_exception_string.length() > 0) throw std::runtime_error(state->p_exception_string.c_str()); if (!state->complete && state->exponent) throw std::runtime_error("Incomplete exponent"); FloatingPoint result = static_cast((state->minus == 2) ? -state->result : state->result); if (state->exponent_minus == 2) state->exponent_number = 0 - state->exponent_number; if (state->after_comma > 0) state->exponent_number -= state->after_comma - 1; if (state->exponent_number >= 0) { result *= static_cast( std::pow(static_cast(10.0), static_cast(state->exponent_number))); #pragma push_macro("max") #undef max if (result > std::numeric_limits::max() || result < -std::numeric_limits::max()) throw std::overflow_error("The value is too big"); #pragma pop_macro("max") } else { bool is_zero = (result == 0); result /= static_cast( std::pow(static_cast(10.0), static_cast(-state->exponent_number))); if (!is_zero && result > -std::numeric_limits::denorm_min() && result < std::numeric_limits::denorm_min()) throw std::underflow_error("The value is too small"); } return pplx::task_from_result(result); } template class type_parser : public _type_parser_base { typedef _type_parser_base base; public: typedef typename base::traits traits; typedef typename base::int_type int_type; static pplx::task parse(streams::streambuf buffer) { return base::template _parse_input<_double_state, double>( buffer, _accept_char, _extract_result); } protected: }; template class type_parser : public _type_parser_base { typedef _type_parser_base base; public: typedef typename base::traits traits; typedef typename base::int_type int_type; static pplx::task parse(streams::streambuf buffer) { return base::template _parse_input<_double_state, float>( buffer, _accept_char, _extract_result); } protected: }; template class type_parser : public _type_parser_base { typedef _type_parser_base base; public: typedef typename base::traits traits; typedef typename base::int_type int_type; static pplx::task parse(streams::streambuf buffer) { return base::template _parse_input<_uint64_state, uint64_t>(buffer, _accept_char, _extract_result); } private: struct _uint64_state { _uint64_state() : result(0), correct(false) {} uint64_t result; bool correct; }; static bool _accept_char(std::shared_ptr<_uint64_state> state, int_type ch) { if (!::isdigit(ch)) return false; // At least one digit was found. state->correct = true; // Shift the existing value by 10, then add the new value. state->result *= 10; state->result += uint64_t(ch - int_type('0')); return true; } static pplx::task _extract_result(std::shared_ptr<_uint64_state> state) { if (!state->correct) throw std::range_error("integer value is too large to fit in 64 bits"); return pplx::task_from_result(state->result); } }; template class type_parser : public _type_parser_base { typedef _type_parser_base base; public: typedef typename base::traits traits; typedef typename base::int_type int_type; static pplx::task parse(streams::streambuf buffer) { return base::template _parse_input<_bool_state, bool>(buffer, _accept_char, _extract_result); } private: struct _bool_state { _bool_state() : state(0) {} // { 0 -- not started, 1 -- 't', 2 -- 'tr', 3 -- 'tru', 4 -- 'f', 5 -- 'fa', 6 -- 'fal', 7 -- 'fals', 8 -- // 'true', 9 -- 'false' } short state; }; static bool _accept_char(std::shared_ptr<_bool_state> state, int_type ch) { switch (state->state) { case 0: if (ch == int_type('t')) state->state = 1; else if (ch == int_type('f')) state->state = 4; else if (ch == int_type('1')) state->state = 8; else if (ch == int_type('0')) state->state = 9; else return false; break; case 1: if (ch == int_type('r')) state->state = 2; else return false; break; case 2: if (ch == int_type('u')) state->state = 3; else return false; break; case 3: if (ch == int_type('e')) state->state = 8; else return false; break; case 4: if (ch == int_type('a')) state->state = 5; else return false; break; case 5: if (ch == int_type('l')) state->state = 6; else return false; break; case 6: if (ch == int_type('s')) state->state = 7; else return false; break; case 7: if (ch == int_type('e')) state->state = 9; else return false; break; case 8: case 9: return false; } return true; } static pplx::task _extract_result(std::shared_ptr<_bool_state> state) { bool correct = (state->state == 8 || state->state == 9); if (!correct) { std::runtime_error exc("cannot parse as Boolean value"); throw exc; } return pplx::task_from_result(state->state == 8); } }; template class type_parser : public _type_parser_base { typedef _type_parser_base base; public: typedef typename base::traits traits; typedef typename base::int_type int_type; static pplx::task parse(streams::streambuf buffer) { return base::_skip_whitespace(buffer).then([=](pplx::task op) -> pplx::task { op.wait(); return type_parser::_get_char(buffer); }); } private: static pplx::task _get_char(streams::streambuf buffer) { concurrency::streams::streambuf buf = buffer; return buf.bumpc().then([=](pplx::task op) -> signed char { int_type val = op.get(); if (val == traits::eof()) throw std::runtime_error("reached end-of-stream while constructing a value"); return static_cast(val); }); } }; template class type_parser : public _type_parser_base { typedef _type_parser_base base; public: typedef typename base::traits traits; typedef typename base::int_type int_type; static pplx::task parse(streams::streambuf buffer) { return base::_skip_whitespace(buffer).then([=](pplx::task op) -> pplx::task { op.wait(); return type_parser::_get_char(buffer); }); } private: static pplx::task _get_char(streams::streambuf buffer) { concurrency::streams::streambuf buf = buffer; return buf.bumpc().then([=](pplx::task op) -> unsigned char { int_type val = op.get(); if (val == traits::eof()) throw std::runtime_error("reached end-of-stream while constructing a value"); return static_cast(val); }); } }; template class type_parser : public _type_parser_base { typedef _type_parser_base base; public: typedef typename base::traits traits; typedef typename base::int_type int_type; static pplx::task parse(streams::streambuf buffer) { return base::_skip_whitespace(buffer).then([=](pplx::task op) -> pplx::task { op.wait(); return _get_char(buffer); }); } private: static pplx::task _get_char(streams::streambuf buffer) { concurrency::streams::streambuf buf = buffer; return buf.bumpc().then([=](pplx::task op) -> char { int_type val = op.get(); if (val == traits::eof()) throw std::runtime_error("reached end-of-stream while constructing a value"); return char(val); }); } }; #ifdef _WIN32 template class type_parser>> : public _type_parser_base { typedef _type_parser_base base; public: typedef typename base::traits traits; typedef typename base::int_type int_type; static pplx::task parse(streams::streambuf buffer) { return base::template _parse_input, std::basic_string>( buffer, _accept_char, _extract_result); } private: static bool _accept_char(const std::shared_ptr>& state, int_type ch) { if (ch == concurrency::streams::char_traits::eof() || isspace(ch)) return false; state->push_back(char(ch)); return true; } static pplx::task> _extract_result(std::shared_ptr> state) { return pplx::task_from_result(utility::conversions::utf8_to_utf16(*state)); } }; #endif //_WIN32 } // namespace streams } // namespace Concurrency #endif #endif // !XSAPI_NO_PPL ================================================ FILE: Include/cpprestinclude/cpprest/uri.h ================================================ /*** * ==++== * * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * ==--== * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ * * Protocol independent support for URIs. * * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk * * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ****/ #pragma once #ifndef _CASA_URI_H #define _CASA_URI_H #include "cpprest/base_uri.h" #include "cpprest/uri_builder.h" #endif ================================================ FILE: Include/cpprestinclude/cpprest/uri_builder.h ================================================ /*** * ==++== * * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * ==--== * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ * * Builder style class for creating URIs. * * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk * * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ****/ #pragma once #include #include #include #include "cpprest/base_uri.h" #include "cpprest/details/uri_parser.h" namespace web { /// /// Builder for constructing URIs incrementally. /// class uri_builder { public: /// /// Creates a builder with an initially empty URI. /// uri_builder() {} /// /// Creates a builder with a existing URI object. /// /// Encoded string containing the URI. uri_builder(const uri &uri_str): m_uri(uri_str.m_components) {} /// /// Get the scheme component of the URI as an encoded string. /// /// The URI scheme as a string. const utility::string_t &scheme() const { return m_uri.m_scheme; } /// /// Get the user information component of the URI as an encoded string. /// /// The URI user information as a string. const utility::string_t &user_info() const { return m_uri.m_user_info; } /// /// Get the host component of the URI as an encoded string. /// /// The URI host as a string. const utility::string_t &host() const { return m_uri.m_host; } /// /// Get the port component of the URI. Returns -1 if no port is specified. /// /// The URI port as an integer. int port() const { return m_uri.m_port; } /// /// Get the path component of the URI as an encoded string. /// /// The URI path as a string. const utility::string_t &path() const { return m_uri.m_path; } /// /// Get the query component of the URI as an encoded string. /// /// The URI query as a string. const utility::string_t &query() const { return m_uri.m_query; } /// /// Get the fragment component of the URI as an encoded string. /// /// The URI fragment as a string. const utility::string_t &fragment() const { return m_uri.m_fragment; } /// /// Set the scheme of the URI. /// /// Uri scheme. /// A reference to this uri_builder to support chaining. uri_builder & set_scheme(const utility::string_t &scheme) { m_uri.m_scheme = scheme; return *this; } /// /// Set the user info component of the URI. /// /// User info as a decoded string. /// Specify whether to apply URI encoding to the given string. /// A reference to this uri_builder to support chaining. uri_builder & set_user_info(const utility::string_t &user_info, bool do_encoding = false) { m_uri.m_user_info = do_encoding ? uri::encode_uri(user_info, uri::components::user_info) : user_info; return *this; } /// /// Set the host component of the URI. /// /// Host as a decoded string. /// Specify whether to apply URI encoding to the given string. /// A reference to this uri_builder to support chaining. uri_builder & set_host(const utility::string_t &host, bool do_encoding = false) { m_uri.m_host = do_encoding ? uri::encode_uri(host, uri::components::host) : host; return *this; } /// /// Set the port component of the URI. /// /// Port as an integer. /// A reference to this uri_builder to support chaining. uri_builder & set_port(int port) { m_uri.m_port = port; return *this; } /// /// Set the port component of the URI. /// /// Port as a string. /// A reference to this uri_builder to support chaining. /// When string can't be converted to an integer the port is left unchanged. uri_builder & set_port(const utility::string_t &port) { utility::istringstream_t portStream(port); int port_tmp; portStream >> port_tmp; if(portStream.fail() || portStream.bad()) { throw std::invalid_argument("invalid port argument, must be non empty string containing integer value"); } m_uri.m_port = port_tmp; return *this; } /// /// Set the path component of the URI. /// /// Path as a decoded string. /// Specify whether to apply URI encoding to the given string. /// A reference to this uri_builder to support chaining. uri_builder & set_path(const utility::string_t &path, bool do_encoding = false) { m_uri.m_path = do_encoding ? uri::encode_uri(path, uri::components::path) : path; return *this; } /// /// Set the query component of the URI. /// /// Query as a decoded string. /// Specify whether apply URI encoding to the given string. /// A reference to this uri_builder to support chaining. uri_builder & set_query(const utility::string_t &query, bool do_encoding = false) { m_uri.m_query = do_encoding ? uri::encode_uri(query, uri::components::query) : query; return *this; } /// /// Set the fragment component of the URI. /// /// Fragment as a decoded string. /// Specify whether to apply URI encoding to the given string. /// A reference to this uri_builder to support chaining. uri_builder & set_fragment(const utility::string_t &fragment, bool do_encoding = false) { m_uri.m_fragment = do_encoding ? uri::encode_uri(fragment, uri::components::fragment) : fragment; return *this; } /// /// Clears all components of the underlying URI in this uri_builder. /// void clear() { m_uri = details::uri_components(); } /// /// Appends another path to the path of this uri_builder. /// /// Path to append as a already encoded string. /// Specify whether to apply URI encoding to the given string. /// A reference to this uri_builder to support chaining. _ASYNCRTIMP uri_builder &append_path(const utility::string_t &path, bool do_encoding = false); /// /// Appends another query to the query of this uri_builder. /// /// Query to append as a decoded string. /// Specify whether to apply URI encoding to the given string. /// A reference to this uri_builder to support chaining. _ASYNCRTIMP uri_builder &append_query(const utility::string_t &query, bool do_encoding = false); /// /// Appends an relative uri (Path, Query and fragment) at the end of the current uri. /// /// The relative uri to append. /// A reference to this uri_builder to support chaining. _ASYNCRTIMP uri_builder &append(const uri &relative_uri); /// /// Appends another query to the query of this uri_builder, encoding it first. This overload is useful when building a query segment of /// the form "element=10", where the right hand side of the query is stored as a type other than a string, for instance, an integral type. /// /// The name portion of the query string /// The value portion of the query string /// A reference to this uri_builder to support chaining. template uri_builder &append_query(const utility::string_t &name, const T &value, bool do_encoding = true) { auto encodedName = name; auto encodedValue = ::utility::conversions::print_string(value, std::locale::classic()); if (do_encoding) { auto encodingCheck = [](int ch) { switch (ch) { // Encode '&', ';', and '=' since they are used // as delimiters in query component. case '&': case ';': case '=': case '%': case '+': return true; default: return !::web::details::uri_parser::is_query_character(ch); } }; encodedName = uri::encode_impl(encodedName, encodingCheck); encodedValue = uri::encode_impl(encodedValue, encodingCheck); } auto encodedQuery = encodedName; encodedQuery.append(_XPLATSTR("=")); encodedQuery.append(encodedValue); // The query key value pair was already encoded by us or the user separately. return append_query(encodedQuery, false); } /// /// Combine and validate the URI components into a encoded string. An exception will be thrown if the URI is invalid. /// /// The created URI as a string. _ASYNCRTIMP utility::string_t to_string(); /// /// Combine and validate the URI components into a URI class instance. An exception will be thrown if the URI is invalid. /// /// The create URI as a URI class instance. _ASYNCRTIMP uri to_uri(); /// /// Validate the generated URI from all existing components of this uri_builder. /// /// Whether the URI is valid. _ASYNCRTIMP bool is_valid(); private: details::uri_components m_uri; }; } // namespace web ================================================ FILE: Include/cpprestinclude/cpprestsdk_impl.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "cpprest/details/asyncrt_utils.hpp" #include "cpprest/details/json_parsing.hpp" #include "cpprest/details/json_serialization.hpp" #include "cpprest/details/json.hpp" #include "cpprest/details/uri.hpp" #include "cpprest/details/uri_builder.hpp" #include "cpprest/details/uri_parser.hpp" #include "cpprest/details/http_msg.hpp" #include "cpprest/details/http_helpers.hpp" #include "cpprest/details/base64.hpp" #include "cpprest/details/http_client_msg.hpp" #if !XSAPI_NO_PPL #include "pplx/details/pplx.hpp" #if HC_PLATFORM == HC_PLATFORM_ANDROID #include "pplx/details/pplxlinux.hpp" #include "pplx/details/threadpool.hpp" #elif HC_PLATFORM == HC_PLATFORM_IOS #include "pplx/details/pplxapple.hpp" #elif HC_PLATFORM_IS_MICROSOFT #include "pplx/details/pplxwin.hpp" #endif #endif ================================================ FILE: Include/cpprestinclude/pplx/details/pplx.hpp ================================================ /*** * ==++== * * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * ==--== * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ * * Parallel Patterns Library implementation (common code across platforms) * * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk * * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ****/ #pragma once #if !defined(_WIN32) || _MSC_VER < 1800 || CPPREST_FORCE_PPLX #include "pplx/pplx.h" // Disable false alarm code analyze warning #if defined(_MSC_VER) #pragma warning (disable : 26165 26110) #endif namespace pplx { namespace details { /// /// Spin lock to allow for locks to be used in global scope /// class _Spin_lock { public: _Spin_lock() : _M_lock(0) { } void lock() { if ( details::atomic_compare_exchange(_M_lock, 1l, 0l) != 0l ) { do { pplx::details::platform::YieldExecution(); } while ( details::atomic_compare_exchange(_M_lock, 1l, 0l) != 0l ); } } void unlock() { // fence for release semantics details::atomic_exchange(_M_lock, 0l); } private: atomic_long _M_lock; }; typedef ::pplx::scoped_lock<_Spin_lock> _Scoped_spin_lock; } // namespace details static struct _pplx_g_sched_t { typedef std::shared_ptr sched_ptr; _pplx_g_sched_t() { m_state = post_ctor; } ~_pplx_g_sched_t() { m_state = post_dtor; } sched_ptr get_scheduler() { switch (m_state) { case post_ctor: // This is the 99.9% case. if (!m_scheduler) { ::pplx::details::_Scoped_spin_lock lock(m_spinlock); if (!m_scheduler) { m_scheduler = std::make_shared< ::pplx::default_scheduler_t>(); } } return m_scheduler; default: // This case means the global m_scheduler is not available. // We spin off an individual scheduler instead. return std::make_shared< ::pplx::default_scheduler_t>(); } } void set_scheduler(sched_ptr scheduler) { if (m_state == pre_ctor || m_state == post_dtor) { throw invalid_operation("Scheduler cannot be initialized now"); } ::pplx::details::_Scoped_spin_lock lock(m_spinlock); if (m_scheduler != nullptr) { throw invalid_operation("Scheduler is already initialized"); } m_scheduler = std::move(scheduler); } enum { pre_ctor = 0, post_ctor = 1, post_dtor = 2 } m_state; private: pplx::details::_Spin_lock m_spinlock; sched_ptr m_scheduler; } _pplx_g_sched; _PPLXIMP std::shared_ptr _pplx_cdecl get_ambient_scheduler() { return _pplx_g_sched.get_scheduler(); } _PPLXIMP void _pplx_cdecl set_ambient_scheduler(std::shared_ptr _Scheduler) { _pplx_g_sched.set_scheduler(std::move(_Scheduler)); } } // namespace pplx #endif ================================================ FILE: Include/cpprestinclude/pplx/details/pplxapple.hpp ================================================ /*** * Copyright (C) Microsoft. All rights reserved. * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. * * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ * * Apple-specific pplx implementations * * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ****/ #pragma once #include #include #include #include #include #include "pplx/pplx.h" // DEVNOTE: // The use of mutexes is suboptimal for synchronization of task execution. // Given that scheduler implementations should use GCD queues, there are potentially better mechanisms available to coordinate tasks (such as dispatch groups). namespace pplx { namespace details { namespace platform { _PPLXIMP long GetCurrentThreadId() { pthread_t threadId = pthread_self(); return (long)threadId; } void YieldExecution() { sleep(0); } } // namespace platform void apple_scheduler::schedule( TaskProc_t proc, void* param) { dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_async_f(queue, param, proc); } } // namespace details } // pplx ================================================ FILE: Include/cpprestinclude/pplx/details/pplxlinux.hpp ================================================ /*** * Copyright (C) Microsoft. All rights reserved. * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. * * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ * * Parallel Patterns Library - Linux version * * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk * * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ****/ #pragma once #include "pplx/pplx.h" #include "pplx/threadpool.h" #include "sys/syscall.h" #ifdef _WIN32 #error "ERROR: This file should only be included in non-windows Build" #endif namespace pplx { namespace details { namespace platform { _PPLXIMP long GetCurrentThreadId() { return reinterpret_cast(reinterpret_cast(pthread_self())); } _PPLXIMP void YieldExecution() { std::this_thread::yield(); } } _PPLXIMP void linux_scheduler::schedule(TaskProc_t proc, void* param) { crossplat::threadpool::shared_instance().service().post(std::bind(proc, param)); } } // namespace details } // namespace pplx ================================================ FILE: Include/cpprestinclude/pplx/details/pplxwin.hpp ================================================ /*** * ==++== * * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * ==--== * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ * * Windows specific implementation of PPL constructs * * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk * * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ****/ #pragma once #if !defined(_WIN32) || _MSC_VER < 1800 || CPPREST_FORCE_PPLX #include "pplx/pplxwin.h" // Disable false alarm code analysis warning #pragma warning (disable : 26165 26110) namespace pplx { namespace details { namespace platform { _PPLXIMP long __cdecl GetCurrentThreadId() { return (long)(::GetCurrentThreadId()); } _PPLXIMP void __cdecl YieldExecution() { YieldProcessor(); } _PPLXIMP size_t __cdecl CaptureCallstack(void **stackData, size_t skipFrames, size_t captureFrames) { (stackData); (skipFrames); (captureFrames); size_t capturedFrames = 0; // RtlCaptureSTackBackTrace is not available in MSDK, so we only call it under Desktop or _DEBUG MSDK. // For MSDK unsupported version, we will return zero frame number. #if !defined(__cplusplus_winrt) capturedFrames = RtlCaptureStackBackTrace(static_cast(skipFrames + 1), static_cast(captureFrames), stackData, nullptr); #endif return capturedFrames; } #if defined(__cplusplus_winrt) volatile long s_asyncId = 0; _PPLXIMP unsigned int __cdecl GetNextAsyncId() { return static_cast(_InterlockedIncrement(&s_asyncId)); } #endif // defined(__cplusplus_winrt) void InitializeCriticalSection(LPCRITICAL_SECTION _cs) { #ifndef __cplusplus_winrt // InitializeCriticalSection can cause STATUS_NO_MEMORY see C28125 __try { ::InitializeCriticalSection(_cs); } __except(GetExceptionCode() == STATUS_NO_MEMORY ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { throw ::std::bad_alloc(); } #else InitializeCriticalSectionEx(_cs, 0, 0); #endif // !__cplusplus_winrt } } // // Event implementation // _PPLXIMP event_impl::event_impl() { static_assert(sizeof(HANDLE) <= sizeof(_M_impl), "HANDLE version mismatch"); #ifndef __cplusplus_winrt _M_impl = CreateEvent(NULL, true, false, NULL); #else _M_impl = CreateEventEx(NULL, NULL, CREATE_EVENT_MANUAL_RESET, EVENT_ALL_ACCESS); #endif // !__cplusplus_winrt if( _M_impl != NULL ) { ResetEvent(static_cast(_M_impl)); } } _PPLXIMP event_impl::~event_impl() { CloseHandle(static_cast(_M_impl)); } _PPLXIMP void event_impl::set() { SetEvent(static_cast(_M_impl)); } _PPLXIMP void event_impl::reset() { ResetEvent(static_cast(_M_impl)); } _PPLXIMP unsigned int event_impl::wait(unsigned int timeout) { DWORD waitTime = (timeout == event_impl::timeout_infinite) ? INFINITE : (DWORD)timeout; DWORD status = WaitForSingleObjectEx(static_cast(_M_impl), waitTime, 0); _ASSERTE((status == WAIT_OBJECT_0) || (waitTime != INFINITE)); return (status == WAIT_OBJECT_0) ? 0 : event_impl::timeout_infinite; } // // critical_section implementation // // TFS# 612702 -- this implementation is unnecessarily recursive. See bug for details. _PPLXIMP critical_section_impl::critical_section_impl() { static_assert(sizeof(CRITICAL_SECTION) <= sizeof(_M_impl), "CRITICAL_SECTION version mismatch"); platform::InitializeCriticalSection(reinterpret_cast(&_M_impl)); } _PPLXIMP critical_section_impl::~critical_section_impl() { DeleteCriticalSection(reinterpret_cast(&_M_impl)); } _PPLXIMP void critical_section_impl::lock() { EnterCriticalSection(reinterpret_cast(&_M_impl)); } _PPLXIMP void critical_section_impl::unlock() { LeaveCriticalSection(reinterpret_cast(&_M_impl)); } #if _WIN32_WINNT >= _WIN32_WINNT_VISTA // // reader_writer_lock implementation // _PPLXIMP reader_writer_lock_impl::reader_writer_lock_impl() : m_locked_exclusive(false) { static_assert(sizeof(SRWLOCK) <= sizeof(_M_impl), "SRWLOCK version mismatch"); InitializeSRWLock(reinterpret_cast(&_M_impl)); } _PPLXIMP void reader_writer_lock_impl::lock() { AcquireSRWLockExclusive(reinterpret_cast(&_M_impl)); m_locked_exclusive = true; } _PPLXIMP void reader_writer_lock_impl::lock_read() { AcquireSRWLockShared(reinterpret_cast(&_M_impl)); } _PPLXIMP void reader_writer_lock_impl::unlock() { if(m_locked_exclusive) { m_locked_exclusive = false; ReleaseSRWLockExclusive(reinterpret_cast(&_M_impl)); } else { ReleaseSRWLockShared(reinterpret_cast(&_M_impl)); } } #endif // _WIN32_WINNT >= _WIN32_WINNT_VISTA // // scheduler implementation // #if defined(__cplusplus_winrt) && !TV_API _PPLXIMP void windows_scheduler::schedule( TaskProc_t proc, _In_ void* param) { auto workItemHandler = ref new Windows::System::Threading::WorkItemHandler([proc, param](Windows::Foundation::IAsyncAction ^ ) { proc(param); }); Windows::System::Threading::ThreadPool::RunAsync(workItemHandler); } #else #if _WIN32_WINNT < _WIN32_WINNT_VISTA struct _Scheduler_Param { TaskProc_t m_proc; void * m_param; _Scheduler_Param(TaskProc_t proc, _In_ void * param) : m_proc(proc), m_param(param) { } static DWORD CALLBACK DefaultWorkCallback(LPVOID lpParameter) { auto schedulerParam = (_Scheduler_Param *)(lpParameter); schedulerParam->m_proc(schedulerParam->m_param); delete schedulerParam; return 1; } }; _PPLXIMP void windows_scheduler::schedule( TaskProc_t proc, _In_ void* param) { auto schedulerParam = new _Scheduler_Param(proc, param); auto work = QueueUserWorkItem(_Scheduler_Param::DefaultWorkCallback, schedulerParam, WT_EXECUTELONGFUNCTION); if (!work) { delete schedulerParam; throw utility::details::create_system_error(GetLastError()); } } #else struct _Scheduler_Param { TaskProc_t m_proc; void * m_param; _Scheduler_Param(TaskProc_t proc, _In_ void * param) : m_proc(proc), m_param(param) { } static void CALLBACK DefaultWorkCallback(PTP_CALLBACK_INSTANCE, PVOID pContext, PTP_WORK) { auto schedulerParam = (_Scheduler_Param *)(pContext); schedulerParam->m_proc(schedulerParam->m_param); delete schedulerParam; } }; _PPLXIMP void windows_scheduler::schedule( TaskProc_t proc, _In_ void* param) { auto schedulerParam = new _Scheduler_Param(proc, param); auto work = CreateThreadpoolWork(_Scheduler_Param::DefaultWorkCallback, schedulerParam, NULL); if (work == nullptr) { delete schedulerParam; throw utility::details::create_system_error(GetLastError()); } SubmitThreadpoolWork(work); CloseThreadpoolWork(work); } #endif // _WIN32_WINNT < _WIN32_WINNT_VISTA #endif } // namespace details } // namespace pplx #endif ================================================ FILE: Include/cpprestinclude/pplx/details/threadpool.hpp ================================================ /*** * ==++== * * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * ==--== * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ **/ #include "pplx/threadpool.h" #if defined(__ANDROID__) #include #include #endif namespace crossplat { #if (defined(ANDROID) || defined(__ANDROID__)) // This pointer will be 0-initialized by default (at load time). std::atomic JVM; static void abort_if_no_jvm() { if (JVM == nullptr) { __android_log_print(ANDROID_LOG_ERROR, "CPPRESTSDK", "%s", "The CppREST SDK must be initialized before first use on android: https://github.com/Microsoft/cpprestsdk/wiki/How-to-build-for-Android"); std::abort(); } } JNIEnv* get_jvm_env() { abort_if_no_jvm(); JNIEnv* env = nullptr; auto result = JVM.load()->AttachCurrentThread(&env, nullptr); if (result != JNI_OK) { throw std::runtime_error("Could not attach to JVM"); } return env; } threadpool& threadpool::shared_instance() { abort_if_no_jvm(); static threadpool s_shared(40); return s_shared; } #else // initialize the static shared threadpool threadpool& threadpool::shared_instance() { static threadpool s_shared(40); return s_shared; } #endif } #if defined(__ANDROID__) void cpprest_init(JavaVM* vm) { crossplat::JVM = vm; } #endif ================================================ FILE: Include/cpprestinclude/pplx/pplx.h ================================================ #if !XSAPI_NO_PPL /*** * ==++== * * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * ==--== * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ * * Parallel Patterns Library * * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk * * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ****/ #pragma once #ifndef _PPLX_H #define _PPLX_H #if (defined(_MSC_VER) && (_MSC_VER >= 1800)) && !CPPREST_FORCE_PPLX #error This file must not be included for Visual Studio 12 or later #endif #ifndef _WIN32 #if defined(_WIN32) || defined(__cplusplus_winrt) #define _WIN32 #endif #endif // _WIN32 #ifdef _NO_PPLXIMP #define _PPLXIMP #else #ifdef _PPLX_EXPORT #define _PPLXIMP __declspec(dllexport) #else #define _PPLXIMP __declspec(dllimport) #endif #endif #include "cpprest/details/cpprest_compat.h" // Use PPLx #ifdef _WIN32 #include "pplx/pplxwin.h" #elif defined(__APPLE__) #undef _PPLXIMP #define _PPLXIMP #include "pplx/pplxlinux.h" #else #include "pplx/pplxlinux.h" #endif // _WIN32 // Common implementation across all the non-concrt versions #include "pplx/pplxcancellation_token.h" #include // conditional expression is constant #if defined(_MSC_VER) #pragma warning(push) #pragma warning(disable: 4127) #endif #pragma pack(push,_CRT_PACKING) /// /// The pplx namespace provides classes and functions that give you access to the Concurrency Runtime, /// a concurrent programming framework for C++. For more information, see . /// /**/ namespace pplx { /// /// Sets the ambient scheduler to be used by the PPL constructs. /// _PPLXIMP void _pplx_cdecl set_ambient_scheduler(std::shared_ptr _Scheduler); /// /// Gets the ambient scheduler to be used by the PPL constructs /// _PPLXIMP std::shared_ptr _pplx_cdecl get_ambient_scheduler(); namespace details { // // An internal exception that is used for cancellation. Users do not "see" this exception except through the // resulting stack unwind. This exception should never be intercepted by user code. It is intended // for use by the runtime only. // class _Interruption_exception : public std::exception { public: _Interruption_exception(){} }; template struct _AutoDeleter { _AutoDeleter(_T *_PPtr) : _Ptr(_PPtr) {} ~_AutoDeleter () { delete _Ptr; } _T *_Ptr; }; struct _TaskProcHandle { _TaskProcHandle() { } virtual ~_TaskProcHandle() {} virtual void invoke() const = 0; static void _pplx_cdecl _RunChoreBridge(void * _Parameter) { auto _PTaskHandle = static_cast<_TaskProcHandle *>(_Parameter); _AutoDeleter<_TaskProcHandle> _AutoDeleter(_PTaskHandle); _PTaskHandle->invoke(); } }; enum _TaskInliningMode { // Disable inline scheduling _NoInline = 0, // Let runtime decide whether to do inline scheduling or not _DefaultAutoInline = 16, // Always do inline scheduling _ForceInline = -1, }; // This is an abstraction that is built on top of the scheduler to provide these additional functionalities // - Ability to wait on a work item // - Ability to cancel a work item // - Ability to inline work on invocation of RunAndWait class _TaskCollectionImpl { public: typedef _TaskProcHandle _TaskProcHandle_t; _TaskCollectionImpl(scheduler_ptr _PScheduler) : _M_pScheduler(_PScheduler) { } void _ScheduleTask(_TaskProcHandle_t* _PTaskHandle, _TaskInliningMode _InliningMode) { if (_InliningMode == _ForceInline) { _TaskProcHandle_t::_RunChoreBridge(_PTaskHandle); } else { _M_pScheduler->schedule(_TaskProcHandle_t::_RunChoreBridge, _PTaskHandle); } } void _Cancel() { // No cancellation support } void _RunAndWait() { // No inlining support yet _Wait(); } void _Wait() { _M_Completed.wait(); } void _Complete() { _M_Completed.set(); } scheduler_ptr _GetScheduler() const { return _M_pScheduler; } // Fire and forget static void _RunTask(TaskProc_t _Proc, void * _Parameter, _TaskInliningMode _InliningMode) { if (_InliningMode == _ForceInline) { _Proc(_Parameter); } else { // Schedule the work on the ambient scheduler get_ambient_scheduler()->schedule(_Proc, _Parameter); } } static bool _pplx_cdecl _Is_cancellation_requested() { // We do not yet have the ability to determine the current task. So return false always return false; } private: extensibility::event_t _M_Completed; scheduler_ptr _M_pScheduler; }; // For create_async lambdas that return a (non-task) result, we oversubscriber the current task for the duration of the // lambda. struct _Task_generator_oversubscriber {}; typedef _TaskCollectionImpl _TaskCollection_t; typedef _TaskInliningMode _TaskInliningMode_t; typedef _Task_generator_oversubscriber _Task_generator_oversubscriber_t; } // namespace details } // namespace pplx #pragma pack(pop) #if defined(_MSC_VER) #pragma warning(pop) #endif #endif // _PPLX_H #endif // !XSAPI_NO_PPL ================================================ FILE: Include/cpprestinclude/pplx/pplxcancellation_token.h ================================================ /*** * Copyright (C) Microsoft. All rights reserved. * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. * * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ * * Parallel Patterns Library : cancellation_token * * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ****/ #pragma once #ifndef _PPLX_H #error This header must not be included directly #endif #ifndef _PPLXCANCELLATION_TOKEN_H #define _PPLXCANCELLATION_TOKEN_H #if (defined(_MSC_VER) && (_MSC_VER >= 1800)) && !CPPREST_FORCE_PPLX #error This file must not be included for Visual Studio 12 or later #endif #include #include #include "pplx/pplxinterface.h" #pragma pack(push,_CRT_PACKING) // All header files are required to be protected from the macro new #pragma push_macro("new") #undef new namespace pplx { /// /// This class describes an exception thrown by the PPL tasks layer in order to force the current task /// to cancel. It is also thrown by the get() method on task, for a /// canceled task. /// /// /// /**/ class task_canceled : public std::exception { private: std::string _message; public: /// /// Constructs a task_canceled object. /// /// /// A descriptive message of the error. /// /**/ explicit task_canceled(_In_z_ const char * _Message) throw() : _message(_Message) { } /// /// Constructs a task_canceled object. /// /**/ task_canceled() throw() : exception() { } ~task_canceled() throw () {} const char* what() const CPPREST_NOEXCEPT { return _message.c_str(); } }; /// /// This class describes an exception thrown when an invalid operation is performed that is not more accurately /// described by another exception type thrown by the Concurrency Runtime. /// /// /// The various methods which throw this exception will generally document under what circumstances they will throw it. /// /**/ class invalid_operation : public std::exception { private: std::string _message; public: /// /// Constructs an invalid_operation object. /// /// /// A descriptive message of the error. /// /**/ invalid_operation(_In_z_ const char * _Message) throw() : _message(_Message) { } /// /// Constructs an invalid_operation object. /// /**/ invalid_operation() throw() : exception() { } ~invalid_operation() throw () {} const char* what() const CPPREST_NOEXCEPT { return _message.c_str(); } }; namespace details { // Base class for all reference counted objects class _RefCounter { public: virtual ~_RefCounter() { _ASSERTE(_M_refCount == 0); } // Acquires a reference // Returns the new reference count. long _Reference() { long _Refcount = atomic_increment(_M_refCount); // 0 - 1 transition is illegal _ASSERTE(_Refcount > 1); return _Refcount; } // Releases the reference // Returns the new reference count long _Release() { long _Refcount = atomic_decrement(_M_refCount); _ASSERTE(_Refcount >= 0); if (_Refcount == 0) { _Destroy(); } return _Refcount; } protected: // Allow derived classes to provide their own deleter virtual void _Destroy() { delete this; } // Only allow instantiation through derived class _RefCounter(long _InitialCount = 1) : _M_refCount(_InitialCount) { _ASSERTE(_M_refCount > 0); } // Reference count atomic_long _M_refCount; }; class _CancellationTokenState; class _CancellationTokenRegistration : public _RefCounter { private: static const long _STATE_CLEAR = 0; static const long _STATE_DEFER_DELETE = 1; static const long _STATE_SYNCHRONIZE = 2; static const long _STATE_CALLED = 3; public: _CancellationTokenRegistration(long _InitialRefs = 1) : _RefCounter(_InitialRefs), _M_state(_STATE_CALLED), _M_pTokenState(NULL) { } _CancellationTokenState *_GetToken() const { return _M_pTokenState; } protected: virtual ~_CancellationTokenRegistration() { _ASSERTE(_M_state != _STATE_CLEAR); } virtual void _Exec() = 0; private: friend class _CancellationTokenState; void _Invoke() { long tid = ::pplx::details::platform::GetCurrentThreadId(); _ASSERTE((tid & 0x3) == 0); // If this ever fires, we need a different encoding for this. long result = atomic_compare_exchange(_M_state, tid, _STATE_CLEAR); if (result == _STATE_CLEAR) { _Exec(); result = atomic_compare_exchange(_M_state, _STATE_CALLED, tid); if (result == _STATE_SYNCHRONIZE) { _M_pSyncBlock->set(); } } _Release(); } atomic_long _M_state; extensibility::event_t *_M_pSyncBlock; _CancellationTokenState *_M_pTokenState; }; template class _CancellationTokenCallback : public _CancellationTokenRegistration { public: _CancellationTokenCallback(const _Function& _Func) : _M_function(_Func) { } protected: virtual void _Exec() { _M_function(); } private: _Function _M_function; }; class CancellationTokenRegistration_TaskProc : public _CancellationTokenRegistration { public: CancellationTokenRegistration_TaskProc(TaskProc_t proc, _In_ void *pData, int initialRefs) : _CancellationTokenRegistration(initialRefs), m_proc(proc), m_pData(pData) { } protected: virtual void _Exec() { m_proc(m_pData); } private: TaskProc_t m_proc; void *m_pData; }; // The base implementation of a cancellation token. class _CancellationTokenState : public _RefCounter { protected: class TokenRegistrationContainer { private: typedef struct _Node { _CancellationTokenRegistration* _M_token; _Node *_M_next; } Node; public: TokenRegistrationContainer() : _M_begin(nullptr), _M_last(nullptr) { } ~TokenRegistrationContainer() { #if defined(_MSC_VER) #pragma warning(push) #pragma warning(disable: 6001) #endif auto node = _M_begin; while (node != nullptr) { Node* tmp = node; node = node->_M_next; ::free(tmp); } #if defined(_MSC_VER) #pragma warning(pop) #endif } void swap(TokenRegistrationContainer& list) { std::swap(list._M_begin, _M_begin); std::swap(list._M_last, _M_last); } bool empty() { return _M_begin == nullptr; } template void for_each(T lambda) { Node* node = _M_begin; while (node != nullptr) { lambda(node->_M_token); node = node->_M_next; } } void push_back(_CancellationTokenRegistration* token) { Node* node = reinterpret_cast(::malloc(sizeof(Node))); if (node == nullptr) { throw ::std::bad_alloc(); } node->_M_token = token; node->_M_next = nullptr; if (_M_begin == nullptr) { _M_begin = node; } else { _M_last->_M_next = node; } _M_last = node; } void remove(_CancellationTokenRegistration* token) { Node* node = _M_begin; Node* prev = nullptr; while (node != nullptr) { if (node->_M_token == token) { if (prev == nullptr) { _M_begin = node->_M_next; } else { prev->_M_next = node->_M_next; } if (node->_M_next == nullptr) { _M_last = prev; } ::free(node); break; } prev = node; node = node->_M_next; } } private: Node *_M_begin; Node *_M_last; }; public: static _CancellationTokenState * _NewTokenState() { return new _CancellationTokenState(); } static _CancellationTokenState *_None() { return reinterpret_cast<_CancellationTokenState *>(2); } static bool _IsValid(_In_opt_ _CancellationTokenState *_PToken) { return (_PToken != NULL && _PToken != _None()); } _CancellationTokenState() : _M_stateFlag(0) { } ~_CancellationTokenState() { TokenRegistrationContainer rundownList; { extensibility::scoped_critical_section_t _Lock(_M_listLock); _M_registrations.swap(rundownList); } rundownList.for_each([](_CancellationTokenRegistration * pRegistration) { pRegistration->_M_state = _CancellationTokenRegistration::_STATE_SYNCHRONIZE; pRegistration->_Release(); }); } bool _IsCanceled() const { return (_M_stateFlag != 0); } void _Cancel() { if (atomic_compare_exchange(_M_stateFlag, 1l, 0l) == 0) { TokenRegistrationContainer rundownList; { extensibility::scoped_critical_section_t _Lock(_M_listLock); _M_registrations.swap(rundownList); } rundownList.for_each([](_CancellationTokenRegistration * pRegistration) { pRegistration->_Invoke(); }); _M_stateFlag = 2; _M_cancelComplete.set(); } } _CancellationTokenRegistration *_RegisterCallback(TaskProc_t _PCallback, _In_ void *_PData, int _InitialRefs = 1) { _CancellationTokenRegistration *pRegistration = new CancellationTokenRegistration_TaskProc(_PCallback, _PData, _InitialRefs); _RegisterCallback(pRegistration); return pRegistration; } void _RegisterCallback(_In_ _CancellationTokenRegistration *_PRegistration) { _PRegistration->_M_state = _CancellationTokenRegistration::_STATE_CLEAR; _PRegistration->_Reference(); _PRegistration->_M_pTokenState = this; bool invoke = true; if (!_IsCanceled()) { extensibility::scoped_critical_section_t _Lock(_M_listLock); if (!_IsCanceled()) { invoke = false; _M_registrations.push_back(_PRegistration); } } if (invoke) { _PRegistration->_Invoke(); } } void _DeregisterCallback(_In_ _CancellationTokenRegistration *_PRegistration) { bool synchronize = false; { extensibility::scoped_critical_section_t _Lock(_M_listLock); // // If a cancellation has occurred, the registration list is guaranteed to be empty if we've observed it under the auspices of the // lock. In this case, we must synchronize with the cancelling thread to guarantee that the cancellation is finished by the time // we return from this method. // if (!_M_registrations.empty()) { _M_registrations.remove(_PRegistration); _PRegistration->_M_state = _CancellationTokenRegistration::_STATE_SYNCHRONIZE; _PRegistration->_Release(); } else { synchronize = true; } } // // If the list is empty, we are in one of several situations: // // - The callback has already been made --> do nothing // - The callback is about to be made --> flag it so it doesn't happen and return // - The callback is in progress elsewhere --> synchronize with it // - The callback is in progress on this thread --> do nothing // if (synchronize) { long result = atomic_compare_exchange( _PRegistration->_M_state, _CancellationTokenRegistration::_STATE_DEFER_DELETE, _CancellationTokenRegistration::_STATE_CLEAR ); switch(result) { case _CancellationTokenRegistration::_STATE_CLEAR: case _CancellationTokenRegistration::_STATE_CALLED: break; case _CancellationTokenRegistration::_STATE_DEFER_DELETE: case _CancellationTokenRegistration::_STATE_SYNCHRONIZE: _ASSERTE(false); break; default: { long tid = result; if (tid == ::pplx::details::platform::GetCurrentThreadId()) { // // It is entirely legal for a caller to Deregister during a callback instead of having to provide their own synchronization // mechanism between the two. In this case, we do *NOT* need to explicitly synchronize with the callback as doing so would // deadlock. If the call happens during, skip any extra synchronization. // break; } extensibility::event_t ev; _PRegistration->_M_pSyncBlock = &ev; long result_1 = atomic_exchange(_PRegistration->_M_state, _CancellationTokenRegistration::_STATE_SYNCHRONIZE); if (result_1 != _CancellationTokenRegistration::_STATE_CALLED) { _PRegistration->_M_pSyncBlock->wait(::pplx::extensibility::event_t::timeout_infinite); } break; } } } } private: // The flag for the token state (whether it is canceled or not) atomic_long _M_stateFlag; // Notification of completion of cancellation of this token. extensibility::event_t _M_cancelComplete; // Hmm.. where do we wait for it?? // Lock to protect the registrations list extensibility::critical_section_t _M_listLock; // The protected list of registrations TokenRegistrationContainer _M_registrations; }; } // namespace details class cancellation_token_source; class cancellation_token; /// /// The cancellation_token_registration class represents a callback notification from a cancellation_token. When the register /// method on a cancellation_token is used to receive notification of when cancellation occurs, a cancellation_token_registration /// object is returned as a handle to the callback so that the caller can request a specific callback no longer be made through use of /// the deregister method. /// class cancellation_token_registration { public: cancellation_token_registration() : _M_pRegistration(NULL) { } ~cancellation_token_registration() { _Clear(); } cancellation_token_registration(const cancellation_token_registration& _Src) { _Assign(_Src._M_pRegistration); } cancellation_token_registration(cancellation_token_registration&& _Src) { _Move(_Src._M_pRegistration); } cancellation_token_registration& operator=(const cancellation_token_registration& _Src) { if (this != &_Src) { _Clear(); _Assign(_Src._M_pRegistration); } return *this; } cancellation_token_registration& operator=(cancellation_token_registration&& _Src) { if (this != &_Src) { _Clear(); _Move(_Src._M_pRegistration); } return *this; } bool operator==(const cancellation_token_registration& _Rhs) const { return _M_pRegistration == _Rhs._M_pRegistration; } bool operator!=(const cancellation_token_registration& _Rhs) const { return !(operator==(_Rhs)); } private: friend class cancellation_token; cancellation_token_registration(_In_ details::_CancellationTokenRegistration *_PRegistration) : _M_pRegistration(_PRegistration) { } void _Clear() { if (_M_pRegistration != NULL) { _M_pRegistration->_Release(); } _M_pRegistration = NULL; } void _Assign(_In_ details::_CancellationTokenRegistration *_PRegistration) { if (_PRegistration != NULL) { _PRegistration->_Reference(); } _M_pRegistration = _PRegistration; } void _Move(_In_ details::_CancellationTokenRegistration *&_PRegistration) { _M_pRegistration = _PRegistration; _PRegistration = NULL; } details::_CancellationTokenRegistration *_M_pRegistration; }; /// /// The cancellation_token class represents the ability to determine whether some operation has been requested to cancel. A given token can /// be associated with a task_group, structured_task_group, or task to provide implicit cancellation. It can also be polled for /// cancellation or have a callback registered for if and when the associated cancellation_token_source is canceled. /// class cancellation_token { public: typedef details::_CancellationTokenState * _ImplType; /// /// Returns a cancellation token which can never be subject to cancellation. /// /// /// A cancellation token that cannot be canceled. /// static cancellation_token none() { return cancellation_token(); } cancellation_token(const cancellation_token& _Src) { _Assign(_Src._M_Impl); } cancellation_token(cancellation_token&& _Src) { _Move(_Src._M_Impl); } cancellation_token& operator=(const cancellation_token& _Src) { if (this != &_Src) { _Clear(); _Assign(_Src._M_Impl); } return *this; } cancellation_token& operator=(cancellation_token&& _Src) { if (this != &_Src) { _Clear(); _Move(_Src._M_Impl); } return *this; } bool operator==(const cancellation_token& _Src) const { return _M_Impl == _Src._M_Impl; } bool operator!=(const cancellation_token& _Src) const { return !(operator==(_Src)); } ~cancellation_token() { _Clear(); } /// /// Returns an indication of whether this token can be canceled or not. /// /// /// An indication of whether this token can be canceled or not. /// bool is_cancelable() const { return (_M_Impl != NULL); } /// /// Returns true if the token has been canceled. /// /// /// The value true if the token has been canceled; otherwise, the value false. /// bool is_canceled() const { return (_M_Impl != NULL && _M_Impl->_IsCanceled()); } /// /// Registers a callback function with the token. If and when the token is canceled, the callback will be made. Note that if the token /// is already canceled at the point where this method is called, the callback will be made immediately and synchronously. /// /// /// The type of the function object that will be called back when this cancellation_token is canceled. /// /// /// The function object that will be called back when this cancellation_token is canceled. /// /// /// A cancellation_token_registration object which can be utilized in the deregister method to deregister a previously registered /// callback and prevent it from being made. The method will throw an invalid_operation exception if /// it is called on a cancellation_token object that was created using the cancellation_token::none /// method. /// template ::pplx::cancellation_token_registration register_callback(const _Function& _Func) const { if (_M_Impl == NULL) { // A callback cannot be registered if the token does not have an associated source. throw invalid_operation(); } #if defined(_MSC_VER) #pragma warning(suppress: 28197) #endif details::_CancellationTokenCallback<_Function> *_PCallback = new details::_CancellationTokenCallback<_Function>(_Func); _M_Impl->_RegisterCallback(_PCallback); return cancellation_token_registration(_PCallback); } /// /// Removes a callback previously registered via the register method based on the cancellation_token_registration object returned /// at the time of registration. /// /// /// The cancellation_token_registration object corresponding to the callback to be deregistered. This token must have been previously /// returned from a call to the register method. /// void deregister_callback(const cancellation_token_registration& _Registration) const { _M_Impl->_DeregisterCallback(_Registration._M_pRegistration); } _ImplType _GetImpl() const { return _M_Impl; } _ImplType _GetImplValue() const { return (_M_Impl == NULL) ? ::pplx::details::_CancellationTokenState::_None() : _M_Impl; } static cancellation_token _FromImpl(_ImplType _Impl) { return cancellation_token(_Impl); } private: friend class cancellation_token_source; _ImplType _M_Impl; void _Clear() { if (_M_Impl != NULL) { _M_Impl->_Release(); } _M_Impl = NULL; } void _Assign(_ImplType _Impl) { if (_Impl != NULL) { _Impl->_Reference(); } _M_Impl = _Impl; } void _Move(_ImplType &_Impl) { _M_Impl = _Impl; _Impl = NULL; } cancellation_token() : _M_Impl(NULL) { } cancellation_token(_ImplType _Impl) : _M_Impl(_Impl) { if (_M_Impl == ::pplx::details::_CancellationTokenState::_None()) { _M_Impl = NULL; } if (_M_Impl != NULL) { _M_Impl->_Reference(); } } }; /// /// The cancellation_token_source class represents the ability to cancel some cancelable operation. /// class cancellation_token_source { public: typedef ::pplx::details::_CancellationTokenState * _ImplType; /// /// Constructs a new cancellation_token_source. The source can be used to flag cancellation of some cancelable operation. /// cancellation_token_source() { _M_Impl = new ::pplx::details::_CancellationTokenState; } cancellation_token_source(const cancellation_token_source& _Src) { _Assign(_Src._M_Impl); } cancellation_token_source(cancellation_token_source&& _Src) { _Move(_Src._M_Impl); } cancellation_token_source& operator=(const cancellation_token_source& _Src) { if (this != &_Src) { _Clear(); _Assign(_Src._M_Impl); } return *this; } cancellation_token_source& operator=(cancellation_token_source&& _Src) { if (this != &_Src) { _Clear(); _Move(_Src._M_Impl); } return *this; } bool operator==(const cancellation_token_source& _Src) const { return _M_Impl == _Src._M_Impl; } bool operator!=(const cancellation_token_source& _Src) const { return !(operator==(_Src)); } ~cancellation_token_source() { if (_M_Impl != NULL) { _M_Impl->_Release(); } } /// /// Returns a cancellation token associated with this source. The returned token can be polled for cancellation /// or provide a callback if and when cancellation occurs. /// /// /// A cancellation token associated with this source. /// cancellation_token get_token() const { return cancellation_token(_M_Impl); } /// /// Creates a cancellation_token_source which is canceled when the provided token is canceled. /// /// /// A token whose cancellation will cause cancellation of the returned token source. Note that the returned token source can also be canceled /// independently of the source contained in this parameter. /// /// /// A cancellation_token_source which is canceled when the token provided by the parameter is canceled. /// static cancellation_token_source create_linked_source(cancellation_token& _Src) { cancellation_token_source newSource; _Src.register_callback( [newSource](){ newSource.cancel(); } ); return newSource; } /// /// Creates a cancellation_token_source which is canceled when one of a series of tokens represented by an STL iterator /// pair is canceled. /// /// /// The STL iterator corresponding to the beginning of the range of tokens to listen for cancellation of. /// /// /// The STL iterator corresponding to the ending of the range of tokens to listen for cancellation of. /// /// /// A cancellation_token_source which is canceled when any of the tokens provided by the range described by the STL iterators /// contained in the and parameters is canceled. /// template static cancellation_token_source create_linked_source(_Iter _Begin, _Iter _End) { cancellation_token_source newSource; for (_Iter _It = _Begin; _It != _End; ++_It) { _It->register_callback( [newSource](){ newSource.cancel(); } ); } return newSource; } /// /// Cancels the token. Any task_group, structured_task_group, or task which utilizes the token will be /// canceled upon this call and throw an exception at the next interruption point. /// void cancel() const { _M_Impl->_Cancel(); } _ImplType _GetImpl() const { return _M_Impl; } static cancellation_token_source _FromImpl(_ImplType _Impl) { return cancellation_token_source(_Impl); } private: _ImplType _M_Impl; void _Clear() { if (_M_Impl != NULL) { _M_Impl->_Release(); } _M_Impl = NULL; } void _Assign(_ImplType _Impl) { if (_Impl != NULL) { _Impl->_Reference(); } _M_Impl = _Impl; } void _Move(_ImplType &_Impl) { _M_Impl = _Impl; _Impl = NULL; } cancellation_token_source(_ImplType _Impl) : _M_Impl(_Impl) { if (_M_Impl == ::pplx::details::_CancellationTokenState::_None()) { _M_Impl = NULL; } if (_M_Impl != NULL) { _M_Impl->_Reference(); } } }; } // namespace pplx #pragma pop_macro("new") #pragma pack(pop) #endif // _PPLXCANCELLATION_TOKEN_H ================================================ FILE: Include/cpprestinclude/pplx/pplxinterface.h ================================================ /*** * ==++== * * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * ==--== * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ * * PPL interfaces * * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk * * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ****/ #pragma once #ifndef _PPLXINTERFACE_H #define _PPLXINTERFACE_H #if (defined(_MSC_VER) && (_MSC_VER >= 1800)) && !CPPREST_FORCE_PPLX #error This file must not be included for Visual Studio 12 or later #endif #if defined(_CRTBLD) #elif defined(_WIN32) #if (_MSC_VER >= 1700) #define _USE_REAL_ATOMICS #endif #else // GCC compiler #define _USE_REAL_ATOMICS #endif #include #ifdef _USE_REAL_ATOMICS #include #endif #define _pplx_cdecl __cdecl namespace pplx { /// /// An elementary abstraction for a task, defined as void (__cdecl * TaskProc_t)(void *). A TaskProc is called to /// invoke the body of a task. /// /**/ typedef void (_pplx_cdecl * TaskProc_t)(void *); /// /// Scheduler Interface /// struct __declspec(novtable) scheduler_interface { virtual void schedule( TaskProc_t, _In_ void* ) = 0; virtual ~scheduler_interface() {} }; /// /// Represents a pointer to a scheduler. This class exists to allow the /// the specification of a shared lifetime by using shared_ptr or just /// a plain reference by using raw pointer. /// struct scheduler_ptr { /// /// Creates a scheduler pointer from shared_ptr to scheduler /// explicit scheduler_ptr(std::shared_ptr scheduler) : m_sharedScheduler(std::move(scheduler)) { m_scheduler = m_sharedScheduler.get(); } /// /// Creates a scheduler pointer from raw pointer to scheduler /// explicit scheduler_ptr(_In_opt_ scheduler_interface * pScheduler) : m_scheduler(pScheduler) { } /// /// Behave like a pointer /// scheduler_interface *operator->() const { return get(); } /// /// Returns the raw pointer to the scheduler /// scheduler_interface * get() const { return m_scheduler; } /// /// Test whether the scheduler pointer is non-null /// operator bool() const { return get() != nullptr; } private: std::shared_ptr m_sharedScheduler; scheduler_interface * m_scheduler; }; /// /// Describes the execution status of a task_group or structured_task_group object. A value of this type is returned /// by numerous methods that wait on tasks scheduled to a task group to complete. /// /// /// /// /// /// /// /**/ enum task_group_status { /// /// The tasks queued to the task_group object have not completed. Note that this value is not presently returned by /// the Concurrency Runtime. /// /**/ not_complete, /// /// The tasks queued to the task_group or structured_task_group object completed successfully. /// /**/ completed, /// /// The task_group or structured_task_group object was canceled. One or more tasks may not have executed. /// /**/ canceled }; namespace details { /// /// Atomics /// #ifdef _USE_REAL_ATOMICS typedef std::atomic atomic_long; typedef std::atomic atomic_size_t; template _T atomic_compare_exchange(std::atomic<_T>& _Target, _T _Exchange, _T _Comparand) { _T _Result = _Comparand; _Target.compare_exchange_strong(_Result, _Exchange); return _Result; } template _T atomic_exchange(std::atomic<_T>& _Target, _T _Value) { return _Target.exchange(_Value); } template _T atomic_increment(std::atomic<_T>& _Target) { return _Target.fetch_add(1) + 1; } template _T atomic_decrement(std::atomic<_T>& _Target) { return _Target.fetch_sub(1) - 1; } template _T atomic_add(std::atomic<_T>& _Target, _T value) { return _Target.fetch_add(value) + value; } #else // not _USE_REAL_ATOMICS typedef long volatile atomic_long; typedef size_t volatile atomic_size_t; template inline T atomic_exchange(T volatile& _Target, T _Value) { return _InterlockedExchange(&_Target, _Value); } inline long atomic_increment(long volatile & _Target) { return _InterlockedIncrement(&_Target); } inline long atomic_add(long volatile & _Target, long value) { return _InterlockedExchangeAdd(&_Target, value) + value; } inline size_t atomic_increment(size_t volatile & _Target) { #if (defined(_M_IX86) || defined(_M_ARM)) return static_cast(_InterlockedIncrement(reinterpret_cast(&_Target))); #else return static_cast(_InterlockedIncrement64(reinterpret_cast<__int64 volatile *>(&_Target))); #endif } inline long atomic_decrement(long volatile & _Target) { return _InterlockedDecrement(&_Target); } inline size_t atomic_decrement(size_t volatile & _Target) { #if (defined(_M_IX86) || defined(_M_ARM)) return static_cast(_InterlockedDecrement(reinterpret_cast(&_Target))); #else return static_cast(_InterlockedDecrement64(reinterpret_cast<__int64 volatile *>(&_Target))); #endif } inline long atomic_compare_exchange(long volatile & _Target, long _Exchange, long _Comparand) { return _InterlockedCompareExchange(&_Target, _Exchange, _Comparand); } inline size_t atomic_compare_exchange(size_t volatile & _Target, size_t _Exchange, size_t _Comparand) { #if (defined(_M_IX86) || defined(_M_ARM)) return static_cast(_InterlockedCompareExchange(reinterpret_cast(_Target), static_cast(_Exchange), static_cast(_Comparand))); #else return static_cast(_InterlockedCompareExchange64(reinterpret_cast<__int64 volatile *>(_Target), static_cast<__int64>(_Exchange), static_cast<__int64>(_Comparand))); #endif } #endif // _USE_REAL_ATOMICS }} // namespace pplx #endif // _PPLXINTERFACE_H ================================================ FILE: Include/cpprestinclude/pplx/pplxlinux.h ================================================ /*** * Copyright (C) Microsoft. All rights reserved. * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. * * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ * * Linux specific pplx implementations * * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk * * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ****/ #pragma once #if (defined(_MSC_VER)) #error This file must not be included for Visual Studio #endif #ifndef _WIN32 #include #include "pthread.h" #include "cpprest/details/cpprest_compat.h" //todo1808 //#if defined(__APPLE__) //#include //#include //#include //#else #include #include //#endif #include "pplx/pplxinterface.h" namespace pplx { // todo1808 // #if defined(__APPLE__) // namespace cpprest_synchronization = ::boost; // #else namespace cpprest_synchronization = ::std; // #endif namespace details { namespace platform { /// /// Returns a unique identifier for the execution thread where this routine in invoked /// _PPLXIMP long _pplx_cdecl GetCurrentThreadId(); /// /// Yields the execution of the current execution thread - typically when spin-waiting /// _PPLXIMP void _pplx_cdecl YieldExecution(); /// /// Caputeres the callstack /// __declspec(noinline) inline static size_t CaptureCallstack(void **, size_t, size_t) { return 0; } } /// /// Manual reset event /// class event_impl { private: cpprest_synchronization::mutex _lock; cpprest_synchronization::condition_variable _condition; bool _signaled; public: static const unsigned int timeout_infinite = 0xFFFFFFFF; event_impl() : _signaled(false) { } void set() { cpprest_synchronization::lock_guard lock(_lock); _signaled = true; _condition.notify_all(); } void reset() { cpprest_synchronization::lock_guard lock(_lock); _signaled = false; } unsigned int wait(unsigned int timeout) { cpprest_synchronization::unique_lock lock(_lock); if (timeout == event_impl::timeout_infinite) { _condition.wait(lock, [this]() -> bool { return _signaled; }); return 0; } else { cpprest_synchronization::chrono::milliseconds period(timeout); auto status = _condition.wait_for(lock, period, [this]() -> bool { return _signaled; }); _ASSERTE(status == _signaled); // Return 0 if the wait completed as a result of signaling the event. Otherwise, return timeout_infinite // Note: this must be consistent with the behavior of the Windows version, which is based on WaitForSingleObjectEx return status ? 0: event_impl::timeout_infinite; } } unsigned int wait() { return wait(event_impl::timeout_infinite); } }; /// /// Reader writer lock /// class reader_writer_lock_impl { private: pthread_rwlock_t _M_reader_writer_lock; public: class scoped_lock_read { public: explicit scoped_lock_read(reader_writer_lock_impl &_Reader_writer_lock) : _M_reader_writer_lock(_Reader_writer_lock) { _M_reader_writer_lock.lock_read(); } ~scoped_lock_read() { _M_reader_writer_lock.unlock(); } private: reader_writer_lock_impl& _M_reader_writer_lock; scoped_lock_read(const scoped_lock_read&); // no copy constructor scoped_lock_read const & operator=(const scoped_lock_read&); // no assignment operator }; reader_writer_lock_impl() { pthread_rwlock_init(&_M_reader_writer_lock, nullptr); } ~reader_writer_lock_impl() { pthread_rwlock_destroy(&_M_reader_writer_lock); } void lock() { pthread_rwlock_wrlock(&_M_reader_writer_lock); } void lock_read() { pthread_rwlock_rdlock(&_M_reader_writer_lock); } void unlock() { pthread_rwlock_unlock(&_M_reader_writer_lock); } }; /// /// Recursive mutex /// class recursive_lock_impl { public: recursive_lock_impl() : _M_owner(-1), _M_recursionCount(0) { } ~recursive_lock_impl() { _ASSERTE(_M_owner == -1); _ASSERTE(_M_recursionCount == 0); } void lock() { auto id = ::pplx::details::platform::GetCurrentThreadId(); if ( _M_owner == id ) { _M_recursionCount++; } else { _M_cs.lock(); _M_owner = id; _M_recursionCount = 1; } } void unlock() { _ASSERTE(_M_owner == ::pplx::details::platform::GetCurrentThreadId()); _ASSERTE(_M_recursionCount >= 1); _M_recursionCount--; if ( _M_recursionCount == 0 ) { _M_owner = -1; _M_cs.unlock(); } } private: cpprest_synchronization::mutex _M_cs; volatile long _M_owner; long _M_recursionCount; }; #if defined(__APPLE__) class apple_scheduler : public pplx::scheduler_interface #else class linux_scheduler : public pplx::scheduler_interface #endif { public: _PPLXIMP virtual void schedule( TaskProc_t proc, _In_ void* param); }; } // namespace details /// /// A generic RAII wrapper for locks that implements the critical_section interface /// cpprest_synchronization::lock_guard /// template class scoped_lock { public: explicit scoped_lock(_Lock& _Critical_section) : _M_critical_section(_Critical_section) { _M_critical_section.lock(); } ~scoped_lock() { _M_critical_section.unlock(); } private: _Lock& _M_critical_section; scoped_lock(const scoped_lock&); // no copy constructor scoped_lock const & operator=(const scoped_lock&); // no assignment operator }; // The extensibility namespace contains the type definitions that are used internally namespace extensibility { typedef ::pplx::details::event_impl event_t; typedef cpprest_synchronization::mutex critical_section_t; typedef scoped_lock scoped_critical_section_t; typedef ::pplx::details::reader_writer_lock_impl reader_writer_lock_t; typedef scoped_lock scoped_rw_lock_t; typedef ::pplx::extensibility::reader_writer_lock_t::scoped_lock_read scoped_read_lock_t; typedef ::pplx::details::recursive_lock_impl recursive_lock_t; typedef scoped_lock scoped_recursive_lock_t; } /// /// Default scheduler type /// #if defined(__APPLE__) typedef details::apple_scheduler default_scheduler_t; #else typedef details::linux_scheduler default_scheduler_t; #endif namespace details { /// /// Terminate the process due to unhandled exception /// #ifndef _REPORT_PPLTASK_UNOBSERVED_EXCEPTION #define _REPORT_PPLTASK_UNOBSERVED_EXCEPTION() do { \ raise(SIGTRAP); \ std::terminate(); \ } while(false) #endif //_REPORT_PPLTASK_UNOBSERVED_EXCEPTION } //see: http://gcc.gnu.org/onlinedocs/gcc/Return-Address.html // this is critical to inline __attribute__ ((always_inline)) inline void* _ReturnAddress() { return __builtin_return_address(0); } } // namespace pplx #endif // !_WIN32 ================================================ FILE: Include/cpprestinclude/pplx/pplxtasks.110.h ================================================ /*** * ==++== * * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * ==--== * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ * * Parallel Patterns Library - PPLx Tasks * * For the latest on this and related APIs, please see http://casablanca.codeplex.com. * * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ****/ #pragma once #ifndef _PPLXTASKS_H #define _PPLXTASKS_H #if (defined(_MSC_VER) && (_MSC_VER >= 1800)) && !CPPREST_FORCE_PPLX #include namespace pplx = Concurrency; #if (_MSC_VER >= 1900) #include namespace Concurrency { namespace extensibility { typedef ::std::condition_variable condition_variable_t; typedef ::std::mutex critical_section_t; typedef ::std::unique_lock< ::std::mutex> scoped_critical_section_t; typedef ::Concurrency::event event_t; typedef ::Concurrency::reader_writer_lock reader_writer_lock_t; typedef ::Concurrency::reader_writer_lock::scoped_lock scoped_rw_lock_t; typedef ::Concurrency::reader_writer_lock::scoped_lock_read scoped_read_lock_t; typedef ::Concurrency::details::_ReentrantBlockingLock recursive_lock_t; typedef recursive_lock_t::_Scoped_lock scoped_recursive_lock_t; } } #endif // _MSC_VER >= 1900 #else #include "pplx/pplx.h" #if defined(__ANDROID__) #include void cpprest_init(JavaVM*); #endif // Cannot build using a compiler that is older than dev10 SP1 #if defined(_MSC_VER) #if _MSC_FULL_VER < 160040219 /*IFSTRIP=IGN*/ #error ERROR: Visual Studio 2010 SP1 or later is required to build ppltasks #endif /*IFSTRIP=IGN*/ #endif /* defined(_MSC_VER) */ #include #include #include #include #include #if defined(_MSC_VER) #if defined(__cplusplus_winrt) #include #include #include #include #ifndef _UITHREADCTXT_SUPPORT #ifdef WINAPI_FAMILY /*IFSTRIP=IGN*/ // It is safe to include winapifamily as WINAPI_FAMILY was defined by the user #include #if WINAPI_FAMILY == WINAPI_FAMILY_APP // UI thread context support is not required for desktop and Windows Store apps #define _UITHREADCTXT_SUPPORT 0 #elif WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP // UI thread context support is not required for desktop and Windows Store apps #define _UITHREADCTXT_SUPPORT 0 #else /* WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP */ #define _UITHREADCTXT_SUPPORT 1 #endif /* WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP */ #else /* WINAPI_FAMILY */ // Not supported without a WINAPI_FAMILY setting. #define _UITHREADCTXT_SUPPORT 0 #endif /* WINAPI_FAMILY */ #endif /* _UITHREADCTXT_SUPPORT */ #if _UITHREADCTXT_SUPPORT #include #endif /* _UITHREADCTXT_SUPPORT */ #pragma detect_mismatch("_PPLTASKS_WITH_WINRT", "1") #else /* defined(__cplusplus_winrt) */ #pragma detect_mismatch("_PPLTASKS_WITH_WINRT", "0") #endif /* defined(__cplusplus_winrt) */ #endif /* defined(_MSC_VER) */ #ifdef _DEBUG #define _DBG_ONLY(X) X #else #define _DBG_ONLY(X) #endif // #ifdef _DEBUG // std::copy_exception changed to std::make_exception_ptr from VS 2010 to VS 11. #ifdef _MSC_VER #if _MSC_VER < 1700 /*IFSTRIP=IGN*/ namespace std { template exception_ptr make_exception_ptr(_E _Except) { return copy_exception(_Except); } } #endif /* _MSC_VER < 1700 */ #ifndef _PPLTASK_ASYNC_LOGGING #if _MSC_VER >= 1800 && defined(__cplusplus_winrt) #define _PPLTASK_ASYNC_LOGGING 1 // Only enable async logging under dev12 winrt #else #define _PPLTASK_ASYNC_LOGGING 0 #endif #endif /* !_PPLTASK_ASYNC_LOGGING */ #endif /* _MSC_VER */ #pragma pack(push,_CRT_PACKING) #if defined(_MSC_VER) #pragma warning(push) #pragma warning(disable: 28197) #pragma warning(disable: 4100) // Unreferenced formal parameter - needed for document generation #pragma warning(disable: 4127) // constant express in if condition - we use it for meta programming #endif /* defined(_MSC_VER) */ // All CRT public header files are required to be protected from the macro new #pragma push_macro("new") #undef new // stuff ported from Dev11 CRT // NOTE: this doesn't actually match std::declval. it behaves differently for void! // so don't blindly change it to std::declval. namespace stdx { template _T&& declval(); } /// /// The pplx namespace provides classes and functions that give you access to the Concurrency Runtime, /// a concurrent programming framework for C++. For more information, see . /// /**/ namespace pplx { /// /// A type that represents the terminal state of a task. Valid values are completed and canceled. /// /// /**/ typedef task_group_status task_status; template class task; template <> class task; // In debug builds, default to 10 frames, unless this is overridden prior to #includ'ing ppltasks.h. In retail builds, default to only one frame. #ifndef PPL_TASK_SAVE_FRAME_COUNT #ifdef _DEBUG #define PPL_TASK_SAVE_FRAME_COUNT 10 #else #define PPL_TASK_SAVE_FRAME_COUNT 1 #endif #endif /// /// Helper macro to determine how many stack frames need to be saved. When any number less or equal to 1 is specified, /// only one frame is captured and no stackwalk will be involved. Otherwise, the number of callstack frames will be captured. /// /// /// This needs to be defined as a macro rather than a function so that if we're only gathering one frame, _ReturnAddress() /// will evaluate to client code, rather than a helper function inside of _TaskCreationCallstack, itself. /// #if PPL_TASK_SAVE_FRAME_COUNT > 1 #if defined(__cplusplus_winrt) && !defined(_DEBUG) #pragma message ("WARNING: Redefinning PPL_TASK_SAVE_FRAME_COUNT under Release build for non-desktop applications is not supported; only one frame will be captured!") #define _CAPTURE_CALLSTACK() ::pplx::details::_TaskCreationCallstack::_CaptureSingleFrameCallstack(_ReturnAddress()) #else #define _CAPTURE_CALLSTACK() ::pplx::details::_TaskCreationCallstack::_CaptureMultiFramesCallstack(PPL_TASK_SAVE_FRAME_COUNT) #endif #else #define _CAPTURE_CALLSTACK() ::pplx::details::_TaskCreationCallstack::_CaptureSingleFrameCallstack(_ReturnAddress()) #endif /// /// Returns an indication of whether the task that is currently executing has received a request to cancel its /// execution. Cancellation is requested on a task if the task was created with a cancellation token, and /// the token source associated with that token is canceled. /// /// /// true if the currently executing task has received a request for cancellation, false otherwise. /// /// /// If you call this method in the body of a task and it returns true, you must respond with a call to /// cancel_current_task to acknowledge the cancellation request, /// after performing any cleanup you need. This will abort the execution of the task and cause it to enter into /// the canceled state. If you do not respond and continue execution, or return instead of calling /// cancel_current_task, the task will enter the completed state when it is done. /// state. /// A task is not cancellable if it was created without a cancellation token. /// /// /// /// /// /**/ inline bool _pplx_cdecl is_task_cancellation_requested() { return ::pplx::details::_TaskCollection_t::_Is_cancellation_requested(); } /// /// Cancels the currently executing task. This function can be called from within the body of a task to abort the /// task's execution and cause it to enter the canceled state. While it may be used in response to /// the is_task_cancellation_requested function, you may /// also use it by itself, to initiate cancellation of the task that is currently executing. /// It is not a supported scenario to call this function if you are not within the body of a task. /// Doing so will result in undefined behavior such as a crash or a hang in your application. /// /// /**/ inline __declspec(noreturn) void _pplx_cdecl cancel_current_task() { throw task_canceled(); } namespace details { /// /// Callstack container, which is used to capture and preserve callstacks in ppltasks. /// Members of this class is examined by vc debugger, thus there will be no public access methods. /// Please note that names of this class should be kept stable for debugger examining. /// class _TaskCreationCallstack { private: // If _M_SingleFrame != nullptr, there will be only one frame of callstacks, which is stored in _M_SingleFrame; // otherwise, _M_Frame will store all the callstack frames. void* _M_SingleFrame; std::vector _M_frames; public: _TaskCreationCallstack() { _M_SingleFrame = nullptr; } // Store one frame of callstack. This function works for both Debug / Release CRT. static _TaskCreationCallstack _CaptureSingleFrameCallstack(void *_SingleFrame) { _TaskCreationCallstack _csc; _csc._M_SingleFrame = _SingleFrame; return _csc; } // Capture _CaptureFrames number of callstack frames. This function only work properly for Desktop or Debug CRT. __declspec(noinline) static _TaskCreationCallstack _CaptureMultiFramesCallstack(size_t _CaptureFrames) { _TaskCreationCallstack _csc; _csc._M_frames.resize(_CaptureFrames); // skip 2 frames to make sure callstack starts from user code _csc._M_frames.resize(::pplx::details::platform::CaptureCallstack(&_csc._M_frames[0], 2, _CaptureFrames)); return _csc; } }; typedef unsigned char _Unit_type; struct _TypeSelectorNoAsync {}; struct _TypeSelectorAsyncOperationOrTask {}; struct _TypeSelectorAsyncOperation : public _TypeSelectorAsyncOperationOrTask { }; struct _TypeSelectorAsyncTask : public _TypeSelectorAsyncOperationOrTask { }; struct _TypeSelectorAsyncAction {}; struct _TypeSelectorAsyncActionWithProgress {}; struct _TypeSelectorAsyncOperationWithProgress {}; template struct _NormalizeVoidToUnitType { typedef _Ty _Type; }; template<> struct _NormalizeVoidToUnitType { typedef _Unit_type _Type; }; template struct _IsUnwrappedAsyncSelector { static const bool _Value = true; }; template<> struct _IsUnwrappedAsyncSelector<_TypeSelectorNoAsync> { static const bool _Value = false; }; template struct _UnwrapTaskType { typedef _Ty _Type; }; template struct _UnwrapTaskType> { typedef _Ty _Type; }; template _TypeSelectorAsyncTask _AsyncOperationKindSelector(task<_T>); _TypeSelectorNoAsync _AsyncOperationKindSelector(...); #if defined(__cplusplus_winrt) template struct _Unhat { typedef _Type _Value; }; template struct _Unhat<_Type^> { typedef _Type _Value; }; value struct _NonUserType { public: int _Dummy; }; template struct _ValueTypeOrRefType { typedef _NonUserType _Value; }; template struct _ValueTypeOrRefType<_Type, true> { typedef _Type _Value; }; template _T2 _ProgressTypeSelector(Windows::Foundation::IAsyncOperationWithProgress<_T1,_T2>^); template _T1 _ProgressTypeSelector(Windows::Foundation::IAsyncActionWithProgress<_T1>^); template struct _GetProgressType { typedef decltype(_ProgressTypeSelector(stdx::declval<_Type>())) _Value; }; template struct _IsIAsyncInfo { static const bool _Value = __is_base_of(Windows::Foundation::IAsyncInfo, typename _Unhat<_Type>::_Value); }; template _TypeSelectorAsyncOperation _AsyncOperationKindSelector(Windows::Foundation::IAsyncOperation<_T>^); _TypeSelectorAsyncAction _AsyncOperationKindSelector(Windows::Foundation::IAsyncAction^); template _TypeSelectorAsyncOperationWithProgress _AsyncOperationKindSelector(Windows::Foundation::IAsyncOperationWithProgress<_T1, _T2>^); template _TypeSelectorAsyncActionWithProgress _AsyncOperationKindSelector(Windows::Foundation::IAsyncActionWithProgress<_T>^); template ::_Value> struct _TaskTypeTraits { typedef typename _UnwrapTaskType<_Type>::_Type _TaskRetType; typedef decltype(_AsyncOperationKindSelector(stdx::declval<_Type>())) _AsyncKind; typedef typename _NormalizeVoidToUnitType<_TaskRetType>::_Type _NormalizedTaskRetType; static const bool _IsAsyncTask = _IsAsync; static const bool _IsUnwrappedTaskOrAsync = _IsUnwrappedAsyncSelector<_AsyncKind>::_Value; }; template struct _TaskTypeTraits<_Type, true > { typedef decltype(((_Type)nullptr)->GetResults()) _TaskRetType; typedef _TaskRetType _NormalizedTaskRetType; typedef decltype(_AsyncOperationKindSelector((_Type)nullptr)) _AsyncKind; static const bool _IsAsyncTask = true; static const bool _IsUnwrappedTaskOrAsync = _IsUnwrappedAsyncSelector<_AsyncKind>::_Value; }; #else /* defined (__cplusplus_winrt) */ template struct _IsIAsyncInfo { static const bool _Value = false; }; template struct _TaskTypeTraits { typedef typename _UnwrapTaskType<_Type>::_Type _TaskRetType; typedef decltype(_AsyncOperationKindSelector(stdx::declval<_Type>())) _AsyncKind; typedef typename _NormalizeVoidToUnitType<_TaskRetType>::_Type _NormalizedTaskRetType; static const bool _IsAsyncTask = false; static const bool _IsUnwrappedTaskOrAsync = _IsUnwrappedAsyncSelector<_AsyncKind>::_Value; }; #endif /* defined (__cplusplus_winrt) */ template auto _IsCallable(_Function _Func, int) -> decltype(_Func(), std::true_type()) { (void)(_Func); return std::true_type(); } template std::false_type _IsCallable(_Function, ...) { return std::false_type(); } template <> struct _TaskTypeTraits { typedef void _TaskRetType; typedef _TypeSelectorNoAsync _AsyncKind; typedef _Unit_type _NormalizedTaskRetType; static const bool _IsAsyncTask = false; static const bool _IsUnwrappedTaskOrAsync = false; }; template task<_Type> _To_task(_Type t); template task _To_task_void(_Func f); struct _BadContinuationParamType{}; template auto _ReturnTypeHelper(_Type t, _Function _Func, int, int) -> decltype(_Func(_To_task(t))); template auto _ReturnTypeHelper(_Type t, _Function _Func, int, ...) -> decltype(_Func(t)); template auto _ReturnTypeHelper(_Type t, _Function _Func, ...) -> _BadContinuationParamType; template auto _IsTaskHelper(_Type t, _Function _Func, int, int) -> decltype(_Func(_To_task(t)), std::true_type()); template std::false_type _IsTaskHelper(_Type t, _Function _Func, int, ...); template auto _VoidReturnTypeHelper(_Function _Func, int, int) -> decltype(_Func(_To_task_void(_Func))); template auto _VoidReturnTypeHelper(_Function _Func, int, ...) -> decltype(_Func()); template auto _VoidIsTaskHelper(_Function _Func, int, int) -> decltype(_Func(_To_task_void(_Func)), std::true_type()); template std::false_type _VoidIsTaskHelper(_Function _Func, int, ...); template struct _FunctionTypeTraits { typedef decltype(_ReturnTypeHelper(stdx::declval<_ExpectedParameterType>(),stdx::declval<_Function>(), 0, 0)) _FuncRetType; static_assert(!std::is_same<_FuncRetType,_BadContinuationParamType>::value, "incorrect parameter type for the callable object in 'then'; consider _ExpectedParameterType or task<_ExpectedParameterType> (see below)"); typedef decltype(_IsTaskHelper(stdx::declval<_ExpectedParameterType>(),stdx::declval<_Function>(), 0, 0)) _Takes_task; }; template struct _FunctionTypeTraits<_Function, void> { typedef decltype(_VoidReturnTypeHelper(stdx::declval<_Function>(), 0, 0)) _FuncRetType; typedef decltype(_VoidIsTaskHelper(stdx::declval<_Function>(), 0, 0)) _Takes_task; }; template struct _ContinuationTypeTraits { typedef task::_FuncRetType>::_TaskRetType> _TaskOfType; }; // _InitFunctorTypeTraits is used to decide whether a task constructed with a lambda should be unwrapped. Depending on how the variable is // declared, the constructor may or may not perform unwrapping. For eg. // // This declaration SHOULD NOT cause unwrapping // task> t1([]() -> task { // task t2([]() {}); // return t2; // }); // // This declaration SHOULD cause unwrapping // task> t1([]() -> task { // task t2([]() {}); // return t2; // }); // If the type of the task is the same as the return type of the function, no unwrapping should take place. Else normal rules apply. template struct _InitFunctorTypeTraits { typedef typename _TaskTypeTraits<_FuncRetType>::_AsyncKind _AsyncKind; static const bool _IsAsyncTask = _TaskTypeTraits<_FuncRetType>::_IsAsyncTask; static const bool _IsUnwrappedTaskOrAsync = _TaskTypeTraits<_FuncRetType>::_IsUnwrappedTaskOrAsync; }; template struct _InitFunctorTypeTraits { typedef _TypeSelectorNoAsync _AsyncKind; static const bool _IsAsyncTask = false; static const bool _IsUnwrappedTaskOrAsync = false; }; /// /// Helper object used for LWT invocation. /// struct _TaskProcThunk { _TaskProcThunk(const std::function & _Callback) : _M_func(_Callback) { } static void _pplx_cdecl _Bridge(void *_PData) { _TaskProcThunk *_PThunk = reinterpret_cast<_TaskProcThunk *>(_PData); _Holder _ThunkHolder(_PThunk); _PThunk->_M_func(); } private: // RAII holder struct _Holder { _Holder(_TaskProcThunk * _PThunk) : _M_pThunk(_PThunk) { } ~_Holder() { delete _M_pThunk; } _TaskProcThunk * _M_pThunk; private: _Holder& operator=(const _Holder&); }; std::function _M_func; _TaskProcThunk& operator=(const _TaskProcThunk&); }; /// /// Schedule a functor with automatic inlining. Note that this is "fire and forget" scheduling, which cannot be /// waited on or canceled after scheduling. /// This schedule method will perform automatic inlining base on . /// /// /// The user functor need to be scheduled. /// /// /// The inlining scheduling policy for current functor. /// static void _ScheduleFuncWithAutoInline(const std::function & _Func, _TaskInliningMode_t _InliningMode) { _TaskCollection_t::_RunTask(&_TaskProcThunk::_Bridge, new _TaskProcThunk(_Func), _InliningMode); } class _ContextCallback { typedef std::function _CallbackFunction; #if defined (__cplusplus_winrt) public: static _ContextCallback _CaptureCurrent() { _ContextCallback _Context; _Context._Capture(); return _Context; } ~_ContextCallback() { _Reset(); } _ContextCallback(bool _DeferCapture = false) { if (_DeferCapture) { _M_context._M_captureMethod = _S_captureDeferred; } else { _M_context._M_pContextCallback = nullptr; } } // Resolves a context that was created as _S_captureDeferred based on the environment (ancestor, current context). void _Resolve(bool _CaptureCurrent) { if(_M_context._M_captureMethod == _S_captureDeferred) { _M_context._M_pContextCallback = nullptr; if (_CaptureCurrent) { if (_IsCurrentOriginSTA()) { _Capture(); } #if _UITHREADCTXT_SUPPORT else { // This method will fail if not called from the UI thread. HRESULT _Hr = CaptureUiThreadContext(&_M_context._M_pContextCallback); if (FAILED(_Hr)) { _M_context._M_pContextCallback = nullptr; } } #endif /* _UITHREADCTXT_SUPPORT */ } } } void _Capture() { HRESULT _Hr = CoGetObjectContext(IID_IContextCallback, reinterpret_cast(&_M_context._M_pContextCallback)); if (FAILED(_Hr)) { _M_context._M_pContextCallback = nullptr; } } _ContextCallback(const _ContextCallback& _Src) { _Assign(_Src._M_context._M_pContextCallback); } _ContextCallback(_ContextCallback&& _Src) { _M_context._M_pContextCallback = _Src._M_context._M_pContextCallback; _Src._M_context._M_pContextCallback = nullptr; } _ContextCallback& operator=(const _ContextCallback& _Src) { if (this != &_Src) { _Reset(); _Assign(_Src._M_context._M_pContextCallback); } return *this; } _ContextCallback& operator=(_ContextCallback&& _Src) { if (this != &_Src) { _M_context._M_pContextCallback = _Src._M_context._M_pContextCallback; _Src._M_context._M_pContextCallback = nullptr; } return *this; } bool _HasCapturedContext() const { _ASSERTE(_M_context._M_captureMethod != _S_captureDeferred); return (_M_context._M_pContextCallback != nullptr); } void _CallInContext(_CallbackFunction _Func) const { if (!_HasCapturedContext()) { _Func(); } else { ComCallData callData; ZeroMemory(&callData, sizeof(callData)); callData.pUserDefined = reinterpret_cast(&_Func); HRESULT _Hr = _M_context._M_pContextCallback->ContextCallback(&_Bridge, &callData, IID_ICallbackWithNoReentrancyToApplicationSTA, 5, nullptr); if (FAILED(_Hr)) { throw ::Platform::Exception::CreateException(_Hr); } } } bool operator==(const _ContextCallback& _Rhs) const { return (_M_context._M_pContextCallback == _Rhs._M_context._M_pContextCallback); } bool operator!=(const _ContextCallback& _Rhs) const { return !(operator==(_Rhs)); } private: void _Reset() { if (_M_context._M_captureMethod != _S_captureDeferred && _M_context._M_pContextCallback != nullptr) { _M_context._M_pContextCallback->Release(); } } void _Assign(IContextCallback *_PContextCallback) { _M_context._M_pContextCallback = _PContextCallback; if (_M_context._M_captureMethod != _S_captureDeferred && _M_context._M_pContextCallback != nullptr) { _M_context._M_pContextCallback->AddRef(); } } static HRESULT __stdcall _Bridge(ComCallData *_PParam) { _CallbackFunction *pFunc = reinterpret_cast<_CallbackFunction *>(_PParam->pUserDefined); (*pFunc)(); return S_OK; } // Returns the origin information for the caller (runtime / Windows Runtime apartment as far as task continuations need know) static bool _IsCurrentOriginSTA() { APTTYPE _AptType; APTTYPEQUALIFIER _AptTypeQualifier; HRESULT hr = CoGetApartmentType(&_AptType, &_AptTypeQualifier); if (SUCCEEDED(hr)) { // We determine the origin of a task continuation by looking at where .then is called, so we can tell whether // to need to marshal the continuation back to the originating apartment. If an STA thread is in executing in // a neutral aparment when it schedules a continuation, we will not marshal continuations back to the STA, // since variables used within a neutral apartment are expected to be apartment neutral. switch(_AptType) { case APTTYPE_MAINSTA: case APTTYPE_STA: return true; default: break; } } return false; } union { IContextCallback *_M_pContextCallback; size_t _M_captureMethod; } _M_context; static const size_t _S_captureDeferred = 1; #else /* defined (__cplusplus_winrt) */ public: static _ContextCallback _CaptureCurrent() { return _ContextCallback(); } _ContextCallback(bool = false) { } _ContextCallback(const _ContextCallback&) { } _ContextCallback(_ContextCallback&&) { } _ContextCallback& operator=(const _ContextCallback&) { return *this; } _ContextCallback& operator=(_ContextCallback&&) { return *this; } bool _HasCapturedContext() const { return false; } void _Resolve(bool) const { } void _CallInContext(_CallbackFunction _Func) const { _Func(); } bool operator==(const _ContextCallback&) const { return true; } bool operator!=(const _ContextCallback&) const { return false; } #endif /* defined (__cplusplus_winrt) */ }; template struct _ResultHolder { void Set(const _Type& _type) { _Result = _type; } _Type Get() { return _Result; } _Type _Result; }; #if defined (__cplusplus_winrt) template struct _ResultHolder<_Type^> { void Set(_Type^ const & _type) { _M_Result = _type; } _Type^ Get() { return _M_Result.Get(); } private: // ::Platform::Agile handle specialization of all hats // including ::Platform::String and ::Platform::Array ::Platform::Agile<_Type^> _M_Result; }; // // The below are for composability with tasks auto-created from when_any / when_all / && / || constructs. // template struct _ResultHolder> { void Set(const std::vector<_Type^>& _type) { _Result.reserve(_type.size()); for (auto _PTask = _type.begin(); _PTask != _type.end(); ++_PTask) { _Result.emplace_back(*_PTask); } } std::vector<_Type^> Get() { // Return vectory with the objects that are marshaled in the proper appartment std::vector<_Type^> _Return; _Return.reserve(_Result.size()); for (auto _PTask = _Result.begin(); _PTask != _Result.end(); ++_PTask) { _Return.push_back(_PTask->Get()); // Platform::Agile will marshal the object to appropriate appartment if neccessary } return _Return; } std::vector< ::Platform::Agile<_Type^> > _Result; }; template struct _ResultHolder > { void Set(const std::pair<_Type^, size_t>& _type) { _M_Result = _type; } std::pair<_Type^, size_t> Get() { return std::make_pair(_M_Result.first.Get(), _M_Result.second); } private: std::pair< ::Platform::Agile<_Type^>, size_t> _M_Result; }; #endif /* defined (__cplusplus_winrt) */ // An exception thrown by the task body is captured in an exception holder and it is shared with all value based continuations rooted at the task. // The exception is 'observed' if the user invokes get()/wait() on any of the tasks that are sharing this exception holder. If the exception // is not observed by the time the internal object owned by the shared pointer destructs, the process will fail fast. struct _ExceptionHolder { private: void ReportUnhandledError() { #if _MSC_VER >= 1800 && defined(__cplusplus_winrt) if (_M_winRTException != nullptr) { ::Platform::Details::ReportUnhandledError(_M_winRTException); } #endif /* defined (__cplusplus_winrt) */ } public: explicit _ExceptionHolder(const std::exception_ptr& _E, const _TaskCreationCallstack &_stackTrace) : _M_exceptionObserved(0), _M_stdException(_E), _M_stackTrace(_stackTrace) #if defined (__cplusplus_winrt) , _M_winRTException(nullptr) #endif /* defined (__cplusplus_winrt) */ { } #if defined (__cplusplus_winrt) explicit _ExceptionHolder(::Platform::Exception^ _E, const _TaskCreationCallstack &_stackTrace) : _M_exceptionObserved(0), _M_winRTException(_E), _M_stackTrace(_stackTrace) { } #endif /* defined (__cplusplus_winrt) */ __declspec(noinline) ~_ExceptionHolder() { if (_M_exceptionObserved == 0) { // If you are trapped here, it means an exception thrown in task chain didn't get handled. // Please add task-based continuation to handle all exceptions coming from tasks. // this->_M_stackTrace keeps the creation callstack of the task generates this exception. _REPORT_PPLTASK_UNOBSERVED_EXCEPTION(); } } void _RethrowUserException() { if (_M_exceptionObserved == 0) { atomic_exchange(_M_exceptionObserved, 1l); } #if defined (__cplusplus_winrt) if (_M_winRTException != nullptr) { throw _M_winRTException; } #endif /* defined (__cplusplus_winrt) */ std::rethrow_exception(_M_stdException); } // A variable that remembers if this exception was every rethrown into user code (and hence handled by the user). Exceptions that // are unobserved when the exception holder is destructed will terminate the process. atomic_long _M_exceptionObserved; // Either _M_stdException or _M_winRTException is populated based on the type of exception encountered. std::exception_ptr _M_stdException; #if defined (__cplusplus_winrt) ::Platform::Exception^ _M_winRTException; #endif /* defined (__cplusplus_winrt) */ // Disassembling this value will point to a source instruction right after a call instruction. If the call is to create_task, // a task constructor or the then method, the task created by that method is the one that encountered this exception. If the call // is to task_completion_event::set_exception, the set_exception method was the source of the exception. // DO NOT REMOVE THIS VARIABLE. It is extremely helpful for debugging. _TaskCreationCallstack _M_stackTrace; }; #if defined (__cplusplus_winrt) /// /// Base converter class for converting asynchronous interfaces to IAsyncOperation /// template ref struct _AsyncInfoImpl abstract : Windows::Foundation::IAsyncOperation<_Result> { internal: // The async action, action with progress or operation with progress that this stub forwards to. ::Platform::Agile<_AsyncOperationType> _M_asyncInfo; Windows::Foundation::AsyncOperationCompletedHandler<_Result>^ _M_CompletedHandler; _AsyncInfoImpl( _AsyncOperationType _AsyncInfo ) : _M_asyncInfo(_AsyncInfo) {} public: virtual void Cancel() { _M_asyncInfo.Get()->Cancel(); } virtual void Close() { _M_asyncInfo.Get()->Close(); } virtual property Windows::Foundation::HResult ErrorCode { Windows::Foundation::HResult get() { return _M_asyncInfo.Get()->ErrorCode; } } virtual property UINT Id { UINT get() { return _M_asyncInfo.Get()->Id; } } virtual property Windows::Foundation::AsyncStatus Status { Windows::Foundation::AsyncStatus get() { return _M_asyncInfo.Get()->Status; } } virtual _Result GetResults() { throw std::runtime_error("derived class must implement"); } virtual property Windows::Foundation::AsyncOperationCompletedHandler<_Result>^ Completed { Windows::Foundation::AsyncOperationCompletedHandler<_Result>^ get() { return _M_CompletedHandler; } void set(Windows::Foundation::AsyncOperationCompletedHandler<_Result>^ value) { _M_CompletedHandler = value; _M_asyncInfo.Get()->Completed = ref new _CompletionHandlerType([&](_AsyncOperationType, Windows::Foundation::AsyncStatus status) { _M_CompletedHandler->Invoke(this, status); }); } } }; /// /// Class _IAsyncOperationWithProgressToAsyncOperationConverter is used to convert an instance of IAsyncOperationWithProgress into IAsyncOperation /// template ref struct _IAsyncOperationWithProgressToAsyncOperationConverter sealed : _AsyncInfoImpl^, Windows::Foundation::AsyncOperationWithProgressCompletedHandler<_Result,_Progress>, _Result> { internal: _IAsyncOperationWithProgressToAsyncOperationConverter(Windows::Foundation::IAsyncOperationWithProgress<_Result,_Progress>^ _Operation) : _AsyncInfoImpl^, Windows::Foundation::AsyncOperationWithProgressCompletedHandler<_Result,_Progress>, _Result>(_Operation) {} public: virtual _Result GetResults() override { return _M_asyncInfo.Get()->GetResults(); } }; /// /// Class _IAsyncActionToAsyncOperationConverter is used to convert an instance of IAsyncAction into IAsyncOperation<_Unit_type> /// ref struct _IAsyncActionToAsyncOperationConverter sealed : _AsyncInfoImpl { internal: _IAsyncActionToAsyncOperationConverter(Windows::Foundation::IAsyncAction^ _Operation) : _AsyncInfoImpl(_Operation) {} public: virtual details::_Unit_type GetResults() override { // Invoke GetResults on the IAsyncAction to allow exceptions to be thrown to higher layers before returning a dummy value. _M_asyncInfo.Get()->GetResults(); return details::_Unit_type(); } }; /// /// Class _IAsyncActionWithProgressToAsyncOperationConverter is used to convert an instance of IAsyncActionWithProgress into IAsyncOperation<_Unit_type> /// template ref struct _IAsyncActionWithProgressToAsyncOperationConverter sealed : _AsyncInfoImpl^, Windows::Foundation::AsyncActionWithProgressCompletedHandler<_Progress>, details::_Unit_type> { internal: _IAsyncActionWithProgressToAsyncOperationConverter(Windows::Foundation::IAsyncActionWithProgress<_Progress>^ _Action) : _AsyncInfoImpl^, Windows::Foundation::AsyncActionWithProgressCompletedHandler<_Progress>, details::_Unit_type>(_Action) {} public: virtual details::_Unit_type GetResults() override { // Invoke GetResults on the IAsyncActionWithProgress to allow exceptions to be thrown before returning a dummy value. _M_asyncInfo.Get()->GetResults(); return details::_Unit_type(); } }; #endif /* defined (__cplusplus_winrt) */ } // namespace details /// /// The task_continuation_context class allows you to specify where you would like a continuation to be executed. /// It is only useful to use this class from a Windows Store app. For non-Windows Store apps, the task continuation's /// execution context is determined by the runtime, and not configurable. /// /// /**/ class task_continuation_context : public details::_ContextCallback { public: /// /// Creates the default task continuation context. /// /// /// The default continuation context. /// /// /// The default context is used if you don't specifiy a continuation context when you call the then method. In Windows /// applications for Windows 7 and below, as well as desktop applications on Windows 8 and higher, the runtime determines where /// task continuations will execute. However, in a Windows Store app, the default continuation context for a continuation on an /// apartment aware task is the apartment where then is invoked. /// An apartment aware task is a task that unwraps a Windows Runtime IAsyncInfo interface, or a task that is descended from such /// a task. Therefore, if you schedule a continuation on an apartment aware task in a Windows Runtime STA, the continuation will execute in /// that STA. /// A continuation on a non-apartment aware task will execute in a context the Runtime chooses. /// /**/ static task_continuation_context use_default() { #if defined (__cplusplus_winrt) // The callback context is created with the context set to CaptureDeferred and resolved when it is used in .then() return task_continuation_context(true); // sets it to deferred, is resolved in the constructor of _ContinuationTaskHandle #else /* defined (__cplusplus_winrt) */ return task_continuation_context(); #endif /* defined (__cplusplus_winrt) */ } #if defined (__cplusplus_winrt) /// /// Creates a task continuation context which allows the Runtime to choose the execution context for a continuation. /// /// /// A task continuation context that represents an arbitrary location. /// /// /// When this continuation context is used the continuation will execute in a context the runtime chooses even if the antecedent task /// is apartment aware. /// use_arbitrary can be used to turn off the default behavior for a continuation on an apartment /// aware task created in an STA. /// This method is only available to Windows Store apps. /// /**/ static task_continuation_context use_arbitrary() { task_continuation_context _Arbitrary(true); _Arbitrary._Resolve(false); return _Arbitrary; } /// /// Returns a task continuation context object that represents the current execution context. /// /// /// The current execution context. /// /// /// This method captures the caller's Windows Runtime context so that continuations can be executed in the right apartment. /// The value returned by use_current can be used to indicate to the Runtime that the continuation should execute in /// the captured context (STA vs MTA) regardless of whether or not the antecedent task is apartment aware. An apartment aware task is /// a task that unwraps a Windows Runtime IAsyncInfo interface, or a task that is descended from such a task. /// This method is only available to Windows Store apps. /// /**/ static task_continuation_context use_current() { task_continuation_context _Current(true); _Current._Resolve(true); return _Current; } #endif /* defined (__cplusplus_winrt) */ private: task_continuation_context(bool _DeferCapture = false) : details::_ContextCallback(_DeferCapture) { } }; class task_options; namespace details { struct _Internal_task_options { bool _M_hasPresetCreationCallstack; _TaskCreationCallstack _M_presetCreationCallstack; void _set_creation_callstack(const _TaskCreationCallstack &_callstack) { _M_hasPresetCreationCallstack = true; _M_presetCreationCallstack = _callstack; } _Internal_task_options() { _M_hasPresetCreationCallstack = false; } }; inline _Internal_task_options &_get_internal_task_options(task_options &options); inline const _Internal_task_options &_get_internal_task_options(const task_options &options); } /// /// Represents the allowed options for creating a task /// class task_options { public: /// /// Default list of task creation options /// task_options() : _M_Scheduler(get_ambient_scheduler()), _M_CancellationToken(cancellation_token::none()), _M_ContinuationContext(task_continuation_context::use_default()), _M_HasCancellationToken(false), _M_HasScheduler(false) { } /// /// Task option that specify a cancellation token /// task_options(cancellation_token _Token) : _M_Scheduler(get_ambient_scheduler()), _M_CancellationToken(_Token), _M_ContinuationContext(task_continuation_context::use_default()), _M_HasCancellationToken(true), _M_HasScheduler(false) { } /// /// Task option that specify a continuation context. This is valid only for continuations (then) /// task_options(task_continuation_context _ContinuationContext) : _M_Scheduler(get_ambient_scheduler()), _M_CancellationToken(cancellation_token::none()), _M_ContinuationContext(_ContinuationContext), _M_HasCancellationToken(false), _M_HasScheduler(false) { } /// /// Task option that specify a cancellation token and a continuation context. This is valid only for continuations (then) /// task_options(cancellation_token _Token, task_continuation_context _ContinuationContext) : _M_Scheduler(get_ambient_scheduler()), _M_CancellationToken(_Token), _M_ContinuationContext(_ContinuationContext), _M_HasCancellationToken(true), _M_HasScheduler(false) { } /// /// Task option that specify a scheduler with shared lifetime /// template task_options(std::shared_ptr<_SchedType> _Scheduler) : _M_Scheduler(std::move(_Scheduler)), _M_CancellationToken(cancellation_token::none()), _M_ContinuationContext(task_continuation_context::use_default()), _M_HasCancellationToken(false), _M_HasScheduler(true) { } /// /// Task option that specify a scheduler reference /// task_options(scheduler_interface& _Scheduler) : _M_Scheduler(&_Scheduler), _M_CancellationToken(cancellation_token::none()), _M_ContinuationContext(task_continuation_context::use_default()), _M_HasCancellationToken(false), _M_HasScheduler(true) { } /// /// Task option that specify a scheduler /// task_options(scheduler_ptr _Scheduler) : _M_Scheduler(std::move(_Scheduler)), _M_CancellationToken(cancellation_token::none()), _M_ContinuationContext(task_continuation_context::use_default()), _M_HasCancellationToken(false), _M_HasScheduler(true) { } /// /// Task option copy constructor /// task_options(const task_options& _TaskOptions) : _M_Scheduler(_TaskOptions.get_scheduler()), _M_CancellationToken(_TaskOptions.get_cancellation_token()), _M_ContinuationContext(_TaskOptions.get_continuation_context()), _M_HasCancellationToken(_TaskOptions.has_cancellation_token()), _M_HasScheduler(_TaskOptions.has_scheduler()) { } /// /// Sets the given token in the options /// void set_cancellation_token(cancellation_token _Token) { _M_CancellationToken = _Token; _M_HasCancellationToken = true; } /// /// Sets the given continuation context in the options /// void set_continuation_context(task_continuation_context _ContinuationContext) { _M_ContinuationContext = _ContinuationContext; } /// /// Indicates whether a cancellation token was specified by the user /// bool has_cancellation_token() const { return _M_HasCancellationToken; } /// /// Returns the cancellation token /// cancellation_token get_cancellation_token() const { return _M_CancellationToken; } /// /// Returns the continuation context /// task_continuation_context get_continuation_context() const { return _M_ContinuationContext; } /// /// Indicates whether a scheduler n was specified by the user /// bool has_scheduler() const { return _M_HasScheduler; } /// /// Returns the scheduler /// scheduler_ptr get_scheduler() const { return _M_Scheduler; } private: task_options const& operator=(task_options const& _Right); friend details::_Internal_task_options &details::_get_internal_task_options(task_options &); friend const details::_Internal_task_options &details::_get_internal_task_options(const task_options &); scheduler_ptr _M_Scheduler; cancellation_token _M_CancellationToken; task_continuation_context _M_ContinuationContext; details::_Internal_task_options _M_InternalTaskOptions; bool _M_HasCancellationToken; bool _M_HasScheduler; }; namespace details { inline _Internal_task_options & _get_internal_task_options(task_options &options) { return options._M_InternalTaskOptions; } inline const _Internal_task_options & _get_internal_task_options(const task_options &options) { return options._M_InternalTaskOptions; } struct _Task_impl_base; template struct _Task_impl; template struct _Task_ptr { typedef std::shared_ptr<_Task_impl<_ReturnType>> _Type; static _Type _Make(_CancellationTokenState * _Ct, scheduler_ptr _Scheduler_arg) { return std::make_shared<_Task_impl<_ReturnType>>(_Ct, _Scheduler_arg); } }; typedef _TaskCollection_t::_TaskProcHandle_t _UnrealizedChore_t; typedef std::shared_ptr<_Task_impl_base> _Task_ptr_base; // The weak-typed base task handler for continuation tasks. struct _ContinuationTaskHandleBase : _UnrealizedChore_t { _ContinuationTaskHandleBase * _M_next; task_continuation_context _M_continuationContext; bool _M_isTaskBasedContinuation; // This field gives inlining scheduling policy for current chore. _TaskInliningMode_t _M_inliningMode; virtual _Task_ptr_base _GetTaskImplBase() const = 0; _ContinuationTaskHandleBase() : _M_next(nullptr), _M_continuationContext(task_continuation_context::use_default()), _M_isTaskBasedContinuation(false), _M_inliningMode(details::_NoInline) { } virtual ~_ContinuationTaskHandleBase() {} }; #if _PPLTASK_ASYNC_LOGGING // GUID used for identifying causality logs from PPLTask const ::Platform::Guid _PPLTaskCausalityPlatformID(0x7A76B220, 0xA758, 0x4E6E, 0xB0, 0xE0, 0xD7, 0xC6, 0xD7, 0x4A, 0x88, 0xFE); __declspec(selectany) volatile long _isCausalitySupported = 0; inline bool _IsCausalitySupported() { #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) if (_isCausalitySupported == 0) { long _causality = 1; OSVERSIONINFOEX _osvi = {}; _osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); // The Causality is supported on Windows version higher than Windows 8 _osvi.dwMajorVersion = 6; _osvi.dwMinorVersion = 3; DWORDLONG _conditionMask = 0; VER_SET_CONDITION( _conditionMask, VER_MAJORVERSION, VER_GREATER_EQUAL ); VER_SET_CONDITION( _conditionMask, VER_MINORVERSION, VER_GREATER_EQUAL ); if ( ::VerifyVersionInfo(&_osvi, VER_MAJORVERSION | VER_MINORVERSION, _conditionMask)) { _causality = 2; } _isCausalitySupported = _causality; return _causality == 2; } return _isCausalitySupported == 2 ? true : false; #else return true; #endif } // Stateful logger rests inside task_impl_base. struct _TaskEventLogger { _Task_impl_base *_M_task; bool _M_scheduled; bool _M_taskPostEventStarted; // Log before scheduling task void _LogScheduleTask(bool _isContinuation) { if (details::_IsCausalitySupported()) { ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceOperationCreation(::Windows::Foundation::Diagnostics::CausalityTraceLevel::Required, ::Windows::Foundation::Diagnostics::CausalitySource::Library, _PPLTaskCausalityPlatformID, reinterpret_cast(_M_task), _isContinuation ? "pplx::PPLTask::ScheduleContinuationTask" : "pplx::PPLTask::ScheduleTask", 0); _M_scheduled = true; } } // It will log the cancel event but not canceled state. _LogTaskCompleted will log the terminal state, which includes cancel state. void _LogCancelTask() { if (details::_IsCausalitySupported()) { ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceOperationRelation(::Windows::Foundation::Diagnostics::CausalityTraceLevel::Important, ::Windows::Foundation::Diagnostics::CausalitySource::Library, _PPLTaskCausalityPlatformID, reinterpret_cast(_M_task), ::Windows::Foundation::Diagnostics::CausalityRelation::Cancel); } } // Log when task reaches terminal state. Note: the task can reach a terminal state (by cancellation or exception) without having run void _LogTaskCompleted(); // Log when task body (which includes user lambda and other scheduling code) begin to run void _LogTaskExecutionStarted() { } // Log when task body finish executing void _LogTaskExecutionCompleted() { if (_M_taskPostEventStarted && details::_IsCausalitySupported()) { ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceSynchronousWorkCompletion(::Windows::Foundation::Diagnostics::CausalityTraceLevel::Required, ::Windows::Foundation::Diagnostics::CausalitySource::Library, ::Windows::Foundation::Diagnostics::CausalitySynchronousWork::CompletionNotification); } } // Log right before user lambda being invoked void _LogWorkItemStarted() { if (details::_IsCausalitySupported()) { ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceSynchronousWorkStart(::Windows::Foundation::Diagnostics::CausalityTraceLevel::Required, ::Windows::Foundation::Diagnostics::CausalitySource::Library, _PPLTaskCausalityPlatformID, reinterpret_cast(_M_task), ::Windows::Foundation::Diagnostics::CausalitySynchronousWork::Execution); } } // Log right after user lambda being invoked void _LogWorkItemCompleted() { if (details::_IsCausalitySupported()) { ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceSynchronousWorkCompletion(::Windows::Foundation::Diagnostics::CausalityTraceLevel::Required, ::Windows::Foundation::Diagnostics::CausalitySource::Library, ::Windows::Foundation::Diagnostics::CausalitySynchronousWork::Execution); ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceSynchronousWorkStart(::Windows::Foundation::Diagnostics::CausalityTraceLevel::Required, ::Windows::Foundation::Diagnostics::CausalitySource::Library, _PPLTaskCausalityPlatformID, reinterpret_cast(_M_task), ::Windows::Foundation::Diagnostics::CausalitySynchronousWork::CompletionNotification); _M_taskPostEventStarted = true; } } _TaskEventLogger(_Task_impl_base *_task): _M_task(_task) { _M_scheduled = false; _M_taskPostEventStarted = false; } }; // Exception safe logger for user lambda struct _TaskWorkItemRAIILogger { _TaskEventLogger &_M_logger; _TaskWorkItemRAIILogger(_TaskEventLogger &_taskHandleLogger): _M_logger(_taskHandleLogger) { _M_logger._LogWorkItemStarted(); } ~_TaskWorkItemRAIILogger() { _M_logger._LogWorkItemCompleted(); } _TaskWorkItemRAIILogger &operator =(const _TaskWorkItemRAIILogger &); // cannot be assigned }; #else inline void _LogCancelTask(_Task_impl_base *) {} struct _TaskEventLogger { void _LogScheduleTask(bool) {} void _LogCancelTask() {} void _LogWorkItemStarted() {} void _LogWorkItemCompleted() {} void _LogTaskExecutionStarted() {} void _LogTaskExecutionCompleted() {} void _LogTaskCompleted() {} _TaskEventLogger(_Task_impl_base *) {} }; struct _TaskWorkItemRAIILogger { _TaskWorkItemRAIILogger(_TaskEventLogger &) {} }; #endif /// /// The _PPLTaskHandle is the strong-typed task handle base. All user task functions need to be wrapped in this task handler /// to be executable by PPL. By deriving from a different _BaseTaskHandle, it can be used for both initial tasks and continuation tasks. /// For initial tasks, _PPLTaskHandle will be derived from _UnrealizedChore_t, and for continuation tasks, it will be derived from /// _ContinuationTaskHandleBase. The life time of the _PPLTaskHandle object is be managed by runtime if task handle is scheduled. /// /// /// The result type of the _Task_impl. /// /// /// The derived task handle class. The operator () needs to be implemented. /// /// /// The base class from which _PPLTaskHandle should be derived. This is either _UnrealizedChore_t or _ContinuationTaskHandleBase. /// template struct _PPLTaskHandle : _BaseTaskHandle { _PPLTaskHandle(const typename _Task_ptr<_ReturnType>::_Type & _PTask) : _M_pTask(_PTask) { } virtual ~_PPLTaskHandle() { // Here is the sink of all task completion code paths _M_pTask->_M_taskEventLogger._LogTaskCompleted(); } virtual void invoke() const { // All exceptions should be rethrown to finish cleanup of the task collection. They will be caught and handled // by the runtime. _ASSERTE((bool)_M_pTask); if (!_M_pTask->_TransitionedToStarted()) { static_cast(this)->_SyncCancelAndPropagateException(); return; } _M_pTask->_M_taskEventLogger._LogTaskExecutionStarted(); try { // All derived task handle must implement this contract function. static_cast(this)->_Perform(); } catch(const task_canceled &) { _M_pTask->_Cancel(true); } catch(const _Interruption_exception &) { _M_pTask->_Cancel(true); } #if defined (__cplusplus_winrt) catch(::Platform::Exception^ _E) { _M_pTask->_CancelWithException(_E); } #endif /* defined (__cplusplus_winrt) */ catch(...) { _M_pTask->_CancelWithException(std::current_exception()); } _M_pTask->_M_taskEventLogger._LogTaskExecutionCompleted(); } // Cast _M_pTask pointer to "type-less" _Task_impl_base pointer, which can be used in _ContinuationTaskHandleBase. // The return value should be automatically optimized by R-value ref. _Task_ptr_base _GetTaskImplBase() const { return _M_pTask; } typename _Task_ptr<_ReturnType>::_Type _M_pTask; private: _PPLTaskHandle const & operator=(_PPLTaskHandle const&); // no assignment operator }; /// /// The base implementation of a first-class task. This class contains all the non-type specific /// implementation details of the task. /// /**/ struct _Task_impl_base { enum _TaskInternalState { // Tracks the state of the task, rather than the task collection on which the task is scheduled _Created, _Started, _PendingCancel, _Completed, _Canceled }; // _M_taskEventLogger - 'this' : used in base member initializer list #if defined(_MSC_VER) #pragma warning(push) #pragma warning(disable: 4355) #endif _Task_impl_base(_CancellationTokenState * _PTokenState, scheduler_ptr _Scheduler_arg) : _M_TaskState(_Created), _M_fFromAsync(false), _M_fUnwrappedTask(false), _M_pRegistration(nullptr), _M_Continuations(nullptr), _M_TaskCollection(_Scheduler_arg), _M_taskEventLogger(this) { // Set cancelation token _M_pTokenState = _PTokenState; _ASSERTE(_M_pTokenState != nullptr); if (_M_pTokenState != _CancellationTokenState::_None()) _M_pTokenState->_Reference(); } #if defined(_MSC_VER) #pragma warning(pop) #endif virtual ~_Task_impl_base() { _ASSERTE(_M_pTokenState != nullptr); if (_M_pTokenState != _CancellationTokenState::_None()) { _M_pTokenState->_Release(); } } task_status _Wait() { bool _DoWait = true; #if defined (__cplusplus_winrt) if (_IsNonBlockingThread()) { // In order to prevent Windows Runtime STA threads from blocking the UI, calling task.wait() task.get() is illegal // if task has not been completed. if (!_IsCompleted() && !_IsCanceled()) { throw invalid_operation("Illegal to wait on a task in a Windows Runtime STA"); } else { // Task Continuations are 'scheduled' *inside* the chore that is executing on the ancestors's task group. If a continuation // needs to be marshalled to a different apartment, instead of scheduling, we make a synchronous cross apartment COM // call to execute the continuation. If it then happens to do something which waits on the ancestor (say it calls .get(), which // task based continuations are wont to do), waiting on the task group results in on the chore that is making this // synchronous callback, which causes a deadlock. To avoid this, we test the state ancestor's event , and we will NOT wait on // if it has finished execution (which means now we are on the inline synchronous callback). _DoWait = false; } } #endif /* defined (__cplusplus_winrt) */ if (_DoWait) { // If this task was created from a Windows Runtime async operation, do not attempt to inline it. The // async operation will take place on a thread in the appropriate apartment Simply wait for the completed // event to be set. if (_M_fFromAsync) { _M_TaskCollection._Wait(); } else { // Wait on the task collection to complete. The task collection is guaranteed to still be // valid since the task must be still within scope so that the _Task_impl_base destructor // has not yet been called. This call to _Wait potentially inlines execution of work. try { // Invoking wait on a task collection resets the state of the task collection. This means that // if the task collection itself were canceled, or had encountered an exception, only the first // call to wait will receive this status. However, both cancellation and exceptions flowing through // tasks set state in the task impl itself. // When it returns cancelled, either work chore or the cancel thread should already have set task's state // properly -- cancelled state or completed state (because there was no interruption point). // For tasks with unwrapped tasks, we should not change the state of current task, since the unwrapped task are still running. _M_TaskCollection._RunAndWait(); } catch(details::_Interruption_exception&) { // The _TaskCollection will never be an interruption point since it has a none token. _ASSERTE(false); } catch(task_canceled&) { // task_canceled is a special exception thrown by cancel_current_task. The spec states that cancel_current_task // must be called from code that is executed within the task (throwing it from parallel work created by and waited // upon by the task is acceptable). We can safely assume that the task wrapper _PPLTaskHandle::operator() has seen // the exception and canceled the task. Swallow the exception here. _ASSERTE(_IsCanceled()); } #if defined (__cplusplus_winrt) catch(::Platform::Exception^ _E) { // Its possible the task body hasn't seen the exception, if so we need to cancel with exception here. if(!_HasUserException()) { _CancelWithException(_E); } // Rethrow will mark the exception as observed. _M_exceptionHolder->_RethrowUserException(); } #endif /* defined (__cplusplus_winrt) */ catch(...) { // Its possible the task body hasn't seen the exception, if so we need to cancel with exception here. if(!_HasUserException()) { _CancelWithException(std::current_exception()); } // Rethrow will mark the exception as observed. _M_exceptionHolder->_RethrowUserException(); } // If the lambda body for this task (executed or waited upon in _RunAndWait above) happened to return a task // which is to be unwrapped and plumbed to the output of this task, we must not only wait on the lambda body, we must // wait on the **INNER** body. It is in theory possible that we could inline such if we plumb a series of things through; // however, this takes the tact of simply waiting upon the completion signal. if (_M_fUnwrappedTask) { _M_TaskCollection._Wait(); } } } if (_HasUserException()) { _M_exceptionHolder->_RethrowUserException(); } else if (_IsCanceled()) { return canceled; } _ASSERTE(_IsCompleted()); return completed; } /// /// Requests cancellation on the task and schedules continuations if the task can be transitioned to a terminal state. /// /// /// Set to true if the cancel takes place as a result of the task body encountering an exception, or because an ancestor or task_completion_event the task /// was registered with were canceled with an exception. A synchronous cancel is one that assures the task could not be running on a different thread at /// the time the cancellation is in progress. An asynchronous cancel is one where the thread performing the cancel has no control over the thread that could /// be executing the task, that is the task could execute concurrently while the cancellation is in progress. /// /// /// Whether an exception other than the internal runtime cancellation exceptions caused this cancellation. /// /// /// Whether this exception came from an ancestor task or a task_completion_event as opposed to an exception that was encountered by the task itself. Only valid when /// _UserException is set to true. /// /// /// The exception holder that represents the exception. Only valid when _UserException is set to true. /// virtual bool _CancelAndRunContinuations(bool _SynchronousCancel, bool _UserException, bool _PropagatedFromAncestor, const std::shared_ptr<_ExceptionHolder>& _ExHolder) = 0; bool _Cancel(bool _SynchronousCancel) { // Send in a dummy value for exception. It is not used when the first parameter is false. return _CancelAndRunContinuations(_SynchronousCancel, false, false, _M_exceptionHolder); } bool _CancelWithExceptionHolder(const std::shared_ptr<_ExceptionHolder>& _ExHolder, bool _PropagatedFromAncestor) { // This task was canceled because an ancestor task encountered an exception. return _CancelAndRunContinuations(true, true, _PropagatedFromAncestor, _ExHolder); } #if defined (__cplusplus_winrt) bool _CancelWithException(::Platform::Exception^ _Exception) { // This task was canceled because the task body encountered an exception. _ASSERTE(!_HasUserException()); return _CancelAndRunContinuations(true, true, false, std::make_shared<_ExceptionHolder>(_Exception, _GetTaskCreationCallstack())); } #endif /* defined (__cplusplus_winrt) */ bool _CancelWithException(const std::exception_ptr& _Exception) { // This task was canceled because the task body encountered an exception. _ASSERTE(!_HasUserException()); return _CancelAndRunContinuations(true, true, false, std::make_shared<_ExceptionHolder>(_Exception, _GetTaskCreationCallstack())); } void _RegisterCancellation(std::weak_ptr<_Task_impl_base> _WeakPtr) { _ASSERTE(details::_CancellationTokenState::_IsValid(_M_pTokenState)); auto _CancellationCallback = [_WeakPtr](){ // Taking ownership of the task prevents dead lock during destruction // if the destructor waits for the cancellations to be finished auto _task = _WeakPtr.lock(); if (_task != nullptr) _task->_Cancel(false); }; _M_pRegistration = new details::_CancellationTokenCallback(_CancellationCallback); _M_pTokenState->_RegisterCallback(_M_pRegistration); } void _DeregisterCancellation() { if (_M_pRegistration != nullptr) { _M_pTokenState->_DeregisterCallback(_M_pRegistration); _M_pRegistration->_Release(); _M_pRegistration = nullptr; } } bool _IsCreated() { return (_M_TaskState == _Created); } bool _IsStarted() { return (_M_TaskState == _Started); } bool _IsPendingCancel() { return (_M_TaskState == _PendingCancel); } bool _IsCompleted() { return (_M_TaskState == _Completed); } bool _IsCanceled() { return (_M_TaskState == _Canceled); } bool _HasUserException() { return static_cast(_M_exceptionHolder); } const std::shared_ptr<_ExceptionHolder>& _GetExceptionHolder() { _ASSERTE(_HasUserException()); return _M_exceptionHolder; } bool _IsApartmentAware() { return _M_fFromAsync; } void _SetAsync(bool _Async = true) { _M_fFromAsync = _Async; } _TaskCreationCallstack _GetTaskCreationCallstack() { return _M_pTaskCreationCallstack; } void _SetTaskCreationCallstack(const _TaskCreationCallstack &_Callstack) { _M_pTaskCreationCallstack = _Callstack; } /// /// Helper function to schedule the task on the Task Collection. /// /// /// The task chore handle that need to be executed. /// /// /// The inlining scheduling policy for current _PTaskHandle. /// void _ScheduleTask(_UnrealizedChore_t * _PTaskHandle, _TaskInliningMode_t _InliningMode) { try { _M_TaskCollection._ScheduleTask(_PTaskHandle, _InliningMode); } catch(const task_canceled &) { // task_canceled is a special exception thrown by cancel_current_task. The spec states that cancel_current_task // must be called from code that is executed within the task (throwing it from parallel work created by and waited // upon by the task is acceptable). We can safely assume that the task wrapper _PPLTaskHandle::operator() has seen // the exception and canceled the task. Swallow the exception here. _ASSERTE(_IsCanceled()); } catch(const _Interruption_exception &) { // The _TaskCollection will never be an interruption point since it has a none token. _ASSERTE(false); } catch(...) { // The exception could have come from two places: // 1. From the chore body, so it already should have been caught and canceled. // In this case swallow the exception. // 2. From trying to actually schedule the task on the scheduler. // In this case cancel the task with the current exception, otherwise the // task will never be signaled leading to deadlock when waiting on the task. if (!_HasUserException()) { _CancelWithException(std::current_exception()); } } } /// /// Function executes a continuation. This function is recorded by a parent task implementation /// when a continuation is created in order to execute later. /// /// /// The continuation task chore handle that need to be executed. /// /**/ void _RunContinuation(_ContinuationTaskHandleBase * _PTaskHandle) { _Task_ptr_base _ImplBase = _PTaskHandle->_GetTaskImplBase(); if (_IsCanceled() && !_PTaskHandle->_M_isTaskBasedContinuation) { if (_HasUserException()) { // If the ancestor encountered an exception, transfer the exception to the continuation // This traverses down the tree to propagate the exception. _ImplBase->_CancelWithExceptionHolder(_GetExceptionHolder(), true); } else { // If the ancestor was canceled, then your own execution should be canceled. // This traverses down the tree to cancel it. _ImplBase->_Cancel(true); } } else { // This can only run when the ancestor has completed or it's a task based continuation that fires when a task is canceled // (with or without a user exception). _ASSERTE(_IsCompleted() || _PTaskHandle->_M_isTaskBasedContinuation); _ASSERTE(!_ImplBase->_IsCanceled()); return _ImplBase->_ScheduleContinuationTask(_PTaskHandle); } // If the handle is not scheduled, we need to manually delete it. delete _PTaskHandle; } // Schedule a continuation to run void _ScheduleContinuationTask(_ContinuationTaskHandleBase * _PTaskHandle) { _M_taskEventLogger._LogScheduleTask(true); // Ensure that the continuation runs in proper context (this might be on a Concurrency Runtime thread or in a different Windows Runtime apartment) if (_PTaskHandle->_M_continuationContext._HasCapturedContext()) { // For those continuations need to be scheduled inside captured context, we will try to apply automatic inlining to their inline modes, // if they haven't been specified as _ForceInline yet. This change will encourage those continuations to be executed inline so that reduce // the cost of marshaling. // For normal continuations we won't do any change here, and their inline policies are completely decided by ._ThenImpl method. if (_PTaskHandle->_M_inliningMode != details::_ForceInline) { _PTaskHandle->_M_inliningMode = details::_DefaultAutoInline; } _ScheduleFuncWithAutoInline([_PTaskHandle]() { // Note that we cannot directly capture "this" pointer, instead, we should use _TaskImplPtr, a shared_ptr to the _Task_impl_base. // Because "this" pointer will be invalid as soon as _PTaskHandle get deleted. _PTaskHandle will be deleted after being scheduled. auto _TaskImplPtr = _PTaskHandle->_GetTaskImplBase(); if (details::_ContextCallback::_CaptureCurrent() == _PTaskHandle->_M_continuationContext) { _TaskImplPtr->_ScheduleTask(_PTaskHandle, details::_ForceInline); } else { // // It's entirely possible that the attempt to marshal the call into a differing context will fail. In this case, we need to handle // the exception and mark the continuation as canceled with the appropriate exception. There is one slight hitch to this: // // NOTE: COM's legacy behavior is to swallow SEH exceptions and marshal them back as HRESULTS. This will in effect turn an SEH into // a C++ exception that gets tagged on the task. One unfortunate result of this is that various pieces of the task infrastructure will // not be in a valid state after this in /EHsc (due to the lack of destructors running, etc...). // try { // Dev10 compiler needs this! auto _PTaskHandle1 = _PTaskHandle; _PTaskHandle->_M_continuationContext._CallInContext( [_PTaskHandle1, _TaskImplPtr](){ _TaskImplPtr->_ScheduleTask(_PTaskHandle1, details::_ForceInline); }); } #if defined (__cplusplus_winrt) catch(::Platform::Exception^ _E) { _TaskImplPtr->_CancelWithException(_E); } #endif /* defined (__cplusplus_winrt) */ catch(...) { _TaskImplPtr->_CancelWithException(std::current_exception()); } } }, _PTaskHandle->_M_inliningMode); } else { _ScheduleTask(_PTaskHandle, _PTaskHandle->_M_inliningMode); } } /// /// Schedule the actual continuation. This will either schedule the function on the continuation task's implementation /// if the task has completed or append it to a list of functions to execute when the task actually does complete. /// /// /// The input type of the task. /// /// /// The output type of the task. /// /**/ void _ScheduleContinuation(_ContinuationTaskHandleBase * _PTaskHandle) { enum { _Nothing, _Schedule, _Cancel, _CancelWithException } _Do = _Nothing; // If the task has canceled, cancel the continuation. If the task has completed, execute the continuation right away. // Otherwise, add it to the list of pending continuations { ::pplx::extensibility::scoped_critical_section_t _LockHolder(_M_ContinuationsCritSec); if (_IsCompleted() || (_IsCanceled() && _PTaskHandle->_M_isTaskBasedContinuation)) { _Do = _Schedule; } else if (_IsCanceled()) { if (_HasUserException()) { _Do = _CancelWithException; } else { _Do = _Cancel; } } else { // chain itself on the continuation chain. _PTaskHandle->_M_next = _M_Continuations; _M_Continuations = _PTaskHandle; } } // Cancellation and execution of continuations should be performed after releasing the lock. Continuations off of // async tasks may execute inline. switch (_Do) { case _Schedule: { _PTaskHandle->_GetTaskImplBase()->_ScheduleContinuationTask(_PTaskHandle); break; } case _Cancel: { // If the ancestor was canceled, then your own execution should be canceled. // This traverses down the tree to cancel it. _PTaskHandle->_GetTaskImplBase()->_Cancel(true); delete _PTaskHandle; break; } case _CancelWithException: { // If the ancestor encountered an exception, transfer the exception to the continuation // This traverses down the tree to propagate the exception. _PTaskHandle->_GetTaskImplBase()->_CancelWithExceptionHolder(_GetExceptionHolder(), true); delete _PTaskHandle; break; } case _Nothing: default: // In this case, we have inserted continuation to continuation chain, // nothing more need to be done, just leave. break; } } void _RunTaskContinuations() { // The link list can no longer be modified at this point, // since all following up continuations will be scheduled by themselves. _ContinuationList _Cur = _M_Continuations, _Next; _M_Continuations = nullptr; while (_Cur) { // Current node might be deleted after running, // so we must fetch the next first. _Next = _Cur->_M_next; _RunContinuation(_Cur); _Cur = _Next; } } #if defined (__cplusplus_winrt) static bool _IsNonBlockingThread() { APTTYPE _AptType; APTTYPEQUALIFIER _AptTypeQualifier; HRESULT hr = CoGetApartmentType(&_AptType, &_AptTypeQualifier); // // If it failed, it's not a Windows Runtime/COM initialized thread. This is not a failure. // if (SUCCEEDED(hr)) { switch(_AptType) { case APTTYPE_STA: case APTTYPE_MAINSTA: return true; break; case APTTYPE_NA: switch(_AptTypeQualifier) { // A thread executing in a neutral apartment is either STA or MTA. To find out if this thread is allowed // to wait, we check the app qualifier. If it is an STA thread executing in a neutral apartment, waiting // is illegal, because the thread is responsible for pumping messages and waiting on a task could take the // thread out of circulation for a while. case APTTYPEQUALIFIER_NA_ON_STA: case APTTYPEQUALIFIER_NA_ON_MAINSTA: return true; break; } break; } } #if _UITHREADCTXT_SUPPORT // This method is used to throw an exepection in _Wait() if called within STA. We // want the same behavior if _Wait is called on the UI thread. if (SUCCEEDED(CaptureUiThreadContext(nullptr))) { return true; } #endif /* _UITHREADCTXT_SUPPORT */ return false; } template static void _AsyncInit(const typename _Task_ptr<_ReturnType>::_Type & _OuterTask, Windows::Foundation::IAsyncOperation::_Value>^ _AsyncOp) { // This method is invoked either when a task is created from an existing async operation or // when a lambda that creates an async operation executes. // If the outer task is pending cancel, cancel the async operation before setting the completed handler. The COM reference on // the IAsyncInfo object will be released when all ^references to the operation go out of scope. // This assertion uses the existence of taskcollection to determine if the task was created from an event. // That is no longer valid as even tasks created from a user lambda could have no underlying taskcollection // when a custom scheduler is used. // _ASSERTE((!_OuterTask->_M_TaskCollection._IsCreated() || _OuterTask->_M_fUnwrappedTask) && !_OuterTask->_IsCanceled()); // Pass the shared_ptr by value into the lambda instead of using 'this'. _AsyncOp->Completed = ref new Windows::Foundation::AsyncOperationCompletedHandler<_ReturnType>( [_OuterTask](Windows::Foundation::IAsyncOperation::_Value>^ _Operation, Windows::Foundation::AsyncStatus _Status) mutable { if (_Status == Windows::Foundation::AsyncStatus::Canceled) { _OuterTask->_Cancel(true); } else if (_Status == Windows::Foundation::AsyncStatus::Error) { _OuterTask->_CancelWithException(::Platform::Exception::ReCreateException(static_cast(_Operation->ErrorCode.Value))); } else { _ASSERTE(_Status == Windows::Foundation::AsyncStatus::Completed); _OuterTask->_FinalizeAndRunContinuations(_Operation->GetResults()); } // Take away this shared pointers reference on the task instead of waiting for the delegate to be released. It could // be released on a different thread after a delay, and not releasing the reference here could cause the tasks to hold // on to resources longer than they should. As an example, without this reset, writing to a file followed by reading from // it using the Windows Runtime Async APIs causes a sharing violation. // Using const_cast is the workaround for failed mutable keywords const_cast<_Task_ptr<_ReturnType>::_Type &>(_OuterTask).reset(); }); _OuterTask->_SetUnwrappedAsyncOp(_AsyncOp); } #endif /* defined (__cplusplus_winrt) */ template static void _AsyncInit(const typename _Task_ptr<_ReturnType>::_Type& _OuterTask, const task<_InternalReturnType> & _UnwrappedTask) { _ASSERTE(_OuterTask->_M_fUnwrappedTask && !_OuterTask->_IsCanceled()); // // We must ensure that continuations off _OuterTask (especially exception handling ones) continue to function in the // presence of an exception flowing out of the inner task _UnwrappedTask. This requires an exception handling continuation // off the inner task which does the appropriate funnelling to the outer one. We use _Then instead of then to prevent // the exception from being marked as observed by our internal continuation. This continuation must be scheduled regardless // of whether or not the _OuterTask task is canceled. // _UnwrappedTask._Then([_OuterTask] (task<_InternalReturnType> _AncestorTask) { if (_AncestorTask._GetImpl()->_IsCompleted()) { _OuterTask->_FinalizeAndRunContinuations(_AncestorTask._GetImpl()->_GetResult()); } else { _ASSERTE(_AncestorTask._GetImpl()->_IsCanceled()); if (_AncestorTask._GetImpl()->_HasUserException()) { // Set _PropagatedFromAncestor to false, since _AncestorTask is not an ancestor of _UnwrappedTask. // Instead, it is the enclosing task. _OuterTask->_CancelWithExceptionHolder(_AncestorTask._GetImpl()->_GetExceptionHolder(), false); } else { _OuterTask->_Cancel(true); } } }, nullptr, details::_DefaultAutoInline); } scheduler_ptr _GetScheduler() const { return _M_TaskCollection._GetScheduler(); } // Tracks the internal state of the task volatile _TaskInternalState _M_TaskState; // Set to true either if the ancestor task had the flag set to true, or if the lambda that does the work of this task returns an // async operation or async action that is unwrapped by the runtime. bool _M_fFromAsync; // Set to true when a continuation unwraps a task or async operation. bool _M_fUnwrappedTask; // An exception thrown by the task body is captured in an exception holder and it is shared with all value based continuations rooted at the task. // The exception is 'observed' if the user invokes get()/wait() on any of the tasks that are sharing this exception holder. If the exception // is not observed by the time the internal object owned by the shared pointer destructs, the process will fail fast. std::shared_ptr<_ExceptionHolder> _M_exceptionHolder; ::pplx::extensibility::critical_section_t _M_ContinuationsCritSec; // The cancellation token state. _CancellationTokenState * _M_pTokenState; // The registration on the token. _CancellationTokenRegistration * _M_pRegistration; typedef _ContinuationTaskHandleBase * _ContinuationList; _ContinuationList _M_Continuations; // The async task collection wrapper ::pplx::details::_TaskCollection_t _M_TaskCollection; // Callstack for function call (constructor or .then) that created this task impl. _TaskCreationCallstack _M_pTaskCreationCallstack; _TaskEventLogger _M_taskEventLogger; private: // Must not be copied by value: _Task_impl_base(const _Task_impl_base&); _Task_impl_base const & operator=(_Task_impl_base const&); }; #if _PPLTASK_ASYNC_LOGGING inline void _TaskEventLogger::_LogTaskCompleted() { if (_M_scheduled) { ::Windows::Foundation::AsyncStatus _State; if (_M_task->_IsCompleted()) _State = ::Windows::Foundation::AsyncStatus::Completed; else if (_M_task->_HasUserException()) _State = ::Windows::Foundation::AsyncStatus::Error; else _State = ::Windows::Foundation::AsyncStatus::Canceled; if (details::_IsCausalitySupported()) { ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceOperationCompletion(::Windows::Foundation::Diagnostics::CausalityTraceLevel::Required, ::Windows::Foundation::Diagnostics::CausalitySource::Library, _PPLTaskCausalityPlatformID, reinterpret_cast(_M_task), _State); } } } #endif /// /// The implementation of a first-class task. This structure contains the task group used to execute /// the task function and handles the scheduling. The _Task_impl is created as a shared_ptr /// member of the the public task class, so its destruction is handled automatically. /// /// /// The result type of this task. /// /**/ template struct _Task_impl : public _Task_impl_base { #if defined (__cplusplus_winrt) typedef Windows::Foundation::IAsyncOperation::_Value> _AsyncOperationType; #endif // defined(__cplusplus_winrt) _Task_impl(_CancellationTokenState * _Ct, scheduler_ptr _Scheduler_arg) : _Task_impl_base(_Ct, _Scheduler_arg) { #if defined (__cplusplus_winrt) _M_unwrapped_async_op = nullptr; #endif /* defined (__cplusplus_winrt) */ } virtual ~_Task_impl() { // We must invoke _DeregisterCancellation in the derived class destructor. Calling it in the base class destructor could cause // a partially initialized _Task_impl to be in the list of registrations for a cancellation token. _DeregisterCancellation(); } virtual bool _CancelAndRunContinuations(bool _SynchronousCancel, bool _UserException, bool _PropagatedFromAncestor, const std::shared_ptr<_ExceptionHolder> & _ExceptionHolder_arg) { bool _RunContinuations = false; { ::pplx::extensibility::scoped_critical_section_t _LockHolder(_M_ContinuationsCritSec); if (_UserException) { _ASSERTE(_SynchronousCancel && !_IsCompleted()); // If the state is _Canceled, the exception has to be coming from an ancestor. _ASSERTE(!_IsCanceled() || _PropagatedFromAncestor); // We should not be canceled with an exception more than once. _ASSERTE(!_HasUserException()); // Mark _PropagatedFromAncestor as used. (void)_PropagatedFromAncestor; if (_M_TaskState == _Canceled) { // If the task has finished cancelling there should not be any continuation records in the array. return false; } else { _ASSERTE(_M_TaskState != _Completed); _M_exceptionHolder = _ExceptionHolder_arg; } } else { // Completed is a non-cancellable state, and if this is an asynchronous cancel, we're unable to do better than the last async cancel // which is to say, cancellation is already initiated, so return early. if (_IsCompleted() || _IsCanceled() || (_IsPendingCancel() && !_SynchronousCancel)) { _ASSERTE(!_IsCompleted() || !_HasUserException()); return false; } _ASSERTE(!_SynchronousCancel || !_HasUserException()); } if (_SynchronousCancel) { // Be aware that this set must be done BEFORE _M_Scheduled being set, or race will happen between this and wait() _M_TaskState = _Canceled; // Cancellation completes the task, so all dependent tasks must be run to cancel them // They are canceled when they begin running (see _RunContinuation) and see that their // ancestor has been canceled. _RunContinuations = true; } else { _ASSERTE(!_UserException); if (_IsStarted()) { #if defined (__cplusplus_winrt) if (_M_unwrapped_async_op != nullptr) { // We will only try to cancel async operation but not unwrapped tasks, since unwrapped tasks cannot be canceled without its token. _M_unwrapped_async_op->Cancel(); } #endif /* defined (__cplusplus_winrt) */ _M_TaskCollection._Cancel(); } // The _M_TaskState variable transitions to _Canceled when cancellation is completed (the task is not executing user code anymore). // In the case of a synchronous cancel, this can happen immediately, whereas with an asynchronous cancel, the task has to move from // _Started to _PendingCancel before it can move to _Canceled when it is finished executing. _M_TaskState = _PendingCancel; _M_taskEventLogger._LogCancelTask(); } } // Only execute continuations and mark the task as completed if we were able to move the task to the _Canceled state. if (_RunContinuations) { _M_TaskCollection._Complete(); if (_M_Continuations) { // Scheduling cancellation with automatic inlining. _ScheduleFuncWithAutoInline([=](){ _RunTaskContinuations(); }, details::_DefaultAutoInline); } } return true; } void _FinalizeAndRunContinuations(_ReturnType _Result) { _M_Result.Set(_Result); { // // Hold this lock to ensure continuations being concurrently either get added // to the _M_Continuations vector or wait for the result // ::pplx::extensibility::scoped_critical_section_t _LockHolder(_M_ContinuationsCritSec); // A task could still be in the _Created state if it was created with a task_completion_event. // It could also be in the _Canceled state for the same reason. _ASSERTE(!_HasUserException() && !_IsCompleted()); if (_IsCanceled()) { return; } // Always transition to "completed" state, even in the face of unacknowledged pending cancellation _M_TaskState = _Completed; } _M_TaskCollection._Complete(); _RunTaskContinuations(); } // // This method is invoked when the starts executing. The task returns early if this method returns true. // bool _TransitionedToStarted() { ::pplx::extensibility::scoped_critical_section_t _LockHolder(_M_ContinuationsCritSec); // Canceled state could only result from antecedent task's canceled state, but that code path will not reach here. _ASSERTE(!_IsCanceled()); if (_IsPendingCancel()) return false; _ASSERTE(_IsCreated()); _M_TaskState = _Started; return true; } #if defined (__cplusplus_winrt) void _SetUnwrappedAsyncOp(_AsyncOperationType^ _AsyncOp) { ::pplx::extensibility::scoped_critical_section_t _LockHolder(_M_ContinuationsCritSec); // Cancel the async operation if the task itself is canceled, since the thread that canceled the task missed it. if (_IsPendingCancel()) { _ASSERTE(!_IsCanceled()); _AsyncOp->Cancel(); } else { _M_unwrapped_async_op = _AsyncOp; } } #endif /* defined (__cplusplus_winrt) */ // Return true if the task has reached a terminal state bool _IsDone() { return _IsCompleted() || _IsCanceled(); } _ReturnType _GetResult() { return _M_Result.Get(); } _ResultHolder<_ReturnType> _M_Result; // this means that the result type must have a public default ctor. #if defined (__cplusplus_winrt) _AsyncOperationType^ _M_unwrapped_async_op; #endif /* defined (__cplusplus_winrt) */ }; template struct _Task_completion_event_impl { private: _Task_completion_event_impl(const _Task_completion_event_impl&); _Task_completion_event_impl& operator=(const _Task_completion_event_impl&); public: typedef std::vector::_Type> _TaskList; _Task_completion_event_impl() : _M_fHasValue(false), _M_fIsCanceled(false) { } bool _HasUserException() { return _M_exceptionHolder != nullptr; } ~_Task_completion_event_impl() { for( auto _TaskIt = _M_tasks.begin(); _TaskIt != _M_tasks.end(); ++_TaskIt ) { _ASSERTE(!_M_fHasValue && !_M_fIsCanceled); // Cancel the tasks since the event was never signaled or canceled. (*_TaskIt)->_Cancel(true); } } // We need to protect the loop over the array, so concurrent_vector would not have helped _TaskList _M_tasks; ::pplx::extensibility::critical_section_t _M_taskListCritSec; _ResultHolder<_ResultType> _M_value; std::shared_ptr<_ExceptionHolder> _M_exceptionHolder; bool _M_fHasValue; bool _M_fIsCanceled; }; // Utility method for dealing with void functions inline std::function<_Unit_type(void)> _MakeVoidToUnitFunc(const std::function& _Func) { return [=]() -> _Unit_type { _Func(); return _Unit_type(); }; } template std::function<_Type(_Unit_type)> _MakeUnitToTFunc(const std::function<_Type(void)>& _Func) { return [=](_Unit_type) -> _Type { return _Func(); }; } template std::function<_Unit_type(_Type)> _MakeTToUnitFunc(const std::function& _Func) { return [=](_Type t) -> _Unit_type { _Func(t); return _Unit_type(); }; } inline std::function<_Unit_type(_Unit_type)> _MakeUnitToUnitFunc(const std::function& _Func) { return [=](_Unit_type) -> _Unit_type { _Func(); return _Unit_type(); }; } } // namespace details /// /// The task_completion_event class allows you to delay the execution of a task until a condition is satisfied, /// or start a task in response to an external event. /// /// /// The result type of this task_completion_event class. /// /// /// Use a task created from a task completion event when your scenario requires you to create a task that will complete, and /// thereby have its continuations scheduled for execution, at some point in the future. The task_completion_event must /// have the same type as the task you create, and calling the set method on the task completion event with a value of that type /// will cause the associated task to complete, and provide that value as a result to its continuations. /// If the task completion event is never signaled, any tasks created from it will be canceled when it is destructed. /// task_completion_event behaves like a smart pointer, and should be passed by value. /// /// /**/ template class task_completion_event { public: /// /// Constructs a task_completion_event object. /// /**/ task_completion_event() : _M_Impl(std::make_shared>()) { } /// /// Sets the task completion event. /// /// /// The result to set this event with. /// /// /// The method returns true if it was successful in setting the event. It returns false if the event is already set. /// /// /// In the presence of multiple or concurrent calls to set, only the first call will succeed and its result (if any) will be stored in the /// task completion event. The remaining sets are ignored and the method will return false. When you set a task completion event, all the /// tasks created from that event will immediately complete, and its continuations, if any, will be scheduled. Task completion objects that have /// a other than void will pass the value to their continuations. /// /**/ bool set(_ResultType _Result) const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas { // Subsequent sets are ignored. This makes races to set benign: the first setter wins and all others are ignored. if (_IsTriggered()) { return false; } _TaskList _Tasks; bool _RunContinuations = false; { ::pplx::extensibility::scoped_critical_section_t _LockHolder(_M_Impl->_M_taskListCritSec); if (!_IsTriggered()) { _M_Impl->_M_value.Set(_Result); _M_Impl->_M_fHasValue = true; _Tasks.swap(_M_Impl->_M_tasks); _RunContinuations = true; } } if (_RunContinuations) { for( auto _TaskIt = _Tasks.begin(); _TaskIt != _Tasks.end(); ++_TaskIt ) { // If current task was cancelled by a cancellation_token, it would be in cancel pending state. if ((*_TaskIt)->_IsPendingCancel()) (*_TaskIt)->_Cancel(true); else { // Tasks created with task_completion_events can be marked as async, (we do this in when_any and when_all // if one of the tasks involved is an async task). Since continuations of async tasks can execute inline, we // need to run continuations after the lock is released. (*_TaskIt)->_FinalizeAndRunContinuations(_M_Impl->_M_value.Get()); } } if (_M_Impl->_HasUserException()) { _M_Impl->_M_exceptionHolder.reset(); } return true; } return false; } template __declspec(noinline) // Ask for no inlining so that the _ReturnAddress intrinsic gives us the expected result bool set_exception(_E _Except) const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas { // It is important that _CAPTURE_CALLSTACK() evaluate to the instruction after the call instruction for set_exception. return _Cancel(std::make_exception_ptr(_Except), _CAPTURE_CALLSTACK()); } /// /// Propagates an exception to all tasks associated with this event. /// /// /// The exception_ptr that indicates the exception to set this event with. /// /**/ __declspec(noinline) // Ask for no inlining so that the _CAPTURE_CALLSTACK gives us the expected result bool set_exception(std::exception_ptr _ExceptionPtr) const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas { // It is important that _CAPTURE_CALLSTACK() evaluate to the instruction after the call instruction for set_exception. return _Cancel(_ExceptionPtr, _CAPTURE_CALLSTACK()); } /// /// Internal method to cancel the task_completion_event. Any task created using this event will be marked as canceled if it has /// not already been set. /// bool _Cancel() const { // Cancel with the stored exception if one exists. return _CancelInternal(); } /// /// Internal method to cancel the task_completion_event with the exception provided. Any task created using this event will be canceled /// with the same exception. /// template bool _Cancel(_ExHolderType _ExHolder, const details::_TaskCreationCallstack &_SetExceptionAddressHint = details::_TaskCreationCallstack ()) const { bool _Canceled; if(_StoreException(_ExHolder, _SetExceptionAddressHint)) { _Canceled = _CancelInternal(); _ASSERTE(_Canceled); } else { _Canceled = false; } return _Canceled; } /// /// Internal method that stores an exception in the task completion event. This is used internally by when_any. /// Note, this does not cancel the task completion event. A task completion event with a stored exception /// can bet set() successfully. If it is canceled, it will cancel with the stored exception, if one is present. /// template bool _StoreException(_ExHolderType _ExHolder, const details::_TaskCreationCallstack &_SetExceptionAddressHint = details::_TaskCreationCallstack ()) const { ::pplx::extensibility::scoped_critical_section_t _LockHolder(_M_Impl->_M_taskListCritSec); if (!_IsTriggered() && !_M_Impl->_HasUserException()) { // Create the exception holder only if we have ensured there we will be successful in setting it onto the // task completion event. Failing to do so will result in an unobserved task exception. _M_Impl->_M_exceptionHolder = _ToExceptionHolder(_ExHolder, _SetExceptionAddressHint); return true; } return false; } /// /// Tests whether current event has been either Set, or Canceled. /// bool _IsTriggered() const { return _M_Impl->_M_fHasValue || _M_Impl->_M_fIsCanceled; } private: static std::shared_ptr _ToExceptionHolder(const std::shared_ptr& _ExHolder, const details::_TaskCreationCallstack&) { return _ExHolder; } static std::shared_ptr _ToExceptionHolder(std::exception_ptr _ExceptionPtr, const details::_TaskCreationCallstack &_SetExceptionAddressHint) { return std::make_shared(_ExceptionPtr, _SetExceptionAddressHint); } template friend class task; // task can register itself with the event by calling the private _RegisterTask template friend class task_completion_event; typedef typename details::_Task_completion_event_impl<_ResultType>::_TaskList _TaskList; /// /// Cancels the task_completion_event. /// bool _CancelInternal() const { // Cancellation of task completion events is an internal only utility. Our usage is such that _CancelInternal // will never be invoked if the task completion event has been set. _ASSERTE(!_M_Impl->_M_fHasValue); if (_M_Impl->_M_fIsCanceled) { return false; } _TaskList _Tasks; bool _Cancel = false; { ::pplx::extensibility::scoped_critical_section_t _LockHolder(_M_Impl->_M_taskListCritSec); _ASSERTE(!_M_Impl->_M_fHasValue); if (!_M_Impl->_M_fIsCanceled) { _M_Impl->_M_fIsCanceled = true; _Tasks.swap(_M_Impl->_M_tasks); _Cancel = true; } } bool _UserException = _M_Impl->_HasUserException(); if (_Cancel) { for( auto _TaskIt = _Tasks.begin(); _TaskIt != _Tasks.end(); ++_TaskIt ) { // Need to call this after the lock is released. See comments in set(). if (_UserException) { (*_TaskIt)->_CancelWithExceptionHolder(_M_Impl->_M_exceptionHolder, true); } else { (*_TaskIt)->_Cancel(true); } } } return _Cancel; } /// /// Register a task with this event. This function is called when a task is constructed using /// a task_completion_event. /// void _RegisterTask(const typename details::_Task_ptr<_ResultType>::_Type & _TaskParam) { ::pplx::extensibility::scoped_critical_section_t _LockHolder(_M_Impl->_M_taskListCritSec); //If an exception was already set on this event, then cancel the task with the stored exception. if(_M_Impl->_HasUserException()) { _TaskParam->_CancelWithExceptionHolder(_M_Impl->_M_exceptionHolder, true); } else if (_M_Impl->_M_fHasValue) { _TaskParam->_FinalizeAndRunContinuations(_M_Impl->_M_value.Get()); } else { _M_Impl->_M_tasks.push_back(_TaskParam); } } std::shared_ptr> _M_Impl; }; /// /// The task_completion_event class allows you to delay the execution of a task until a condition is satisfied, /// or start a task in response to an external event. /// /// /// Use a task created from a task completion event when your scenario requires you to create a task that will complete, and /// thereby have its continuations scheduled for execution, at some point in the future. The task_completion_event must /// have the same type as the task you create, and calling the set method on the task completion event with a value of that type /// will cause the associated task to complete, and provide that value as a result to its continuations. /// If the task completion event is never signaled, any tasks created from it will be canceled when it is destructed. /// task_completion_event behaves like a smart pointer, and should be passed by value. /// /// /**/ template<> class task_completion_event { public: /// /// Sets the task completion event. /// /// /// The method returns true if it was successful in setting the event. It returns false if the event is already set. /// /// /// In the presence of multiple or concurrent calls to set, only the first call will succeed and its result (if any) will be stored in the /// task completion event. The remaining sets are ignored and the method will return false. When you set a task completion event, all the /// tasks created from that event will immediately complete, and its continuations, if any, will be scheduled. Task completion objects that have /// a other than void will pass the value to their continuations. /// /**/ bool set() const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas { return _M_unitEvent.set(details::_Unit_type()); } template __declspec(noinline) // Ask for no inlining so that the _ReturnAddress intrinsic gives us the expected result bool set_exception(_E _Except) const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas { return _M_unitEvent._Cancel(std::make_exception_ptr(_Except), _CAPTURE_CALLSTACK()); } /// /// Propagates an exception to all tasks associated with this event. /// /// /// The exception_ptr that indicates the exception to set this event with. /// /**/ __declspec(noinline) // Ask for no inlining so that the _CAPTURE_CALLSTACK intrinsic gives us the expected result bool set_exception(std::exception_ptr _ExceptionPtr) const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas { // It is important that _CAPTURE_CALLSTACK() evaluate to the instruction after the call instruction for set_exception. return _M_unitEvent._Cancel(_ExceptionPtr, _CAPTURE_CALLSTACK()); } /// /// Cancel the task_completion_event. Any task created using this event will be marked as canceled if it has /// not already been set. /// void _Cancel() const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas { _M_unitEvent._Cancel(); } /// /// Cancel the task_completion_event with the exception holder provided. Any task created using this event will be canceled /// with the same exception. /// void _Cancel(const std::shared_ptr& _ExHolder) const { _M_unitEvent._Cancel(_ExHolder); } /// /// Method that stores an exception in the task completion event. This is used internally by when_any. /// Note, this does not cancel the task completion event. A task completion event with a stored exception /// can bet set() successfully. If it is canceled, it will cancel with the stored exception, if one is present. /// bool _StoreException(const std::shared_ptr& _ExHolder) const { return _M_unitEvent._StoreException(_ExHolder); } /// /// Test whether current event has been either Set, or Canceled. /// bool _IsTriggered() const { return _M_unitEvent._IsTriggered(); } private: template friend class task; // task can register itself with the event by calling the private _RegisterTask /// /// Register a task with this event. This function is called when a task is constructed using /// a task_completion_event. /// void _RegisterTask(details::_Task_ptr::_Type _TaskParam) { _M_unitEvent._RegisterTask(_TaskParam); } // The void event contains an event a dummy type so common code can be used for events with void and non-void results. task_completion_event _M_unitEvent; }; namespace details { // // Compile-time validation helpers // // Task constructor validation: issue helpful diagnostics for common user errors. Do not attempt full validation here. // // Anything callable is fine template auto _IsValidTaskCtor(_Ty _Param, int,int,int,int) -> decltype(_Param(), std::true_type()); #if defined (__cplusplus_winrt) // Anything that has GetResults is fine: this covers all async operations template auto _IsValidTaskCtor(_Ty _Param, int, int, int,...) -> decltype(_Param->GetResults(), std::true_type()); #endif // Allow parameters with set: this covers task_completion_event template auto _IsValidTaskCtor(_Ty _Param, int, int, ...) -> decltype(_Param.set(stdx::declval<_ReturnType>()), std::true_type()); template auto _IsValidTaskCtor(_Ty _Param, int, ...) -> decltype(_Param.set(), std::true_type()); // All else is invalid template std::false_type _IsValidTaskCtor(_Ty _Param, ...); template void _ValidateTaskConstructorArgs(_Ty _Param) { static_assert(std::is_same(_Param,0,0,0,0)),std::true_type>::value, #if defined (__cplusplus_winrt) "incorrect argument for task constructor; can be a callable object, an asynchronous operation, or a task_completion_event" #else /* defined (__cplusplus_winrt) */ "incorrect argument for task constructor; can be a callable object or a task_completion_event" #endif /* defined (__cplusplus_winrt) */ ); #if defined (__cplusplus_winrt) static_assert(!(std::is_same<_Ty,_ReturnType>::value && details::_IsIAsyncInfo<_Ty>::_Value), "incorrect template argument for task; consider using the return type of the async operation"); #endif /* defined (__cplusplus_winrt) */ } #if defined (__cplusplus_winrt) // Helpers for create_async validation // // A parameter lambda taking no arguments is valid template static auto _IsValidCreateAsync(_Ty _Param, int, int, int, int) -> decltype(_Param(), std::true_type()); // A parameter lambda taking an cancellation_token argument is valid template static auto _IsValidCreateAsync(_Ty _Param, int, int, int, ...) -> decltype(_Param(cancellation_token::none()), std::true_type()); // A parameter lambda taking a progress report argument is valid template static auto _IsValidCreateAsync(_Ty _Param, int, int, ...) -> decltype(_Param(details::_ProgressReporterCtorArgType()), std::true_type()); // A parameter lambda taking a progress report and a cancellation_token argument is valid template static auto _IsValidCreateAsync(_Ty _Param, int, ...) -> decltype(_Param(details::_ProgressReporterCtorArgType(), cancellation_token::none()), std::true_type()); // All else is invalid template static std::false_type _IsValidCreateAsync(_Ty _Param, ...); #endif /* defined (__cplusplus_winrt) */ } /// /// A helper class template that transforms a continuation lambda that either takes or returns void, or both, into a lambda that takes and returns a /// non-void type (details::_Unit_type is used to substitute for void). This is to minimize the special handling required for 'void'. /// template class _Continuation_func_transformer { public: static auto _Perform(std::function<_OutType(_InpType)> _Func) -> decltype(_Func) { return _Func; } }; template class _Continuation_func_transformer { public: static auto _Perform(std::function<_OutType(void)> _Func) -> decltype(details::_MakeUnitToTFunc<_OutType>(_Func)) { return details::_MakeUnitToTFunc<_OutType>(_Func); } }; template class _Continuation_func_transformer<_InType, void> { public: static auto _Perform(std::function _Func) -> decltype(details::_MakeTToUnitFunc<_InType>(_Func)) { return details::_MakeTToUnitFunc<_InType>(_Func); } }; template<> class _Continuation_func_transformer { public: static auto _Perform(std::function _Func) -> decltype(details::_MakeUnitToUnitFunc(_Func)) { return details::_MakeUnitToUnitFunc(_Func); } }; // A helper class template that transforms an intial task lambda returns void into a lambda that returns a non-void type (details::_Unit_type is used // to substitute for void). This is to minimize the special handling required for 'void'. template class _Init_func_transformer { public: static auto _Perform(std::function<_RetType(void)> _Func) -> decltype(_Func) { return _Func; } }; template<> class _Init_func_transformer { public: static auto _Perform(std::function _Func) -> decltype(details::_MakeVoidToUnitFunc(_Func)) { return details::_MakeVoidToUnitFunc(_Func); } }; /// /// The Parallel Patterns Library (PPL) task class. A task object represents work that can be executed asynchronously, /// and concurrently with other tasks and parallel work produced by parallel algorithms in the Concurrency Runtime. It produces /// a result of type on successful completion. Tasks of type task<void> produce no result. /// A task can be waited upon and canceled independently of other tasks. It can also be composed with other tasks using /// continuations(then), and join(when_all) and choice(when_any) patterns. /// /// /// The result type of this task. /// /// /// For more information, see . /// /**/ template class task { public: /// /// The type of the result an object of this class produces. /// /**/ typedef _ReturnType result_type; /// /// Constructs a task object. /// /// /// The default constructor for a task is only present in order to allow tasks to be used within containers. /// A default constructed task cannot be used until you assign a valid task to it. Methods such as get, wait or then /// will throw an invalid_argument exception when called on a default constructed task. /// A task that is created from a task_completion_event will complete (and have its continuations scheduled) when the task /// completion event is set. /// The version of the constructor that takes a cancellation token creates a task that can be canceled using the /// cancellation_token_source the token was obtained from. Tasks created without a cancellation token are not cancelable. /// Tasks created from a Windows::Foundation::IAsyncInfo interface or a lambda that returns an IAsyncInfo interface /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created /// from a lamda that returns a task<result_type> reach their terminal state when the inner task reaches its terminal state, /// and not when the lamda returns. /// task behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads /// without the need for locks. /// The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available /// to Windows Store apps. /// For more information, see . /// /**/ task() : _M_Impl(nullptr) { // The default constructor should create a task with a nullptr impl. This is a signal that the // task is not usable and should throw if any wait(), get() or then() APIs are used. } /// /// Constructs a task object. /// /// /// The type of the parameter from which the task is to be constructed. /// /// /// The parameter from which the task is to be constructed. This could be a lambda, a function object, a task_completion_event<result_type> /// object, or a Windows::Foundation::IAsyncInfo if you are using tasks in your Windows Store app. The lambda or function /// object should be a type equivalent to std::function<X(void)>, where X can be a variable of type result_type, /// task<result_type>, or a Windows::Foundation::IAsyncInfo in Windows Store apps. /// /// /// The cancellation token to associate with this task. A task created without a cancellation token cannot be canceled. It implicitly receives /// the token cancellation_token::none(). /// /// /// The default constructor for a task is only present in order to allow tasks to be used within containers. /// A default constructed task cannot be used until you assign a valid task to it. Methods such as get, wait or then /// will throw an invalid_argument exception when called on a default constructed task. /// A task that is created from a task_completion_event will complete (and have its continuations scheduled) when the task /// completion event is set. /// The version of the constructor that takes a cancellation token creates a task that can be canceled using the /// cancellation_token_source the token was obtained from. Tasks created without a cancellation token are not cancelable. /// Tasks created from a Windows::Foundation::IAsyncInfo interface or a lambda that returns an IAsyncInfo interface /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created /// from a lamda that returns a task<result_type> reach their terminal state when the inner task reaches its terminal state, /// and not when the lamda returns. /// task behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads /// without the need for locks. /// The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available /// to Windows Store apps. /// For more information, see . /// /**/ template __declspec(noinline) // Ask for no inlining so that the _CAPTURE_CALLSTACK gives us the expected result explicit task(_Ty _Param) { task_options _TaskOptions; details::_ValidateTaskConstructorArgs<_ReturnType,_Ty>(_Param); _CreateImpl(_TaskOptions.get_cancellation_token()._GetImplValue(), _TaskOptions.get_scheduler()); // Do not move the next line out of this function. It is important that _CAPTURE_CALLSTACK() evaluate to the the call site of the task constructor. _SetTaskCreationCallstack(_CAPTURE_CALLSTACK()); _TaskInitMaybeFunctor(_Param, details::_IsCallable(_Param,0)); } /// /// Constructs a task object. /// /// /// The type of the parameter from which the task is to be constructed. /// /// /// The parameter from which the task is to be constructed. This could be a lambda, a function object, a task_completion_event<result_type> /// object, or a Windows::Foundation::IAsyncInfo if you are using tasks in your Windows Store app. The lambda or function /// object should be a type equivalent to std::function<X(void)>, where X can be a variable of type result_type, /// task<result_type>, or a Windows::Foundation::IAsyncInfo in Windows Store apps. /// /// /// The task options include cancellation token, scheduler etc /// /// /// The default constructor for a task is only present in order to allow tasks to be used within containers. /// A default constructed task cannot be used until you assign a valid task to it. Methods such as get, wait or then /// will throw an invalid_argument exception when called on a default constructed task. /// A task that is created from a task_completion_event will complete (and have its continuations scheduled) when the task /// completion event is set. /// The version of the constructor that takes a cancellation token creates a task that can be canceled using the /// cancellation_token_source the token was obtained from. Tasks created without a cancellation token are not cancelable. /// Tasks created from a Windows::Foundation::IAsyncInfo interface or a lambda that returns an IAsyncInfo interface /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created /// from a lamda that returns a task<result_type> reach their terminal state when the inner task reaches its terminal state, /// and not when the lamda returns. /// task behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads /// without the need for locks. /// The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available /// to Windows Store apps. /// For more information, see . /// /**/ template __declspec(noinline) // Ask for no inlining so that the _CAPTURE_CALLSTACK gives us the expected result explicit task(_Ty _Param, const task_options &_TaskOptions) { details::_ValidateTaskConstructorArgs<_ReturnType,_Ty>(_Param); _CreateImpl(_TaskOptions.get_cancellation_token()._GetImplValue(), _TaskOptions.get_scheduler()); // Do not move the next line out of this function. It is important that _CAPTURE_CALLSTACK() evaluate to the the call site of the task constructor. _SetTaskCreationCallstack(details::_get_internal_task_options(_TaskOptions)._M_hasPresetCreationCallstack ? details::_get_internal_task_options(_TaskOptions)._M_presetCreationCallstack : _CAPTURE_CALLSTACK()); _TaskInitMaybeFunctor(_Param, details::_IsCallable(_Param,0)); } /// /// Constructs a task object. /// /// /// The source task object. /// /// /// The default constructor for a task is only present in order to allow tasks to be used within containers. /// A default constructed task cannot be used until you assign a valid task to it. Methods such as get, wait or then /// will throw an invalid_argument exception when called on a default constructed task. /// A task that is created from a task_completion_event will complete (and have its continuations scheduled) when the task /// completion event is set. /// The version of the constructor that takes a cancellation token creates a task that can be canceled using the /// cancellation_token_source the token was obtained from. Tasks created without a cancellation token are not cancelable. /// Tasks created from a Windows::Foundation::IAsyncInfo interface or a lambda that returns an IAsyncInfo interface /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created /// from a lamda that returns a task<result_type> reach their terminal state when the inner task reaches its terminal state, /// and not when the lamda returns. /// task behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads /// without the need for locks. /// The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available /// to Windows Store apps. /// For more information, see . /// /**/ task(const task& _Other): _M_Impl(_Other._M_Impl) {} /// /// Constructs a task object. /// /// /// The source task object. /// /// /// The default constructor for a task is only present in order to allow tasks to be used within containers. /// A default constructed task cannot be used until you assign a valid task to it. Methods such as get, wait or then /// will throw an invalid_argument exception when called on a default constructed task. /// A task that is created from a task_completion_event will complete (and have its continuations scheduled) when the task /// completion event is set. /// The version of the constructor that takes a cancellation token creates a task that can be canceled using the /// cancellation_token_source the token was obtained from. Tasks created without a cancellation token are not cancelable. /// Tasks created from a Windows::Foundation::IAsyncInfo interface or a lambda that returns an IAsyncInfo interface /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created /// from a lamda that returns a task<result_type> reach their terminal state when the inner task reaches its terminal state, /// and not when the lamda returns. /// task behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads /// without the need for locks. /// The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available /// to Windows Store apps. /// For more information, see . /// /**/ task(task&& _Other): _M_Impl(std::move(_Other._M_Impl)) {} /// /// Replaces the contents of one task object with another. /// /// /// The source task object. /// /// /// As task behaves like a smart pointer, after a copy assignment, this task objects represents the same /// actual task as does. /// /**/ task& operator=(const task& _Other) { if (this != &_Other) { _M_Impl = _Other._M_Impl; } return *this; } /// /// Replaces the contents of one task object with another. /// /// /// The source task object. /// /// /// As task behaves like a smart pointer, after a copy assignment, this task objects represents the same /// actual task as does. /// /**/ task& operator=(task&& _Other) { if (this != &_Other) { _M_Impl = std::move(_Other._M_Impl); } return *this; } /// /// Adds a continuation task to this task. /// /// /// The type of the function object that will be invoked by this task. /// /// /// The continuation function to execute when this task completes. This continuation function must take as input /// a variable of either result_type or task<result_type>, where result_type is the type /// of the result this task produces. /// /// /// The newly created continuation task. The result type of the returned task is determined by what returns. /// /// /// The overloads of then that take a lambda or functor that returns a Windows::Foundation::IAsyncInfo interface, are only available /// to Windows Store apps. /// For more information on how to use task continuations to compose asynchronous work, see . /// /**/ template __declspec(noinline) // Ask for no inlining so that the _CAPTURE_CALLSTACK gives us the expected result auto then(const _Function& _Func) const -> typename details::_ContinuationTypeTraits<_Function, _ReturnType>::_TaskOfType { task_options _TaskOptions; details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(_CAPTURE_CALLSTACK()); return _ThenImpl<_ReturnType, _Function>(_Func, _TaskOptions); } /// /// Adds a continuation task to this task. /// /// /// The type of the function object that will be invoked by this task. /// /// /// The continuation function to execute when this task completes. This continuation function must take as input /// a variable of either result_type or task<result_type>, where result_type is the type /// of the result this task produces. /// /// /// The task options include cancellation token, scheduler and continuation context. By default the former 3 /// options are inherited from the antecedent task /// /// /// The newly created continuation task. The result type of the returned task is determined by what returns. /// /// /// The overloads of then that take a lambda or functor that returns a Windows::Foundation::IAsyncInfo interface, are only available /// to Windows Store apps. /// For more information on how to use task continuations to compose asynchronous work, see . /// /**/ template __declspec(noinline) // Ask for no inlining so that the _CAPTURE_CALLSTACK gives us the expected result auto then(const _Function& _Func, task_options _TaskOptions) const -> typename details::_ContinuationTypeTraits<_Function, _ReturnType>::_TaskOfType { details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(_CAPTURE_CALLSTACK()); return _ThenImpl<_ReturnType, _Function>(_Func, _TaskOptions); } /// /// Adds a continuation task to this task. /// /// /// The type of the function object that will be invoked by this task. /// /// /// The continuation function to execute when this task completes. This continuation function must take as input /// a variable of either result_type or task<result_type>, where result_type is the type /// of the result this task produces. /// /// /// The cancellation token to associate with the continuation task. A continuation task that is created without a cancellation token will inherit /// the token of its antecedent task. /// /// /// A variable that specifies where the continuation should execute. This variable is only useful when used in a Windows Store /// style app. For more information, see task_continuation_context /// /// /// The newly created continuation task. The result type of the returned task is determined by what returns. /// /// /// The overloads of then that take a lambda or functor that returns a Windows::Foundation::IAsyncInfo interface, are only available /// to Windows Store apps. /// For more information on how to use task continuations to compose asynchronous work, see . /// /**/ template __declspec(noinline) // Ask for no inlining so that the _CAPTURE_CALLSTACK gives us the expected result auto then(const _Function& _Func, cancellation_token _CancellationToken, task_continuation_context _ContinuationContext) const -> typename details::_ContinuationTypeTraits<_Function, _ReturnType>::_TaskOfType { task_options _TaskOptions(_CancellationToken, _ContinuationContext); details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(_CAPTURE_CALLSTACK()); return _ThenImpl<_ReturnType, _Function>(_Func, _TaskOptions); } /// /// Waits for this task to reach a terminal state. It is possible for wait to execute the task inline, if all of the tasks /// dependencies are satisfied, and it has not already been picked up for execution by a background worker. /// /// /// A task_status value which could be either completed or canceled. If the task encountered an exception /// during execution, or an exception was propagated to it from an antecedent task, wait will throw that exception. /// /**/ task_status wait() const { if (!_M_Impl) { throw invalid_operation("wait() cannot be called on a default constructed task."); } return _M_Impl->_Wait(); } /// /// Returns the result this task produced. If the task is not in a terminal state, a call to get will wait for the task to /// finish. This method does not return a value when called on a task with a result_type of void. /// /// /// The result of the task. /// /// /// If the task is canceled, a call to get will throw a task_canceled exception. If the task /// encountered an different exception or an exception was propagated to it from an antecedent task, a call to get will throw that exception. /// /**/ _ReturnType get() const { if (!_M_Impl) { throw invalid_operation("get() cannot be called on a default constructed task."); } if (_M_Impl->_Wait() == canceled) { throw task_canceled(); } return _M_Impl->_GetResult(); } /// /// Determines if the task is completed. /// /// /// True if the task has completed, false otherwise. /// /// /// The function returns true if the task is completed or canceled (with or without user exception). /// bool is_done() const { if (!_M_Impl) { throw invalid_operation("is_done() cannot be called on a default constructed task."); } return _M_Impl->_IsDone(); } /// /// Returns the scheduler for this task /// /// /// A pointer to the scheduler /// scheduler_ptr scheduler() const { if (!_M_Impl) { throw invalid_operation("scheduler() cannot be called on a default constructed task."); } return _M_Impl->_GetScheduler(); } /// /// Determines whether the task unwraps a Windows Runtime IAsyncInfo interface or is descended from such a task. /// /// /// true if the task unwraps an IAsyncInfo interface or is descended from such a task, false otherwise. /// /**/ bool is_apartment_aware() const { if (!_M_Impl) { throw invalid_operation("is_apartment_aware() cannot be called on a default constructed task."); } return _M_Impl->_IsApartmentAware(); } /// /// Determines whether two task objects represent the same internal task. /// /// /// true if the objects refer to the same underlying task, and false otherwise. /// /**/ bool operator==(const task<_ReturnType>& _Rhs) const { return (_M_Impl == _Rhs._M_Impl); } /// /// Determines whether two task objects represent different internal tasks. /// /// /// true if the objects refer to different underlying tasks, and false otherwise. /// /**/ bool operator!=(const task<_ReturnType>& _Rhs) const { return !operator==(_Rhs); } /// /// Create an underlying task implementation. /// void _CreateImpl(details::_CancellationTokenState * _Ct, scheduler_ptr _Scheduler) { _ASSERTE(_Ct != nullptr); _M_Impl = details::_Task_ptr<_ReturnType>::_Make(_Ct, _Scheduler); if (_Ct != details::_CancellationTokenState::_None()) { _M_Impl->_RegisterCancellation(_M_Impl); } } /// /// Return the underlying implementation for this task. /// const typename details::_Task_ptr<_ReturnType>::_Type & _GetImpl() const { return _M_Impl; } /// /// Set the implementation of the task to be the supplied implementaion. /// void _SetImpl(const typename details::_Task_ptr<_ReturnType>::_Type & _Impl) { _ASSERTE(!_M_Impl); _M_Impl = _Impl; } /// /// Set the implementation of the task to be the supplied implementaion using a move instead of a copy. /// void _SetImpl(typename details::_Task_ptr<_ReturnType>::_Type && _Impl) { _ASSERTE(!_M_Impl); _M_Impl = std::move(_Impl); } /// /// Sets a property determining whether the task is apartment aware. /// void _SetAsync(bool _Async = true) { _GetImpl()->_SetAsync(_Async); } /// /// Sets a field in the task impl to the return callstack for calls to the task constructors and the then method. /// void _SetTaskCreationCallstack(const details::_TaskCreationCallstack &_callstack) { _GetImpl()->_SetTaskCreationCallstack(_callstack); } /// /// An internal version of then that takes additional flags and always execute the continuation inline by default. /// When _ForceInline is set to false, continuations inlining will be limited to default _DefaultAutoInline. /// This function is Used for runtime internal continuations only. /// template auto _Then(const _Function& _Func, details::_CancellationTokenState *_PTokenState, details::_TaskInliningMode_t _InliningMode = details::_ForceInline) const -> typename details::_ContinuationTypeTraits<_Function, _ReturnType>::_TaskOfType { // inherit from antecedent auto _Scheduler = _GetImpl()->_GetScheduler(); return _ThenImpl<_ReturnType, _Function>(_Func, _PTokenState, task_continuation_context::use_default(), _Scheduler, _CAPTURE_CALLSTACK(), _InliningMode); } private: template friend class task; // The task handle type used to construct an 'initial task' - a task with no dependents. template struct _InitialTaskHandle : details::_PPLTaskHandle<_ReturnType, _InitialTaskHandle<_InternalReturnType, _Function, _TypeSelection>, details::_UnrealizedChore_t> { _Function _M_function; _InitialTaskHandle(const typename details::_Task_ptr<_ReturnType>::_Type & _TaskImpl, const _Function & _func) : details::_PPLTaskHandle<_ReturnType, _InitialTaskHandle<_InternalReturnType, _Function, _TypeSelection>, details::_UnrealizedChore_t>::_PPLTaskHandle(_TaskImpl) , _M_function(_func) { } virtual ~_InitialTaskHandle() {} template auto _LogWorkItemAndInvokeUserLambda(_Func && _func) const -> decltype(_func()) { details::_TaskWorkItemRAIILogger _LogWorkItem(this->_M_pTask->_M_taskEventLogger); CASABLANCA_UNREFERENCED_PARAMETER(_LogWorkItem); return _func(); } void _Perform() const { _Init(_TypeSelection()); } void _SyncCancelAndPropagateException() const { this->_M_pTask->_Cancel(true); } // // Overload 0: returns _InternalReturnType // // This is the most basic task with no unwrapping // void _Init(details::_TypeSelectorNoAsync) const { this->_M_pTask->_FinalizeAndRunContinuations(_LogWorkItemAndInvokeUserLambda(_Init_func_transformer<_InternalReturnType>::_Perform(_M_function))); } // // Overload 1: returns IAsyncOperation<_InternalReturnType>^ (only uder /ZW) // or // returns task<_InternalReturnType> // // This is task whose functor returns an async operation or a task which will be unwrapped for continuation // Depending on the output type, the right _AsyncInit gets invoked // void _Init(details::_TypeSelectorAsyncOperationOrTask) const { details::_Task_impl_base::_AsyncInit<_ReturnType, _InternalReturnType>(this->_M_pTask, _LogWorkItemAndInvokeUserLambda(_M_function)); } #if defined (__cplusplus_winrt) // // Overload 2: returns IAsyncAction^ // // This is task whose functor returns an async action which will be unwrapped for continuation // void _Init(details::_TypeSelectorAsyncAction) const { details::_Task_impl_base::_AsyncInit<_ReturnType, _InternalReturnType>(this->_M_pTask, ref new details::_IAsyncActionToAsyncOperationConverter(_LogWorkItemAndInvokeUserLambda(_M_function))); } // // Overload 3: returns IAsyncOperationWithProgress<_InternalReturnType, _ProgressType>^ // // This is task whose functor returns an async operation with progress which will be unwrapped for continuation // void _Init(details::_TypeSelectorAsyncOperationWithProgress) const { typedef details::_GetProgressType::_Value _ProgressType; details::_Task_impl_base::_AsyncInit<_ReturnType, _InternalReturnType>(this->_M_pTask, ref new details::_IAsyncOperationWithProgressToAsyncOperationConverter<_InternalReturnType,_ProgressType>(_LogWorkItemAndInvokeUserLambda(_M_function))); } // // Overload 4: returns IAsyncActionWithProgress<_ProgressType>^ // // This is task whose functor returns an async action with progress which will be unwrapped for continuation // void _Init(details::_TypeSelectorAsyncActionWithProgress) const { typedef details::_GetProgressType::_Value _ProgressType; details::_Task_impl_base::_AsyncInit<_ReturnType, _InternalReturnType>(this->_M_pTask, ref new details::_IAsyncActionWithProgressToAsyncOperationConverter<_ProgressType>(_LogWorkItemAndInvokeUserLambda(_M_function))); } #endif /* defined (__cplusplus_winrt) */ }; /// /// The task handle type used to create a 'continuation task'. /// template struct _ContinuationTaskHandle : details::_PPLTaskHandle::_Type, _ContinuationTaskHandle<_InternalReturnType, _ContinuationReturnType, _Function, _IsTaskBased, _TypeSelection>, details::_ContinuationTaskHandleBase> { typedef typename details::_NormalizeVoidToUnitType<_ContinuationReturnType>::_Type _NormalizedContinuationReturnType; typename details::_Task_ptr<_ReturnType>::_Type _M_ancestorTaskImpl; _Function _M_function; _ContinuationTaskHandle(const typename details::_Task_ptr<_ReturnType>::_Type & _AncestorImpl, const typename details::_Task_ptr<_NormalizedContinuationReturnType>::_Type & _ContinuationImpl, const _Function & _Func, const task_continuation_context & _Context, details::_TaskInliningMode_t _InliningMode) : details::_PPLTaskHandle::_Type, _ContinuationTaskHandle<_InternalReturnType, _ContinuationReturnType, _Function, _IsTaskBased, _TypeSelection>, details::_ContinuationTaskHandleBase> ::_PPLTaskHandle(_ContinuationImpl) , _M_ancestorTaskImpl(_AncestorImpl) , _M_function(_Func) { this->_M_isTaskBasedContinuation = _IsTaskBased::value; this->_M_continuationContext = _Context; this->_M_continuationContext._Resolve(_AncestorImpl->_IsApartmentAware()); this->_M_inliningMode = _InliningMode; } virtual ~_ContinuationTaskHandle() {} template auto _LogWorkItemAndInvokeUserLambda(_Func && _func, _Arg && _value) const -> decltype(_func(std::forward<_Arg>(_value))) { details::_TaskWorkItemRAIILogger _LogWorkItem(this->_M_pTask->_M_taskEventLogger); CASABLANCA_UNREFERENCED_PARAMETER(_LogWorkItem); return _func(std::forward<_Arg>(_value)); } void _Perform() const { _Continue(_IsTaskBased(), _TypeSelection()); } void _SyncCancelAndPropagateException() const { if (_M_ancestorTaskImpl->_HasUserException()) { // If the ancestor encountered an exception, transfer the exception to the continuation // This traverses down the tree to propagate the exception. this->_M_pTask->_CancelWithExceptionHolder(_M_ancestorTaskImpl->_GetExceptionHolder(), true); } else { // If the ancestor was canceled, then your own execution should be canceled. // This traverses down the tree to cancel it. this->_M_pTask->_Cancel(true); } } // // Overload 0-0: _InternalReturnType -> _TaskType // // This is a straight task continuation which simply invokes its target with the ancestor's completion argument // void _Continue(std::false_type, details::_TypeSelectorNoAsync) const { this->_M_pTask->_FinalizeAndRunContinuations( _LogWorkItemAndInvokeUserLambda(_Continuation_func_transformer<_InternalReturnType, _ContinuationReturnType>::_Perform(_M_function), _M_ancestorTaskImpl->_GetResult())); } // // Overload 0-1: _InternalReturnType -> IAsyncOperation<_TaskType>^ (only uder /ZW) // or // _InternalReturnType -> task<_TaskType> // // This is a straight task continuation which returns an async operation or a task which will be unwrapped for continuation // Depending on the output type, the right _AsyncInit gets invoked // void _Continue(std::false_type, details::_TypeSelectorAsyncOperationOrTask) const { typedef typename details::_FunctionTypeTraits<_Function, _InternalReturnType>::_FuncRetType _FuncOutputType; details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>( this->_M_pTask, _LogWorkItemAndInvokeUserLambda(_Continuation_func_transformer<_InternalReturnType, _FuncOutputType>::_Perform(_M_function), _M_ancestorTaskImpl->_GetResult()) ); } #if defined (__cplusplus_winrt) // // Overload 0-2: _InternalReturnType -> IAsyncAction^ // // This is a straight task continuation which returns an async action which will be unwrapped for continuation // void _Continue(std::false_type, details::_TypeSelectorAsyncAction) const { typedef details::_FunctionTypeTraits<_Function, _InternalReturnType>::_FuncRetType _FuncOutputType; details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>( this->_M_pTask, ref new details::_IAsyncActionToAsyncOperationConverter( _LogWorkItemAndInvokeUserLambda(_Continuation_func_transformer<_InternalReturnType, _FuncOutputType>::_Perform(_M_function), _M_ancestorTaskImpl->_GetResult()))); } // // Overload 0-3: _InternalReturnType -> IAsyncOperationWithProgress<_TaskType, _ProgressType>^ // // This is a straight task continuation which returns an async operation with progress which will be unwrapped for continuation // void _Continue(std::false_type, details::_TypeSelectorAsyncOperationWithProgress) const { typedef details::_FunctionTypeTraits<_Function, _InternalReturnType>::_FuncRetType _FuncOutputType; auto _OpWithProgress = _LogWorkItemAndInvokeUserLambda(_Continuation_func_transformer<_InternalReturnType, _FuncOutputType>::_Perform(_M_function), _M_ancestorTaskImpl->_GetResult()); typedef details::_GetProgressType::_Value _ProgressType; details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>( this->_M_pTask, ref new details::_IAsyncOperationWithProgressToAsyncOperationConverter<_ContinuationReturnType, _ProgressType>(_OpWithProgress)); } // // Overload 0-4: _InternalReturnType -> IAsyncActionWithProgress<_ProgressType>^ // // This is a straight task continuation which returns an async action with progress which will be unwrapped for continuation // void _Continue(std::false_type, details::_TypeSelectorAsyncActionWithProgress) const { typedef details::_FunctionTypeTraits<_Function, _InternalReturnType>::_FuncRetType _FuncOutputType; auto _OpWithProgress = _LogWorkItemAndInvokeUserLambda(_Continuation_func_transformer<_InternalReturnType, _FuncOutputType>::_Perform(_M_function), _M_ancestorTaskImpl->_GetResult()); typedef details::_GetProgressType::_Value _ProgressType; details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>( this->_M_pTask, ref new details::_IAsyncActionWithProgressToAsyncOperationConverter<_ProgressType>(_OpWithProgress)); } #endif /* defined (__cplusplus_winrt) */ // // Overload 1-0: task<_InternalReturnType> -> _TaskType // // This is an exception handling type of continuation which takes the task rather than the task's result. // void _Continue(std::true_type, details::_TypeSelectorNoAsync) const { typedef task<_InternalReturnType> _FuncInputType; task<_InternalReturnType> _ResultTask; _ResultTask._SetImpl(std::move(_M_ancestorTaskImpl)); this->_M_pTask->_FinalizeAndRunContinuations( _LogWorkItemAndInvokeUserLambda(_Continuation_func_transformer<_FuncInputType, _ContinuationReturnType>::_Perform(_M_function), std::move(_ResultTask))); } // // Overload 1-1: task<_InternalReturnType> -> IAsyncOperation<_TaskType>^ // or // task<_TaskType> // // This is an exception handling type of continuation which takes the task rather than // the task's result. It also returns an async operation or a task which will be unwrapped // for continuation // void _Continue(std::true_type, details::_TypeSelectorAsyncOperationOrTask) const { // The continuation takes a parameter of type task<_Input>, which is the same as the ancestor task. task<_InternalReturnType> _ResultTask; _ResultTask._SetImpl(std::move(_M_ancestorTaskImpl)); details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>(this->_M_pTask, _LogWorkItemAndInvokeUserLambda(_M_function, std::move(_ResultTask))); } #if defined (__cplusplus_winrt) // // Overload 1-2: task<_InternalReturnType> -> IAsyncAction^ // // This is an exception handling type of continuation which takes the task rather than // the task's result. It also returns an async action which will be unwrapped for continuation // void _Continue(std::true_type, details::_TypeSelectorAsyncAction) const { // The continuation takes a parameter of type task<_Input>, which is the same as the ancestor task. task<_InternalReturnType> _ResultTask; _ResultTask._SetImpl(std::move(_M_ancestorTaskImpl)); details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>(this->_M_pTask, ref new details::_IAsyncActionToAsyncOperationConverter(_LogWorkItemAndInvokeUserLambda(_M_function, std::move(_ResultTask)))); } // // Overload 1-3: task<_InternalReturnType> -> IAsyncOperationWithProgress<_TaskType, _ProgressType>^ // // This is an exception handling type of continuation which takes the task rather than // the task's result. It also returns an async operation with progress which will be unwrapped // for continuation // void _Continue(std::true_type, details::_TypeSelectorAsyncOperationWithProgress) const { // The continuation takes a parameter of type task<_Input>, which is the same as the ancestor task. task<_InternalReturnType> _ResultTask; _ResultTask._SetImpl(std::move(_M_ancestorTaskImpl)); typedef details::_GetProgressType::_Value _ProgressType; details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>(this->_M_pTask, ref new details::_IAsyncOperationWithProgressToAsyncOperationConverter<_ContinuationReturnType, _ProgressType>( _LogWorkItemAndInvokeUserLambda(_M_function, std::move(_ResultTask)))); } // // Overload 1-4: task<_InternalReturnType> -> IAsyncActionWithProgress<_ProgressType>^ // // This is an exception handling type of continuation which takes the task rather than // the task's result. It also returns an async operation with progress which will be unwrapped // for continuation // void _Continue(std::true_type, details::_TypeSelectorAsyncActionWithProgress) const { // The continuation takes a parameter of type task<_Input>, which is the same as the ancestor task. task<_InternalReturnType> _ResultTask; _ResultTask._SetImpl(std::move(_M_ancestorTaskImpl)); typedef details::_GetProgressType::_Value _ProgressType; details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>(this->_M_pTask, ref new details::_IAsyncActionWithProgressToAsyncOperationConverter<_ProgressType>( _LogWorkItemAndInvokeUserLambda(_M_function, std::move(_ResultTask)))); } #endif /* defined (__cplusplus_winrt) */ }; /// /// Initializes a task using a lambda, function pointer or function object. /// template void _TaskInitWithFunctor(const _Function& _Func) { typedef typename details::_InitFunctorTypeTraits<_InternalReturnType, decltype(_Func())> _Async_type_traits; _M_Impl->_M_fFromAsync = _Async_type_traits::_IsAsyncTask; _M_Impl->_M_fUnwrappedTask = _Async_type_traits::_IsUnwrappedTaskOrAsync; _M_Impl->_M_taskEventLogger._LogScheduleTask(false); _M_Impl->_ScheduleTask(new _InitialTaskHandle<_InternalReturnType, _Function, typename _Async_type_traits::_AsyncKind>(_GetImpl(), _Func), details::_NoInline); } /// /// Initializes a task using a task completion event. /// void _TaskInitNoFunctor(task_completion_event<_ReturnType>& _Event) { _Event._RegisterTask(_M_Impl); } #if defined (__cplusplus_winrt) /// /// Initializes a task using an asynchronous operation IAsyncOperation^ /// void _TaskInitAsyncOp(Windows::Foundation::IAsyncOperation::_Value>^ _AsyncOp) { _M_Impl->_M_fFromAsync = true; // Mark this task as started here since we can set the state in the constructor without acquiring a lock. Once _AsyncInit // returns a completion could execute concurrently and the task must be fully initialized before that happens. _M_Impl->_M_TaskState = details::_Task_impl_base::_Started; // Pass the shared pointer into _AsyncInit for storage in the Async Callback. details::_Task_impl_base::_AsyncInit<_ReturnType, _ReturnType>(_M_Impl, _AsyncOp); } /// /// Initializes a task using an asynchronous operation IAsyncOperation^ /// void _TaskInitNoFunctor(Windows::Foundation::IAsyncOperation::_Value>^ _AsyncOp) { _TaskInitAsyncOp(_AsyncOp); } /// /// Initializes a task using an asynchronous operation with progress IAsyncOperationWithProgress^ /// template void _TaskInitNoFunctor(Windows::Foundation::IAsyncOperationWithProgress::_Value, _Progress>^ _AsyncOp) { _TaskInitAsyncOp(ref new details::_IAsyncOperationWithProgressToAsyncOperationConverter::_Value, _Progress>(_AsyncOp)); } #endif /* defined (__cplusplus_winrt) */ /// /// Initializes a task using a callable object. /// template void _TaskInitMaybeFunctor(_Function & _Func, std::true_type) { _TaskInitWithFunctor<_ReturnType, _Function>(_Func); } /// /// Initializes a task using a non-callable object. /// template void _TaskInitMaybeFunctor(_Ty & _Param, std::false_type) { _TaskInitNoFunctor(_Param); } template auto _ThenImpl(const _Function& _Func, const task_options& _TaskOptions) const -> typename details::_ContinuationTypeTraits<_Function, _InternalReturnType>::_TaskOfType { if (!_M_Impl) { throw invalid_operation("then() cannot be called on a default constructed task."); } details::_CancellationTokenState *_PTokenState = _TaskOptions.has_cancellation_token() ? _TaskOptions.get_cancellation_token()._GetImplValue() : nullptr; auto _Scheduler = _TaskOptions.has_scheduler() ? _TaskOptions.get_scheduler() : _GetImpl()->_GetScheduler(); auto _CreationStack = details::_get_internal_task_options(_TaskOptions)._M_hasPresetCreationCallstack ? details::_get_internal_task_options(_TaskOptions)._M_presetCreationCallstack : details::_TaskCreationCallstack(); return _ThenImpl<_InternalReturnType, _Function>(_Func, _PTokenState, _TaskOptions.get_continuation_context(), _Scheduler, _CreationStack); } /// /// The one and only implementation of then for void and non-void tasks. /// template auto _ThenImpl(const _Function& _Func, details::_CancellationTokenState *_PTokenState, const task_continuation_context& _ContinuationContext, scheduler_ptr _Scheduler, details::_TaskCreationCallstack _CreationStack, details::_TaskInliningMode_t _InliningMode = details::_NoInline) const -> typename details::_ContinuationTypeTraits<_Function, _InternalReturnType>::_TaskOfType { if (!_M_Impl) { throw invalid_operation("then() cannot be called on a default constructed task."); } typedef details::_FunctionTypeTraits<_Function, _InternalReturnType> _Function_type_traits; typedef details::_TaskTypeTraits _Async_type_traits; typedef typename _Async_type_traits::_TaskRetType _TaskType; // // A **nullptr** token state indicates that it was not provided by the user. In this case, we inherit the antecedent's token UNLESS this is a // an exception handling continuation. In that case, we break the chain with a _None. That continuation is never canceled unless the user // explicitly passes the same token. // if (_PTokenState == nullptr) { if (_Function_type_traits::_Takes_task::value) { _PTokenState = details::_CancellationTokenState::_None(); } else { _PTokenState = _GetImpl()->_M_pTokenState; } } task<_TaskType> _ContinuationTask; _ContinuationTask._CreateImpl(_PTokenState, _Scheduler); _ContinuationTask._GetImpl()->_M_fFromAsync = (_GetImpl()->_M_fFromAsync || _Async_type_traits::_IsAsyncTask); _ContinuationTask._GetImpl()->_M_fUnwrappedTask = _Async_type_traits::_IsUnwrappedTaskOrAsync; _ContinuationTask._SetTaskCreationCallstack(_CreationStack); _GetImpl()->_ScheduleContinuation(new _ContinuationTaskHandle<_InternalReturnType, _TaskType, _Function, typename _Function_type_traits::_Takes_task, typename _Async_type_traits::_AsyncKind>( _GetImpl(), _ContinuationTask._GetImpl(), _Func, _ContinuationContext, _InliningMode)); return _ContinuationTask; } // The underlying implementation for this task typename details::_Task_ptr<_ReturnType>::_Type _M_Impl; }; /// /// The Parallel Patterns Library (PPL) task class. A task object represents work that can be executed asynchronously, /// and concurrently with other tasks and parallel work produced by parallel algorithms in the Concurrency Runtime. It produces /// a result of type on successful completion. Tasks of type task<void> produce no result. /// A task can be waited upon and canceled independently of other tasks. It can also be composed with other tasks using /// continuations(then), and join(when_all) and choice(when_any) patterns. /// /// /// For more information, see . /// /**/ template<> class task { public: /// /// The type of the result an object of this class produces. /// /**/ typedef void result_type; /// /// Constructs a task object. /// /// /// The default constructor for a task is only present in order to allow tasks to be used within containers. /// A default constructed task cannot be used until you assign a valid task to it. Methods such as get, wait or then /// will throw an invalid_argument exception when called on a default constructed task. /// A task that is created from a task_completion_event will complete (and have its continuations scheduled) when the task /// completion event is set. /// The version of the constructor that takes a cancellation token creates a task that can be canceled using the /// cancellation_token_source the token was obtained from. Tasks created without a cancellation token are not cancelable. /// Tasks created from a Windows::Foundation::IAsyncInfo interface or a lambda that returns an IAsyncInfo interface /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created /// from a lamda that returns a task<result_type> reach their terminal state when the inner task reaches its terminal state, /// and not when the lamda returns. /// task behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads /// without the need for locks. /// The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available /// to Windows Store apps. /// For more information, see . /// /**/ task() : _M_unitTask() { // The default constructor should create a task with a nullptr impl. This is a signal that the // task is not usable and should throw if any wait(), get() or then() APIs are used. } /// /// Constructs a task object. /// /// /// The type of the parameter from which the task is to be constructed. /// /// /// The parameter from which the task is to be constructed. This could be a lambda, a function object, a task_completion_event<result_type> /// object, or a Windows::Foundation::IAsyncInfo if you are using tasks in your Windows Store app. The lambda or function /// object should be a type equivalent to std::function<X(void)>, where X can be a variable of type result_type, /// task<result_type>, or a Windows::Foundation::IAsyncInfo in Windows Store apps. /// /// /// The default constructor for a task is only present in order to allow tasks to be used within containers. /// A default constructed task cannot be used until you assign a valid task to it. Methods such as get, wait or then /// will throw an invalid_argument exception when called on a default constructed task. /// A task that is created from a task_completion_event will complete (and have its continuations scheduled) when the task /// completion event is set. /// The version of the constructor that takes a cancellation token creates a task that can be canceled using the /// cancellation_token_source the token was obtained from. Tasks created without a cancellation token are not cancelable. /// Tasks created from a Windows::Foundation::IAsyncInfo interface or a lambda that returns an IAsyncInfo interface /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created /// from a lamda that returns a task<result_type> reach their terminal state when the inner task reaches its terminal state, /// and not when the lamda returns. /// task behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads /// without the need for locks. /// The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available /// to Windows Store apps. /// For more information, see . /// /**/ template __declspec(noinline) // Ask for no inlining so that the _CAPTURE_CALLSTACK gives us the expected result explicit task(_Ty _Param, const task_options& _TaskOptions = task_options()) { details::_ValidateTaskConstructorArgs(_Param); _M_unitTask._CreateImpl(_TaskOptions.get_cancellation_token()._GetImplValue(), _TaskOptions.get_scheduler()); // Do not move the next line out of this function. It is important that _CAPTURE_CALLSTACK() evaluate to the the call site of the task constructor. _M_unitTask._SetTaskCreationCallstack(details::_get_internal_task_options(_TaskOptions)._M_hasPresetCreationCallstack ? details::_get_internal_task_options(_TaskOptions)._M_presetCreationCallstack : _CAPTURE_CALLSTACK()); _TaskInitMaybeFunctor(_Param, details::_IsCallable(_Param,0)); } /// /// Constructs a task object. /// /// /// The source task object. /// /// /// The default constructor for a task is only present in order to allow tasks to be used within containers. /// A default constructed task cannot be used until you assign a valid task to it. Methods such as get, wait or then /// will throw an invalid_argument exception when called on a default constructed task. /// A task that is created from a task_completion_event will complete (and have its continuations scheduled) when the task /// completion event is set. /// The version of the constructor that takes a cancellation token creates a task that can be canceled using the /// cancellation_token_source the token was obtained from. Tasks created without a cancellation token are not cancelable. /// Tasks created from a Windows::Foundation::IAsyncInfo interface or a lambda that returns an IAsyncInfo interface /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created /// from a lamda that returns a task<result_type> reach their terminal state when the inner task reaches its terminal state, /// and not when the lamda returns. /// task behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads /// without the need for locks. /// The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available /// to Windows Store apps. /// For more information, see . /// /**/ task(const task& _Other): _M_unitTask(_Other._M_unitTask){} /// /// Constructs a task object. /// /// /// The source task object. /// /// /// The default constructor for a task is only present in order to allow tasks to be used within containers. /// A default constructed task cannot be used until you assign a valid task to it. Methods such as get, wait or then /// will throw an invalid_argument exception when called on a default constructed task. /// A task that is created from a task_completion_event will complete (and have its continuations scheduled) when the task /// completion event is set. /// The version of the constructor that takes a cancellation token creates a task that can be canceled using the /// cancellation_token_source the token was obtained from. Tasks created without a cancellation token are not cancelable. /// Tasks created from a Windows::Foundation::IAsyncInfo interface or a lambda that returns an IAsyncInfo interface /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created /// from a lamda that returns a task<result_type> reach their terminal state when the inner task reaches its terminal state, /// and not when the lamda returns. /// task behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads /// without the need for locks. /// The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available /// to Windows Store apps. /// For more information, see . /// /**/ task(task&& _Other) : _M_unitTask(std::move(_Other._M_unitTask)) {} /// /// Replaces the contents of one task object with another. /// /// /// The source task object. /// /// /// As task behaves like a smart pointer, after a copy assignment, this task objects represents the same /// actual task as does. /// /**/ task& operator=(const task& _Other) { if (this != &_Other) { _M_unitTask = _Other._M_unitTask; } return *this; } /// /// Replaces the contents of one task object with another. /// /// /// The source task object. /// /// /// As task behaves like a smart pointer, after a copy assignment, this task objects represents the same /// actual task as does. /// /**/ task& operator=(task&& _Other) { if (this != &_Other) { _M_unitTask = std::move(_Other._M_unitTask); } return *this; } /// /// Adds a continuation task to this task. /// /// /// The type of the function object that will be invoked by this task. /// /// /// The continuation function to execute when this task completes. This continuation function must take as input /// a variable of either result_type or task<result_type>, where result_type is the type /// of the result this task produces. /// /// /// The cancellation token to associate with the continuation task. A continuation task that is created without a cancellation token will inherit /// the token of its antecedent task. /// /// /// The newly created continuation task. The result type of the returned task is determined by what returns. /// /// /// The overloads of then that take a lambda or functor that returns a Windows::Foundation::IAsyncInfo interface, are only available /// to Windows Store apps. /// For more information on how to use task continuations to compose asynchronous work, see . /// /**/ template __declspec(noinline) // Ask for no inlining so that the _CAPTURE_CALLSTACK gives us the expected result auto then(const _Function& _Func, task_options _TaskOptions = task_options()) const -> typename details::_ContinuationTypeTraits<_Function, void>::_TaskOfType { details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(_CAPTURE_CALLSTACK()); return _M_unitTask._ThenImpl(_Func, _TaskOptions); } /// /// Adds a continuation task to this task. /// /// /// The type of the function object that will be invoked by this task. /// /// /// The continuation function to execute when this task completes. This continuation function must take as input /// a variable of either result_type or task<result_type>, where result_type is the type /// of the result this task produces. /// /// /// The cancellation token to associate with the continuation task. A continuation task that is created without a cancellation token will inherit /// the token of its antecedent task. /// /// /// A variable that specifies where the continuation should execute. This variable is only useful when used in a Windows Store /// style app. For more information, see task_continuation_context /// /// /// The newly created continuation task. The result type of the returned task is determined by what returns. /// /// /// The overloads of then that take a lambda or functor that returns a Windows::Foundation::IAsyncInfo interface, are only available /// to Windows Store apps. /// For more information on how to use task continuations to compose asynchronous work, see . /// /**/ template __declspec(noinline) // Ask for no inlining so that the _CAPTURE_CALLSTACK gives us the expected result auto then(const _Function& _Func, cancellation_token _CancellationToken, task_continuation_context _ContinuationContext) const -> typename details::_ContinuationTypeTraits<_Function, void>::_TaskOfType { task_options _TaskOptions(_CancellationToken, _ContinuationContext); details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(_CAPTURE_CALLSTACK()); return _M_unitTask._ThenImpl(_Func, _TaskOptions); } /// /// Waits for this task to reach a terminal state. It is possible for wait to execute the task inline, if all of the tasks /// dependencies are satisfied, and it has not already been picked up for execution by a background worker. /// /// /// A task_status value which could be either completed or canceled. If the task encountered an exception /// during execution, or an exception was propagated to it from an antecedent task, wait will throw that exception. /// /**/ task_status wait() const { return _M_unitTask.wait(); } /// /// Returns the result this task produced. If the task is not in a terminal state, a call to get will wait for the task to /// finish. This method does not return a value when called on a task with a result_type of void. /// /// /// If the task is canceled, a call to get will throw a task_canceled exception. If the task /// encountered an different exception or an exception was propagated to it from an antecedent task, a call to get will throw that exception. /// /**/ void get() const { _M_unitTask.get(); } /// /// Determines if the task is completed. /// /// /// True if the task has completed, false otherwise. /// /// /// The function returns true if the task is completed or canceled (with or without user exception). /// bool is_done() const { return _M_unitTask.is_done(); } /// /// Returns the scheduler for this task /// /// /// A pointer to the scheduler /// scheduler_ptr scheduler() const { return _M_unitTask.scheduler(); } /// /// Determines whether the task unwraps a Windows Runtime IAsyncInfo interface or is descended from such a task. /// /// /// true if the task unwraps an IAsyncInfo interface or is descended from such a task, false otherwise. /// /**/ bool is_apartment_aware() const { return _M_unitTask.is_apartment_aware(); } /// /// Determines whether two task objects represent the same internal task. /// /// /// true if the objects refer to the same underlying task, and false otherwise. /// /**/ bool operator==(const task& _Rhs) const { return (_M_unitTask == _Rhs._M_unitTask); } /// /// Determines whether two task objects represent different internal tasks. /// /// /// true if the objects refer to different underlying tasks, and false otherwise. /// /**/ bool operator!=(const task& _Rhs) const { return !operator==(_Rhs); } /// /// Create an underlying task implementation. /// void _CreateImpl(details::_CancellationTokenState * _Ct, scheduler_ptr _Scheduler) { _M_unitTask._CreateImpl(_Ct, _Scheduler); } /// /// Return the underlying implementation for this task. /// const details::_Task_ptr::_Type & _GetImpl() const { return _M_unitTask._M_Impl; } /// /// Set the implementation of the task to be the supplied implementaion. /// void _SetImpl(const details::_Task_ptr::_Type & _Impl) { _M_unitTask._SetImpl(_Impl); } /// /// Set the implementation of the task to be the supplied implementaion using a move instead of a copy. /// void _SetImpl(details::_Task_ptr::_Type && _Impl) { _M_unitTask._SetImpl(std::move(_Impl)); } /// /// Sets a property determining whether the task is apartment aware. /// void _SetAsync(bool _Async = true) { _M_unitTask._SetAsync(_Async); } /// /// Sets a field in the task impl to the return callstack for calls to the task constructors and the then method. /// void _SetTaskCreationCallstack(const details::_TaskCreationCallstack &_callstack) { _M_unitTask._SetTaskCreationCallstack(_callstack); } /// /// An internal version of then that takes additional flags and executes the continuation inline. Used for runtime internal continuations only. /// template auto _Then(const _Function& _Func, details::_CancellationTokenState *_PTokenState, details::_TaskInliningMode_t _InliningMode = details::_ForceInline) const -> typename details::_ContinuationTypeTraits<_Function, void>::_TaskOfType { // inherit from antecedent auto _Scheduler = _GetImpl()->_GetScheduler(); return _M_unitTask._ThenImpl(_Func, _PTokenState, task_continuation_context::use_default(), _Scheduler, _CAPTURE_CALLSTACK(), _InliningMode); } private: template friend class task; template friend class task_completion_event; /// /// Initializes a task using a task completion event. /// void _TaskInitNoFunctor(task_completion_event& _Event) { _M_unitTask._TaskInitNoFunctor(_Event._M_unitEvent); } #if defined (__cplusplus_winrt) /// /// Initializes a task using an asynchronous action IAsyncAction^ /// void _TaskInitNoFunctor(Windows::Foundation::IAsyncAction^ _AsyncAction) { _M_unitTask._TaskInitAsyncOp(ref new details::_IAsyncActionToAsyncOperationConverter(_AsyncAction)); } /// /// Initializes a task using an asynchronous action with progress IAsyncActionWithProgress<_P>^ /// template void _TaskInitNoFunctor(Windows::Foundation::IAsyncActionWithProgress<_P>^ _AsyncActionWithProgress) { _M_unitTask._TaskInitAsyncOp(ref new details::_IAsyncActionWithProgressToAsyncOperationConverter<_P>(_AsyncActionWithProgress)); } #endif /* defined (__cplusplus_winrt) */ /// /// Initializes a task using a callable object. /// template void _TaskInitMaybeFunctor(_Function & _Func, std::true_type) { _M_unitTask._TaskInitWithFunctor(_Func); } /// /// Initializes a task using a non-callable object. /// template void _TaskInitMaybeFunctor(_T & _Param, std::false_type) { _TaskInitNoFunctor(_Param); } // The void task contains a task of a dummy type so common code can be used for tasks with void and non-void results. task _M_unitTask; }; namespace details { /// /// The following type traits are used for the create_task function. /// #if defined (__cplusplus_winrt) // Unwrap functions for asyncOperations template _Ty _GetUnwrappedType(Windows::Foundation::IAsyncOperation<_Ty>^); void _GetUnwrappedType(Windows::Foundation::IAsyncAction^); template _Ty _GetUnwrappedType(Windows::Foundation::IAsyncOperationWithProgress<_Ty, _Progress>^); template void _GetUnwrappedType(Windows::Foundation::IAsyncActionWithProgress<_Progress>^); #endif /* defined (__cplusplus_winrt) */ // Unwrap task template _Ty _GetUnwrappedType(task<_Ty>); // Unwrap all supportted types template auto _GetUnwrappedReturnType(_Ty _Arg, int) -> decltype(_GetUnwrappedType(_Arg)); // fallback template _Ty _GetUnwrappedReturnType(_Ty, ...); /// /// _GetTaskType functions will retrieve task type T in task[T](Arg), /// for given constructor argument Arg and its property "callable". /// It will automatically unwrap argument to get the final return type if necessary. /// // Non-Callable template _Ty _GetTaskType(task_completion_event<_Ty>, std::false_type); // Non-Callable template auto _GetTaskType(_Ty _NonFunc, std::false_type) -> decltype(_GetUnwrappedType(_NonFunc)); // Callable template auto _GetTaskType(_Ty _Func, std::true_type) -> decltype(_GetUnwrappedReturnType(_Func(), 0)); // Special callable returns void void _GetTaskType(std::function, std::true_type); struct _BadArgType{}; template auto _FilterValidTaskType(_Ty _Param, int) -> decltype(_GetTaskType(_Param, _IsCallable(_Param, 0))); template _BadArgType _FilterValidTaskType(_Ty _Param, ...); template struct _TaskTypeFromParam { typedef decltype(_FilterValidTaskType(stdx::declval<_Ty>(), 0)) _Type; }; } // namespace details /// /// Creates a PPL task object. create_task can be used anywhere you would have used a task constructor. /// It is provided mainly for convenience, because it allows use of the auto keyword while creating tasks. /// /// /// The type of the parameter from which the task is to be constructed. /// /// /// The parameter from which the task is to be constructed. This could be a lambda or function object, a task_completion_event /// object, a different task object, or a Windows::Foundation::IAsyncInfo interface if you are using tasks in your Windows Store app. /// /// /// A new task of type T, that is inferred from . /// /// /// The first overload behaves like a task constructor that takes a single parameter. /// The second overload associates the cancellation token provided with the newly created task. If you use this overload you are not /// allowed to pass in a different task object as the first parameter. /// The type of the returned task is inferred from the first parameter to the function. If is a task_completion_event<T>, /// a task<T>, or a functor that returns either type T or task<T>, the type of the created task is task<T>. /// In a Windows Store app, if is of type Windows::Foundation::IAsyncOperation<T>^ or /// Windows::Foundation::IAsyncOperationWithProgress<T,P>^, or a functor that returns either of those types, the created task will be of type task<T>. /// If is of type Windows::Foundation::IAsyncAction^ or Windows::Foundation::IAsyncActionWithProgress<P>^, or a functor /// that returns either of those types, the created task will have type task<void>. /// /// /// /**/ template __declspec(noinline) auto create_task(_Ty _Param, task_options _TaskOptions = task_options()) -> task::_Type> { static_assert(!std::is_same::_Type,details::_BadArgType>::value, #if defined (__cplusplus_winrt) "incorrect argument for create_task; can be a callable object, an asynchronous operation, or a task_completion_event" #else /* defined (__cplusplus_winrt) */ "incorrect argument for create_task; can be a callable object or a task_completion_event" #endif /* defined (__cplusplus_winrt) */ ); details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(_CAPTURE_CALLSTACK()); task::_Type> _CreatedTask(_Param, _TaskOptions); return _CreatedTask; } /// /// Creates a PPL task object. create_task can be used anywhere you would have used a task constructor. /// It is provided mainly for convenience, because it allows use of the auto keyword while creating tasks. /// /// /// The type of the parameter from which the task is to be constructed. /// /// /// The parameter from which the task is to be constructed. This could be a lambda or function object, a task_completion_event /// object, a different task object, or a Windows::Foundation::IAsyncInfo interface if you are using tasks in your Windows Store app. /// /// /// The cancellation token to associate with the task. When the source for this token is canceled, cancellation will be requested on the task. /// /// /// A new task of type T, that is inferred from . /// /// /// The first overload behaves like a task constructor that takes a single parameter. /// The second overload associates the cancellation token provided with the newly created task. If you use this overload you are not /// allowed to pass in a different task object as the first parameter. /// The type of the returned task is inferred from the first parameter to the function. If is a task_completion_event<T>, /// a task<T>, or a functor that returns either type T or task<T>, the type of the created task is task<T>. /// In a Windows Store app, if is of type Windows::Foundation::IAsyncOperation<T>^ or /// Windows::Foundation::IAsyncOperationWithProgress<T,P>^, or a functor that returns either of those types, the created task will be of type task<T>. /// If is of type Windows::Foundation::IAsyncAction^ or Windows::Foundation::IAsyncActionWithProgress<P>^, or a functor /// that returns either of those types, the created task will have type task<void>. /// /// /// /**/ template __declspec(noinline) task<_ReturnType> create_task(const task<_ReturnType>& _Task) { task<_ReturnType> _CreatedTask(_Task); return _CreatedTask; } #if defined (__cplusplus_winrt) namespace details { template task<_T> _To_task_helper(Windows::Foundation::IAsyncOperation<_T>^ op) { return task<_T>(op); } template task<_T> _To_task_helper(Windows::Foundation::IAsyncOperationWithProgress<_T, _Progress>^ op) { return task<_T>(op); } inline task _To_task_helper(Windows::Foundation::IAsyncAction^ op) { return task(op); } template task _To_task_helper(Windows::Foundation::IAsyncActionWithProgress<_Progress>^ op) { return task(op); } template class _ProgressDispatcherBase { public: virtual ~_ProgressDispatcherBase() { } virtual void _Report(const _ProgressType& _Val) = 0; }; template class _ProgressDispatcher : public _ProgressDispatcherBase<_ProgressType> { public: virtual ~_ProgressDispatcher() { } _ProgressDispatcher(_ClassPtrType _Ptr) : _M_ptr(_Ptr) { } virtual void _Report(const _ProgressType& _Val) { _M_ptr->_FireProgress(_Val); } private: _ClassPtrType _M_ptr; }; class _ProgressReporterCtorArgType{}; } // namespace details /// /// The progress reporter class allows reporting progress notifications of a specific type. Each progress_reporter object is bound /// to a particular asynchronous action or operation. /// /// /// The payload type of each progress notification reported through the progress reporter. /// /// /// This type is only available to Windows Store apps. /// /// /**/ template class progress_reporter { typedef std::shared_ptr> _PtrType; public: /// /// Sends a progress report to the asynchronous action or operation to which this progress reporter is bound. /// /// /// The payload to report through a progress notification. /// /**/ void report(const _ProgressType& _Val) const { _M_dispatcher->_Report(_Val); } template static progress_reporter _CreateReporter(_ClassPtrType _Ptr) { progress_reporter _Reporter; details::_ProgressDispatcherBase<_ProgressType> *_PDispatcher = new details::_ProgressDispatcher<_ProgressType, _ClassPtrType>(_Ptr); _Reporter._M_dispatcher = _PtrType(_PDispatcher); return _Reporter; } progress_reporter() {} private: progress_reporter(details::_ProgressReporterCtorArgType); _PtrType _M_dispatcher; }; namespace details { // // maps internal definitions for AsyncStatus and defines states that are not client visible // enum _AsyncStatusInternal { _AsyncCreated = -1, // externally invisible // client visible states (must match AsyncStatus exactly) _AsyncStarted = 0, // Windows::Foundation::AsyncStatus::Started, _AsyncCompleted = 1, // Windows::Foundation::AsyncStatus::Completed, _AsyncCanceled = 2, // Windows::Foundation::AsyncStatus::Canceled, _AsyncError = 3, // Windows::Foundation::AsyncStatus::Error, // non-client visible internal states _AsyncCancelPending, _AsyncClosed, _AsyncUndefined }; // // designates whether the "GetResults" method returns a single result (after complete fires) or multiple results // (which are progressively consumable between Start state and before Close is called) // enum _AsyncResultType { SingleResult = 0x0001, MultipleResults = 0x0002 }; // *************************************************************************** // Template type traits and helpers for async production APIs: // struct _ZeroArgumentFunctor { }; struct _OneArgumentFunctor { }; struct _TwoArgumentFunctor { }; // **************************************** // CLASS TYPES: // ******************** // TWO ARGUMENTS: // non-void arg: template _Arg1 _Arg1ClassHelperThunk(_ReturnType (_Class::*)(_Arg1, _Arg2) const); // non-void arg: template _Arg2 _Arg2ClassHelperThunk(_ReturnType (_Class::*)(_Arg1, _Arg2) const); template _ReturnType _ReturnTypeClassHelperThunk(_ReturnType (_Class::*)(_Arg1, _Arg2) const); template _TwoArgumentFunctor _ArgumentCountHelper(_ReturnType (_Class::*)(_Arg1, _Arg2) const); // ******************** // ONE ARGUMENT: // non-void arg: template _Arg1 _Arg1ClassHelperThunk(_ReturnType (_Class::*)(_Arg1) const); // non-void arg: template void _Arg2ClassHelperThunk(_ReturnType (_Class::*)(_Arg1) const); template _ReturnType _ReturnTypeClassHelperThunk(_ReturnType (_Class::*)(_Arg1) const); template _OneArgumentFunctor _ArgumentCountHelper(_ReturnType (_Class::*)(_Arg1) const); // ******************** // ZERO ARGUMENT: // void arg: template void _Arg1ClassHelperThunk(_ReturnType (_Class::*)() const); // void arg: template void _Arg2ClassHelperThunk(_ReturnType (_Class::*)() const); // void arg: template _ReturnType _ReturnTypeClassHelperThunk(_ReturnType (_Class::*)() const); template _ZeroArgumentFunctor _ArgumentCountHelper(_ReturnType (_Class::*)() const); // **************************************** // POINTER TYPES: // ******************** // TWO ARGUMENTS: template _Arg1 _Arg1PFNHelperThunk(_ReturnType(__cdecl *)(_Arg1, _Arg2)); template _Arg2 _Arg2PFNHelperThunk(_ReturnType(__cdecl *)(_Arg1, _Arg2)); template _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__cdecl *)(_Arg1, _Arg2)); template _TwoArgumentFunctor _ArgumentCountHelper(_ReturnType(__cdecl *)(_Arg1, _Arg2)); template _Arg1 _Arg1PFNHelperThunk(_ReturnType(__stdcall *)(_Arg1, _Arg2)); template _Arg2 _Arg2PFNHelperThunk(_ReturnType(__stdcall *)(_Arg1, _Arg2)); template _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__stdcall *)(_Arg1, _Arg2)); template _TwoArgumentFunctor _ArgumentCountHelper(_ReturnType(__stdcall *)(_Arg1, _Arg2)); template _Arg1 _Arg1PFNHelperThunk(_ReturnType(__fastcall *)(_Arg1, _Arg2)); template _Arg2 _Arg2PFNHelperThunk(_ReturnType(__fastcall *)(_Arg1, _Arg2)); template _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__fastcall *)(_Arg1, _Arg2)); template _TwoArgumentFunctor _ArgumentCountHelper(_ReturnType(__fastcall *)(_Arg1, _Arg2)); // ******************** // ONE ARGUMENT: template _Arg1 _Arg1PFNHelperThunk(_ReturnType(__cdecl *)(_Arg1)); template void _Arg2PFNHelperThunk(_ReturnType(__cdecl *)(_Arg1)); template _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__cdecl *)(_Arg1)); template _OneArgumentFunctor _ArgumentCountHelper(_ReturnType(__cdecl *)(_Arg1)); template _Arg1 _Arg1PFNHelperThunk(_ReturnType(__stdcall *)(_Arg1)); template void _Arg2PFNHelperThunk(_ReturnType(__stdcall *)(_Arg1)); template _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__stdcall *)(_Arg1)); template _OneArgumentFunctor _ArgumentCountHelper(_ReturnType(__stdcall *)(_Arg1)); template _Arg1 _Arg1PFNHelperThunk(_ReturnType(__fastcall *)(_Arg1)); template void _Arg2PFNHelperThunk(_ReturnType(__fastcall *)(_Arg1)); template _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__fastcall *)(_Arg1)); template _OneArgumentFunctor _ArgumentCountHelper(_ReturnType(__fastcall *)(_Arg1)); // ******************** // ZERO ARGUMENT: template void _Arg1PFNHelperThunk(_ReturnType(__cdecl *)()); template void _Arg2PFNHelperThunk(_ReturnType(__cdecl *)()); template _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__cdecl *)()); template _ZeroArgumentFunctor _ArgumentCountHelper(_ReturnType(__cdecl *)()); template void _Arg1PFNHelperThunk(_ReturnType(__stdcall *)()); template void _Arg2PFNHelperThunk(_ReturnType(__stdcall *)()); template _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__stdcall *)()); template _ZeroArgumentFunctor _ArgumentCountHelper(_ReturnType(__stdcall *)()); template void _Arg1PFNHelperThunk(_ReturnType(__fastcall *)()); template void _Arg2PFNHelperThunk(_ReturnType(__fastcall *)()); template _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__fastcall *)()); template _ZeroArgumentFunctor _ArgumentCountHelper(_ReturnType(__fastcall *)()); template struct _FunctorArguments { static const size_t _Count = 0; }; template<> struct _FunctorArguments<_OneArgumentFunctor> { static const size_t _Count = 1; }; template<> struct _FunctorArguments<_TwoArgumentFunctor> { static const size_t _Count = 2; }; template struct _FunctorTypeTraits { typedef decltype(_ArgumentCountHelper(&(_T::operator()))) _ArgumentCountType; static const size_t _ArgumentCount = _FunctorArguments<_ArgumentCountType>::_Count; typedef decltype(_ReturnTypeClassHelperThunk(&(_T::operator()))) _ReturnType; typedef decltype(_Arg1ClassHelperThunk(&(_T::operator()))) _Argument1Type; typedef decltype(_Arg2ClassHelperThunk(&(_T::operator()))) _Argument2Type; }; template struct _FunctorTypeTraits<_T *> { typedef decltype(_ArgumentCountHelper(stdx::declval<_T*>())) _ArgumentCountType; static const size_t _ArgumentCount = _FunctorArguments<_ArgumentCountType>::_Count; typedef decltype(_ReturnTypePFNHelperThunk(stdx::declval<_T*>())) _ReturnType; typedef decltype(_Arg1PFNHelperThunk(stdx::declval<_T*>())) _Argument1Type; typedef decltype(_Arg2PFNHelperThunk(stdx::declval<_T*>())) _Argument2Type; }; template struct _ProgressTypeTraits { static const bool _TakesProgress = false; typedef void _ProgressType; }; template struct _ProgressTypeTraits> { static const bool _TakesProgress = true; typedef typename _T _ProgressType; }; template::_ArgumentCount> struct _CAFunctorOptions { static const bool _TakesProgress = false; static const bool _TakesToken = false; typedef void _ProgressType; }; template struct _CAFunctorOptions<_T, 1> { private: typedef typename _FunctorTypeTraits<_T>::_Argument1Type _Argument1Type; public: static const bool _TakesProgress = _ProgressTypeTraits<_Argument1Type>::_TakesProgress; static const bool _TakesToken = !_TakesProgress; typedef typename _ProgressTypeTraits<_Argument1Type>::_ProgressType _ProgressType; }; template struct _CAFunctorOptions<_T, 2> { private: typedef typename _FunctorTypeTraits<_T>::_Argument1Type _Argument1Type; public: static const bool _TakesProgress = true; static const bool _TakesToken = true; typedef typename _ProgressTypeTraits<_Argument1Type>::_ProgressType _ProgressType; }; ref class _Zip { }; // *************************************************************************** // Async Operation Task Generators // // // Functor returns an IAsyncInfo - result needs to be wrapped in a task: // template struct _SelectorTaskGenerator { template static task<_ReturnType> _GenerateTask_0(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) { task_options _taskOptinos(_Cts.get_token()); details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); return task<_ReturnType>(_Func(), _taskOptinos); } template static task<_ReturnType> _GenerateTask_1C(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) { task_options _taskOptinos(_Cts.get_token()); details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); return task<_ReturnType>(_Func(_Cts.get_token()), _taskOptinos); } template static task<_ReturnType> _GenerateTask_1P(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) { task_options _taskOptinos(_Cts.get_token()); details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); return task<_ReturnType>(_Func(_Progress), _taskOptinos); } template static task<_ReturnType> _GenerateTask_2PC(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) { task_options _taskOptinos(_Cts.get_token()); details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); return task<_ReturnType>(_Func(_Progress, _Cts.get_token()), _taskOptinos); } }; template struct _SelectorTaskGenerator<_AsyncSelector, void> { template static task _GenerateTask_0(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) { task_options _taskOptinos(_Cts.get_token()); details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); return task(_Func(), _taskOptinos); } template static task _GenerateTask_1C(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) { task_options _taskOptinos(_Cts.get_token()); details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); return task(_Func(_Cts.get_token()), _taskOptinos); } template static task _GenerateTask_1P(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) { task_options _taskOptinos(_Cts.get_token()); details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); return task(_Func(_Progress), _taskOptinos); } template static task _GenerateTask_2PC(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) { task_options _taskOptinos(_Cts.get_token()); details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); return task(_Func(_Progress, _Cts.get_token()), _taskOptinos); } }; // // Functor returns a result - it needs to be wrapped in a task: // template struct _SelectorTaskGenerator<_TypeSelectorNoAsync, _ReturnType> { #pragma warning(push) #pragma warning(disable: 4702) template static task<_ReturnType> _GenerateTask_0(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) { task_options _taskOptinos(_Cts.get_token()); details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); return task<_ReturnType>( [=]() -> _ReturnType { _Task_generator_oversubscriber_t _Oversubscriber; (_Oversubscriber); return _Func(); }, _taskOptinos); } #pragma warning(pop) template static task<_ReturnType> _GenerateTask_1C(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) { task_options _taskOptinos(_Cts.get_token()); details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); return task<_ReturnType>( [=]() -> _ReturnType { _Task_generator_oversubscriber_t _Oversubscriber; (_Oversubscriber); return _Func(_Cts.get_token()); }, _taskOptinos); } template static task<_ReturnType> _GenerateTask_1P(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) { task_options _taskOptinos(_Cts.get_token()); details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); return task<_ReturnType>( [=]() -> _ReturnType { _Task_generator_oversubscriber_t _Oversubscriber; (_Oversubscriber); return _Func(_Progress); }, _taskOptinos); } template static task<_ReturnType> _GenerateTask_2PC(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) { task_options _taskOptinos(_Cts.get_token()); details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); return task<_ReturnType>( [=]() -> _ReturnType { _Task_generator_oversubscriber_t _Oversubscriber; (_Oversubscriber); return _Func(_Progress, _Cts.get_token()); }, _taskOptinos); } }; template<> struct _SelectorTaskGenerator<_TypeSelectorNoAsync, void> { template static task _GenerateTask_0(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) { task_options _taskOptinos(_Cts.get_token()); details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); return task( [=]() { _Task_generator_oversubscriber_t _Oversubscriber; (_Oversubscriber); _Func(); }, _taskOptinos); } template static task _GenerateTask_1C(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) { task_options _taskOptinos(_Cts.get_token()); details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); return task( [=]() { _Task_generator_oversubscriber_t _Oversubscriber; (_Oversubscriber); _Func(_Cts.get_token()); }, _taskOptinos); } template static task _GenerateTask_1P(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) { task_options _taskOptinos(_Cts.get_token()); details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); return task( [=]() { _Task_generator_oversubscriber_t _Oversubscriber; (_Oversubscriber); _Func(_Progress); }, _taskOptinos); } template static task _GenerateTask_2PC(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) { task_options _taskOptinos(_Cts.get_token()); details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); return task( [=]() { _Task_generator_oversubscriber_t _Oversubscriber; (_Oversubscriber); _Func(_Progress, _Cts.get_token()); }, _taskOptinos); } }; // // Functor returns a task - the task can directly be returned: // template struct _SelectorTaskGenerator<_TypeSelectorAsyncTask, _ReturnType> { template static task<_ReturnType> _GenerateTask_0(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) { return _Func(); } template static task<_ReturnType> _GenerateTask_1C(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) { return _Func(_Cts.get_token()); } template static task<_ReturnType> _GenerateTask_1P(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) { return _Func(_Progress); } template static task<_ReturnType> _GenerateTask_2PC(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) { return _Func(_Progress, _Cts.get_token()); } }; template<> struct _SelectorTaskGenerator<_TypeSelectorAsyncTask, void> { template static task _GenerateTask_0(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) { return _Func(); } template static task _GenerateTask_1C(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) { return _Func(_Cts.get_token()); } template static task _GenerateTask_1P(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) { return _Func(_Progress); } template static task _GenerateTask_2PC(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) { return _Func(_Progress, _Cts.get_token()); } }; template struct _TaskGenerator { }; template struct _TaskGenerator<_Generator, false, false> { template static auto _GenerateTask(const _Function& _Func, _ClassPtr _Ptr, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) -> decltype(_Generator::_GenerateTask_0(_Func, _Cts, _callstack)) { return _Generator::_GenerateTask_0(_Func, _Cts, _callstack); } }; template struct _TaskGenerator<_Generator, true, false> { template static auto _GenerateTask(const _Function& _Func, _ClassPtr _Ptr, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) -> decltype(_Generator::_GenerateTask_0(_Func, _Cts, _callstack)) { return _Generator::_GenerateTask_1C(_Func, _Cts, _callstack); } }; template struct _TaskGenerator<_Generator, false, true> { template static auto _GenerateTask(const _Function& _Func, _ClassPtr _Ptr, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) -> decltype(_Generator::_GenerateTask_0(_Func, _Cts, _callstack)) { return _Generator::_GenerateTask_1P(_Func, progress_reporter<_ProgressType>::_CreateReporter(_Ptr), _Cts, _callstack); } }; template struct _TaskGenerator<_Generator, true, true> { template static auto _GenerateTask(const _Function& _Func, _ClassPtr _Ptr, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) -> decltype(_Generator::_GenerateTask_0(_Func, _Cts, _callstack)) { return _Generator::_GenerateTask_2PC(_Func, progress_reporter<_ProgressType>::_CreateReporter(_Ptr), _Cts, _callstack); } }; // *************************************************************************** // Async Operation Attributes Classes // // These classes are passed through the hierarchy of async base classes in order to hold multiple attributes of a given async construct in // a single container. An attribute class must define: // // Mandatory: // ------------------------- // // _AsyncBaseType : The Windows Runtime interface which is being implemented. // _CompletionDelegateType : The Windows Runtime completion delegate type for the interface. // _ProgressDelegateType : If _TakesProgress is true, the Windows Runtime progress delegate type for the interface. If it is false, an empty Windows Runtime type. // _ReturnType : The return type of the async construct (void for actions / non-void for operations) // // _TakesProgress : An indication as to whether or not // // _Generate_Task : A function adapting the user's function into what's necessary to produce the appropriate task // // Optional: // ------------------------- // template struct _AsyncAttributes { }; template struct _AsyncAttributes<_Function, _ProgressType, _ReturnType, _TaskTraits, _TakesToken, true> { typedef typename Windows::Foundation::IAsyncOperationWithProgress<_ReturnType, _ProgressType> _AsyncBaseType; typedef typename Windows::Foundation::AsyncOperationProgressHandler<_ReturnType, _ProgressType> _ProgressDelegateType; typedef typename Windows::Foundation::AsyncOperationWithProgressCompletedHandler<_ReturnType, _ProgressType> _CompletionDelegateType; typedef typename _ReturnType _ReturnType; typedef typename _ProgressType _ProgressType; typedef typename _TaskTraits::_AsyncKind _AsyncKind; typedef typename _SelectorTaskGenerator<_AsyncKind, _ReturnType> _SelectorTaskGenerator; typedef typename _TaskGenerator<_SelectorTaskGenerator, _TakesToken, true> _TaskGenerator; static const bool _TakesProgress = true; static const bool _TakesToken = _TakesToken; template static task<_ReturnType> _Generate_Task(const _Function& _Func, _ClassPtr _Ptr, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) { return _TaskGenerator::_GenerateTask<_Function, _ClassPtr, _ProgressType>(_Func, _Ptr, _Cts, _callstack); } }; template struct _AsyncAttributes<_Function, _ProgressType, _ReturnType, _TaskTraits, _TakesToken, false> { typedef typename Windows::Foundation::IAsyncOperation<_ReturnType> _AsyncBaseType; typedef _Zip _ProgressDelegateType; typedef typename Windows::Foundation::AsyncOperationCompletedHandler<_ReturnType> _CompletionDelegateType; typedef typename _ReturnType _ReturnType; typedef typename _TaskTraits::_AsyncKind _AsyncKind; typedef typename _SelectorTaskGenerator<_AsyncKind, _ReturnType> _SelectorTaskGenerator; typedef typename _TaskGenerator<_SelectorTaskGenerator, _TakesToken, false> _TaskGenerator; static const bool _TakesProgress = false; static const bool _TakesToken = _TakesToken; template static task<_ReturnType> _Generate_Task(const _Function& _Func, _ClassPtr _Ptr, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) { return _TaskGenerator::_GenerateTask<_Function, _ClassPtr, _ProgressType>(_Func, _Ptr, _Cts, _callstack); } }; template struct _AsyncAttributes<_Function, _ProgressType, void, _TaskTraits, _TakesToken, true> { typedef typename Windows::Foundation::IAsyncActionWithProgress<_ProgressType> _AsyncBaseType; typedef typename Windows::Foundation::AsyncActionProgressHandler<_ProgressType> _ProgressDelegateType; typedef typename Windows::Foundation::AsyncActionWithProgressCompletedHandler<_ProgressType> _CompletionDelegateType; typedef void _ReturnType; typedef typename _ProgressType _ProgressType; typedef typename _TaskTraits::_AsyncKind _AsyncKind; typedef typename _SelectorTaskGenerator<_AsyncKind, _ReturnType> _SelectorTaskGenerator; typedef typename _TaskGenerator<_SelectorTaskGenerator, _TakesToken, true> _TaskGenerator; static const bool _TakesProgress = true; static const bool _TakesToken = _TakesToken; template static task<_ReturnType> _Generate_Task(const _Function& _Func, _ClassPtr _Ptr, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) { return _TaskGenerator::_GenerateTask<_Function, _ClassPtr, _ProgressType>(_Func, _Ptr, _Cts, _callstack); } }; template struct _AsyncAttributes<_Function, _ProgressType, void, _TaskTraits, _TakesToken, false> { typedef typename Windows::Foundation::IAsyncAction _AsyncBaseType; typedef _Zip _ProgressDelegateType; typedef typename Windows::Foundation::AsyncActionCompletedHandler _CompletionDelegateType; typedef void _ReturnType; typedef typename _TaskTraits::_AsyncKind _AsyncKind; typedef typename _SelectorTaskGenerator<_AsyncKind, _ReturnType> _SelectorTaskGenerator; typedef typename _TaskGenerator<_SelectorTaskGenerator, _TakesToken, false> _TaskGenerator; static const bool _TakesProgress = false; static const bool _TakesToken = _TakesToken; template static task<_ReturnType> _Generate_Task(const _Function& _Func, _ClassPtr _Ptr, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) { return _TaskGenerator::_GenerateTask<_Function, _ClassPtr, _ProgressType>(_Func, _Ptr, _Cts, _callstack); } }; template struct _AsyncLambdaTypeTraits { typedef typename _FunctorTypeTraits<_Function>::_ReturnType _ReturnType; typedef typename _FunctorTypeTraits<_Function>::_Argument1Type _Argument1Type; typedef typename _CAFunctorOptions<_Function>::_ProgressType _ProgressType; static const bool _TakesProgress = _CAFunctorOptions<_Function>::_TakesProgress; static const bool _TakesToken = _CAFunctorOptions<_Function>::_TakesToken; typedef typename _TaskTypeTraits<_ReturnType> _TaskTraits; typedef typename _AsyncAttributes<_Function, _ProgressType, typename _TaskTraits::_TaskRetType, _TaskTraits, _TakesToken, _TakesProgress> _AsyncAttributes; }; // *************************************************************************** // AsyncInfo (and completion) Layer: // // // Internal base class implementation for async operations (based on internal Windows representation for ABI level async operations) // template < typename _Attributes, _AsyncResultType resultType = SingleResult > ref class _AsyncInfoBase abstract : _Attributes::_AsyncBaseType { internal: _AsyncInfoBase() : _M_currentStatus(_AsyncStatusInternal::_AsyncCreated), _M_errorCode(S_OK), _M_completeDelegate(nullptr), _M_CompleteDelegateAssigned(0), _M_CallbackMade(0) { _M_id = ::pplx::details::platform::GetNextAsyncId(); } public: virtual typename _Attributes::_ReturnType GetResults() { throw ::Platform::Exception::CreateException(E_UNEXPECTED); } virtual property unsigned int Id { unsigned int get() { _CheckValidStateForAsyncInfoCall(); return _M_id; } void set(unsigned int id) { _CheckValidStateForAsyncInfoCall(); if (id == 0) { throw ::Platform::Exception::CreateException(E_INVALIDARG); } else if (_M_currentStatus != _AsyncStatusInternal::_AsyncCreated) { throw ::Platform::Exception::CreateException(E_ILLEGAL_METHOD_CALL); } _M_id = id; } } virtual property Windows::Foundation::AsyncStatus Status { Windows::Foundation::AsyncStatus get() { _CheckValidStateForAsyncInfoCall(); _AsyncStatusInternal _Current = _M_currentStatus; // // Map our internal cancel pending to cancelled. This way "pending cancelled" looks to the outside as "cancelled" but // can still transition to "completed" if the operation completes without acknowledging the cancellation request // switch(_Current) { case _AsyncCancelPending: _Current = _AsyncCanceled; break; case _AsyncCreated: _Current = _AsyncStarted; break; default: break; } return static_cast(_Current); } } virtual property Windows::Foundation::HResult ErrorCode { Windows::Foundation::HResult get() { _CheckValidStateForAsyncInfoCall(); Windows::Foundation::HResult _Hr; _Hr.Value = _M_errorCode; return _Hr; } } virtual property typename _Attributes::_ProgressDelegateType^ Progress { typename typename _Attributes::_ProgressDelegateType^ get() { return _GetOnProgress(); } void set(typename _Attributes::_ProgressDelegateType^ _ProgressHandler) { _PutOnProgress(_ProgressHandler); } } virtual void Cancel() { if (_TransitionToState(_AsyncCancelPending)) { _OnCancel(); } } virtual void Close() { if (_TransitionToState(_AsyncClosed)) { _OnClose(); } else { if (_M_currentStatus != _AsyncClosed) // Closed => Closed transition is just ignored { throw ::Platform::Exception::CreateException(E_ILLEGAL_STATE_CHANGE); } } } virtual property typename _Attributes::_CompletionDelegateType^ Completed { typename _Attributes::_CompletionDelegateType^ get() { _CheckValidStateForDelegateCall(); return _M_completeDelegate; } void set(typename _Attributes::_CompletionDelegateType^ _CompleteHandler) { _CheckValidStateForDelegateCall(); // this delegate property is "write once" if (InterlockedIncrement(&_M_CompleteDelegateAssigned) == 1) { _M_completeDelegateContext = _ContextCallback::_CaptureCurrent(); _M_completeDelegate = _CompleteHandler; // Guarantee that the write of _M_completeDelegate is ordered with respect to the read of state below // as perceived from _FireCompletion on another thread. MemoryBarrier(); if (_IsTerminalState()) { _FireCompletion(); } } else { throw ::Platform::Exception::CreateException(E_ILLEGAL_DELEGATE_ASSIGNMENT); } } } protected private: // _Start - this is not externally visible since async operations "hot start" before returning to the caller void _Start() { if (_TransitionToState(_AsyncStarted)) { _OnStart(); } else { throw ::Platform::Exception::CreateException(E_ILLEGAL_STATE_CHANGE); } } void _FireCompletion() { _TryTransitionToCompleted(); // we guarantee that completion can only ever be fired once if (_M_completeDelegate != nullptr && InterlockedIncrement(&_M_CallbackMade) == 1) { _M_completeDelegateContext._CallInContext([=] { _M_completeDelegate((_Attributes::_AsyncBaseType^)this, this->Status); _M_completeDelegate = nullptr; }); } } virtual typename _Attributes::_ProgressDelegateType^ _GetOnProgress() { throw ::Platform::Exception::CreateException(E_UNEXPECTED); } virtual void _PutOnProgress(typename _Attributes::_ProgressDelegateType^ _ProgressHandler) { throw ::Platform::Exception::CreateException(E_UNEXPECTED); } bool _TryTransitionToCompleted() { return _TransitionToState(_AsyncStatusInternal::_AsyncCompleted); } bool _TryTransitionToCancelled() { return _TransitionToState(_AsyncStatusInternal::_AsyncCanceled); } bool _TryTransitionToError(const HRESULT error) { _InterlockedCompareExchange(reinterpret_cast(&_M_errorCode), error, S_OK); return _TransitionToState(_AsyncStatusInternal::_AsyncError); } // This method checks to see if the delegate properties can be // modified in the current state and generates the appropriate // error hr in the case of violation. inline void _CheckValidStateForDelegateCall() { if (_M_currentStatus == _AsyncClosed) { throw ::Platform::Exception::CreateException(E_ILLEGAL_METHOD_CALL); } } // This method checks to see if results can be collected in the // current state and generates the appropriate error hr in // the case of a violation. inline void _CheckValidStateForResultsCall() { _AsyncStatusInternal _Current = _M_currentStatus; if (_Current == _AsyncError) { throw ::Platform::Exception::CreateException(_M_errorCode); } #pragma warning(push) #pragma warning(disable: 4127) // Conditional expression is constant // single result illegal before transition to Completed or Cancelled state if (resultType == SingleResult) #pragma warning(pop) { if (_Current != _AsyncCompleted) { throw ::Platform::Exception::CreateException(E_ILLEGAL_METHOD_CALL); } } // multiple results can be called after Start has been called and before/after Completed else if (_Current != _AsyncStarted && _Current != _AsyncCancelPending && _Current != _AsyncCanceled && _Current != _AsyncCompleted) { throw ::Platform::Exception::CreateException(E_ILLEGAL_METHOD_CALL); } } // This method can be called by derived classes periodically to determine // whether the asynchronous operation should continue processing or should // be halted. inline bool _ContinueAsyncOperation() { return (_M_currentStatus == _AsyncStarted); } // These two methods are used to allow the async worker implementation do work on // state transitions. No real "work" should be done in these methods. In other words // they should not block for a long time on UI timescales. virtual void _OnStart() = 0; virtual void _OnClose() = 0; virtual void _OnCancel() = 0; private: // This method is used to check if calls to the AsyncInfo properties // (id, status, errorcode) are legal in the current state. It also // generates the appropriate error hr to return in the case of an // illegal call. inline void _CheckValidStateForAsyncInfoCall() { _AsyncStatusInternal _Current = _M_currentStatus; if (_Current == _AsyncClosed) { throw ::Platform::Exception::CreateException(E_ILLEGAL_METHOD_CALL); } else if (_Current == _AsyncCreated) { throw ::Platform::Exception::CreateException(E_ASYNC_OPERATION_NOT_STARTED); } } inline bool _TransitionToState(const _AsyncStatusInternal _NewState) { _AsyncStatusInternal _Current = _M_currentStatus; // This enforces the valid state transitions of the asynchronous worker object // state machine. switch(_NewState) { case _AsyncStatusInternal::_AsyncStarted: if (_Current != _AsyncCreated) { return false; } break; case _AsyncStatusInternal::_AsyncCompleted: if (_Current != _AsyncStarted && _Current != _AsyncCancelPending) { return false; } break; case _AsyncStatusInternal::_AsyncCancelPending: if (_Current != _AsyncStarted) { return false; } break; case _AsyncStatusInternal::_AsyncCanceled: if (_Current != _AsyncStarted && _Current != _AsyncCancelPending) { return false; } break; case _AsyncStatusInternal::_AsyncError: if (_Current != _AsyncStarted && _Current != _AsyncCancelPending) { return false; } break; case _AsyncStatusInternal::_AsyncClosed: if (!_IsTerminalState(_Current)) { return false; } break; default: return false; break; } // attempt the transition to the new state // Note: if currentStatus_ == _Current, then there was no intervening write // by the async work object and the swap succeeded. _AsyncStatusInternal _RetState = static_cast<_AsyncStatusInternal>( _InterlockedCompareExchange(reinterpret_cast(&_M_currentStatus), _NewState, static_cast(_Current))); // ICE returns the former state, if the returned state and the // state we captured at the beginning of this method are the same, // the swap succeeded. return (_RetState == _Current); } inline bool _IsTerminalState() { return _IsTerminalState(_M_currentStatus); } inline bool _IsTerminalState(_AsyncStatusInternal status) { return (status == _AsyncError || status == _AsyncCanceled || status == _AsyncCompleted || status == _AsyncClosed); } private: _ContextCallback _M_completeDelegateContext; typename _Attributes::_CompletionDelegateType^ volatile _M_completeDelegate; _AsyncStatusInternal volatile _M_currentStatus; HRESULT volatile _M_errorCode; unsigned int _M_id; long volatile _M_CompleteDelegateAssigned; long volatile _M_CallbackMade; }; // *************************************************************************** // Progress Layer (optional): // template< typename _Attributes, bool _HasProgress, _AsyncResultType _ResultType = SingleResult > ref class _AsyncProgressBase abstract : _AsyncInfoBase<_Attributes, _ResultType> { }; template< typename _Attributes, _AsyncResultType _ResultType> ref class _AsyncProgressBase<_Attributes, true, _ResultType> abstract : _AsyncInfoBase<_Attributes, _ResultType> { internal: _AsyncProgressBase() : _AsyncInfoBase<_Attributes, _ResultType>(), _M_progressDelegate(nullptr) { } virtual typename _Attributes::_ProgressDelegateType^ _GetOnProgress() override { _CheckValidStateForDelegateCall(); return _M_progressDelegate; } virtual void _PutOnProgress(typename _Attributes::_ProgressDelegateType^ _ProgressHandler) override { _CheckValidStateForDelegateCall(); _M_progressDelegate = _ProgressHandler; _M_progressDelegateContext = _ContextCallback::_CaptureCurrent(); } void _FireProgress(const typename _Attributes::_ProgressType& _ProgressValue) { if (_M_progressDelegate != nullptr) { _M_progressDelegateContext._CallInContext([=] { _M_progressDelegate((_Attributes::_AsyncBaseType^)this, _ProgressValue); }); } } private: _ContextCallback _M_progressDelegateContext; typename _Attributes::_ProgressDelegateType^ _M_progressDelegate; }; template ref class _AsyncBaseProgressLayer abstract : _AsyncProgressBase<_Attributes, _Attributes::_TakesProgress, _ResultType> { }; // *************************************************************************** // Task Adaptation Layer: // // // _AsyncTaskThunkBase provides a bridge between IAsync and task. // template ref class _AsyncTaskThunkBase abstract : _AsyncBaseProgressLayer<_Attributes> { public: virtual _ReturnType GetResults() override { _CheckValidStateForResultsCall(); return _M_task.get(); } internal: typedef task<_ReturnType> _TaskType; _AsyncTaskThunkBase(const _TaskType& _Task) : _M_task(_Task) { } _AsyncTaskThunkBase() { } protected: virtual void _OnStart() override { _M_task.then( [=](_TaskType _Antecedent) { try { _Antecedent.get(); } catch(task_canceled&) { _TryTransitionToCancelled(); } catch(::Platform::Exception^ _Ex) { _TryTransitionToError(_Ex->HResult); } catch(...) { _TryTransitionToError(E_FAIL); } _FireCompletion(); }); } internal: _TaskType _M_task; cancellation_token_source _M_cts; }; template ref class _AsyncTaskThunk : _AsyncTaskThunkBase<_Attributes, typename _Attributes::_ReturnType> { internal: _AsyncTaskThunk(const _TaskType& _Task) : _AsyncTaskThunkBase(_Task) { } _AsyncTaskThunk() { } protected: virtual void _OnClose() override { } virtual void _OnCancel() override { _M_cts.cancel(); } }; // *************************************************************************** // Async Creation Layer: // template ref class _AsyncTaskGeneratorThunk sealed : _AsyncTaskThunk::_AsyncAttributes> { internal: typedef typename _AsyncLambdaTypeTraits<_Function>::_AsyncAttributes _Attributes; typedef typename _AsyncTaskThunk<_Attributes> _Base; typedef typename _Attributes::_AsyncBaseType _AsyncBaseType; _AsyncTaskGeneratorThunk(const _Function& _Func, const _TaskCreationCallstack &_callstack) : _M_func(_Func), _M_creationCallstack(_callstack) { // Virtual call here is safe as the class is declared 'sealed' _Start(); } protected: // // The only thing we must do different from the base class is we must spin the hot task on transition from Created->Started. Otherwise, // let the base thunk handle everything. // virtual void _OnStart() override { // // Call the appropriate task generator to actually produce a task of the expected type. This might adapt the user lambda for progress reports, // wrap the return result in a task, or allow for direct return of a task depending on the form of the lambda. // _M_task = _Attributes::_Generate_Task(_M_func, this, _M_cts, _M_creationCallstack); _Base::_OnStart(); } virtual void _OnCancel() override { _Base::_OnCancel(); } private: _TaskCreationCallstack _M_creationCallstack; _Function _M_func; }; } // namespace details /// /// Creates a Windows Runtime asynchronous construct based on a user supplied lambda or function object. The return type of create_async is /// one of either IAsyncAction^, IAsyncActionWithProgress<TProgress>^, IAsyncOperation<TResult>^, or /// IAsyncOperationWithProgress<TResult, TProgress>^ based on the signature of the lambda passed to the method. /// /// /// The lambda or function object from which to create a Windows Runtime asynchronous construct. /// /// /// An asynchronous construct represented by an IAsyncAction^, IAsyncActionWithProgress<TProgress>^, IAsyncOperation<TResult>^, or an /// IAsyncOperationWithProgress<TResult, TProgress>^. The interface returned depends on the signature of the lambda passed into the function. /// /// /// The return type of the lambda determines whether the construct is an action or an operation. /// Lambdas that return void cause the creation of actions. Lambdas that return a result of type TResult cause the creation of /// operations of TResult. /// The lambda may also return a task<TResult> which encapsulates the aysnchronous work within itself or is the continuation of /// a chain of tasks that represent the asynchronous work. In this case, the lambda itself is executed inline, since the tasks are the ones that /// execute asynchronously, and the return type of the lambda is unwrapped to produce the asynchronous construct returned by create_async. /// This implies that a lambda that returns a task<void> will cause the creation of actions, and a lambda that returns a task<TResult> will /// cause the creation of operations of TResult. /// The lambda may take either zero, one or two arguments. The valid arguments are progress_reporter<TProgress> and /// cancellation_token, in that order if both are used. A lambda without arguments causes the creation of an asynchronous construct without /// the capability for progress reporting. A lambda that takes a progress_reporter<TProgress> will cause create_async to return an asynchronous /// construct which reports progress of type TProgress each time the report method of the progress_reporter object is called. A lambda that /// takes a cancellation_token may use that token to check for cancellation, or pass it to tasks that it creates so that cancellation of the /// asynchronous construct causes cancellation of those tasks. /// If the body of the lambda or function object returns a result (and not a task<TResult>), the lamdba will be executed /// asynchronously within the process MTA in the context of a task the Runtime implicitly creates for it. The IAsyncInfo::Cancel method will /// cause cancellation of the implicit task. /// If the body of the lambda returns a task, the lamba executes inline, and by declaring the lambda to take an argument of type /// cancellation_token you can trigger cancellation of any tasks you create within the lambda by passing that token in when you create them. /// You may also use the register_callback method on the token to cause the Runtime to invoke a callback when you call IAsyncInfo::Cancel on /// the async operation or action produced.. /// This function is only available to Windows Store apps. /// /// /// /// /**/ template __declspec(noinline) details::_AsyncTaskGeneratorThunk<_Function> ^create_async(const _Function& _Func) { static_assert(std::is_same::value, "argument to create_async must be a callable object taking zero, one or two arguments"); return ref new details::_AsyncTaskGeneratorThunk<_Function>(_Func, _CAPTURE_CALLSTACK()); } #endif /* defined (__cplusplus_winrt) */ namespace details { // Helper struct for when_all operators to know when tasks have completed template struct _RunAllParam { _RunAllParam() : _M_completeCount(0), _M_numTasks(0) { } void _Resize(size_t _Len, bool _SkipVector = false) { _M_numTasks = _Len; if (!_SkipVector) { _M_vector._Result.resize(_Len); } } task_completion_event<_Unit_type> _M_completed; _ResultHolder > _M_vector; _ResultHolder<_Type> _M_mergeVal; atomic_size_t _M_completeCount; size_t _M_numTasks; }; template struct _RunAllParam > { _RunAllParam() : _M_completeCount(0), _M_numTasks(0) { } void _Resize(size_t _Len, bool _SkipVector = false) { _M_numTasks = _Len; if (!_SkipVector) { _M_vector.resize(_Len); } } task_completion_event<_Unit_type> _M_completed; std::vector<_ResultHolder > > _M_vector; atomic_size_t _M_completeCount; size_t _M_numTasks; }; // Helper struct specialization for void template<> struct _RunAllParam<_Unit_type> { _RunAllParam() : _M_completeCount(0), _M_numTasks(0) { } void _Resize(size_t _Len) { _M_numTasks = _Len; } task_completion_event<_Unit_type> _M_completed; atomic_size_t _M_completeCount; size_t _M_numTasks; }; inline void _JoinAllTokens_Add(const cancellation_token_source& _MergedSrc, _CancellationTokenState *_PJoinedTokenState) { if (_PJoinedTokenState != nullptr && _PJoinedTokenState != _CancellationTokenState::_None()) { cancellation_token _T = cancellation_token::_FromImpl(_PJoinedTokenState); _T.register_callback( [=](){ _MergedSrc.cancel(); }); } } template void _WhenAllContinuationWrapper(_RunAllParam<_ElementType>* _PParam, _Function _Func, task<_TaskType>& _Task) { if (_Task._GetImpl()->_IsCompleted()) { _Func(); if (atomic_increment(_PParam->_M_completeCount) == _PParam->_M_numTasks) { // Inline execute its direct continuation, the _ReturnTask _PParam->_M_completed.set(_Unit_type()); // It's safe to delete it since all usage of _PParam in _ReturnTask has been finished. delete _PParam; } } else { _ASSERTE(_Task._GetImpl()->_IsCanceled()); if (_Task._GetImpl()->_HasUserException()) { // _Cancel will return false if the TCE is already canceled with or without exception _PParam->_M_completed._Cancel(_Task._GetImpl()->_GetExceptionHolder()); } else { _PParam->_M_completed._Cancel(); } if (atomic_increment(_PParam->_M_completeCount) == _PParam->_M_numTasks) { delete _PParam; } } } template struct _WhenAllImpl { static task> _Perform(const task_options& _TaskOptions, _Iterator _Begin, _Iterator _End) { _CancellationTokenState *_PTokenState = _TaskOptions.has_cancellation_token() ? _TaskOptions.get_cancellation_token()._GetImplValue() : nullptr; auto _PParam = new _RunAllParam<_ElementType>(); cancellation_token_source _MergedSource; // Step1: Create task completion event. task_options _Options(_TaskOptions); _Options.set_cancellation_token(_MergedSource.get_token()); task<_Unit_type> _All_tasks_completed(_PParam->_M_completed, _Options); // The return task must be created before step 3 to enforce inline execution. auto _ReturnTask = _All_tasks_completed._Then([=](_Unit_type) -> std::vector<_ElementType> { return _PParam->_M_vector.Get(); }, nullptr); // Step2: Combine and check tokens, and count elements in range. if (_PTokenState) { _JoinAllTokens_Add(_MergedSource, _PTokenState); _PParam->_Resize(static_cast(std::distance(_Begin, _End))); } else { size_t _TaskNum = 0; for (auto _PTask = _Begin; _PTask != _End; ++_PTask) { _TaskNum++; _JoinAllTokens_Add(_MergedSource, _PTask->_GetImpl()->_M_pTokenState); } _PParam->_Resize(_TaskNum); } // Step3: Check states of previous tasks. if( _Begin == _End ) { _PParam->_M_completed.set(_Unit_type()); delete _PParam; } else { size_t _Index = 0; for (auto _PTask = _Begin; _PTask != _End; ++_PTask) { if (_PTask->is_apartment_aware()) { _ReturnTask._SetAsync(); } _PTask->_Then([_PParam, _Index](task<_ElementType> _ResultTask) { auto _PParamCopy = _PParam; auto _IndexCopy = _Index; auto _Func = [_PParamCopy, _IndexCopy, &_ResultTask](){ _PParamCopy->_M_vector._Result[_IndexCopy] = _ResultTask._GetImpl()->_GetResult(); }; _WhenAllContinuationWrapper(_PParam, _Func, _ResultTask); }, _CancellationTokenState::_None()); _Index++; } } return _ReturnTask; } }; template struct _WhenAllImpl, _Iterator> { static task> _Perform(const task_options& _TaskOptions, _Iterator _Begin, _Iterator _End) { _CancellationTokenState *_PTokenState = _TaskOptions.has_cancellation_token() ? _TaskOptions.get_cancellation_token()._GetImplValue() : nullptr; auto _PParam = new _RunAllParam>(); cancellation_token_source _MergedSource; // Step1: Create task completion event. task_options _Options(_TaskOptions); _Options.set_cancellation_token(_MergedSource.get_token()); task<_Unit_type> _All_tasks_completed(_PParam->_M_completed, _Options); // The return task must be created before step 3 to enforce inline execution. auto _ReturnTask = _All_tasks_completed._Then([=](_Unit_type) -> std::vector<_ElementType> { _ASSERTE(_PParam->_M_completeCount == _PParam->_M_numTasks); std::vector<_ElementType> _Result; for(size_t _I = 0; _I < _PParam->_M_numTasks; _I++) { const std::vector<_ElementType>& _Vec = _PParam->_M_vector[_I].Get(); _Result.insert(_Result.end(), _Vec.begin(), _Vec.end()); } return _Result; }, nullptr); // Step2: Combine and check tokens, and count elements in range. if (_PTokenState) { _JoinAllTokens_Add(_MergedSource, _PTokenState); _PParam->_Resize(static_cast(std::distance(_Begin, _End))); } else { size_t _TaskNum = 0; for (auto _PTask = _Begin; _PTask != _End; ++_PTask) { _TaskNum++; _JoinAllTokens_Add(_MergedSource, _PTask->_GetImpl()->_M_pTokenState); } _PParam->_Resize(_TaskNum); } // Step3: Check states of previous tasks. if( _Begin == _End ) { _PParam->_M_completed.set(_Unit_type()); delete _PParam; } else { size_t _Index = 0; for (auto _PTask = _Begin; _PTask != _End; ++_PTask) { if (_PTask->is_apartment_aware()) { _ReturnTask._SetAsync(); } _PTask->_Then([_PParam, _Index](task> _ResultTask) { auto _PParamCopy = _PParam; auto _IndexCopy = _Index; auto _Func = [_PParamCopy, _IndexCopy, &_ResultTask]() { _PParamCopy->_M_vector[_IndexCopy].Set(_ResultTask._GetImpl()->_GetResult()); }; _WhenAllContinuationWrapper(_PParam, _Func, _ResultTask); }, _CancellationTokenState::_None()); _Index++; } } return _ReturnTask; } }; template struct _WhenAllImpl { static task _Perform(const task_options& _TaskOptions, _Iterator _Begin, _Iterator _End) { _CancellationTokenState *_PTokenState = _TaskOptions.has_cancellation_token() ? _TaskOptions.get_cancellation_token()._GetImplValue() : nullptr; auto _PParam = new _RunAllParam<_Unit_type>(); cancellation_token_source _MergedSource; // Step1: Create task completion event. task_options _Options(_TaskOptions); _Options.set_cancellation_token(_MergedSource.get_token()); task<_Unit_type> _All_tasks_completed(_PParam->_M_completed, _Options); // The return task must be created before step 3 to enforce inline execution. auto _ReturnTask = _All_tasks_completed._Then([=](_Unit_type) { }, nullptr); // Step2: Combine and check tokens, and count elements in range. if (_PTokenState) { _JoinAllTokens_Add(_MergedSource, _PTokenState); _PParam->_Resize(static_cast(std::distance(_Begin, _End))); } else { size_t _TaskNum = 0; for (auto _PTask = _Begin; _PTask != _End; ++_PTask) { _TaskNum++; _JoinAllTokens_Add(_MergedSource, _PTask->_GetImpl()->_M_pTokenState); } _PParam->_Resize(_TaskNum); } // Step3: Check states of previous tasks. if( _Begin == _End ) { _PParam->_M_completed.set(_Unit_type()); delete _PParam; } else { for (auto _PTask = _Begin; _PTask != _End; ++_PTask) { if (_PTask->is_apartment_aware()) { _ReturnTask._SetAsync(); } _PTask->_Then([_PParam](task _ResultTask) { auto _Func = [](){}; _WhenAllContinuationWrapper(_PParam, _Func, _ResultTask); }, _CancellationTokenState::_None()); } } return _ReturnTask; } }; template task> _WhenAllVectorAndValue(const task>& _VectorTask, const task<_ReturnType>& _ValueTask, bool _OutputVectorFirst) { auto _PParam = new _RunAllParam<_ReturnType>(); cancellation_token_source _MergedSource; // Step1: Create task completion event. task<_Unit_type> _All_tasks_completed(_PParam->_M_completed, _MergedSource.get_token()); // The return task must be created before step 3 to enforce inline execution. auto _ReturnTask = _All_tasks_completed._Then([=](_Unit_type) -> std::vector<_ReturnType> { _ASSERTE(_PParam->_M_completeCount == 2); auto _Result = _PParam->_M_vector.Get(); // copy by value auto _mergeVal = _PParam->_M_mergeVal.Get(); if (_OutputVectorFirst == true) { _Result.push_back(_mergeVal); } else { _Result.insert(_Result.begin(), _mergeVal); } return _Result; }, nullptr); // Step2: Combine and check tokens. _JoinAllTokens_Add(_MergedSource, _VectorTask._GetImpl()->_M_pTokenState); _JoinAllTokens_Add(_MergedSource, _ValueTask._GetImpl()->_M_pTokenState); // Step3: Check states of previous tasks. _PParam->_Resize(2, true); if (_VectorTask.is_apartment_aware() || _ValueTask.is_apartment_aware()) { _ReturnTask._SetAsync(); } _VectorTask._Then([_PParam](task> _ResultTask) { auto _PParamCopy = _PParam; auto _Func = [_PParamCopy, &_ResultTask]() { auto _ResultLocal = _ResultTask._GetImpl()->_GetResult(); _PParamCopy->_M_vector.Set(_ResultLocal); }; _WhenAllContinuationWrapper(_PParam, _Func, _ResultTask); }, _CancellationTokenState::_None()); _ValueTask._Then([_PParam](task<_ReturnType> _ResultTask) { auto _PParamCopy = _PParam; auto _Func = [_PParamCopy, &_ResultTask]() { auto _ResultLocal = _ResultTask._GetImpl()->_GetResult(); _PParamCopy->_M_mergeVal.Set(_ResultLocal); }; _WhenAllContinuationWrapper(_PParam, _Func, _ResultTask); }, _CancellationTokenState::_None()); return _ReturnTask; } } // namespace details /// /// Creates a task that will complete successfully when all of the tasks supplied as arguments complete successfully. /// /// /// The type of the input iterator. /// /// /// The position of the first element in the range of elements to be combined into the resulting task. /// /// /// The position of the first element beyond the range of elements to be combined into the resulting task. /// /// /// A task that completes sucessfully when all of the input tasks have completed successfully. If the input tasks are of type T, /// the output of this function will be a task<std::vector<T>>. If the input tasks are of type void the output /// task will also be a task<void>. /// /// /// If one of the tasks is canceled or throws an exception, the returned task will complete early, in the canceled state, and the exception, /// if one is encoutered, will be thrown if you call get() or wait() on that task. /// /// /**/ template auto when_all(_Iterator _Begin, _Iterator _End, const task_options& _TaskOptions = task_options()) -> decltype (details::_WhenAllImpl::value_type::result_type, _Iterator>::_Perform(_TaskOptions, _Begin, _End)) { typedef typename std::iterator_traits<_Iterator>::value_type::result_type _ElementType; return details::_WhenAllImpl<_ElementType, _Iterator>::_Perform(_TaskOptions, _Begin, _End); } /// /// Creates a task that will complete succesfully when both of the tasks supplied as arguments complete successfully. /// /// /// The type of the returned task. /// /// /// The first task to combine into the resulting task. /// /// /// The second task to combine into the resulting task. /// /// /// A task that completes successfully when both of the input tasks have completed successfully. If the input tasks are of type T, /// the output of this function will be a task<std::vector<T>>. If the input tasks are of type void the output /// task will also be a task<void>. /// To allow for a construct of the sort taskA && taskB && taskC, which are combined in pairs, the && operator /// produces a task<std::vector<T>> if either one or both of the tasks are of type task<std::vector<T>>. /// /// /// If one of the tasks is canceled or throws an exception, the returned task will complete early, in the canceled state, and the exception, /// if one is encoutered, will be thrown if you call get() or wait() on that task. /// /// /**/ template task> operator&&(const task<_ReturnType> & _Lhs, const task<_ReturnType> & _Rhs) { task<_ReturnType> _PTasks[2] = {_Lhs, _Rhs}; return when_all(_PTasks, _PTasks+2); } /// /// Creates a task that will complete succesfully when both of the tasks supplied as arguments complete successfully. /// /// /// The type of the returned task. /// /// /// The first task to combine into the resulting task. /// /// /// The second task to combine into the resulting task. /// /// /// A task that completes successfully when both of the input tasks have completed successfully. If the input tasks are of type T, /// the output of this function will be a task<std::vector<T>>. If the input tasks are of type void the output /// task will also be a task<void>. /// To allow for a construct of the sort taskA && taskB && taskC, which are combined in pairs, the && operator /// produces a task<std::vector<T>> if either one or both of the tasks are of type task<std::vector<T>>. /// /// /// If one of the tasks is canceled or throws an exception, the returned task will complete early, in the canceled state, and the exception, /// if one is encoutered, will be thrown if you call get() or wait() on that task. /// /// /**/ template task> operator&&(const task> & _Lhs, const task<_ReturnType> & _Rhs) { return details::_WhenAllVectorAndValue(_Lhs, _Rhs, true); } /// /// Creates a task that will complete succesfully when both of the tasks supplied as arguments complete successfully. /// /// /// The type of the returned task. /// /// /// The first task to combine into the resulting task. /// /// /// The second task to combine into the resulting task. /// /// /// A task that completes successfully when both of the input tasks have completed successfully. If the input tasks are of type T, /// the output of this function will be a task<std::vector<T>>. If the input tasks are of type void the output /// task will also be a task<void>. /// To allow for a construct of the sort taskA && taskB && taskC, which are combined in pairs, the && operator /// produces a task<std::vector<T>> if either one or both of the tasks are of type task<std::vector<T>>. /// /// /// If one of the tasks is canceled or throws an exception, the returned task will complete early, in the canceled state, and the exception, /// if one is encoutered, will be thrown if you call get() or wait() on that task. /// /// /**/ template task> operator&&(const task<_ReturnType> & _Lhs, const task> & _Rhs) { return details::_WhenAllVectorAndValue(_Rhs, _Lhs, false); } /// /// Creates a task that will complete succesfully when both of the tasks supplied as arguments complete successfully. /// /// /// The type of the returned task. /// /// /// The first task to combine into the resulting task. /// /// /// The second task to combine into the resulting task. /// /// /// A task that completes successfully when both of the input tasks have completed successfully. If the input tasks are of type T, /// the output of this function will be a task<std::vector<T>>. If the input tasks are of type void the output /// task will also be a task<void>. /// To allow for a construct of the sort taskA && taskB && taskC, which are combined in pairs, the && operator /// produces a task<std::vector<T>> if either one or both of the tasks are of type task<std::vector<T>>. /// /// /// If one of the tasks is canceled or throws an exception, the returned task will complete early, in the canceled state, and the exception, /// if one is encoutered, will be thrown if you call get() or wait() on that task. /// /// /**/ template task> operator&&(const task> & _Lhs, const task> & _Rhs) { task> _PTasks[2] = {_Lhs, _Rhs}; return when_all(_PTasks, _PTasks+2); } /// /// Creates a task that will complete succesfully when both of the tasks supplied as arguments complete successfully. /// /// /// The type of the returned task. /// /// /// The first task to combine into the resulting task. /// /// /// The second task to combine into the resulting task. /// /// /// A task that completes successfully when both of the input tasks have completed successfully. If the input tasks are of type T, /// the output of this function will be a task<std::vector<T>>. If the input tasks are of type void the output /// task will also be a task<void>. /// To allow for a construct of the sort taskA && taskB && taskC, which are combined in pairs, the && operator /// produces a task<std::vector<T>> if either one or both of the tasks are of type task<std::vector<T>>. /// /// /// If one of the tasks is canceled or throws an exception, the returned task will complete early, in the canceled state, and the exception, /// if one is encoutered, will be thrown if you call get() or wait() on that task. /// /// /**/ inline task operator&&(const task & _Lhs, const task & _Rhs) { task _PTasks[2] = {_Lhs, _Rhs}; return when_all(_PTasks, _PTasks+2); } namespace details { // Helper struct for when_any operators to know when tasks have completed template struct _RunAnyParam { _RunAnyParam() : _M_exceptionRelatedToken(nullptr), _M_completeCount(0), _M_numTasks(0), _M_fHasExplicitToken(false) { } ~_RunAnyParam() { if (_CancellationTokenState::_IsValid(_M_exceptionRelatedToken)) _M_exceptionRelatedToken->_Release(); } task_completion_event<_CompletionType> _M_Completed; cancellation_token_source _M_cancellationSource; _CancellationTokenState * _M_exceptionRelatedToken; atomic_size_t _M_completeCount; size_t _M_numTasks; bool _M_fHasExplicitToken; }; template void _WhenAnyContinuationWrapper(_RunAnyParam<_CompletionType> * _PParam, const _Function & _Func, task<_TaskType>& _Task) { bool _IsTokenCancled = !_PParam->_M_fHasExplicitToken && _Task._GetImpl()->_M_pTokenState != _CancellationTokenState::_None() && _Task._GetImpl()->_M_pTokenState->_IsCanceled(); if (_Task._GetImpl()->_IsCompleted() && !_IsTokenCancled) { _Func(); if (atomic_increment(_PParam->_M_completeCount) == _PParam->_M_numTasks) { delete _PParam; } } else { _ASSERTE(_Task._GetImpl()->_IsCanceled() || _IsTokenCancled); if (_Task._GetImpl()->_HasUserException() && !_IsTokenCancled) { if (_PParam->_M_Completed._StoreException(_Task._GetImpl()->_GetExceptionHolder())) { // This can only enter once. _PParam->_M_exceptionRelatedToken = _Task._GetImpl()->_M_pTokenState; _ASSERTE(_PParam->_M_exceptionRelatedToken); // Deref token will be done in the _PParam destructor. if (_PParam->_M_exceptionRelatedToken != _CancellationTokenState::_None()) { _PParam->_M_exceptionRelatedToken->_Reference(); } } } if (atomic_increment(_PParam->_M_completeCount) == _PParam->_M_numTasks) { // If no one has be completed so far, we need to make some final cancellation decision. if (!_PParam->_M_Completed._IsTriggered()) { // If we already explicit token, we can skip the token join part. if (!_PParam->_M_fHasExplicitToken) { if (_PParam->_M_exceptionRelatedToken) { _JoinAllTokens_Add(_PParam->_M_cancellationSource, _PParam->_M_exceptionRelatedToken); } else { // If haven't captured any exception token yet, there was no exception for all those tasks, // so just pick a random token (current one) for normal cancellation. _JoinAllTokens_Add(_PParam->_M_cancellationSource, _Task._GetImpl()->_M_pTokenState); } } // Do exception cancellation or normal cancellation based on whether it has stored exception. _PParam->_M_Completed._Cancel(); } delete _PParam; } } } template struct _WhenAnyImpl { static task> _Perform(const task_options& _TaskOptions, _Iterator _Begin, _Iterator _End) { if( _Begin == _End ) { throw invalid_operation("when_any(begin, end) cannot be called on an empty container."); } _CancellationTokenState *_PTokenState = _TaskOptions.has_cancellation_token() ? _TaskOptions.get_cancellation_token()._GetImplValue() : nullptr; auto _PParam = new _RunAnyParam, _CancellationTokenState *>>(); if (_PTokenState) { _JoinAllTokens_Add(_PParam->_M_cancellationSource, _PTokenState); _PParam->_M_fHasExplicitToken = true; } task_options _Options(_TaskOptions); _Options.set_cancellation_token(_PParam->_M_cancellationSource.get_token()); task, _CancellationTokenState *>> _Any_tasks_completed(_PParam->_M_Completed, _Options); // Keep a copy ref to the token source auto _CancellationSource = _PParam->_M_cancellationSource; _PParam->_M_numTasks = static_cast(std::distance(_Begin, _End)); size_t _Index = 0; for (auto _PTask = _Begin; _PTask != _End; ++_PTask) { if (_PTask->is_apartment_aware()) { _Any_tasks_completed._SetAsync(); } _PTask->_Then([_PParam, _Index](task<_ElementType> _ResultTask) { auto _PParamCopy = _PParam; // Dev10 auto _IndexCopy = _Index; // Dev10 auto _Func = [&_ResultTask, _PParamCopy, _IndexCopy]() { _PParamCopy->_M_Completed.set(std::make_pair(std::make_pair(_ResultTask._GetImpl()->_GetResult(), _IndexCopy), _ResultTask._GetImpl()->_M_pTokenState)); }; _WhenAnyContinuationWrapper(_PParam, _Func, _ResultTask); }, _CancellationTokenState::_None()); _Index++; } // All _Any_tasks_completed._SetAsync() must be finished before this return continuation task being created. return _Any_tasks_completed._Then([=](std::pair, _CancellationTokenState *> _Result) -> std::pair<_ElementType, size_t> { _ASSERTE(_Result.second); if (!_PTokenState) { _JoinAllTokens_Add(_CancellationSource, _Result.second); } return _Result.first; }, nullptr); } }; template struct _WhenAnyImpl { static task _Perform(const task_options& _TaskOptions, _Iterator _Begin, _Iterator _End) { if( _Begin == _End ) { throw invalid_operation("when_any(begin, end) cannot be called on an empty container."); } _CancellationTokenState *_PTokenState = _TaskOptions.has_cancellation_token() ? _TaskOptions.get_cancellation_token()._GetImplValue() : nullptr; auto _PParam = new _RunAnyParam>(); if (_PTokenState) { _JoinAllTokens_Add(_PParam->_M_cancellationSource, _PTokenState); _PParam->_M_fHasExplicitToken = true; } task_options _Options(_TaskOptions); _Options.set_cancellation_token(_PParam->_M_cancellationSource.get_token()); task> _Any_tasks_completed(_PParam->_M_Completed, _Options); // Keep a copy ref to the token source auto _CancellationSource = _PParam->_M_cancellationSource; _PParam->_M_numTasks = static_cast(std::distance(_Begin, _End)); size_t _Index = 0; for (auto _PTask = _Begin; _PTask != _End; ++_PTask) { if (_PTask->is_apartment_aware()) { _Any_tasks_completed._SetAsync(); } _PTask->_Then([_PParam, _Index](task _ResultTask) { auto _PParamCopy = _PParam; // Dev10 auto _IndexCopy = _Index; // Dev10 auto _Func = [&_ResultTask, _PParamCopy, _IndexCopy]() { _PParamCopy->_M_Completed.set(std::make_pair(_IndexCopy, _ResultTask._GetImpl()->_M_pTokenState)); }; _WhenAnyContinuationWrapper(_PParam, _Func, _ResultTask); }, _CancellationTokenState::_None()); _Index++; } // All _Any_tasks_completed._SetAsync() must be finished before this return continuation task being created. return _Any_tasks_completed._Then([=](std::pair _Result) -> size_t { _ASSERTE(_Result.second); if (!_PTokenState) { _JoinAllTokens_Add(_CancellationSource, _Result.second); } return _Result.first; }, nullptr); } }; } // namespace details /// /// Creates a task that will complete successfully when any of the tasks supplied as arguments completes successfully. /// /// /// The type of the input iterator. /// /// /// The position of the first element in the range of elements to be combined into the resulting task. /// /// /// The position of the first element beyond the range of elements to be combined into the resulting task. /// /// /// A task that completes successfully when any one of the input tasks has completed successfully. If the input tasks are of type T, /// the output of this function will be a task<std::pair<T, size_t>>>, where the first element of the pair is the result /// of the completing task, and the second element is the index of the task that finished. If the input tasks are of type void /// the output is a task<size_t>, where the result is the index of the completing task. /// /// /**/ template auto when_any(_Iterator _Begin, _Iterator _End, const task_options& _TaskOptions = task_options()) -> decltype (details::_WhenAnyImpl::value_type::result_type, _Iterator>::_Perform(_TaskOptions, _Begin, _End)) { typedef typename std::iterator_traits<_Iterator>::value_type::result_type _ElementType; return details::_WhenAnyImpl<_ElementType, _Iterator>::_Perform(_TaskOptions, _Begin, _End); } /// /// Creates a task that will complete successfully when any of the tasks supplied as arguments completes successfully. /// /// /// The type of the input iterator. /// /// /// The position of the first element in the range of elements to be combined into the resulting task. /// /// /// The position of the first element beyond the range of elements to be combined into the resulting task. /// /// /// The cancellation token which controls cancellation of the returned task. If you do not provide a cancellation token, the resulting /// task will receive the cancellation token of the task that causes it to complete. /// /// /// A task that completes successfully when any one of the input tasks has completed successfully. If the input tasks are of type T, /// the output of this function will be a task<std::pair<T, size_t>>>, where the first element of the pair is the result /// of the completing task, and the second element is the index of the task that finished. If the input tasks are of type void /// the output is a task<size_t>, where the result is the index of the completing task. /// /// /**/ template auto when_any(_Iterator _Begin, _Iterator _End, cancellation_token _CancellationToken) -> decltype (details::_WhenAnyImpl::value_type::result_type, _Iterator>::_Perform(_CancellationToken._GetImplValue(), _Begin, _End)) { typedef typename std::iterator_traits<_Iterator>::value_type::result_type _ElementType; return details::_WhenAnyImpl<_ElementType, _Iterator>::_Perform(_CancellationToken._GetImplValue(), _Begin, _End); } /// /// Creates a task that will complete successfully when either of the tasks supplied as arguments completes successfully. /// /// /// The type of the returned task. /// /// /// The first task to combine into the resulting task. /// /// /// The second task to combine into the resulting task. /// /// /// A task that completes sucessfully when either of the input tasks has completed successfully. If the input tasks are of type T, /// the output of this function will be a task<std::vector<T>. If the input tasks are of type void the output task /// will also be a task<void>. /// To allow for a construct of the sort taskA || taskB && taskC, which are combined in pairs, with && taking precedence /// over ||, the operator|| produces a task<std::vector<T>> if one of the tasks is of type task<std::vector<T>> /// and the other one is of type task<T>. /// /// /// If both of the tasks are canceled or throw exceptions, the returned task will complete in the canceled state, and one of the exceptions, /// if any are encountered, will be thrown when you call get() or wait() on that task. /// /// /**/ template task<_ReturnType> operator||(const task<_ReturnType> & _Lhs, const task<_ReturnType> & _Rhs) { auto _PParam = new details::_RunAnyParam>(); task> _Any_tasks_completed(_PParam->_M_Completed, _PParam->_M_cancellationSource.get_token()); // Chain the return continuation task here to ensure it will get inline execution when _M_Completed.set is called, // So that _PParam can be used before it getting deleted. auto _ReturnTask = _Any_tasks_completed._Then([=](std::pair<_ReturnType, size_t> _Ret) -> _ReturnType { _ASSERTE(_Ret.second); _JoinAllTokens_Add(_PParam->_M_cancellationSource, reinterpret_cast(_Ret.second)); return _Ret.first; }, nullptr); if (_Lhs.is_apartment_aware() || _Rhs.is_apartment_aware()) { _ReturnTask._SetAsync(); } _PParam->_M_numTasks = 2; auto _Continuation = [_PParam](task<_ReturnType> _ResultTask) { // Dev10 compiler bug auto _PParamCopy = _PParam; auto _Func = [&_ResultTask, _PParamCopy]() { _PParamCopy->_M_Completed.set(std::make_pair(_ResultTask._GetImpl()->_GetResult(), reinterpret_cast(_ResultTask._GetImpl()->_M_pTokenState))); }; _WhenAnyContinuationWrapper(_PParam, _Func, _ResultTask); }; _Lhs._Then(_Continuation, details::_CancellationTokenState::_None()); _Rhs._Then(_Continuation, details::_CancellationTokenState::_None()); return _ReturnTask; } /// /// Creates a task that will complete successfully when any of the tasks supplied as arguments completes successfully. /// /// /// The type of the returned task. /// /// /// The first task to combine into the resulting task. /// /// /// The second task to combine into the resulting task. /// /// /// A task that completes sucessfully when either of the input tasks has completed successfully. If the input tasks are of type T, /// the output of this function will be a task<std::vector<T>. If the input tasks are of type void the output task /// will also be a task<void>. /// To allow for a construct of the sort taskA || taskB && taskC, which are combined in pairs, with && taking precedence /// over ||, the operator|| produces a task<std::vector<T>> if one of the tasks is of type task<std::vector<T>> /// and the other one is of type task<T>. /// /// /// If both of the tasks are canceled or throw exceptions, the returned task will complete in the canceled state, and one of the exceptions, /// if any are encountered, will be thrown when you call get() or wait() on that task. /// /// /**/ template task> operator||(const task> & _Lhs, const task<_ReturnType> & _Rhs) { auto _PParam = new details::_RunAnyParam, details::_CancellationTokenState *>>(); task, details::_CancellationTokenState *>> _Any_tasks_completed(_PParam->_M_Completed, _PParam->_M_cancellationSource.get_token()); // Chain the return continuation task here to ensure it will get inline execution when _M_Completed.set is called, // So that _PParam can be used before it getting deleted. auto _ReturnTask = _Any_tasks_completed._Then([=](std::pair, details::_CancellationTokenState *> _Ret) -> std::vector<_ReturnType> { _ASSERTE(_Ret.second); _JoinAllTokens_Add(_PParam->_M_cancellationSource, _Ret.second); return _Ret.first; }, nullptr); if (_Lhs.is_apartment_aware() || _Rhs.is_apartment_aware()) { _ReturnTask._SetAsync(); } _PParam->_M_numTasks = 2; _Lhs._Then([_PParam](task> _ResultTask) { // Dev10 compiler bug auto _PParamCopy = _PParam; auto _Func = [&_ResultTask, _PParamCopy]() { auto _Result = _ResultTask._GetImpl()->_GetResult(); _PParamCopy->_M_Completed.set(std::make_pair(_Result, _ResultTask._GetImpl()->_M_pTokenState)); }; _WhenAnyContinuationWrapper(_PParam, _Func, _ResultTask); }, details::_CancellationTokenState::_None()); _Rhs._Then([_PParam](task<_ReturnType> _ResultTask) { auto _PParamCopy = _PParam; auto _Func = [&_ResultTask, _PParamCopy]() { auto _Result = _ResultTask._GetImpl()->_GetResult(); std::vector<_ReturnType> _Vec; _Vec.push_back(_Result); _PParamCopy->_M_Completed.set(std::make_pair(_Vec, _ResultTask._GetImpl()->_M_pTokenState)); }; _WhenAnyContinuationWrapper(_PParam, _Func, _ResultTask); }, details::_CancellationTokenState::_None()); return _ReturnTask; } /// /// Creates a task that will complete successfully when any of the tasks supplied as arguments completes successfully. /// /// /// The type of the returned task. /// /// /// The first task to combine into the resulting task. /// /// /// The second task to combine into the resulting task. /// /// /// A task that completes sucessfully when either of the input tasks has completed successfully. If the input tasks are of type T, /// the output of this function will be a task<std::vector<T>. If the input tasks are of type void the output task /// will also be a task<void>. /// To allow for a construct of the sort taskA || taskB && taskC, which are combined in pairs, with && taking precedence /// over ||, the operator|| produces a task<std::vector<T>> if one of the tasks is of type task<std::vector<T>> /// and the other one is of type task<T>. /// /// /// If both of the tasks are canceled or throw exceptions, the returned task will complete in the canceled state, and one of the exceptions, /// if any are encountered, will be thrown when you call get() or wait() on that task. /// /// /**/ template task> operator||(const task<_ReturnType> & _Lhs, const task> & _Rhs) { return _Rhs || _Lhs; } /// /// Creates a task that will complete successfully when any of the tasks supplied as arguments completes successfully. /// /// /// The type of the returned task. /// /// /// The first task to combine into the resulting task. /// /// /// The second task to combine into the resulting task. /// /// /// A task that completes sucessfully when either of the input tasks has completed successfully. If the input tasks are of type T, /// the output of this function will be a task<std::vector<T>. If the input tasks are of type void the output task /// will also be a task<void>. /// To allow for a construct of the sort taskA || taskB && taskC, which are combined in pairs, with && taking precedence /// over ||, the operator|| produces a task<std::vector<T>> if one of the tasks is of type task<std::vector<T>> /// and the other one is of type task<T>. /// /// /// If both of the tasks are canceled or throw exceptions, the returned task will complete in the canceled state, and one of the exceptions, /// if any are encountered, will be thrown when you call get() or wait() on that task. /// /// /**/ inline task operator||(const task & _Lhs, const task & _Rhs) { auto _PParam = new details::_RunAnyParam>(); task> _Any_task_completed(_PParam->_M_Completed, _PParam->_M_cancellationSource.get_token()); // Chain the return continuation task here to ensure it will get inline execution when _M_Completed.set is called, // So that _PParam can be used before it getting deleted. auto _ReturnTask = _Any_task_completed._Then([=](std::pair _Ret) { _ASSERTE(_Ret.second); details::_JoinAllTokens_Add(_PParam->_M_cancellationSource, _Ret.second); }, nullptr); if (_Lhs.is_apartment_aware() || _Rhs.is_apartment_aware()) { _ReturnTask._SetAsync(); } _PParam->_M_numTasks = 2; auto _Continuation = [_PParam](task _ResultTask) mutable { // Dev10 compiler needs this. auto _PParam1 = _PParam; auto _Func = [&_ResultTask, _PParam1]() { _PParam1->_M_Completed.set(std::make_pair(details::_Unit_type(), _ResultTask._GetImpl()->_M_pTokenState)); }; _WhenAnyContinuationWrapper(_PParam, _Func, _ResultTask); }; _Lhs._Then(_Continuation, details::_CancellationTokenState::_None()); _Rhs._Then(_Continuation, details::_CancellationTokenState::_None()); return _ReturnTask; } template task<_Ty> task_from_result(_Ty _Param, const task_options& _TaskOptions = task_options()) { task_completion_event<_Ty> _Tce; _Tce.set(_Param); return create_task(_Tce, _TaskOptions); } // Work around VS 2010 compiler bug #if _MSC_VER == 1600 inline task task_from_result(bool _Param) { task_completion_event _Tce; _Tce.set(_Param); return create_task(_Tce, task_options()); } #endif inline task task_from_result(const task_options& _TaskOptions = task_options()) { task_completion_event _Tce; _Tce.set(); return create_task(_Tce, _TaskOptions); } template task<_TaskType> task_from_exception(_ExType _Exception, const task_options& _TaskOptions = task_options()) { task_completion_event<_TaskType> _Tce; _Tce.set_exception(_Exception); return create_task(_Tce, _TaskOptions); } namespace details { /// /// A convenient extension to Concurrency: loop until a condition is no longer met /// /// /// A function representing the body of the loop. It will be invoked at least once and /// then repetitively as long as it returns true. /// inline task do_while(std::function(void)> func) { task first = func(); return first.then([=](bool guard) -> task { if (guard) return do_while(func); else return first; }); } } // namespace details } // namespace Concurrency #pragma pop_macro("new") #if defined(_MSC_VER) #pragma warning(pop) #endif #pragma pack(pop) #endif // (defined(_MSC_VER) && (_MSC_VER >= 1800)) #ifndef _CONCRT_H #ifndef _LWRCASE_CNCRRNCY #define _LWRCASE_CNCRRNCY // Note to reader: we're using lower-case namespace names everywhere, but the 'Concurrency' namespace // is capitalized for historical reasons. The alias let's us pretend that style issue doesn't exist. namespace Concurrency {} namespace concurrency = Concurrency; #endif #endif #endif // _PPLXTASKS_H ================================================ FILE: Include/cpprestinclude/pplx/pplxtasks.140.h ================================================ #if !XSAPI_NO_PPL /*** * ==++== * * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * ==--== * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ * * Parallel Patterns Library - PPLx Tasks * * For the latest on this and related APIs, please see http://casablanca.codeplex.com. * * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ****/ #pragma once #ifndef _PPLXTASKS_H #define _PPLXTASKS_H #if (defined(_MSC_VER) && (_MSC_VER >= 1800)) && !CPPREST_FORCE_PPLX #include namespace pplx = Concurrency; #if (_MSC_VER >= 1900) #if HC_PLATFORM != HC_PLATFORM_GDK #include #endif namespace Concurrency { namespace extensibility { typedef ::std::condition_variable condition_variable_t; typedef ::std::mutex critical_section_t; typedef ::std::unique_lock< ::std::mutex> scoped_critical_section_t; #if HC_PLATFORM != HC_PLATFORM_GDK typedef ::Concurrency::event event_t; typedef ::Concurrency::reader_writer_lock reader_writer_lock_t; typedef ::Concurrency::reader_writer_lock::scoped_lock scoped_rw_lock_t; typedef ::Concurrency::reader_writer_lock::scoped_lock_read scoped_read_lock_t; typedef ::Concurrency::details::_ReentrantBlockingLock recursive_lock_t; typedef recursive_lock_t::_Scoped_lock scoped_recursive_lock_t; #endif } } #endif // _MSC_VER >= 1900 #else #include "pplx/pplx.h" #if defined(__ANDROID__) #include void cpprest_init(JavaVM*); #endif // Cannot build using a compiler that is older than dev10 SP1 #if defined(_MSC_VER) #if _MSC_FULL_VER < 160040219 /*IFSTRIP=IGN*/ #error ERROR: Visual Studio 2010 SP1 or later is required to build ppltasks #endif /*IFSTRIP=IGN*/ #endif /* defined(_MSC_VER) */ #include #include #include #include #include #if defined(_MSC_VER) #if defined(__cplusplus_winrt) #include #include #include #include #ifndef _UITHREADCTXT_SUPPORT #ifdef WINAPI_FAMILY /*IFSTRIP=IGN*/ // It is safe to include winapifamily as WINAPI_FAMILY was defined by the user #include #if WINAPI_FAMILY == WINAPI_FAMILY_APP // UI thread context support is not required for desktop and Windows Store apps #define _UITHREADCTXT_SUPPORT 0 #elif WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP // UI thread context support is not required for desktop and Windows Store apps #define _UITHREADCTXT_SUPPORT 0 #else /* WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP */ #define _UITHREADCTXT_SUPPORT 1 #endif /* WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP */ #else /* WINAPI_FAMILY */ // Not supported without a WINAPI_FAMILY setting. #define _UITHREADCTXT_SUPPORT 0 #endif /* WINAPI_FAMILY */ #endif /* _UITHREADCTXT_SUPPORT */ #if _UITHREADCTXT_SUPPORT #include #endif /* _UITHREADCTXT_SUPPORT */ #pragma detect_mismatch("_PPLTASKS_WITH_WINRT", "1") #else /* defined(__cplusplus_winrt) */ #pragma detect_mismatch("_PPLTASKS_WITH_WINRT", "0") #endif /* defined(__cplusplus_winrt) */ #endif /* defined(_MSC_VER) */ #ifdef _DEBUG #define _DBG_ONLY(X) X #else #define _DBG_ONLY(X) #endif // #ifdef _DEBUG // std::copy_exception changed to std::make_exception_ptr from VS 2010 to VS 11. #ifdef _MSC_VER #if _MSC_VER < 1700 /*IFSTRIP=IGN*/ namespace std { template exception_ptr make_exception_ptr(_E _Except) { return copy_exception(_Except); } } #endif /* _MSC_VER < 1700 */ #ifndef _PPLTASK_ASYNC_LOGGING #if _MSC_VER >= 1800 && defined(__cplusplus_winrt) #define _PPLTASK_ASYNC_LOGGING 1 // Only enable async logging under dev12 winrt #else #define _PPLTASK_ASYNC_LOGGING 0 #endif #endif /* !_PPLTASK_ASYNC_LOGGING */ #endif /* _MSC_VER */ #pragma pack(push,_CRT_PACKING) #if defined(_MSC_VER) #pragma warning(push) #pragma warning(disable: 28197) #pragma warning(disable: 4100) // Unreferenced formal parameter - needed for document generation #pragma warning(disable: 4127) // constant express in if condition - we use it for meta programming #endif /* defined(_MSC_VER) */ // All CRT public header files are required to be protected from the macro new #pragma push_macro("new") #undef new // stuff ported from Dev11 CRT // NOTE: this doesn't actually match std::declval. it behaves differently for void! // so don't blindly change it to std::declval. namespace stdx { template _T&& declval(); } /// /// The pplx namespace provides classes and functions that give you access to the Concurrency Runtime, /// a concurrent programming framework for C++. For more information, see . /// /**/ namespace pplx { /// /// A type that represents the terminal state of a task. Valid values are completed and canceled. /// /// /**/ typedef task_group_status task_status; template class task; template <> class task; // In debug builds, default to 10 frames, unless this is overridden prior to #includ'ing ppltasks.h. In retail builds, default to only one frame. #ifndef PPL_TASK_SAVE_FRAME_COUNT #ifdef _DEBUG #define PPL_TASK_SAVE_FRAME_COUNT 10 #else #define PPL_TASK_SAVE_FRAME_COUNT 1 #endif #endif /// /// Helper macro to determine how many stack frames need to be saved. When any number less or equal to 1 is specified, /// only one frame is captured and no stackwalk will be involved. Otherwise, the number of callstack frames will be captured. /// /// /// This needs to be defined as a macro rather than a function so that if we're only gathering one frame, _ReturnAddress() /// will evaluate to client code, rather than a helper function inside of _TaskCreationCallstack, itself. /// #if PPL_TASK_SAVE_FRAME_COUNT > 1 #if defined(__cplusplus_winrt) && !defined(_DEBUG) #pragma message ("WARNING: Redefinning PPL_TASK_SAVE_FRAME_COUNT under Release build for non-desktop applications is not supported; only one frame will be captured!") #define _CAPTURE_CALLSTACK() ::pplx::details::_TaskCreationCallstack::_CaptureSingleFrameCallstack(_ReturnAddress()) #else #define _CAPTURE_CALLSTACK() ::pplx::details::_TaskCreationCallstack::_CaptureMultiFramesCallstack(PPL_TASK_SAVE_FRAME_COUNT) #endif #else #define _CAPTURE_CALLSTACK() ::pplx::details::_TaskCreationCallstack::_CaptureSingleFrameCallstack(_ReturnAddress()) #endif /// /// Returns an indication of whether the task that is currently executing has received a request to cancel its /// execution. Cancellation is requested on a task if the task was created with a cancellation token, and /// the token source associated with that token is canceled. /// /// /// true if the currently executing task has received a request for cancellation, false otherwise. /// /// /// If you call this method in the body of a task and it returns true, you must respond with a call to /// cancel_current_task to acknowledge the cancellation request, /// after performing any cleanup you need. This will abort the execution of the task and cause it to enter into /// the canceled state. If you do not respond and continue execution, or return instead of calling /// cancel_current_task, the task will enter the completed state when it is done. /// state. /// A task is not cancellable if it was created without a cancellation token. /// /// /// /// /// /**/ inline bool _pplx_cdecl is_task_cancellation_requested() { return ::pplx::details::_TaskCollection_t::_Is_cancellation_requested(); } /// /// Cancels the currently executing task. This function can be called from within the body of a task to abort the /// task's execution and cause it to enter the canceled state. While it may be used in response to /// the is_task_cancellation_requested function, you may /// also use it by itself, to initiate cancellation of the task that is currently executing. /// It is not a supported scenario to call this function if you are not within the body of a task. /// Doing so will result in undefined behavior such as a crash or a hang in your application. /// /// /**/ inline __declspec(noreturn) void _pplx_cdecl cancel_current_task() { throw task_canceled(); } namespace details { /// /// Callstack container, which is used to capture and preserve callstacks in ppltasks. /// Members of this class is examined by vc debugger, thus there will be no public access methods. /// Please note that names of this class should be kept stable for debugger examining. /// class _TaskCreationCallstack { private: // If _M_SingleFrame != nullptr, there will be only one frame of callstacks, which is stored in _M_SingleFrame; // otherwise, _M_Frame will store all the callstack frames. void* _M_SingleFrame; std::vector _M_frames; public: _TaskCreationCallstack() { _M_SingleFrame = nullptr; } // Store one frame of callstack. This function works for both Debug / Release CRT. static _TaskCreationCallstack _CaptureSingleFrameCallstack(void *_SingleFrame) { _TaskCreationCallstack _csc; _csc._M_SingleFrame = _SingleFrame; return _csc; } // Capture _CaptureFrames number of callstack frames. This function only work properly for Desktop or Debug CRT. __declspec(noinline) static _TaskCreationCallstack _CaptureMultiFramesCallstack(size_t _CaptureFrames) { _TaskCreationCallstack _csc; _csc._M_frames.resize(_CaptureFrames); // skip 2 frames to make sure callstack starts from user code _csc._M_frames.resize(::pplx::details::platform::CaptureCallstack(&_csc._M_frames[0], 2, _CaptureFrames)); return _csc; } }; typedef unsigned char _Unit_type; struct _TypeSelectorNoAsync {}; struct _TypeSelectorAsyncOperationOrTask {}; struct _TypeSelectorAsyncOperation : public _TypeSelectorAsyncOperationOrTask { }; struct _TypeSelectorAsyncTask : public _TypeSelectorAsyncOperationOrTask { }; struct _TypeSelectorAsyncAction {}; struct _TypeSelectorAsyncActionWithProgress {}; struct _TypeSelectorAsyncOperationWithProgress {}; template struct _NormalizeVoidToUnitType { typedef _Ty _Type; }; template<> struct _NormalizeVoidToUnitType { typedef _Unit_type _Type; }; template struct _IsUnwrappedAsyncSelector { static const bool _Value = true; }; template<> struct _IsUnwrappedAsyncSelector<_TypeSelectorNoAsync> { static const bool _Value = false; }; template struct _UnwrapTaskType { typedef _Ty _Type; }; template struct _UnwrapTaskType> { typedef _Ty _Type; }; template _TypeSelectorAsyncTask _AsyncOperationKindSelector(task<_T>); _TypeSelectorNoAsync _AsyncOperationKindSelector(...); #if defined(__cplusplus_winrt) template struct _Unhat { typedef _Type _Value; }; template struct _Unhat<_Type^> { typedef _Type _Value; }; value struct _NonUserType { public: int _Dummy; }; template struct _ValueTypeOrRefType { typedef _NonUserType _Value; }; template struct _ValueTypeOrRefType<_Type, true> { typedef _Type _Value; }; template _T2 _ProgressTypeSelector(Windows::Foundation::IAsyncOperationWithProgress<_T1,_T2>^); template _T1 _ProgressTypeSelector(Windows::Foundation::IAsyncActionWithProgress<_T1>^); template struct _GetProgressType { typedef decltype(_ProgressTypeSelector(stdx::declval<_Type>())) _Value; }; template struct _IsIAsyncInfo { static const bool _Value = __is_base_of(Windows::Foundation::IAsyncInfo, typename _Unhat<_Type>::_Value); }; template _TypeSelectorAsyncOperation _AsyncOperationKindSelector(Windows::Foundation::IAsyncOperation<_T>^); _TypeSelectorAsyncAction _AsyncOperationKindSelector(Windows::Foundation::IAsyncAction^); template _TypeSelectorAsyncOperationWithProgress _AsyncOperationKindSelector(Windows::Foundation::IAsyncOperationWithProgress<_T1, _T2>^); template _TypeSelectorAsyncActionWithProgress _AsyncOperationKindSelector(Windows::Foundation::IAsyncActionWithProgress<_T>^); template ::_Value> struct _TaskTypeTraits { typedef typename _UnwrapTaskType<_Type>::_Type _TaskRetType; typedef decltype(_AsyncOperationKindSelector(stdx::declval<_Type>())) _AsyncKind; typedef typename _NormalizeVoidToUnitType<_TaskRetType>::_Type _NormalizedTaskRetType; static const bool _IsAsyncTask = _IsAsync; static const bool _IsUnwrappedTaskOrAsync = _IsUnwrappedAsyncSelector<_AsyncKind>::_Value; }; template struct _TaskTypeTraits<_Type, true > { typedef decltype(((_Type)nullptr)->GetResults()) _TaskRetType; typedef _TaskRetType _NormalizedTaskRetType; typedef decltype(_AsyncOperationKindSelector((_Type)nullptr)) _AsyncKind; static const bool _IsAsyncTask = true; static const bool _IsUnwrappedTaskOrAsync = _IsUnwrappedAsyncSelector<_AsyncKind>::_Value; }; #else /* defined (__cplusplus_winrt) */ template struct _IsIAsyncInfo { static const bool _Value = false; }; template struct _TaskTypeTraits { typedef typename _UnwrapTaskType<_Type>::_Type _TaskRetType; typedef decltype(_AsyncOperationKindSelector(stdx::declval<_Type>())) _AsyncKind; typedef typename _NormalizeVoidToUnitType<_TaskRetType>::_Type _NormalizedTaskRetType; static const bool _IsAsyncTask = false; static const bool _IsUnwrappedTaskOrAsync = _IsUnwrappedAsyncSelector<_AsyncKind>::_Value; }; #endif /* defined (__cplusplus_winrt) */ template auto _IsCallable(_Function _Func, int) -> decltype(_Func(), std::true_type()) { (void)(_Func); return std::true_type(); } template std::false_type _IsCallable(_Function, ...) { return std::false_type(); } template <> struct _TaskTypeTraits { typedef void _TaskRetType; typedef _TypeSelectorNoAsync _AsyncKind; typedef _Unit_type _NormalizedTaskRetType; static const bool _IsAsyncTask = false; static const bool _IsUnwrappedTaskOrAsync = false; }; template task<_Type> _To_task(_Type t); template task _To_task_void(_Func f); struct _BadContinuationParamType{}; template auto _ReturnTypeHelper(_Type t, _Function _Func, int, int) -> decltype(_Func(_To_task(t))); template auto _ReturnTypeHelper(_Type t, _Function _Func, int, ...) -> decltype(_Func(t)); template auto _ReturnTypeHelper(_Type t, _Function _Func, ...) -> _BadContinuationParamType; template auto _IsTaskHelper(_Type t, _Function _Func, int, int) -> decltype(_Func(_To_task(t)), std::true_type()); template std::false_type _IsTaskHelper(_Type t, _Function _Func, int, ...); template auto _VoidReturnTypeHelper(_Function _Func, int, int) -> decltype(_Func(_To_task_void(_Func))); template auto _VoidReturnTypeHelper(_Function _Func, int, ...) -> decltype(_Func()); template auto _VoidIsTaskHelper(_Function _Func, int, int) -> decltype(_Func(_To_task_void(_Func)), std::true_type()); template std::false_type _VoidIsTaskHelper(_Function _Func, int, ...); template struct _FunctionTypeTraits { typedef decltype(_ReturnTypeHelper(stdx::declval<_ExpectedParameterType>(),stdx::declval<_Function>(), 0, 0)) _FuncRetType; static_assert(!std::is_same<_FuncRetType,_BadContinuationParamType>::value, "incorrect parameter type for the callable object in 'then'; consider _ExpectedParameterType or task<_ExpectedParameterType> (see below)"); typedef decltype(_IsTaskHelper(stdx::declval<_ExpectedParameterType>(),stdx::declval<_Function>(), 0, 0)) _Takes_task; }; template struct _FunctionTypeTraits<_Function, void> { typedef decltype(_VoidReturnTypeHelper(stdx::declval<_Function>(), 0, 0)) _FuncRetType; typedef decltype(_VoidIsTaskHelper(stdx::declval<_Function>(), 0, 0)) _Takes_task; }; template struct _ContinuationTypeTraits { typedef task::_FuncRetType>::_TaskRetType> _TaskOfType; }; // _InitFunctorTypeTraits is used to decide whether a task constructed with a lambda should be unwrapped. Depending on how the variable is // declared, the constructor may or may not perform unwrapping. For eg. // // This declaration SHOULD NOT cause unwrapping // task> t1([]() -> task { // task t2([]() {}); // return t2; // }); // // This declaration SHOULD cause unwrapping // task> t1([]() -> task { // task t2([]() {}); // return t2; // }); // If the type of the task is the same as the return type of the function, no unwrapping should take place. Else normal rules apply. template struct _InitFunctorTypeTraits { typedef typename _TaskTypeTraits<_FuncRetType>::_AsyncKind _AsyncKind; static const bool _IsAsyncTask = _TaskTypeTraits<_FuncRetType>::_IsAsyncTask; static const bool _IsUnwrappedTaskOrAsync = _TaskTypeTraits<_FuncRetType>::_IsUnwrappedTaskOrAsync; }; template struct _InitFunctorTypeTraits { typedef _TypeSelectorNoAsync _AsyncKind; static const bool _IsAsyncTask = false; static const bool _IsUnwrappedTaskOrAsync = false; }; /// /// Helper object used for LWT invocation. /// struct _TaskProcThunk { _TaskProcThunk(const std::function & _Callback) : _M_func(_Callback) { } static void _pplx_cdecl _Bridge(void *_PData) { _TaskProcThunk *_PThunk = reinterpret_cast<_TaskProcThunk *>(_PData); _Holder _ThunkHolder(_PThunk); _PThunk->_M_func(); } private: // RAII holder struct _Holder { _Holder(_TaskProcThunk * _PThunk) : _M_pThunk(_PThunk) { } ~_Holder() { delete _M_pThunk; } _TaskProcThunk * _M_pThunk; private: _Holder& operator=(const _Holder&); }; std::function _M_func; _TaskProcThunk& operator=(const _TaskProcThunk&); }; /// /// Schedule a functor with automatic inlining. Note that this is "fire and forget" scheduling, which cannot be /// waited on or canceled after scheduling. /// This schedule method will perform automatic inlining base on . /// /// /// The user functor need to be scheduled. /// /// /// The inlining scheduling policy for current functor. /// static void _ScheduleFuncWithAutoInline(const std::function & _Func, _TaskInliningMode_t _InliningMode) { _TaskCollection_t::_RunTask(&_TaskProcThunk::_Bridge, new _TaskProcThunk(_Func), _InliningMode); } class _ContextCallback { typedef std::function _CallbackFunction; #if defined (__cplusplus_winrt) public: static _ContextCallback _CaptureCurrent() { _ContextCallback _Context; _Context._Capture(); return _Context; } ~_ContextCallback() { _Reset(); } _ContextCallback(bool _DeferCapture = false) { if (_DeferCapture) { _M_context._M_captureMethod = _S_captureDeferred; } else { _M_context._M_pContextCallback = nullptr; } } // Resolves a context that was created as _S_captureDeferred based on the environment (ancestor, current context). void _Resolve(bool _CaptureCurrent) { if(_M_context._M_captureMethod == _S_captureDeferred) { _M_context._M_pContextCallback = nullptr; if (_CaptureCurrent) { if (_IsCurrentOriginSTA()) { _Capture(); } #if _UITHREADCTXT_SUPPORT else { // This method will fail if not called from the UI thread. HRESULT _Hr = CaptureUiThreadContext(&_M_context._M_pContextCallback); if (FAILED(_Hr)) { _M_context._M_pContextCallback = nullptr; } } #endif /* _UITHREADCTXT_SUPPORT */ } } } void _Capture() { HRESULT _Hr = CoGetObjectContext(IID_IContextCallback, reinterpret_cast(&_M_context._M_pContextCallback)); if (FAILED(_Hr)) { _M_context._M_pContextCallback = nullptr; } } _ContextCallback(const _ContextCallback& _Src) { _Assign(_Src._M_context._M_pContextCallback); } _ContextCallback(_ContextCallback&& _Src) { _M_context._M_pContextCallback = _Src._M_context._M_pContextCallback; _Src._M_context._M_pContextCallback = nullptr; } _ContextCallback& operator=(const _ContextCallback& _Src) { if (this != &_Src) { _Reset(); _Assign(_Src._M_context._M_pContextCallback); } return *this; } _ContextCallback& operator=(_ContextCallback&& _Src) { if (this != &_Src) { _M_context._M_pContextCallback = _Src._M_context._M_pContextCallback; _Src._M_context._M_pContextCallback = nullptr; } return *this; } bool _HasCapturedContext() const { _ASSERTE(_M_context._M_captureMethod != _S_captureDeferred); return (_M_context._M_pContextCallback != nullptr); } void _CallInContext(_CallbackFunction _Func) const { if (!_HasCapturedContext()) { _Func(); } else { ComCallData callData; ZeroMemory(&callData, sizeof(callData)); callData.pUserDefined = reinterpret_cast(&_Func); HRESULT _Hr = _M_context._M_pContextCallback->ContextCallback(&_Bridge, &callData, IID_ICallbackWithNoReentrancyToApplicationSTA, 5, nullptr); if (FAILED(_Hr)) { throw ::Platform::Exception::CreateException(_Hr); } } } bool operator==(const _ContextCallback& _Rhs) const { return (_M_context._M_pContextCallback == _Rhs._M_context._M_pContextCallback); } bool operator!=(const _ContextCallback& _Rhs) const { return !(operator==(_Rhs)); } private: void _Reset() { if (_M_context._M_captureMethod != _S_captureDeferred && _M_context._M_pContextCallback != nullptr) { _M_context._M_pContextCallback->Release(); } } void _Assign(IContextCallback *_PContextCallback) { _M_context._M_pContextCallback = _PContextCallback; if (_M_context._M_captureMethod != _S_captureDeferred && _M_context._M_pContextCallback != nullptr) { _M_context._M_pContextCallback->AddRef(); } } static HRESULT __stdcall _Bridge(ComCallData *_PParam) { _CallbackFunction *pFunc = reinterpret_cast<_CallbackFunction *>(_PParam->pUserDefined); (*pFunc)(); return S_OK; } // Returns the origin information for the caller (runtime / Windows Runtime apartment as far as task continuations need know) static bool _IsCurrentOriginSTA() { APTTYPE _AptType; APTTYPEQUALIFIER _AptTypeQualifier; HRESULT hr = CoGetApartmentType(&_AptType, &_AptTypeQualifier); if (SUCCEEDED(hr)) { // We determine the origin of a task continuation by looking at where .then is called, so we can tell whether // to need to marshal the continuation back to the originating apartment. If an STA thread is in executing in // a neutral aparment when it schedules a continuation, we will not marshal continuations back to the STA, // since variables used within a neutral apartment are expected to be apartment neutral. switch(_AptType) { case APTTYPE_MAINSTA: case APTTYPE_STA: return true; default: break; } } return false; } union { IContextCallback *_M_pContextCallback; size_t _M_captureMethod; } _M_context; static const size_t _S_captureDeferred = 1; #else /* defined (__cplusplus_winrt) */ public: static _ContextCallback _CaptureCurrent() { return _ContextCallback(); } _ContextCallback(bool = false) { } _ContextCallback(const _ContextCallback&) { } _ContextCallback(_ContextCallback&&) { } _ContextCallback& operator=(const _ContextCallback&) { return *this; } _ContextCallback& operator=(_ContextCallback&&) { return *this; } bool _HasCapturedContext() const { return false; } void _Resolve(bool) const { } void _CallInContext(_CallbackFunction _Func) const { _Func(); } bool operator==(const _ContextCallback&) const { return true; } bool operator!=(const _ContextCallback&) const { return false; } #endif /* defined (__cplusplus_winrt) */ }; template struct _ResultHolder { void Set(const _Type& _type) { _Result = _type; } _Type Get() { return _Result; } _Type _Result; }; #if defined (__cplusplus_winrt) template struct _ResultHolder<_Type^> { void Set(_Type^ const & _type) { _M_Result = _type; } _Type^ Get() { return _M_Result.Get(); } private: // ::Platform::Agile handle specialization of all hats // including ::Platform::String and ::Platform::Array ::Platform::Agile<_Type^> _M_Result; }; // // The below are for composability with tasks auto-created from when_any / when_all / && / || constructs. // template struct _ResultHolder> { void Set(const std::vector<_Type^>& _type) { _Result.reserve(_type.size()); for (auto _PTask = _type.begin(); _PTask != _type.end(); ++_PTask) { _Result.emplace_back(*_PTask); } } std::vector<_Type^> Get() { // Return vectory with the objects that are marshaled in the proper appartment std::vector<_Type^> _Return; _Return.reserve(_Result.size()); for (auto _PTask = _Result.begin(); _PTask != _Result.end(); ++_PTask) { _Return.push_back(_PTask->Get()); // Platform::Agile will marshal the object to appropriate appartment if neccessary } return _Return; } std::vector< ::Platform::Agile<_Type^> > _Result; }; template struct _ResultHolder > { void Set(const std::pair<_Type^, size_t>& _type) { _M_Result = _type; } std::pair<_Type^, size_t> Get() { return std::make_pair(_M_Result.first.Get(), _M_Result.second); } private: std::pair< ::Platform::Agile<_Type^>, size_t> _M_Result; }; #endif /* defined (__cplusplus_winrt) */ // An exception thrown by the task body is captured in an exception holder and it is shared with all value based continuations rooted at the task. // The exception is 'observed' if the user invokes get()/wait() on any of the tasks that are sharing this exception holder. If the exception // is not observed by the time the internal object owned by the shared pointer destructs, the process will fail fast. struct _ExceptionHolder { private: void ReportUnhandledError() { #if _MSC_VER >= 1800 && defined(__cplusplus_winrt) if (_M_winRTException != nullptr) { ::Platform::Details::ReportUnhandledError(_M_winRTException); } #endif /* defined (__cplusplus_winrt) */ } public: explicit _ExceptionHolder(const std::exception_ptr& _E, const _TaskCreationCallstack &_stackTrace) : _M_exceptionObserved(0), _M_stdException(_E), _M_stackTrace(_stackTrace) #if defined (__cplusplus_winrt) , _M_winRTException(nullptr) #endif /* defined (__cplusplus_winrt) */ { } #if defined (__cplusplus_winrt) explicit _ExceptionHolder(::Platform::Exception^ _E, const _TaskCreationCallstack &_stackTrace) : _M_exceptionObserved(0), _M_winRTException(_E), _M_stackTrace(_stackTrace) { } #endif /* defined (__cplusplus_winrt) */ __declspec(noinline) ~_ExceptionHolder() { if (_M_exceptionObserved == 0) { // If you are trapped here, it means an exception thrown in task chain didn't get handled. // Please add task-based continuation to handle all exceptions coming from tasks. // this->_M_stackTrace keeps the creation callstack of the task generates this exception. _REPORT_PPLTASK_UNOBSERVED_EXCEPTION(); } } void _RethrowUserException() { if (_M_exceptionObserved == 0) { atomic_exchange(_M_exceptionObserved, 1l); } #if defined (__cplusplus_winrt) if (_M_winRTException != nullptr) { throw _M_winRTException; } #endif /* defined (__cplusplus_winrt) */ std::rethrow_exception(_M_stdException); } // A variable that remembers if this exception was every rethrown into user code (and hence handled by the user). Exceptions that // are unobserved when the exception holder is destructed will terminate the process. atomic_long _M_exceptionObserved; // Either _M_stdException or _M_winRTException is populated based on the type of exception encountered. std::exception_ptr _M_stdException; #if defined (__cplusplus_winrt) ::Platform::Exception^ _M_winRTException; #endif /* defined (__cplusplus_winrt) */ // Disassembling this value will point to a source instruction right after a call instruction. If the call is to create_task, // a task constructor or the then method, the task created by that method is the one that encountered this exception. If the call // is to task_completion_event::set_exception, the set_exception method was the source of the exception. // DO NOT REMOVE THIS VARIABLE. It is extremely helpful for debugging. _TaskCreationCallstack _M_stackTrace; }; #if defined (__cplusplus_winrt) /// /// Base converter class for converting asynchronous interfaces to IAsyncOperation /// template ref struct _AsyncInfoImpl abstract : Windows::Foundation::IAsyncOperation<_Result> { internal: // The async action, action with progress or operation with progress that this stub forwards to. ::Platform::Agile<_AsyncOperationType> _M_asyncInfo; Windows::Foundation::AsyncOperationCompletedHandler<_Result>^ _M_CompletedHandler; _AsyncInfoImpl( _AsyncOperationType _AsyncInfo ) : _M_asyncInfo(_AsyncInfo) {} public: virtual void Cancel() { _M_asyncInfo.Get()->Cancel(); } virtual void Close() { _M_asyncInfo.Get()->Close(); } virtual property Windows::Foundation::HResult ErrorCode { Windows::Foundation::HResult get() { return _M_asyncInfo.Get()->ErrorCode; } } virtual property UINT Id { UINT get() { return _M_asyncInfo.Get()->Id; } } virtual property Windows::Foundation::AsyncStatus Status { Windows::Foundation::AsyncStatus get() { return _M_asyncInfo.Get()->Status; } } virtual _Result GetResults() { throw std::runtime_error("derived class must implement"); } virtual property Windows::Foundation::AsyncOperationCompletedHandler<_Result>^ Completed { Windows::Foundation::AsyncOperationCompletedHandler<_Result>^ get() { return _M_CompletedHandler; } void set(Windows::Foundation::AsyncOperationCompletedHandler<_Result>^ value) { _M_CompletedHandler = value; _M_asyncInfo.Get()->Completed = ref new _CompletionHandlerType([&](_AsyncOperationType, Windows::Foundation::AsyncStatus status) { _M_CompletedHandler->Invoke(this, status); }); } } }; /// /// Class _IAsyncOperationWithProgressToAsyncOperationConverter is used to convert an instance of IAsyncOperationWithProgress into IAsyncOperation /// template ref struct _IAsyncOperationWithProgressToAsyncOperationConverter sealed : _AsyncInfoImpl^, Windows::Foundation::AsyncOperationWithProgressCompletedHandler<_Result,_Progress>, _Result> { internal: _IAsyncOperationWithProgressToAsyncOperationConverter(Windows::Foundation::IAsyncOperationWithProgress<_Result,_Progress>^ _Operation) : _AsyncInfoImpl^, Windows::Foundation::AsyncOperationWithProgressCompletedHandler<_Result,_Progress>, _Result>(_Operation) {} public: virtual _Result GetResults() override { return _M_asyncInfo.Get()->GetResults(); } }; /// /// Class _IAsyncActionToAsyncOperationConverter is used to convert an instance of IAsyncAction into IAsyncOperation<_Unit_type> /// ref struct _IAsyncActionToAsyncOperationConverter sealed : _AsyncInfoImpl { internal: _IAsyncActionToAsyncOperationConverter(Windows::Foundation::IAsyncAction^ _Operation) : _AsyncInfoImpl(_Operation) {} public: virtual details::_Unit_type GetResults() override { // Invoke GetResults on the IAsyncAction to allow exceptions to be thrown to higher layers before returning a dummy value. _M_asyncInfo.Get()->GetResults(); return details::_Unit_type(); } }; /// /// Class _IAsyncActionWithProgressToAsyncOperationConverter is used to convert an instance of IAsyncActionWithProgress into IAsyncOperation<_Unit_type> /// template ref struct _IAsyncActionWithProgressToAsyncOperationConverter sealed : _AsyncInfoImpl^, Windows::Foundation::AsyncActionWithProgressCompletedHandler<_Progress>, details::_Unit_type> { internal: _IAsyncActionWithProgressToAsyncOperationConverter(Windows::Foundation::IAsyncActionWithProgress<_Progress>^ _Action) : _AsyncInfoImpl^, Windows::Foundation::AsyncActionWithProgressCompletedHandler<_Progress>, details::_Unit_type>(_Action) {} public: virtual details::_Unit_type GetResults() override { // Invoke GetResults on the IAsyncActionWithProgress to allow exceptions to be thrown before returning a dummy value. _M_asyncInfo.Get()->GetResults(); return details::_Unit_type(); } }; #endif /* defined (__cplusplus_winrt) */ } // namespace details /// /// The task_continuation_context class allows you to specify where you would like a continuation to be executed. /// It is only useful to use this class from a Windows Store app. For non-Windows Store apps, the task continuation's /// execution context is determined by the runtime, and not configurable. /// /// /**/ class task_continuation_context : public details::_ContextCallback { public: /// /// Creates the default task continuation context. /// /// /// The default continuation context. /// /// /// The default context is used if you don't specifiy a continuation context when you call the then method. In Windows /// applications for Windows 7 and below, as well as desktop applications on Windows 8 and higher, the runtime determines where /// task continuations will execute. However, in a Windows Store app, the default continuation context for a continuation on an /// apartment aware task is the apartment where then is invoked. /// An apartment aware task is a task that unwraps a Windows Runtime IAsyncInfo interface, or a task that is descended from such /// a task. Therefore, if you schedule a continuation on an apartment aware task in a Windows Runtime STA, the continuation will execute in /// that STA. /// A continuation on a non-apartment aware task will execute in a context the Runtime chooses. /// /**/ static task_continuation_context use_default() { #if defined (__cplusplus_winrt) // The callback context is created with the context set to CaptureDeferred and resolved when it is used in .then() return task_continuation_context(true); // sets it to deferred, is resolved in the constructor of _ContinuationTaskHandle #else /* defined (__cplusplus_winrt) */ return task_continuation_context(); #endif /* defined (__cplusplus_winrt) */ } #if defined (__cplusplus_winrt) /// /// Creates a task continuation context which allows the Runtime to choose the execution context for a continuation. /// /// /// A task continuation context that represents an arbitrary location. /// /// /// When this continuation context is used the continuation will execute in a context the runtime chooses even if the antecedent task /// is apartment aware. /// use_arbitrary can be used to turn off the default behavior for a continuation on an apartment /// aware task created in an STA. /// This method is only available to Windows Store apps. /// /**/ static task_continuation_context use_arbitrary() { task_continuation_context _Arbitrary(true); _Arbitrary._Resolve(false); return _Arbitrary; } /// /// Returns a task continuation context object that represents the current execution context. /// /// /// The current execution context. /// /// /// This method captures the caller's Windows Runtime context so that continuations can be executed in the right apartment. /// The value returned by use_current can be used to indicate to the Runtime that the continuation should execute in /// the captured context (STA vs MTA) regardless of whether or not the antecedent task is apartment aware. An apartment aware task is /// a task that unwraps a Windows Runtime IAsyncInfo interface, or a task that is descended from such a task. /// This method is only available to Windows Store apps. /// /**/ static task_continuation_context use_current() { task_continuation_context _Current(true); _Current._Resolve(true); return _Current; } #endif /* defined (__cplusplus_winrt) */ private: task_continuation_context(bool _DeferCapture = false) : details::_ContextCallback(_DeferCapture) { } }; class task_options; namespace details { struct _Internal_task_options { bool _M_hasPresetCreationCallstack; _TaskCreationCallstack _M_presetCreationCallstack; void _set_creation_callstack(const _TaskCreationCallstack &_callstack) { _M_hasPresetCreationCallstack = true; _M_presetCreationCallstack = _callstack; } _Internal_task_options() { _M_hasPresetCreationCallstack = false; } }; inline _Internal_task_options &_get_internal_task_options(task_options &options); inline const _Internal_task_options &_get_internal_task_options(const task_options &options); } /// /// Represents the allowed options for creating a task /// class task_options { public: /// /// Default list of task creation options /// task_options() : _M_Scheduler(get_ambient_scheduler()), _M_CancellationToken(cancellation_token::none()), _M_ContinuationContext(task_continuation_context::use_default()), _M_HasCancellationToken(false), _M_HasScheduler(false) { } /// /// Task option that specify a cancellation token /// task_options(cancellation_token _Token) : _M_Scheduler(get_ambient_scheduler()), _M_CancellationToken(_Token), _M_ContinuationContext(task_continuation_context::use_default()), _M_HasCancellationToken(true), _M_HasScheduler(false) { } /// /// Task option that specify a continuation context. This is valid only for continuations (then) /// task_options(task_continuation_context _ContinuationContext) : _M_Scheduler(get_ambient_scheduler()), _M_CancellationToken(cancellation_token::none()), _M_ContinuationContext(_ContinuationContext), _M_HasCancellationToken(false), _M_HasScheduler(false) { } /// /// Task option that specify a cancellation token and a continuation context. This is valid only for continuations (then) /// task_options(cancellation_token _Token, task_continuation_context _ContinuationContext) : _M_Scheduler(get_ambient_scheduler()), _M_CancellationToken(_Token), _M_ContinuationContext(_ContinuationContext), _M_HasCancellationToken(true), _M_HasScheduler(false) { } /// /// Task option that specify a scheduler with shared lifetime /// template task_options(std::shared_ptr<_SchedType> _Scheduler) : _M_Scheduler(std::move(_Scheduler)), _M_CancellationToken(cancellation_token::none()), _M_ContinuationContext(task_continuation_context::use_default()), _M_HasCancellationToken(false), _M_HasScheduler(true) { } /// /// Task option that specify a scheduler reference /// task_options(scheduler_interface& _Scheduler) : _M_Scheduler(&_Scheduler), _M_CancellationToken(cancellation_token::none()), _M_ContinuationContext(task_continuation_context::use_default()), _M_HasCancellationToken(false), _M_HasScheduler(true) { } /// /// Task option that specify a scheduler /// task_options(scheduler_ptr _Scheduler) : _M_Scheduler(std::move(_Scheduler)), _M_CancellationToken(cancellation_token::none()), _M_ContinuationContext(task_continuation_context::use_default()), _M_HasCancellationToken(false), _M_HasScheduler(true) { } /// /// Task option copy constructor /// task_options(const task_options& _TaskOptions) : _M_Scheduler(_TaskOptions.get_scheduler()), _M_CancellationToken(_TaskOptions.get_cancellation_token()), _M_ContinuationContext(_TaskOptions.get_continuation_context()), _M_HasCancellationToken(_TaskOptions.has_cancellation_token()), _M_HasScheduler(_TaskOptions.has_scheduler()) { } /// /// Sets the given token in the options /// void set_cancellation_token(cancellation_token _Token) { _M_CancellationToken = _Token; _M_HasCancellationToken = true; } /// /// Sets the given continuation context in the options /// void set_continuation_context(task_continuation_context _ContinuationContext) { _M_ContinuationContext = _ContinuationContext; } /// /// Indicates whether a cancellation token was specified by the user /// bool has_cancellation_token() const { return _M_HasCancellationToken; } /// /// Returns the cancellation token /// cancellation_token get_cancellation_token() const { return _M_CancellationToken; } /// /// Returns the continuation context /// task_continuation_context get_continuation_context() const { return _M_ContinuationContext; } /// /// Indicates whether a scheduler n was specified by the user /// bool has_scheduler() const { return _M_HasScheduler; } /// /// Returns the scheduler /// scheduler_ptr get_scheduler() const { return _M_Scheduler; } private: task_options const& operator=(task_options const& _Right); friend details::_Internal_task_options &details::_get_internal_task_options(task_options &); friend const details::_Internal_task_options &details::_get_internal_task_options(const task_options &); scheduler_ptr _M_Scheduler; cancellation_token _M_CancellationToken; task_continuation_context _M_ContinuationContext; details::_Internal_task_options _M_InternalTaskOptions; bool _M_HasCancellationToken; bool _M_HasScheduler; }; namespace details { inline _Internal_task_options & _get_internal_task_options(task_options &options) { return options._M_InternalTaskOptions; } inline const _Internal_task_options & _get_internal_task_options(const task_options &options) { return options._M_InternalTaskOptions; } struct _Task_impl_base; template struct _Task_impl; template struct _Task_ptr { typedef std::shared_ptr<_Task_impl<_ReturnType>> _Type; static _Type _Make(_CancellationTokenState * _Ct, scheduler_ptr _Scheduler_arg) { return std::make_shared<_Task_impl<_ReturnType>>(_Ct, _Scheduler_arg); } }; typedef _TaskCollection_t::_TaskProcHandle_t _UnrealizedChore_t; typedef std::shared_ptr<_Task_impl_base> _Task_ptr_base; // The weak-typed base task handler for continuation tasks. struct _ContinuationTaskHandleBase : _UnrealizedChore_t { _ContinuationTaskHandleBase * _M_next; task_continuation_context _M_continuationContext; bool _M_isTaskBasedContinuation; // This field gives inlining scheduling policy for current chore. _TaskInliningMode_t _M_inliningMode; virtual _Task_ptr_base _GetTaskImplBase() const = 0; _ContinuationTaskHandleBase() : _M_next(nullptr), _M_continuationContext(task_continuation_context::use_default()), _M_isTaskBasedContinuation(false), _M_inliningMode(details::_NoInline) { } virtual ~_ContinuationTaskHandleBase() {} }; #if _PPLTASK_ASYNC_LOGGING // GUID used for identifying causality logs from PPLTask const ::Platform::Guid _PPLTaskCausalityPlatformID(0x7A76B220, 0xA758, 0x4E6E, 0xB0, 0xE0, 0xD7, 0xC6, 0xD7, 0x4A, 0x88, 0xFE); __declspec(selectany) volatile long _isCausalitySupported = 0; inline bool _IsCausalitySupported() { #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) if (_isCausalitySupported == 0) { long _causality = 1; OSVERSIONINFOEX _osvi = {}; _osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); // The Causality is supported on Windows version higher than Windows 8 _osvi.dwMajorVersion = 6; _osvi.dwMinorVersion = 3; DWORDLONG _conditionMask = 0; VER_SET_CONDITION( _conditionMask, VER_MAJORVERSION, VER_GREATER_EQUAL ); VER_SET_CONDITION( _conditionMask, VER_MINORVERSION, VER_GREATER_EQUAL ); if ( ::VerifyVersionInfo(&_osvi, VER_MAJORVERSION | VER_MINORVERSION, _conditionMask)) { _causality = 2; } _isCausalitySupported = _causality; return _causality == 2; } return _isCausalitySupported == 2 ? true : false; #else return true; #endif } // Stateful logger rests inside task_impl_base. struct _TaskEventLogger { _Task_impl_base *_M_task; bool _M_scheduled; bool _M_taskPostEventStarted; // Log before scheduling task void _LogScheduleTask(bool _isContinuation) { if (details::_IsCausalitySupported()) { ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceOperationCreation(::Windows::Foundation::Diagnostics::CausalityTraceLevel::Required, ::Windows::Foundation::Diagnostics::CausalitySource::Library, _PPLTaskCausalityPlatformID, reinterpret_cast(_M_task), _isContinuation ? "pplx::PPLTask::ScheduleContinuationTask" : "pplx::PPLTask::ScheduleTask", 0); _M_scheduled = true; } } // It will log the cancel event but not canceled state. _LogTaskCompleted will log the terminal state, which includes cancel state. void _LogCancelTask() { if (details::_IsCausalitySupported()) { ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceOperationRelation(::Windows::Foundation::Diagnostics::CausalityTraceLevel::Important, ::Windows::Foundation::Diagnostics::CausalitySource::Library, _PPLTaskCausalityPlatformID, reinterpret_cast(_M_task), ::Windows::Foundation::Diagnostics::CausalityRelation::Cancel); } } // Log when task reaches terminal state. Note: the task can reach a terminal state (by cancellation or exception) without having run void _LogTaskCompleted(); // Log when task body (which includes user lambda and other scheduling code) begin to run void _LogTaskExecutionStarted() { } // Log when task body finish executing void _LogTaskExecutionCompleted() { if (_M_taskPostEventStarted && details::_IsCausalitySupported()) { ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceSynchronousWorkCompletion(::Windows::Foundation::Diagnostics::CausalityTraceLevel::Required, ::Windows::Foundation::Diagnostics::CausalitySource::Library, ::Windows::Foundation::Diagnostics::CausalitySynchronousWork::CompletionNotification); } } // Log right before user lambda being invoked void _LogWorkItemStarted() { if (details::_IsCausalitySupported()) { ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceSynchronousWorkStart(::Windows::Foundation::Diagnostics::CausalityTraceLevel::Required, ::Windows::Foundation::Diagnostics::CausalitySource::Library, _PPLTaskCausalityPlatformID, reinterpret_cast(_M_task), ::Windows::Foundation::Diagnostics::CausalitySynchronousWork::Execution); } } // Log right after user lambda being invoked void _LogWorkItemCompleted() { if (details::_IsCausalitySupported()) { ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceSynchronousWorkCompletion(::Windows::Foundation::Diagnostics::CausalityTraceLevel::Required, ::Windows::Foundation::Diagnostics::CausalitySource::Library, ::Windows::Foundation::Diagnostics::CausalitySynchronousWork::Execution); ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceSynchronousWorkStart(::Windows::Foundation::Diagnostics::CausalityTraceLevel::Required, ::Windows::Foundation::Diagnostics::CausalitySource::Library, _PPLTaskCausalityPlatformID, reinterpret_cast(_M_task), ::Windows::Foundation::Diagnostics::CausalitySynchronousWork::CompletionNotification); _M_taskPostEventStarted = true; } } _TaskEventLogger(_Task_impl_base *_task): _M_task(_task) { _M_scheduled = false; _M_taskPostEventStarted = false; } }; // Exception safe logger for user lambda struct _TaskWorkItemRAIILogger { _TaskEventLogger &_M_logger; _TaskWorkItemRAIILogger(_TaskEventLogger &_taskHandleLogger): _M_logger(_taskHandleLogger) { _M_logger._LogWorkItemStarted(); } ~_TaskWorkItemRAIILogger() { _M_logger._LogWorkItemCompleted(); } _TaskWorkItemRAIILogger &operator =(const _TaskWorkItemRAIILogger &); // cannot be assigned }; #else inline void _LogCancelTask(_Task_impl_base *) {} struct _TaskEventLogger { void _LogScheduleTask(bool) {} void _LogCancelTask() {} void _LogWorkItemStarted() {} void _LogWorkItemCompleted() {} void _LogTaskExecutionStarted() {} void _LogTaskExecutionCompleted() {} void _LogTaskCompleted() {} _TaskEventLogger(_Task_impl_base *) {} }; struct _TaskWorkItemRAIILogger { _TaskWorkItemRAIILogger(_TaskEventLogger &) {} }; #endif /// /// The _PPLTaskHandle is the strong-typed task handle base. All user task functions need to be wrapped in this task handler /// to be executable by PPL. By deriving from a different _BaseTaskHandle, it can be used for both initial tasks and continuation tasks. /// For initial tasks, _PPLTaskHandle will be derived from _UnrealizedChore_t, and for continuation tasks, it will be derived from /// _ContinuationTaskHandleBase. The life time of the _PPLTaskHandle object is be managed by runtime if task handle is scheduled. /// /// /// The result type of the _Task_impl. /// /// /// The derived task handle class. The operator () needs to be implemented. /// /// /// The base class from which _PPLTaskHandle should be derived. This is either _UnrealizedChore_t or _ContinuationTaskHandleBase. /// template struct _PPLTaskHandle : _BaseTaskHandle { _PPLTaskHandle(const typename _Task_ptr<_ReturnType>::_Type & _PTask) : _M_pTask(_PTask) { } virtual ~_PPLTaskHandle() { // Here is the sink of all task completion code paths _M_pTask->_M_taskEventLogger._LogTaskCompleted(); } virtual void invoke() const { // All exceptions should be rethrown to finish cleanup of the task collection. They will be caught and handled // by the runtime. _ASSERTE((bool)_M_pTask); if (!_M_pTask->_TransitionedToStarted()) { static_cast(this)->_SyncCancelAndPropagateException(); return; } _M_pTask->_M_taskEventLogger._LogTaskExecutionStarted(); try { // All derived task handle must implement this contract function. static_cast(this)->_Perform(); } catch(const task_canceled &) { _M_pTask->_Cancel(true); } catch(const _Interruption_exception &) { _M_pTask->_Cancel(true); } #if defined (__cplusplus_winrt) catch(::Platform::Exception^ _E) { _M_pTask->_CancelWithException(_E); } #endif /* defined (__cplusplus_winrt) */ catch(...) { _M_pTask->_CancelWithException(std::current_exception()); } _M_pTask->_M_taskEventLogger._LogTaskExecutionCompleted(); } // Cast _M_pTask pointer to "type-less" _Task_impl_base pointer, which can be used in _ContinuationTaskHandleBase. // The return value should be automatically optimized by R-value ref. _Task_ptr_base _GetTaskImplBase() const { return _M_pTask; } typename _Task_ptr<_ReturnType>::_Type _M_pTask; private: _PPLTaskHandle const & operator=(_PPLTaskHandle const&); // no assignment operator }; /// /// The base implementation of a first-class task. This class contains all the non-type specific /// implementation details of the task. /// /**/ struct _Task_impl_base { enum _TaskInternalState { // Tracks the state of the task, rather than the task collection on which the task is scheduled _Created, _Started, _PendingCancel, _Completed, _Canceled }; // _M_taskEventLogger - 'this' : used in base member initializer list #if defined(_MSC_VER) #pragma warning(push) #pragma warning(disable: 4355) #endif _Task_impl_base(_CancellationTokenState * _PTokenState, scheduler_ptr _Scheduler_arg) : _M_TaskState(_Created), _M_fFromAsync(false), _M_fUnwrappedTask(false), _M_pRegistration(nullptr), _M_Continuations(nullptr), _M_TaskCollection(_Scheduler_arg), _M_taskEventLogger(this) { // Set cancelation token _M_pTokenState = _PTokenState; _ASSERTE(_M_pTokenState != nullptr); if (_M_pTokenState != _CancellationTokenState::_None()) _M_pTokenState->_Reference(); } #if defined(_MSC_VER) #pragma warning(pop) #endif virtual ~_Task_impl_base() { _ASSERTE(_M_pTokenState != nullptr); if (_M_pTokenState != _CancellationTokenState::_None()) { _M_pTokenState->_Release(); } } task_status _Wait() { bool _DoWait = true; #if defined (__cplusplus_winrt) if (_IsNonBlockingThread()) { // In order to prevent Windows Runtime STA threads from blocking the UI, calling task.wait() task.get() is illegal // if task has not been completed. if (!_IsCompleted() && !_IsCanceled()) { throw invalid_operation("Illegal to wait on a task in a Windows Runtime STA"); } else { // Task Continuations are 'scheduled' *inside* the chore that is executing on the ancestors's task group. If a continuation // needs to be marshalled to a different apartment, instead of scheduling, we make a synchronous cross apartment COM // call to execute the continuation. If it then happens to do something which waits on the ancestor (say it calls .get(), which // task based continuations are wont to do), waiting on the task group results in on the chore that is making this // synchronous callback, which causes a deadlock. To avoid this, we test the state ancestor's event , and we will NOT wait on // if it has finished execution (which means now we are on the inline synchronous callback). _DoWait = false; } } #endif /* defined (__cplusplus_winrt) */ if (_DoWait) { // If this task was created from a Windows Runtime async operation, do not attempt to inline it. The // async operation will take place on a thread in the appropriate apartment Simply wait for the completed // event to be set. if (_M_fFromAsync) { _M_TaskCollection._Wait(); } else { // Wait on the task collection to complete. The task collection is guaranteed to still be // valid since the task must be still within scope so that the _Task_impl_base destructor // has not yet been called. This call to _Wait potentially inlines execution of work. try { // Invoking wait on a task collection resets the state of the task collection. This means that // if the task collection itself were canceled, or had encountered an exception, only the first // call to wait will receive this status. However, both cancellation and exceptions flowing through // tasks set state in the task impl itself. // When it returns cancelled, either work chore or the cancel thread should already have set task's state // properly -- cancelled state or completed state (because there was no interruption point). // For tasks with unwrapped tasks, we should not change the state of current task, since the unwrapped task are still running. _M_TaskCollection._RunAndWait(); } catch(details::_Interruption_exception&) { // The _TaskCollection will never be an interruption point since it has a none token. _ASSERTE(false); } catch(task_canceled&) { // task_canceled is a special exception thrown by cancel_current_task. The spec states that cancel_current_task // must be called from code that is executed within the task (throwing it from parallel work created by and waited // upon by the task is acceptable). We can safely assume that the task wrapper _PPLTaskHandle::operator() has seen // the exception and canceled the task. Swallow the exception here. _ASSERTE(_IsCanceled()); } #if defined (__cplusplus_winrt) catch(::Platform::Exception^ _E) { // Its possible the task body hasn't seen the exception, if so we need to cancel with exception here. if(!_HasUserException()) { _CancelWithException(_E); } // Rethrow will mark the exception as observed. _M_exceptionHolder->_RethrowUserException(); } #endif /* defined (__cplusplus_winrt) */ catch(...) { // Its possible the task body hasn't seen the exception, if so we need to cancel with exception here. if(!_HasUserException()) { _CancelWithException(std::current_exception()); } // Rethrow will mark the exception as observed. _M_exceptionHolder->_RethrowUserException(); } // If the lambda body for this task (executed or waited upon in _RunAndWait above) happened to return a task // which is to be unwrapped and plumbed to the output of this task, we must not only wait on the lambda body, we must // wait on the **INNER** body. It is in theory possible that we could inline such if we plumb a series of things through; // however, this takes the tact of simply waiting upon the completion signal. if (_M_fUnwrappedTask) { _M_TaskCollection._Wait(); } } } if (_HasUserException()) { _M_exceptionHolder->_RethrowUserException(); } else if (_IsCanceled()) { return canceled; } _ASSERTE(_IsCompleted()); return completed; } /// /// Requests cancellation on the task and schedules continuations if the task can be transitioned to a terminal state. /// /// /// Set to true if the cancel takes place as a result of the task body encountering an exception, or because an ancestor or task_completion_event the task /// was registered with were canceled with an exception. A synchronous cancel is one that assures the task could not be running on a different thread at /// the time the cancellation is in progress. An asynchronous cancel is one where the thread performing the cancel has no control over the thread that could /// be executing the task, that is the task could execute concurrently while the cancellation is in progress. /// /// /// Whether an exception other than the internal runtime cancellation exceptions caused this cancellation. /// /// /// Whether this exception came from an ancestor task or a task_completion_event as opposed to an exception that was encountered by the task itself. Only valid when /// _UserException is set to true. /// /// /// The exception holder that represents the exception. Only valid when _UserException is set to true. /// virtual bool _CancelAndRunContinuations(bool _SynchronousCancel, bool _UserException, bool _PropagatedFromAncestor, const std::shared_ptr<_ExceptionHolder>& _ExHolder) = 0; bool _Cancel(bool _SynchronousCancel) { // Send in a dummy value for exception. It is not used when the first parameter is false. return _CancelAndRunContinuations(_SynchronousCancel, false, false, _M_exceptionHolder); } bool _CancelWithExceptionHolder(const std::shared_ptr<_ExceptionHolder>& _ExHolder, bool _PropagatedFromAncestor) { // This task was canceled because an ancestor task encountered an exception. return _CancelAndRunContinuations(true, true, _PropagatedFromAncestor, _ExHolder); } #if defined (__cplusplus_winrt) bool _CancelWithException(::Platform::Exception^ _Exception) { // This task was canceled because the task body encountered an exception. _ASSERTE(!_HasUserException()); return _CancelAndRunContinuations(true, true, false, std::make_shared<_ExceptionHolder>(_Exception, _GetTaskCreationCallstack())); } #endif /* defined (__cplusplus_winrt) */ bool _CancelWithException(const std::exception_ptr& _Exception) { // This task was canceled because the task body encountered an exception. _ASSERTE(!_HasUserException()); return _CancelAndRunContinuations(true, true, false, std::make_shared<_ExceptionHolder>(_Exception, _GetTaskCreationCallstack())); } void _RegisterCancellation(std::weak_ptr<_Task_impl_base> _WeakPtr) { _ASSERTE(details::_CancellationTokenState::_IsValid(_M_pTokenState)); auto _CancellationCallback = [_WeakPtr](){ // Taking ownership of the task prevents dead lock during destruction // if the destructor waits for the cancellations to be finished auto _task = _WeakPtr.lock(); if (_task != nullptr) _task->_Cancel(false); }; _M_pRegistration = new details::_CancellationTokenCallback(_CancellationCallback); _M_pTokenState->_RegisterCallback(_M_pRegistration); } void _DeregisterCancellation() { if (_M_pRegistration != nullptr) { _M_pTokenState->_DeregisterCallback(_M_pRegistration); _M_pRegistration->_Release(); _M_pRegistration = nullptr; } } bool _IsCreated() { return (_M_TaskState == _Created); } bool _IsStarted() { return (_M_TaskState == _Started); } bool _IsPendingCancel() { return (_M_TaskState == _PendingCancel); } bool _IsCompleted() { return (_M_TaskState == _Completed); } bool _IsCanceled() { return (_M_TaskState == _Canceled); } bool _HasUserException() { return static_cast(_M_exceptionHolder); } const std::shared_ptr<_ExceptionHolder>& _GetExceptionHolder() { _ASSERTE(_HasUserException()); return _M_exceptionHolder; } bool _IsApartmentAware() { return _M_fFromAsync; } void _SetAsync(bool _Async = true) { _M_fFromAsync = _Async; } _TaskCreationCallstack _GetTaskCreationCallstack() { return _M_pTaskCreationCallstack; } void _SetTaskCreationCallstack(const _TaskCreationCallstack &_Callstack) { _M_pTaskCreationCallstack = _Callstack; } /// /// Helper function to schedule the task on the Task Collection. /// /// /// The task chore handle that need to be executed. /// /// /// The inlining scheduling policy for current _PTaskHandle. /// void _ScheduleTask(_UnrealizedChore_t * _PTaskHandle, _TaskInliningMode_t _InliningMode) { try { _M_TaskCollection._ScheduleTask(_PTaskHandle, _InliningMode); } catch(const task_canceled &) { // task_canceled is a special exception thrown by cancel_current_task. The spec states that cancel_current_task // must be called from code that is executed within the task (throwing it from parallel work created by and waited // upon by the task is acceptable). We can safely assume that the task wrapper _PPLTaskHandle::operator() has seen // the exception and canceled the task. Swallow the exception here. _ASSERTE(_IsCanceled()); } catch(const _Interruption_exception &) { // The _TaskCollection will never be an interruption point since it has a none token. _ASSERTE(false); } catch(...) { // The exception could have come from two places: // 1. From the chore body, so it already should have been caught and canceled. // In this case swallow the exception. // 2. From trying to actually schedule the task on the scheduler. // In this case cancel the task with the current exception, otherwise the // task will never be signaled leading to deadlock when waiting on the task. if (!_HasUserException()) { _CancelWithException(std::current_exception()); } } } /// /// Function executes a continuation. This function is recorded by a parent task implementation /// when a continuation is created in order to execute later. /// /// /// The continuation task chore handle that need to be executed. /// /**/ void _RunContinuation(_ContinuationTaskHandleBase * _PTaskHandle) { _Task_ptr_base _ImplBase = _PTaskHandle->_GetTaskImplBase(); if (_IsCanceled() && !_PTaskHandle->_M_isTaskBasedContinuation) { if (_HasUserException()) { // If the ancestor encountered an exception, transfer the exception to the continuation // This traverses down the tree to propagate the exception. _ImplBase->_CancelWithExceptionHolder(_GetExceptionHolder(), true); } else { // If the ancestor was canceled, then your own execution should be canceled. // This traverses down the tree to cancel it. _ImplBase->_Cancel(true); } } else { // This can only run when the ancestor has completed or it's a task based continuation that fires when a task is canceled // (with or without a user exception). _ASSERTE(_IsCompleted() || _PTaskHandle->_M_isTaskBasedContinuation); _ASSERTE(!_ImplBase->_IsCanceled()); return _ImplBase->_ScheduleContinuationTask(_PTaskHandle); } // If the handle is not scheduled, we need to manually delete it. delete _PTaskHandle; } // Schedule a continuation to run void _ScheduleContinuationTask(_ContinuationTaskHandleBase * _PTaskHandle) { _M_taskEventLogger._LogScheduleTask(true); // Ensure that the continuation runs in proper context (this might be on a Concurrency Runtime thread or in a different Windows Runtime apartment) if (_PTaskHandle->_M_continuationContext._HasCapturedContext()) { // For those continuations need to be scheduled inside captured context, we will try to apply automatic inlining to their inline modes, // if they haven't been specified as _ForceInline yet. This change will encourage those continuations to be executed inline so that reduce // the cost of marshaling. // For normal continuations we won't do any change here, and their inline policies are completely decided by ._ThenImpl method. if (_PTaskHandle->_M_inliningMode != details::_ForceInline) { _PTaskHandle->_M_inliningMode = details::_DefaultAutoInline; } _ScheduleFuncWithAutoInline([_PTaskHandle]() { // Note that we cannot directly capture "this" pointer, instead, we should use _TaskImplPtr, a shared_ptr to the _Task_impl_base. // Because "this" pointer will be invalid as soon as _PTaskHandle get deleted. _PTaskHandle will be deleted after being scheduled. auto _TaskImplPtr = _PTaskHandle->_GetTaskImplBase(); if (details::_ContextCallback::_CaptureCurrent() == _PTaskHandle->_M_continuationContext) { _TaskImplPtr->_ScheduleTask(_PTaskHandle, details::_ForceInline); } else { // // It's entirely possible that the attempt to marshal the call into a differing context will fail. In this case, we need to handle // the exception and mark the continuation as canceled with the appropriate exception. There is one slight hitch to this: // // NOTE: COM's legacy behavior is to swallow SEH exceptions and marshal them back as HRESULTS. This will in effect turn an SEH into // a C++ exception that gets tagged on the task. One unfortunate result of this is that various pieces of the task infrastructure will // not be in a valid state after this in /EHsc (due to the lack of destructors running, etc...). // try { // Dev10 compiler needs this! auto _PTaskHandle1 = _PTaskHandle; _PTaskHandle->_M_continuationContext._CallInContext( [_PTaskHandle1, _TaskImplPtr](){ _TaskImplPtr->_ScheduleTask(_PTaskHandle1, details::_ForceInline); }); } #if defined (__cplusplus_winrt) catch(::Platform::Exception^ _E) { _TaskImplPtr->_CancelWithException(_E); } #endif /* defined (__cplusplus_winrt) */ catch(...) { _TaskImplPtr->_CancelWithException(std::current_exception()); } } }, _PTaskHandle->_M_inliningMode); } else { _ScheduleTask(_PTaskHandle, _PTaskHandle->_M_inliningMode); } } /// /// Schedule the actual continuation. This will either schedule the function on the continuation task's implementation /// if the task has completed or append it to a list of functions to execute when the task actually does complete. /// /// /// The input type of the task. /// /// /// The output type of the task. /// /**/ void _ScheduleContinuation(_ContinuationTaskHandleBase * _PTaskHandle) { enum { _Nothing, _Schedule, _Cancel, _CancelWithException } _Do = _Nothing; // If the task has canceled, cancel the continuation. If the task has completed, execute the continuation right away. // Otherwise, add it to the list of pending continuations { ::pplx::extensibility::scoped_critical_section_t _LockHolder(_M_ContinuationsCritSec); if (_IsCompleted() || (_IsCanceled() && _PTaskHandle->_M_isTaskBasedContinuation)) { _Do = _Schedule; } else if (_IsCanceled()) { if (_HasUserException()) { _Do = _CancelWithException; } else { _Do = _Cancel; } } else { // chain itself on the continuation chain. _PTaskHandle->_M_next = _M_Continuations; _M_Continuations = _PTaskHandle; } } // Cancellation and execution of continuations should be performed after releasing the lock. Continuations off of // async tasks may execute inline. switch (_Do) { case _Schedule: { _PTaskHandle->_GetTaskImplBase()->_ScheduleContinuationTask(_PTaskHandle); break; } case _Cancel: { // If the ancestor was canceled, then your own execution should be canceled. // This traverses down the tree to cancel it. _PTaskHandle->_GetTaskImplBase()->_Cancel(true); delete _PTaskHandle; break; } case _CancelWithException: { // If the ancestor encountered an exception, transfer the exception to the continuation // This traverses down the tree to propagate the exception. _PTaskHandle->_GetTaskImplBase()->_CancelWithExceptionHolder(_GetExceptionHolder(), true); delete _PTaskHandle; break; } case _Nothing: default: // In this case, we have inserted continuation to continuation chain, // nothing more need to be done, just leave. break; } } void _RunTaskContinuations() { // The link list can no longer be modified at this point, // since all following up continuations will be scheduled by themselves. _ContinuationList _Cur = _M_Continuations, _Next; _M_Continuations = nullptr; while (_Cur) { // Current node might be deleted after running, // so we must fetch the next first. _Next = _Cur->_M_next; _RunContinuation(_Cur); _Cur = _Next; } } #if defined (__cplusplus_winrt) static bool _IsNonBlockingThread() { APTTYPE _AptType; APTTYPEQUALIFIER _AptTypeQualifier; HRESULT hr = CoGetApartmentType(&_AptType, &_AptTypeQualifier); // // If it failed, it's not a Windows Runtime/COM initialized thread. This is not a failure. // if (SUCCEEDED(hr)) { switch(_AptType) { case APTTYPE_STA: case APTTYPE_MAINSTA: return true; break; case APTTYPE_NA: switch(_AptTypeQualifier) { // A thread executing in a neutral apartment is either STA or MTA. To find out if this thread is allowed // to wait, we check the app qualifier. If it is an STA thread executing in a neutral apartment, waiting // is illegal, because the thread is responsible for pumping messages and waiting on a task could take the // thread out of circulation for a while. case APTTYPEQUALIFIER_NA_ON_STA: case APTTYPEQUALIFIER_NA_ON_MAINSTA: return true; break; } break; } } #if _UITHREADCTXT_SUPPORT // This method is used to throw an exepection in _Wait() if called within STA. We // want the same behavior if _Wait is called on the UI thread. if (SUCCEEDED(CaptureUiThreadContext(nullptr))) { return true; } #endif /* _UITHREADCTXT_SUPPORT */ return false; } template static void _AsyncInit(const typename _Task_ptr<_ReturnType>::_Type & _OuterTask, Windows::Foundation::IAsyncOperation::_Value>^ _AsyncOp) { // This method is invoked either when a task is created from an existing async operation or // when a lambda that creates an async operation executes. // If the outer task is pending cancel, cancel the async operation before setting the completed handler. The COM reference on // the IAsyncInfo object will be released when all ^references to the operation go out of scope. // This assertion uses the existence of taskcollection to determine if the task was created from an event. // That is no longer valid as even tasks created from a user lambda could have no underlying taskcollection // when a custom scheduler is used. // _ASSERTE((!_OuterTask->_M_TaskCollection._IsCreated() || _OuterTask->_M_fUnwrappedTask) && !_OuterTask->_IsCanceled()); // Pass the shared_ptr by value into the lambda instead of using 'this'. _AsyncOp->Completed = ref new Windows::Foundation::AsyncOperationCompletedHandler<_ReturnType>( [_OuterTask](Windows::Foundation::IAsyncOperation::_Value>^ _Operation, Windows::Foundation::AsyncStatus _Status) mutable { if (_Status == Windows::Foundation::AsyncStatus::Canceled) { _OuterTask->_Cancel(true); } else if (_Status == Windows::Foundation::AsyncStatus::Error) { _OuterTask->_CancelWithException(::Platform::Exception::ReCreateException(static_cast(_Operation->ErrorCode.Value))); } else { _ASSERTE(_Status == Windows::Foundation::AsyncStatus::Completed); _OuterTask->_FinalizeAndRunContinuations(_Operation->GetResults()); } // Take away this shared pointers reference on the task instead of waiting for the delegate to be released. It could // be released on a different thread after a delay, and not releasing the reference here could cause the tasks to hold // on to resources longer than they should. As an example, without this reset, writing to a file followed by reading from // it using the Windows Runtime Async APIs causes a sharing violation. // Using const_cast is the workaround for failed mutable keywords const_cast<_Task_ptr<_ReturnType>::_Type &>(_OuterTask).reset(); }); _OuterTask->_SetUnwrappedAsyncOp(_AsyncOp); } #endif /* defined (__cplusplus_winrt) */ template static void _AsyncInit(const typename _Task_ptr<_ReturnType>::_Type& _OuterTask, const task<_InternalReturnType> & _UnwrappedTask) { _ASSERTE(_OuterTask->_M_fUnwrappedTask && !_OuterTask->_IsCanceled()); // // We must ensure that continuations off _OuterTask (especially exception handling ones) continue to function in the // presence of an exception flowing out of the inner task _UnwrappedTask. This requires an exception handling continuation // off the inner task which does the appropriate funnelling to the outer one. We use _Then instead of then to prevent // the exception from being marked as observed by our internal continuation. This continuation must be scheduled regardless // of whether or not the _OuterTask task is canceled. // _UnwrappedTask._Then([_OuterTask] (task<_InternalReturnType> _AncestorTask) { if (_AncestorTask._GetImpl()->_IsCompleted()) { _OuterTask->_FinalizeAndRunContinuations(_AncestorTask._GetImpl()->_GetResult()); } else { _ASSERTE(_AncestorTask._GetImpl()->_IsCanceled()); if (_AncestorTask._GetImpl()->_HasUserException()) { // Set _PropagatedFromAncestor to false, since _AncestorTask is not an ancestor of _UnwrappedTask. // Instead, it is the enclosing task. _OuterTask->_CancelWithExceptionHolder(_AncestorTask._GetImpl()->_GetExceptionHolder(), false); } else { _OuterTask->_Cancel(true); } } }, nullptr, details::_DefaultAutoInline); } scheduler_ptr _GetScheduler() const { return _M_TaskCollection._GetScheduler(); } // Tracks the internal state of the task volatile _TaskInternalState _M_TaskState; // Set to true either if the ancestor task had the flag set to true, or if the lambda that does the work of this task returns an // async operation or async action that is unwrapped by the runtime. bool _M_fFromAsync; // Set to true when a continuation unwraps a task or async operation. bool _M_fUnwrappedTask; // An exception thrown by the task body is captured in an exception holder and it is shared with all value based continuations rooted at the task. // The exception is 'observed' if the user invokes get()/wait() on any of the tasks that are sharing this exception holder. If the exception // is not observed by the time the internal object owned by the shared pointer destructs, the process will fail fast. std::shared_ptr<_ExceptionHolder> _M_exceptionHolder; ::pplx::extensibility::critical_section_t _M_ContinuationsCritSec; // The cancellation token state. _CancellationTokenState * _M_pTokenState; // The registration on the token. _CancellationTokenRegistration * _M_pRegistration; typedef _ContinuationTaskHandleBase * _ContinuationList; _ContinuationList _M_Continuations; // The async task collection wrapper ::pplx::details::_TaskCollection_t _M_TaskCollection; // Callstack for function call (constructor or .then) that created this task impl. _TaskCreationCallstack _M_pTaskCreationCallstack; _TaskEventLogger _M_taskEventLogger; private: // Must not be copied by value: _Task_impl_base(const _Task_impl_base&); _Task_impl_base const & operator=(_Task_impl_base const&); }; #if _PPLTASK_ASYNC_LOGGING inline void _TaskEventLogger::_LogTaskCompleted() { if (_M_scheduled) { ::Windows::Foundation::AsyncStatus _State; if (_M_task->_IsCompleted()) _State = ::Windows::Foundation::AsyncStatus::Completed; else if (_M_task->_HasUserException()) _State = ::Windows::Foundation::AsyncStatus::Error; else _State = ::Windows::Foundation::AsyncStatus::Canceled; if (details::_IsCausalitySupported()) { ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceOperationCompletion(::Windows::Foundation::Diagnostics::CausalityTraceLevel::Required, ::Windows::Foundation::Diagnostics::CausalitySource::Library, _PPLTaskCausalityPlatformID, reinterpret_cast(_M_task), _State); } } } #endif /// /// The implementation of a first-class task. This structure contains the task group used to execute /// the task function and handles the scheduling. The _Task_impl is created as a shared_ptr /// member of the the public task class, so its destruction is handled automatically. /// /// /// The result type of this task. /// /**/ template struct _Task_impl : public _Task_impl_base { #if defined (__cplusplus_winrt) typedef Windows::Foundation::IAsyncOperation::_Value> _AsyncOperationType; #endif // defined(__cplusplus_winrt) _Task_impl(_CancellationTokenState * _Ct, scheduler_ptr _Scheduler_arg) : _Task_impl_base(_Ct, _Scheduler_arg) { #if defined (__cplusplus_winrt) _M_unwrapped_async_op = nullptr; #endif /* defined (__cplusplus_winrt) */ } virtual ~_Task_impl() { // We must invoke _DeregisterCancellation in the derived class destructor. Calling it in the base class destructor could cause // a partially initialized _Task_impl to be in the list of registrations for a cancellation token. _DeregisterCancellation(); } virtual bool _CancelAndRunContinuations(bool _SynchronousCancel, bool _UserException, bool _PropagatedFromAncestor, const std::shared_ptr<_ExceptionHolder> & _ExceptionHolder_arg) { bool _RunContinuations = false; { ::pplx::extensibility::scoped_critical_section_t _LockHolder(_M_ContinuationsCritSec); if (_UserException) { _ASSERTE(_SynchronousCancel && !_IsCompleted()); // If the state is _Canceled, the exception has to be coming from an ancestor. _ASSERTE(!_IsCanceled() || _PropagatedFromAncestor); // We should not be canceled with an exception more than once. _ASSERTE(!_HasUserException()); // Mark _PropagatedFromAncestor as used. (void)_PropagatedFromAncestor; if (_M_TaskState == _Canceled) { // If the task has finished cancelling there should not be any continuation records in the array. return false; } else { _ASSERTE(_M_TaskState != _Completed); _M_exceptionHolder = _ExceptionHolder_arg; } } else { // Completed is a non-cancellable state, and if this is an asynchronous cancel, we're unable to do better than the last async cancel // which is to say, cancellation is already initiated, so return early. if (_IsCompleted() || _IsCanceled() || (_IsPendingCancel() && !_SynchronousCancel)) { _ASSERTE(!_IsCompleted() || !_HasUserException()); return false; } _ASSERTE(!_SynchronousCancel || !_HasUserException()); } if (_SynchronousCancel) { // Be aware that this set must be done BEFORE _M_Scheduled being set, or race will happen between this and wait() _M_TaskState = _Canceled; // Cancellation completes the task, so all dependent tasks must be run to cancel them // They are canceled when they begin running (see _RunContinuation) and see that their // ancestor has been canceled. _RunContinuations = true; } else { _ASSERTE(!_UserException); if (_IsStarted()) { #if defined (__cplusplus_winrt) if (_M_unwrapped_async_op != nullptr) { // We will only try to cancel async operation but not unwrapped tasks, since unwrapped tasks cannot be canceled without its token. _M_unwrapped_async_op->Cancel(); } #endif /* defined (__cplusplus_winrt) */ _M_TaskCollection._Cancel(); } // The _M_TaskState variable transitions to _Canceled when cancellation is completed (the task is not executing user code anymore). // In the case of a synchronous cancel, this can happen immediately, whereas with an asynchronous cancel, the task has to move from // _Started to _PendingCancel before it can move to _Canceled when it is finished executing. _M_TaskState = _PendingCancel; _M_taskEventLogger._LogCancelTask(); } } // Only execute continuations and mark the task as completed if we were able to move the task to the _Canceled state. if (_RunContinuations) { _M_TaskCollection._Complete(); if (_M_Continuations) { // Scheduling cancellation with automatic inlining. _ScheduleFuncWithAutoInline([=](){ _RunTaskContinuations(); }, details::_DefaultAutoInline); } } return true; } void _FinalizeAndRunContinuations(_ReturnType _Result) { _M_Result.Set(_Result); { // // Hold this lock to ensure continuations being concurrently either get added // to the _M_Continuations vector or wait for the result // ::pplx::extensibility::scoped_critical_section_t _LockHolder(_M_ContinuationsCritSec); // A task could still be in the _Created state if it was created with a task_completion_event. // It could also be in the _Canceled state for the same reason. _ASSERTE(!_HasUserException() && !_IsCompleted()); if (_IsCanceled()) { return; } // Always transition to "completed" state, even in the face of unacknowledged pending cancellation _M_TaskState = _Completed; } _M_TaskCollection._Complete(); _RunTaskContinuations(); } // // This method is invoked when the starts executing. The task returns early if this method returns true. // bool _TransitionedToStarted() { ::pplx::extensibility::scoped_critical_section_t _LockHolder(_M_ContinuationsCritSec); // Canceled state could only result from antecedent task's canceled state, but that code path will not reach here. _ASSERTE(!_IsCanceled()); if (_IsPendingCancel()) return false; _ASSERTE(_IsCreated()); _M_TaskState = _Started; return true; } #if defined (__cplusplus_winrt) void _SetUnwrappedAsyncOp(_AsyncOperationType^ _AsyncOp) { ::pplx::extensibility::scoped_critical_section_t _LockHolder(_M_ContinuationsCritSec); // Cancel the async operation if the task itself is canceled, since the thread that canceled the task missed it. if (_IsPendingCancel()) { _ASSERTE(!_IsCanceled()); _AsyncOp->Cancel(); } else { _M_unwrapped_async_op = _AsyncOp; } } #endif /* defined (__cplusplus_winrt) */ // Return true if the task has reached a terminal state bool _IsDone() { return _IsCompleted() || _IsCanceled(); } _ReturnType _GetResult() { return _M_Result.Get(); } _ResultHolder<_ReturnType> _M_Result; // this means that the result type must have a public default ctor. #if defined (__cplusplus_winrt) _AsyncOperationType^ _M_unwrapped_async_op; #endif /* defined (__cplusplus_winrt) */ }; template struct _Task_completion_event_impl { private: _Task_completion_event_impl(const _Task_completion_event_impl&); _Task_completion_event_impl& operator=(const _Task_completion_event_impl&); public: typedef std::vector::_Type> _TaskList; _Task_completion_event_impl() : _M_fHasValue(false), _M_fIsCanceled(false) { } bool _HasUserException() { return _M_exceptionHolder != nullptr; } ~_Task_completion_event_impl() { for( auto _TaskIt = _M_tasks.begin(); _TaskIt != _M_tasks.end(); ++_TaskIt ) { _ASSERTE(!_M_fHasValue && !_M_fIsCanceled); // Cancel the tasks since the event was never signaled or canceled. (*_TaskIt)->_Cancel(true); } } // We need to protect the loop over the array, so concurrent_vector would not have helped _TaskList _M_tasks; ::pplx::extensibility::critical_section_t _M_taskListCritSec; _ResultHolder<_ResultType> _M_value; std::shared_ptr<_ExceptionHolder> _M_exceptionHolder; bool _M_fHasValue; bool _M_fIsCanceled; }; // Utility method for dealing with void functions inline std::function<_Unit_type(void)> _MakeVoidToUnitFunc(const std::function& _Func) { return [=]() -> _Unit_type { _Func(); return _Unit_type(); }; } template std::function<_Type(_Unit_type)> _MakeUnitToTFunc(const std::function<_Type(void)>& _Func) { return [=](_Unit_type) -> _Type { return _Func(); }; } template std::function<_Unit_type(_Type)> _MakeTToUnitFunc(const std::function& _Func) { return [=](_Type t) -> _Unit_type { _Func(t); return _Unit_type(); }; } inline std::function<_Unit_type(_Unit_type)> _MakeUnitToUnitFunc(const std::function& _Func) { return [=](_Unit_type) -> _Unit_type { _Func(); return _Unit_type(); }; } } // namespace details /// /// The task_completion_event class allows you to delay the execution of a task until a condition is satisfied, /// or start a task in response to an external event. /// /// /// The result type of this task_completion_event class. /// /// /// Use a task created from a task completion event when your scenario requires you to create a task that will complete, and /// thereby have its continuations scheduled for execution, at some point in the future. The task_completion_event must /// have the same type as the task you create, and calling the set method on the task completion event with a value of that type /// will cause the associated task to complete, and provide that value as a result to its continuations. /// If the task completion event is never signaled, any tasks created from it will be canceled when it is destructed. /// task_completion_event behaves like a smart pointer, and should be passed by value. /// /// /**/ template class task_completion_event { public: /// /// Constructs a task_completion_event object. /// /**/ task_completion_event() : _M_Impl(std::make_shared>()) { } /// /// Sets the task completion event. /// /// /// The result to set this event with. /// /// /// The method returns true if it was successful in setting the event. It returns false if the event is already set. /// /// /// In the presence of multiple or concurrent calls to set, only the first call will succeed and its result (if any) will be stored in the /// task completion event. The remaining sets are ignored and the method will return false. When you set a task completion event, all the /// tasks created from that event will immediately complete, and its continuations, if any, will be scheduled. Task completion objects that have /// a other than void will pass the value to their continuations. /// /**/ bool set(_ResultType _Result) const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas { // Subsequent sets are ignored. This makes races to set benign: the first setter wins and all others are ignored. if (_IsTriggered()) { return false; } _TaskList _Tasks; bool _RunContinuations = false; { ::pplx::extensibility::scoped_critical_section_t _LockHolder(_M_Impl->_M_taskListCritSec); if (!_IsTriggered()) { _M_Impl->_M_value.Set(_Result); _M_Impl->_M_fHasValue = true; _Tasks.swap(_M_Impl->_M_tasks); _RunContinuations = true; } } if (_RunContinuations) { for( auto _TaskIt = _Tasks.begin(); _TaskIt != _Tasks.end(); ++_TaskIt ) { // If current task was cancelled by a cancellation_token, it would be in cancel pending state. if ((*_TaskIt)->_IsPendingCancel()) (*_TaskIt)->_Cancel(true); else { // Tasks created with task_completion_events can be marked as async, (we do this in when_any and when_all // if one of the tasks involved is an async task). Since continuations of async tasks can execute inline, we // need to run continuations after the lock is released. (*_TaskIt)->_FinalizeAndRunContinuations(_M_Impl->_M_value.Get()); } } if (_M_Impl->_HasUserException()) { _M_Impl->_M_exceptionHolder.reset(); } return true; } return false; } template __declspec(noinline) // Ask for no inlining so that the _ReturnAddress intrinsic gives us the expected result bool set_exception(_E _Except) const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas { // It is important that _CAPTURE_CALLSTACK() evaluate to the instruction after the call instruction for set_exception. return _Cancel(std::make_exception_ptr(_Except), _CAPTURE_CALLSTACK()); } /// /// Propagates an exception to all tasks associated with this event. /// /// /// The exception_ptr that indicates the exception to set this event with. /// /**/ __declspec(noinline) // Ask for no inlining so that the _CAPTURE_CALLSTACK gives us the expected result bool set_exception(std::exception_ptr _ExceptionPtr) const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas { // It is important that _CAPTURE_CALLSTACK() evaluate to the instruction after the call instruction for set_exception. return _Cancel(_ExceptionPtr, _CAPTURE_CALLSTACK()); } /// /// Internal method to cancel the task_completion_event. Any task created using this event will be marked as canceled if it has /// not already been set. /// bool _Cancel() const { // Cancel with the stored exception if one exists. return _CancelInternal(); } /// /// Internal method to cancel the task_completion_event with the exception provided. Any task created using this event will be canceled /// with the same exception. /// template bool _Cancel(_ExHolderType _ExHolder, const details::_TaskCreationCallstack &_SetExceptionAddressHint = details::_TaskCreationCallstack ()) const { bool _Canceled; if(_StoreException(_ExHolder, _SetExceptionAddressHint)) { _Canceled = _CancelInternal(); _ASSERTE(_Canceled); } else { _Canceled = false; } return _Canceled; } /// /// Internal method that stores an exception in the task completion event. This is used internally by when_any. /// Note, this does not cancel the task completion event. A task completion event with a stored exception /// can bet set() successfully. If it is canceled, it will cancel with the stored exception, if one is present. /// template bool _StoreException(_ExHolderType _ExHolder, const details::_TaskCreationCallstack &_SetExceptionAddressHint = details::_TaskCreationCallstack ()) const { ::pplx::extensibility::scoped_critical_section_t _LockHolder(_M_Impl->_M_taskListCritSec); if (!_IsTriggered() && !_M_Impl->_HasUserException()) { // Create the exception holder only if we have ensured there we will be successful in setting it onto the // task completion event. Failing to do so will result in an unobserved task exception. _M_Impl->_M_exceptionHolder = _ToExceptionHolder(_ExHolder, _SetExceptionAddressHint); return true; } return false; } /// /// Tests whether current event has been either Set, or Canceled. /// bool _IsTriggered() const { return _M_Impl->_M_fHasValue || _M_Impl->_M_fIsCanceled; } private: static std::shared_ptr _ToExceptionHolder(const std::shared_ptr& _ExHolder, const details::_TaskCreationCallstack&) { return _ExHolder; } static std::shared_ptr _ToExceptionHolder(std::exception_ptr _ExceptionPtr, const details::_TaskCreationCallstack &_SetExceptionAddressHint) { return std::make_shared(_ExceptionPtr, _SetExceptionAddressHint); } template friend class task; // task can register itself with the event by calling the private _RegisterTask template friend class task_completion_event; typedef typename details::_Task_completion_event_impl<_ResultType>::_TaskList _TaskList; /// /// Cancels the task_completion_event. /// bool _CancelInternal() const { // Cancellation of task completion events is an internal only utility. Our usage is such that _CancelInternal // will never be invoked if the task completion event has been set. _ASSERTE(!_M_Impl->_M_fHasValue); if (_M_Impl->_M_fIsCanceled) { return false; } _TaskList _Tasks; bool _Cancel = false; { ::pplx::extensibility::scoped_critical_section_t _LockHolder(_M_Impl->_M_taskListCritSec); _ASSERTE(!_M_Impl->_M_fHasValue); if (!_M_Impl->_M_fIsCanceled) { _M_Impl->_M_fIsCanceled = true; _Tasks.swap(_M_Impl->_M_tasks); _Cancel = true; } } bool _UserException = _M_Impl->_HasUserException(); if (_Cancel) { for( auto _TaskIt = _Tasks.begin(); _TaskIt != _Tasks.end(); ++_TaskIt ) { // Need to call this after the lock is released. See comments in set(). if (_UserException) { (*_TaskIt)->_CancelWithExceptionHolder(_M_Impl->_M_exceptionHolder, true); } else { (*_TaskIt)->_Cancel(true); } } } return _Cancel; } /// /// Register a task with this event. This function is called when a task is constructed using /// a task_completion_event. /// void _RegisterTask(const typename details::_Task_ptr<_ResultType>::_Type & _TaskParam) { ::pplx::extensibility::scoped_critical_section_t _LockHolder(_M_Impl->_M_taskListCritSec); //If an exception was already set on this event, then cancel the task with the stored exception. if(_M_Impl->_HasUserException()) { _TaskParam->_CancelWithExceptionHolder(_M_Impl->_M_exceptionHolder, true); } else if (_M_Impl->_M_fHasValue) { _TaskParam->_FinalizeAndRunContinuations(_M_Impl->_M_value.Get()); } else { _M_Impl->_M_tasks.push_back(_TaskParam); } } std::shared_ptr> _M_Impl; }; /// /// The task_completion_event class allows you to delay the execution of a task until a condition is satisfied, /// or start a task in response to an external event. /// /// /// Use a task created from a task completion event when your scenario requires you to create a task that will complete, and /// thereby have its continuations scheduled for execution, at some point in the future. The task_completion_event must /// have the same type as the task you create, and calling the set method on the task completion event with a value of that type /// will cause the associated task to complete, and provide that value as a result to its continuations. /// If the task completion event is never signaled, any tasks created from it will be canceled when it is destructed. /// task_completion_event behaves like a smart pointer, and should be passed by value. /// /// /**/ template<> class task_completion_event { public: /// /// Sets the task completion event. /// /// /// The method returns true if it was successful in setting the event. It returns false if the event is already set. /// /// /// In the presence of multiple or concurrent calls to set, only the first call will succeed and its result (if any) will be stored in the /// task completion event. The remaining sets are ignored and the method will return false. When you set a task completion event, all the /// tasks created from that event will immediately complete, and its continuations, if any, will be scheduled. Task completion objects that have /// a other than void will pass the value to their continuations. /// /**/ bool set() const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas { return _M_unitEvent.set(details::_Unit_type()); } template __declspec(noinline) // Ask for no inlining so that the _ReturnAddress intrinsic gives us the expected result bool set_exception(_E _Except) const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas { return _M_unitEvent._Cancel(std::make_exception_ptr(_Except), _CAPTURE_CALLSTACK()); } /// /// Propagates an exception to all tasks associated with this event. /// /// /// The exception_ptr that indicates the exception to set this event with. /// /**/ __declspec(noinline) // Ask for no inlining so that the _CAPTURE_CALLSTACK intrinsic gives us the expected result bool set_exception(std::exception_ptr _ExceptionPtr) const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas { // It is important that _CAPTURE_CALLSTACK() evaluate to the instruction after the call instruction for set_exception. return _M_unitEvent._Cancel(_ExceptionPtr, _CAPTURE_CALLSTACK()); } /// /// Cancel the task_completion_event. Any task created using this event will be marked as canceled if it has /// not already been set. /// void _Cancel() const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas { _M_unitEvent._Cancel(); } /// /// Cancel the task_completion_event with the exception holder provided. Any task created using this event will be canceled /// with the same exception. /// void _Cancel(const std::shared_ptr& _ExHolder) const { _M_unitEvent._Cancel(_ExHolder); } /// /// Method that stores an exception in the task completion event. This is used internally by when_any. /// Note, this does not cancel the task completion event. A task completion event with a stored exception /// can bet set() successfully. If it is canceled, it will cancel with the stored exception, if one is present. /// bool _StoreException(const std::shared_ptr& _ExHolder) const { return _M_unitEvent._StoreException(_ExHolder); } /// /// Test whether current event has been either Set, or Canceled. /// bool _IsTriggered() const { return _M_unitEvent._IsTriggered(); } private: template friend class task; // task can register itself with the event by calling the private _RegisterTask /// /// Register a task with this event. This function is called when a task is constructed using /// a task_completion_event. /// void _RegisterTask(details::_Task_ptr::_Type _TaskParam) { _M_unitEvent._RegisterTask(_TaskParam); } // The void event contains an event a dummy type so common code can be used for events with void and non-void results. task_completion_event _M_unitEvent; }; namespace details { // // Compile-time validation helpers // // Task constructor validation: issue helpful diagnostics for common user errors. Do not attempt full validation here. // // Anything callable is fine template auto _IsValidTaskCtor(_Ty _Param, int,int,int,int) -> decltype(_Param(), std::true_type()); #if defined (__cplusplus_winrt) // Anything that has GetResults is fine: this covers all async operations template auto _IsValidTaskCtor(_Ty _Param, int, int, int,...) -> decltype(_Param->GetResults(), std::true_type()); #endif // Allow parameters with set: this covers task_completion_event template auto _IsValidTaskCtor(_Ty _Param, int, int, ...) -> decltype(_Param.set(stdx::declval<_ReturnType>()), std::true_type()); template auto _IsValidTaskCtor(_Ty _Param, int, ...) -> decltype(_Param.set(), std::true_type()); // All else is invalid template std::false_type _IsValidTaskCtor(_Ty _Param, ...); template void _ValidateTaskConstructorArgs(_Ty _Param) { static_assert(std::is_same(_Param,0,0,0,0)),std::true_type>::value, #if defined (__cplusplus_winrt) "incorrect argument for task constructor; can be a callable object, an asynchronous operation, or a task_completion_event" #else /* defined (__cplusplus_winrt) */ "incorrect argument for task constructor; can be a callable object or a task_completion_event" #endif /* defined (__cplusplus_winrt) */ ); #if defined (__cplusplus_winrt) static_assert(!(std::is_same<_Ty,_ReturnType>::value && details::_IsIAsyncInfo<_Ty>::_Value), "incorrect template argument for task; consider using the return type of the async operation"); #endif /* defined (__cplusplus_winrt) */ } #if defined (__cplusplus_winrt) // Helpers for create_async validation // // A parameter lambda taking no arguments is valid template static auto _IsValidCreateAsync(_Ty _Param, int, int, int, int) -> decltype(_Param(), std::true_type()); // A parameter lambda taking an cancellation_token argument is valid template static auto _IsValidCreateAsync(_Ty _Param, int, int, int, ...) -> decltype(_Param(cancellation_token::none()), std::true_type()); // A parameter lambda taking a progress report argument is valid template static auto _IsValidCreateAsync(_Ty _Param, int, int, ...) -> decltype(_Param(details::_ProgressReporterCtorArgType()), std::true_type()); // A parameter lambda taking a progress report and a cancellation_token argument is valid template static auto _IsValidCreateAsync(_Ty _Param, int, ...) -> decltype(_Param(details::_ProgressReporterCtorArgType(), cancellation_token::none()), std::true_type()); // All else is invalid template static std::false_type _IsValidCreateAsync(_Ty _Param, ...); #endif /* defined (__cplusplus_winrt) */ } /// /// A helper class template that transforms a continuation lambda that either takes or returns void, or both, into a lambda that takes and returns a /// non-void type (details::_Unit_type is used to substitute for void). This is to minimize the special handling required for 'void'. /// template class _Continuation_func_transformer { public: static auto _Perform(std::function<_OutType(_InpType)> _Func) -> decltype(_Func) { return _Func; } }; template class _Continuation_func_transformer { public: static auto _Perform(std::function<_OutType(void)> _Func) -> decltype(details::_MakeUnitToTFunc<_OutType>(_Func)) { return details::_MakeUnitToTFunc<_OutType>(_Func); } }; template class _Continuation_func_transformer<_InType, void> { public: static auto _Perform(std::function _Func) -> decltype(details::_MakeTToUnitFunc<_InType>(_Func)) { return details::_MakeTToUnitFunc<_InType>(_Func); } }; template<> class _Continuation_func_transformer { public: static auto _Perform(std::function _Func) -> decltype(details::_MakeUnitToUnitFunc(_Func)) { return details::_MakeUnitToUnitFunc(_Func); } }; // A helper class template that transforms an intial task lambda returns void into a lambda that returns a non-void type (details::_Unit_type is used // to substitute for void). This is to minimize the special handling required for 'void'. template class _Init_func_transformer { public: static auto _Perform(std::function<_RetType(void)> _Func) -> decltype(_Func) { return _Func; } }; template<> class _Init_func_transformer { public: static auto _Perform(std::function _Func) -> decltype(details::_MakeVoidToUnitFunc(_Func)) { return details::_MakeVoidToUnitFunc(_Func); } }; /// /// The Parallel Patterns Library (PPL) task class. A task object represents work that can be executed asynchronously, /// and concurrently with other tasks and parallel work produced by parallel algorithms in the Concurrency Runtime. It produces /// a result of type on successful completion. Tasks of type task<void> produce no result. /// A task can be waited upon and canceled independently of other tasks. It can also be composed with other tasks using /// continuations(then), and join(when_all) and choice(when_any) patterns. /// /// /// The result type of this task. /// /// /// For more information, see . /// /**/ template class task { public: /// /// The type of the result an object of this class produces. /// /**/ typedef _ReturnType result_type; /// /// Constructs a task object. /// /// /// The default constructor for a task is only present in order to allow tasks to be used within containers. /// A default constructed task cannot be used until you assign a valid task to it. Methods such as get, wait or then /// will throw an invalid_argument exception when called on a default constructed task. /// A task that is created from a task_completion_event will complete (and have its continuations scheduled) when the task /// completion event is set. /// The version of the constructor that takes a cancellation token creates a task that can be canceled using the /// cancellation_token_source the token was obtained from. Tasks created without a cancellation token are not cancelable. /// Tasks created from a Windows::Foundation::IAsyncInfo interface or a lambda that returns an IAsyncInfo interface /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created /// from a lamda that returns a task<result_type> reach their terminal state when the inner task reaches its terminal state, /// and not when the lamda returns. /// task behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads /// without the need for locks. /// The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available /// to Windows Store apps. /// For more information, see . /// /**/ task() : _M_Impl(nullptr) { // The default constructor should create a task with a nullptr impl. This is a signal that the // task is not usable and should throw if any wait(), get() or then() APIs are used. } /// /// Constructs a task object. /// /// /// The type of the parameter from which the task is to be constructed. /// /// /// The parameter from which the task is to be constructed. This could be a lambda, a function object, a task_completion_event<result_type> /// object, or a Windows::Foundation::IAsyncInfo if you are using tasks in your Windows Store app. The lambda or function /// object should be a type equivalent to std::function<X(void)>, where X can be a variable of type result_type, /// task<result_type>, or a Windows::Foundation::IAsyncInfo in Windows Store apps. /// /// /// The cancellation token to associate with this task. A task created without a cancellation token cannot be canceled. It implicitly receives /// the token cancellation_token::none(). /// /// /// The default constructor for a task is only present in order to allow tasks to be used within containers. /// A default constructed task cannot be used until you assign a valid task to it. Methods such as get, wait or then /// will throw an invalid_argument exception when called on a default constructed task. /// A task that is created from a task_completion_event will complete (and have its continuations scheduled) when the task /// completion event is set. /// The version of the constructor that takes a cancellation token creates a task that can be canceled using the /// cancellation_token_source the token was obtained from. Tasks created without a cancellation token are not cancelable. /// Tasks created from a Windows::Foundation::IAsyncInfo interface or a lambda that returns an IAsyncInfo interface /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created /// from a lamda that returns a task<result_type> reach their terminal state when the inner task reaches its terminal state, /// and not when the lamda returns. /// task behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads /// without the need for locks. /// The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available /// to Windows Store apps. /// For more information, see . /// /**/ template __declspec(noinline) // Ask for no inlining so that the _CAPTURE_CALLSTACK gives us the expected result explicit task(_Ty _Param) { task_options _TaskOptions; details::_ValidateTaskConstructorArgs<_ReturnType,_Ty>(_Param); _CreateImpl(_TaskOptions.get_cancellation_token()._GetImplValue(), _TaskOptions.get_scheduler()); // Do not move the next line out of this function. It is important that _CAPTURE_CALLSTACK() evaluate to the the call site of the task constructor. _SetTaskCreationCallstack(_CAPTURE_CALLSTACK()); _TaskInitMaybeFunctor(_Param, details::_IsCallable(_Param,0)); } /// /// Constructs a task object. /// /// /// The type of the parameter from which the task is to be constructed. /// /// /// The parameter from which the task is to be constructed. This could be a lambda, a function object, a task_completion_event<result_type> /// object, or a Windows::Foundation::IAsyncInfo if you are using tasks in your Windows Store app. The lambda or function /// object should be a type equivalent to std::function<X(void)>, where X can be a variable of type result_type, /// task<result_type>, or a Windows::Foundation::IAsyncInfo in Windows Store apps. /// /// /// The task options include cancellation token, scheduler etc /// /// /// The default constructor for a task is only present in order to allow tasks to be used within containers. /// A default constructed task cannot be used until you assign a valid task to it. Methods such as get, wait or then /// will throw an invalid_argument exception when called on a default constructed task. /// A task that is created from a task_completion_event will complete (and have its continuations scheduled) when the task /// completion event is set. /// The version of the constructor that takes a cancellation token creates a task that can be canceled using the /// cancellation_token_source the token was obtained from. Tasks created without a cancellation token are not cancelable. /// Tasks created from a Windows::Foundation::IAsyncInfo interface or a lambda that returns an IAsyncInfo interface /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created /// from a lamda that returns a task<result_type> reach their terminal state when the inner task reaches its terminal state, /// and not when the lamda returns. /// task behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads /// without the need for locks. /// The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available /// to Windows Store apps. /// For more information, see . /// /**/ template __declspec(noinline) // Ask for no inlining so that the _CAPTURE_CALLSTACK gives us the expected result explicit task(_Ty _Param, const task_options &_TaskOptions) { details::_ValidateTaskConstructorArgs<_ReturnType,_Ty>(_Param); _CreateImpl(_TaskOptions.get_cancellation_token()._GetImplValue(), _TaskOptions.get_scheduler()); // Do not move the next line out of this function. It is important that _CAPTURE_CALLSTACK() evaluate to the the call site of the task constructor. _SetTaskCreationCallstack(details::_get_internal_task_options(_TaskOptions)._M_hasPresetCreationCallstack ? details::_get_internal_task_options(_TaskOptions)._M_presetCreationCallstack : _CAPTURE_CALLSTACK()); _TaskInitMaybeFunctor(_Param, details::_IsCallable(_Param,0)); } /// /// Constructs a task object. /// /// /// The source task object. /// /// /// The default constructor for a task is only present in order to allow tasks to be used within containers. /// A default constructed task cannot be used until you assign a valid task to it. Methods such as get, wait or then /// will throw an invalid_argument exception when called on a default constructed task. /// A task that is created from a task_completion_event will complete (and have its continuations scheduled) when the task /// completion event is set. /// The version of the constructor that takes a cancellation token creates a task that can be canceled using the /// cancellation_token_source the token was obtained from. Tasks created without a cancellation token are not cancelable. /// Tasks created from a Windows::Foundation::IAsyncInfo interface or a lambda that returns an IAsyncInfo interface /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created /// from a lamda that returns a task<result_type> reach their terminal state when the inner task reaches its terminal state, /// and not when the lamda returns. /// task behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads /// without the need for locks. /// The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available /// to Windows Store apps. /// For more information, see . /// /**/ task(const task& _Other): _M_Impl(_Other._M_Impl) {} /// /// Constructs a task object. /// /// /// The source task object. /// /// /// The default constructor for a task is only present in order to allow tasks to be used within containers. /// A default constructed task cannot be used until you assign a valid task to it. Methods such as get, wait or then /// will throw an invalid_argument exception when called on a default constructed task. /// A task that is created from a task_completion_event will complete (and have its continuations scheduled) when the task /// completion event is set. /// The version of the constructor that takes a cancellation token creates a task that can be canceled using the /// cancellation_token_source the token was obtained from. Tasks created without a cancellation token are not cancelable. /// Tasks created from a Windows::Foundation::IAsyncInfo interface or a lambda that returns an IAsyncInfo interface /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created /// from a lamda that returns a task<result_type> reach their terminal state when the inner task reaches its terminal state, /// and not when the lamda returns. /// task behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads /// without the need for locks. /// The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available /// to Windows Store apps. /// For more information, see . /// /**/ task(task&& _Other): _M_Impl(std::move(_Other._M_Impl)) {} /// /// Replaces the contents of one task object with another. /// /// /// The source task object. /// /// /// As task behaves like a smart pointer, after a copy assignment, this task objects represents the same /// actual task as does. /// /**/ task& operator=(const task& _Other) { if (this != &_Other) { _M_Impl = _Other._M_Impl; } return *this; } /// /// Replaces the contents of one task object with another. /// /// /// The source task object. /// /// /// As task behaves like a smart pointer, after a copy assignment, this task objects represents the same /// actual task as does. /// /**/ task& operator=(task&& _Other) { if (this != &_Other) { _M_Impl = std::move(_Other._M_Impl); } return *this; } /// /// Adds a continuation task to this task. /// /// /// The type of the function object that will be invoked by this task. /// /// /// The continuation function to execute when this task completes. This continuation function must take as input /// a variable of either result_type or task<result_type>, where result_type is the type /// of the result this task produces. /// /// /// The newly created continuation task. The result type of the returned task is determined by what returns. /// /// /// The overloads of then that take a lambda or functor that returns a Windows::Foundation::IAsyncInfo interface, are only available /// to Windows Store apps. /// For more information on how to use task continuations to compose asynchronous work, see . /// /**/ template __declspec(noinline) // Ask for no inlining so that the _CAPTURE_CALLSTACK gives us the expected result auto then(const _Function& _Func) const -> typename details::_ContinuationTypeTraits<_Function, _ReturnType>::_TaskOfType { task_options _TaskOptions; details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(_CAPTURE_CALLSTACK()); return _ThenImpl<_ReturnType, _Function>(_Func, _TaskOptions); } /// /// Adds a continuation task to this task. /// /// /// The type of the function object that will be invoked by this task. /// /// /// The continuation function to execute when this task completes. This continuation function must take as input /// a variable of either result_type or task<result_type>, where result_type is the type /// of the result this task produces. /// /// /// The task options include cancellation token, scheduler and continuation context. By default the former 3 /// options are inherited from the antecedent task /// /// /// The newly created continuation task. The result type of the returned task is determined by what returns. /// /// /// The overloads of then that take a lambda or functor that returns a Windows::Foundation::IAsyncInfo interface, are only available /// to Windows Store apps. /// For more information on how to use task continuations to compose asynchronous work, see . /// /**/ template __declspec(noinline) // Ask for no inlining so that the _CAPTURE_CALLSTACK gives us the expected result auto then(const _Function& _Func, task_options _TaskOptions) const -> typename details::_ContinuationTypeTraits<_Function, _ReturnType>::_TaskOfType { details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(_CAPTURE_CALLSTACK()); return _ThenImpl<_ReturnType, _Function>(_Func, _TaskOptions); } /// /// Adds a continuation task to this task. /// /// /// The type of the function object that will be invoked by this task. /// /// /// The continuation function to execute when this task completes. This continuation function must take as input /// a variable of either result_type or task<result_type>, where result_type is the type /// of the result this task produces. /// /// /// The cancellation token to associate with the continuation task. A continuation task that is created without a cancellation token will inherit /// the token of its antecedent task. /// /// /// A variable that specifies where the continuation should execute. This variable is only useful when used in a Windows Store /// style app. For more information, see task_continuation_context /// /// /// The newly created continuation task. The result type of the returned task is determined by what returns. /// /// /// The overloads of then that take a lambda or functor that returns a Windows::Foundation::IAsyncInfo interface, are only available /// to Windows Store apps. /// For more information on how to use task continuations to compose asynchronous work, see . /// /**/ template __declspec(noinline) // Ask for no inlining so that the _CAPTURE_CALLSTACK gives us the expected result auto then(const _Function& _Func, cancellation_token _CancellationToken, task_continuation_context _ContinuationContext) const -> typename details::_ContinuationTypeTraits<_Function, _ReturnType>::_TaskOfType { task_options _TaskOptions(_CancellationToken, _ContinuationContext); details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(_CAPTURE_CALLSTACK()); return _ThenImpl<_ReturnType, _Function>(_Func, _TaskOptions); } /// /// Waits for this task to reach a terminal state. It is possible for wait to execute the task inline, if all of the tasks /// dependencies are satisfied, and it has not already been picked up for execution by a background worker. /// /// /// A task_status value which could be either completed or canceled. If the task encountered an exception /// during execution, or an exception was propagated to it from an antecedent task, wait will throw that exception. /// /**/ task_status wait() const { if (!_M_Impl) { throw invalid_operation("wait() cannot be called on a default constructed task."); } return _M_Impl->_Wait(); } /// /// Returns the result this task produced. If the task is not in a terminal state, a call to get will wait for the task to /// finish. This method does not return a value when called on a task with a result_type of void. /// /// /// The result of the task. /// /// /// If the task is canceled, a call to get will throw a task_canceled exception. If the task /// encountered an different exception or an exception was propagated to it from an antecedent task, a call to get will throw that exception. /// /**/ _ReturnType get() const { if (!_M_Impl) { throw invalid_operation("get() cannot be called on a default constructed task."); } if (_M_Impl->_Wait() == canceled) { throw task_canceled(); } return _M_Impl->_GetResult(); } /// /// Determines if the task is completed. /// /// /// True if the task has completed, false otherwise. /// /// /// The function returns true if the task is completed or canceled (with or without user exception). /// bool is_done() const { if (!_M_Impl) { throw invalid_operation("is_done() cannot be called on a default constructed task."); } return _M_Impl->_IsDone(); } /// /// Returns the scheduler for this task /// /// /// A pointer to the scheduler /// scheduler_ptr scheduler() const { if (!_M_Impl) { throw invalid_operation("scheduler() cannot be called on a default constructed task."); } return _M_Impl->_GetScheduler(); } /// /// Determines whether the task unwraps a Windows Runtime IAsyncInfo interface or is descended from such a task. /// /// /// true if the task unwraps an IAsyncInfo interface or is descended from such a task, false otherwise. /// /**/ bool is_apartment_aware() const { if (!_M_Impl) { throw invalid_operation("is_apartment_aware() cannot be called on a default constructed task."); } return _M_Impl->_IsApartmentAware(); } /// /// Determines whether two task objects represent the same internal task. /// /// /// true if the objects refer to the same underlying task, and false otherwise. /// /**/ bool operator==(const task<_ReturnType>& _Rhs) const { return (_M_Impl == _Rhs._M_Impl); } /// /// Determines whether two task objects represent different internal tasks. /// /// /// true if the objects refer to different underlying tasks, and false otherwise. /// /**/ bool operator!=(const task<_ReturnType>& _Rhs) const { return !operator==(_Rhs); } /// /// Create an underlying task implementation. /// void _CreateImpl(details::_CancellationTokenState * _Ct, scheduler_ptr _Scheduler) { _ASSERTE(_Ct != nullptr); _M_Impl = details::_Task_ptr<_ReturnType>::_Make(_Ct, _Scheduler); if (_Ct != details::_CancellationTokenState::_None()) { _M_Impl->_RegisterCancellation(_M_Impl); } } /// /// Return the underlying implementation for this task. /// const typename details::_Task_ptr<_ReturnType>::_Type & _GetImpl() const { return _M_Impl; } /// /// Set the implementation of the task to be the supplied implementaion. /// void _SetImpl(const typename details::_Task_ptr<_ReturnType>::_Type & _Impl) { _ASSERTE(!_M_Impl); _M_Impl = _Impl; } /// /// Set the implementation of the task to be the supplied implementaion using a move instead of a copy. /// void _SetImpl(typename details::_Task_ptr<_ReturnType>::_Type && _Impl) { _ASSERTE(!_M_Impl); _M_Impl = std::move(_Impl); } /// /// Sets a property determining whether the task is apartment aware. /// void _SetAsync(bool _Async = true) { _GetImpl()->_SetAsync(_Async); } /// /// Sets a field in the task impl to the return callstack for calls to the task constructors and the then method. /// void _SetTaskCreationCallstack(const details::_TaskCreationCallstack &_callstack) { _GetImpl()->_SetTaskCreationCallstack(_callstack); } /// /// An internal version of then that takes additional flags and always execute the continuation inline by default. /// When _ForceInline is set to false, continuations inlining will be limited to default _DefaultAutoInline. /// This function is Used for runtime internal continuations only. /// template auto _Then(const _Function& _Func, details::_CancellationTokenState *_PTokenState, details::_TaskInliningMode_t _InliningMode = details::_ForceInline) const -> typename details::_ContinuationTypeTraits<_Function, _ReturnType>::_TaskOfType { // inherit from antecedent auto _Scheduler = _GetImpl()->_GetScheduler(); return _ThenImpl<_ReturnType, _Function>(_Func, _PTokenState, task_continuation_context::use_default(), _Scheduler, _CAPTURE_CALLSTACK(), _InliningMode); } private: template friend class task; // The task handle type used to construct an 'initial task' - a task with no dependents. template struct _InitialTaskHandle : details::_PPLTaskHandle<_ReturnType, _InitialTaskHandle<_InternalReturnType, _Function, _TypeSelection>, details::_UnrealizedChore_t> { _Function _M_function; _InitialTaskHandle(const typename details::_Task_ptr<_ReturnType>::_Type & _TaskImpl, const _Function & _func) : details::_PPLTaskHandle<_ReturnType, _InitialTaskHandle<_InternalReturnType, _Function, _TypeSelection>, details::_UnrealizedChore_t>::_PPLTaskHandle(_TaskImpl) , _M_function(_func) { } virtual ~_InitialTaskHandle() {} template auto _LogWorkItemAndInvokeUserLambda(_Func && _func) const -> decltype(_func()) { details::_TaskWorkItemRAIILogger _LogWorkItem(this->_M_pTask->_M_taskEventLogger); CASABLANCA_UNREFERENCED_PARAMETER(_LogWorkItem); return _func(); } void _Perform() const { _Init(_TypeSelection()); } void _SyncCancelAndPropagateException() const { this->_M_pTask->_Cancel(true); } // // Overload 0: returns _InternalReturnType // // This is the most basic task with no unwrapping // void _Init(details::_TypeSelectorNoAsync) const { this->_M_pTask->_FinalizeAndRunContinuations(_LogWorkItemAndInvokeUserLambda(_Init_func_transformer<_InternalReturnType>::_Perform(_M_function))); } // // Overload 1: returns IAsyncOperation<_InternalReturnType>^ (only uder /ZW) // or // returns task<_InternalReturnType> // // This is task whose functor returns an async operation or a task which will be unwrapped for continuation // Depending on the output type, the right _AsyncInit gets invoked // void _Init(details::_TypeSelectorAsyncOperationOrTask) const { details::_Task_impl_base::_AsyncInit<_ReturnType, _InternalReturnType>(this->_M_pTask, _LogWorkItemAndInvokeUserLambda(_M_function)); } #if defined (__cplusplus_winrt) // // Overload 2: returns IAsyncAction^ // // This is task whose functor returns an async action which will be unwrapped for continuation // void _Init(details::_TypeSelectorAsyncAction) const { details::_Task_impl_base::_AsyncInit<_ReturnType, _InternalReturnType>(this->_M_pTask, ref new details::_IAsyncActionToAsyncOperationConverter(_LogWorkItemAndInvokeUserLambda(_M_function))); } // // Overload 3: returns IAsyncOperationWithProgress<_InternalReturnType, _ProgressType>^ // // This is task whose functor returns an async operation with progress which will be unwrapped for continuation // void _Init(details::_TypeSelectorAsyncOperationWithProgress) const { typedef details::_GetProgressType::_Value _ProgressType; details::_Task_impl_base::_AsyncInit<_ReturnType, _InternalReturnType>(this->_M_pTask, ref new details::_IAsyncOperationWithProgressToAsyncOperationConverter<_InternalReturnType,_ProgressType>(_LogWorkItemAndInvokeUserLambda(_M_function))); } // // Overload 4: returns IAsyncActionWithProgress<_ProgressType>^ // // This is task whose functor returns an async action with progress which will be unwrapped for continuation // void _Init(details::_TypeSelectorAsyncActionWithProgress) const { typedef details::_GetProgressType::_Value _ProgressType; details::_Task_impl_base::_AsyncInit<_ReturnType, _InternalReturnType>(this->_M_pTask, ref new details::_IAsyncActionWithProgressToAsyncOperationConverter<_ProgressType>(_LogWorkItemAndInvokeUserLambda(_M_function))); } #endif /* defined (__cplusplus_winrt) */ }; /// /// The task handle type used to create a 'continuation task'. /// template struct _ContinuationTaskHandle : details::_PPLTaskHandle::_Type, _ContinuationTaskHandle<_InternalReturnType, _ContinuationReturnType, _Function, _IsTaskBased, _TypeSelection>, details::_ContinuationTaskHandleBase> { typedef typename details::_NormalizeVoidToUnitType<_ContinuationReturnType>::_Type _NormalizedContinuationReturnType; typename details::_Task_ptr<_ReturnType>::_Type _M_ancestorTaskImpl; _Function _M_function; _ContinuationTaskHandle(const typename details::_Task_ptr<_ReturnType>::_Type & _AncestorImpl, const typename details::_Task_ptr<_NormalizedContinuationReturnType>::_Type & _ContinuationImpl, const _Function & _Func, const task_continuation_context & _Context, details::_TaskInliningMode_t _InliningMode) : details::_PPLTaskHandle::_Type, _ContinuationTaskHandle<_InternalReturnType, _ContinuationReturnType, _Function, _IsTaskBased, _TypeSelection>, details::_ContinuationTaskHandleBase> ::_PPLTaskHandle(_ContinuationImpl) , _M_ancestorTaskImpl(_AncestorImpl) , _M_function(_Func) { this->_M_isTaskBasedContinuation = _IsTaskBased::value; this->_M_continuationContext = _Context; this->_M_continuationContext._Resolve(_AncestorImpl->_IsApartmentAware()); this->_M_inliningMode = _InliningMode; } virtual ~_ContinuationTaskHandle() {} template auto _LogWorkItemAndInvokeUserLambda(_Func && _func, _Arg && _value) const -> decltype(_func(std::forward<_Arg>(_value))) { details::_TaskWorkItemRAIILogger _LogWorkItem(this->_M_pTask->_M_taskEventLogger); CASABLANCA_UNREFERENCED_PARAMETER(_LogWorkItem); return _func(std::forward<_Arg>(_value)); } void _Perform() const { _Continue(_IsTaskBased(), _TypeSelection()); } void _SyncCancelAndPropagateException() const { if (_M_ancestorTaskImpl->_HasUserException()) { // If the ancestor encountered an exception, transfer the exception to the continuation // This traverses down the tree to propagate the exception. this->_M_pTask->_CancelWithExceptionHolder(_M_ancestorTaskImpl->_GetExceptionHolder(), true); } else { // If the ancestor was canceled, then your own execution should be canceled. // This traverses down the tree to cancel it. this->_M_pTask->_Cancel(true); } } // // Overload 0-0: _InternalReturnType -> _TaskType // // This is a straight task continuation which simply invokes its target with the ancestor's completion argument // void _Continue(std::false_type, details::_TypeSelectorNoAsync) const { this->_M_pTask->_FinalizeAndRunContinuations( _LogWorkItemAndInvokeUserLambda(_Continuation_func_transformer<_InternalReturnType, _ContinuationReturnType>::_Perform(_M_function), _M_ancestorTaskImpl->_GetResult())); } // // Overload 0-1: _InternalReturnType -> IAsyncOperation<_TaskType>^ (only uder /ZW) // or // _InternalReturnType -> task<_TaskType> // // This is a straight task continuation which returns an async operation or a task which will be unwrapped for continuation // Depending on the output type, the right _AsyncInit gets invoked // void _Continue(std::false_type, details::_TypeSelectorAsyncOperationOrTask) const { typedef typename details::_FunctionTypeTraits<_Function, _InternalReturnType>::_FuncRetType _FuncOutputType; details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>( this->_M_pTask, _LogWorkItemAndInvokeUserLambda(_Continuation_func_transformer<_InternalReturnType, _FuncOutputType>::_Perform(_M_function), _M_ancestorTaskImpl->_GetResult()) ); } #if defined (__cplusplus_winrt) // // Overload 0-2: _InternalReturnType -> IAsyncAction^ // // This is a straight task continuation which returns an async action which will be unwrapped for continuation // void _Continue(std::false_type, details::_TypeSelectorAsyncAction) const { typedef details::_FunctionTypeTraits<_Function, _InternalReturnType>::_FuncRetType _FuncOutputType; details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>( this->_M_pTask, ref new details::_IAsyncActionToAsyncOperationConverter( _LogWorkItemAndInvokeUserLambda(_Continuation_func_transformer<_InternalReturnType, _FuncOutputType>::_Perform(_M_function), _M_ancestorTaskImpl->_GetResult()))); } // // Overload 0-3: _InternalReturnType -> IAsyncOperationWithProgress<_TaskType, _ProgressType>^ // // This is a straight task continuation which returns an async operation with progress which will be unwrapped for continuation // void _Continue(std::false_type, details::_TypeSelectorAsyncOperationWithProgress) const { typedef details::_FunctionTypeTraits<_Function, _InternalReturnType>::_FuncRetType _FuncOutputType; auto _OpWithProgress = _LogWorkItemAndInvokeUserLambda(_Continuation_func_transformer<_InternalReturnType, _FuncOutputType>::_Perform(_M_function), _M_ancestorTaskImpl->_GetResult()); typedef details::_GetProgressType::_Value _ProgressType; details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>( this->_M_pTask, ref new details::_IAsyncOperationWithProgressToAsyncOperationConverter<_ContinuationReturnType, _ProgressType>(_OpWithProgress)); } // // Overload 0-4: _InternalReturnType -> IAsyncActionWithProgress<_ProgressType>^ // // This is a straight task continuation which returns an async action with progress which will be unwrapped for continuation // void _Continue(std::false_type, details::_TypeSelectorAsyncActionWithProgress) const { typedef details::_FunctionTypeTraits<_Function, _InternalReturnType>::_FuncRetType _FuncOutputType; auto _OpWithProgress = _LogWorkItemAndInvokeUserLambda(_Continuation_func_transformer<_InternalReturnType, _FuncOutputType>::_Perform(_M_function), _M_ancestorTaskImpl->_GetResult()); typedef details::_GetProgressType::_Value _ProgressType; details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>( this->_M_pTask, ref new details::_IAsyncActionWithProgressToAsyncOperationConverter<_ProgressType>(_OpWithProgress)); } #endif /* defined (__cplusplus_winrt) */ // // Overload 1-0: task<_InternalReturnType> -> _TaskType // // This is an exception handling type of continuation which takes the task rather than the task's result. // void _Continue(std::true_type, details::_TypeSelectorNoAsync) const { typedef task<_InternalReturnType> _FuncInputType; task<_InternalReturnType> _ResultTask; _ResultTask._SetImpl(std::move(_M_ancestorTaskImpl)); this->_M_pTask->_FinalizeAndRunContinuations( _LogWorkItemAndInvokeUserLambda(_Continuation_func_transformer<_FuncInputType, _ContinuationReturnType>::_Perform(_M_function), std::move(_ResultTask))); } // // Overload 1-1: task<_InternalReturnType> -> IAsyncOperation<_TaskType>^ // or // task<_TaskType> // // This is an exception handling type of continuation which takes the task rather than // the task's result. It also returns an async operation or a task which will be unwrapped // for continuation // void _Continue(std::true_type, details::_TypeSelectorAsyncOperationOrTask) const { // The continuation takes a parameter of type task<_Input>, which is the same as the ancestor task. task<_InternalReturnType> _ResultTask; _ResultTask._SetImpl(std::move(_M_ancestorTaskImpl)); details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>(this->_M_pTask, _LogWorkItemAndInvokeUserLambda(_M_function, std::move(_ResultTask))); } #if defined (__cplusplus_winrt) // // Overload 1-2: task<_InternalReturnType> -> IAsyncAction^ // // This is an exception handling type of continuation which takes the task rather than // the task's result. It also returns an async action which will be unwrapped for continuation // void _Continue(std::true_type, details::_TypeSelectorAsyncAction) const { // The continuation takes a parameter of type task<_Input>, which is the same as the ancestor task. task<_InternalReturnType> _ResultTask; _ResultTask._SetImpl(std::move(_M_ancestorTaskImpl)); details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>(this->_M_pTask, ref new details::_IAsyncActionToAsyncOperationConverter(_LogWorkItemAndInvokeUserLambda(_M_function, std::move(_ResultTask)))); } // // Overload 1-3: task<_InternalReturnType> -> IAsyncOperationWithProgress<_TaskType, _ProgressType>^ // // This is an exception handling type of continuation which takes the task rather than // the task's result. It also returns an async operation with progress which will be unwrapped // for continuation // void _Continue(std::true_type, details::_TypeSelectorAsyncOperationWithProgress) const { // The continuation takes a parameter of type task<_Input>, which is the same as the ancestor task. task<_InternalReturnType> _ResultTask; _ResultTask._SetImpl(std::move(_M_ancestorTaskImpl)); typedef details::_GetProgressType::_Value _ProgressType; details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>(this->_M_pTask, ref new details::_IAsyncOperationWithProgressToAsyncOperationConverter<_ContinuationReturnType, _ProgressType>( _LogWorkItemAndInvokeUserLambda(_M_function, std::move(_ResultTask)))); } // // Overload 1-4: task<_InternalReturnType> -> IAsyncActionWithProgress<_ProgressType>^ // // This is an exception handling type of continuation which takes the task rather than // the task's result. It also returns an async operation with progress which will be unwrapped // for continuation // void _Continue(std::true_type, details::_TypeSelectorAsyncActionWithProgress) const { // The continuation takes a parameter of type task<_Input>, which is the same as the ancestor task. task<_InternalReturnType> _ResultTask; _ResultTask._SetImpl(std::move(_M_ancestorTaskImpl)); typedef details::_GetProgressType::_Value _ProgressType; details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>(this->_M_pTask, ref new details::_IAsyncActionWithProgressToAsyncOperationConverter<_ProgressType>( _LogWorkItemAndInvokeUserLambda(_M_function, std::move(_ResultTask)))); } #endif /* defined (__cplusplus_winrt) */ }; /// /// Initializes a task using a lambda, function pointer or function object. /// template void _TaskInitWithFunctor(const _Function& _Func) { typedef typename details::_InitFunctorTypeTraits<_InternalReturnType, decltype(_Func())> _Async_type_traits; _M_Impl->_M_fFromAsync = _Async_type_traits::_IsAsyncTask; _M_Impl->_M_fUnwrappedTask = _Async_type_traits::_IsUnwrappedTaskOrAsync; _M_Impl->_M_taskEventLogger._LogScheduleTask(false); _M_Impl->_ScheduleTask(new _InitialTaskHandle<_InternalReturnType, _Function, typename _Async_type_traits::_AsyncKind>(_GetImpl(), _Func), details::_NoInline); } /// /// Initializes a task using a task completion event. /// void _TaskInitNoFunctor(task_completion_event<_ReturnType>& _Event) { _Event._RegisterTask(_M_Impl); } #if defined (__cplusplus_winrt) /// /// Initializes a task using an asynchronous operation IAsyncOperation^ /// void _TaskInitAsyncOp(Windows::Foundation::IAsyncOperation::_Value>^ _AsyncOp) { _M_Impl->_M_fFromAsync = true; // Mark this task as started here since we can set the state in the constructor without acquiring a lock. Once _AsyncInit // returns a completion could execute concurrently and the task must be fully initialized before that happens. _M_Impl->_M_TaskState = details::_Task_impl_base::_Started; // Pass the shared pointer into _AsyncInit for storage in the Async Callback. details::_Task_impl_base::_AsyncInit<_ReturnType, _ReturnType>(_M_Impl, _AsyncOp); } /// /// Initializes a task using an asynchronous operation IAsyncOperation^ /// void _TaskInitNoFunctor(Windows::Foundation::IAsyncOperation::_Value>^ _AsyncOp) { _TaskInitAsyncOp(_AsyncOp); } /// /// Initializes a task using an asynchronous operation with progress IAsyncOperationWithProgress^ /// template void _TaskInitNoFunctor(Windows::Foundation::IAsyncOperationWithProgress::_Value, _Progress>^ _AsyncOp) { _TaskInitAsyncOp(ref new details::_IAsyncOperationWithProgressToAsyncOperationConverter::_Value, _Progress>(_AsyncOp)); } #endif /* defined (__cplusplus_winrt) */ /// /// Initializes a task using a callable object. /// template void _TaskInitMaybeFunctor(_Function & _Func, std::true_type) { _TaskInitWithFunctor<_ReturnType, _Function>(_Func); } /// /// Initializes a task using a non-callable object. /// template void _TaskInitMaybeFunctor(_Ty & _Param, std::false_type) { _TaskInitNoFunctor(_Param); } template auto _ThenImpl(const _Function& _Func, const task_options& _TaskOptions) const -> typename details::_ContinuationTypeTraits<_Function, _InternalReturnType>::_TaskOfType { if (!_M_Impl) { throw invalid_operation("then() cannot be called on a default constructed task."); } details::_CancellationTokenState *_PTokenState = _TaskOptions.has_cancellation_token() ? _TaskOptions.get_cancellation_token()._GetImplValue() : nullptr; auto _Scheduler = _TaskOptions.has_scheduler() ? _TaskOptions.get_scheduler() : _GetImpl()->_GetScheduler(); auto _CreationStack = details::_get_internal_task_options(_TaskOptions)._M_hasPresetCreationCallstack ? details::_get_internal_task_options(_TaskOptions)._M_presetCreationCallstack : details::_TaskCreationCallstack(); return _ThenImpl<_InternalReturnType, _Function>(_Func, _PTokenState, _TaskOptions.get_continuation_context(), _Scheduler, _CreationStack); } /// /// The one and only implementation of then for void and non-void tasks. /// template auto _ThenImpl(const _Function& _Func, details::_CancellationTokenState *_PTokenState, const task_continuation_context& _ContinuationContext, scheduler_ptr _Scheduler, details::_TaskCreationCallstack _CreationStack, details::_TaskInliningMode_t _InliningMode = details::_NoInline) const -> typename details::_ContinuationTypeTraits<_Function, _InternalReturnType>::_TaskOfType { if (!_M_Impl) { throw invalid_operation("then() cannot be called on a default constructed task."); } typedef details::_FunctionTypeTraits<_Function, _InternalReturnType> _Function_type_traits; typedef details::_TaskTypeTraits _Async_type_traits; typedef typename _Async_type_traits::_TaskRetType _TaskType; // // A **nullptr** token state indicates that it was not provided by the user. In this case, we inherit the antecedent's token UNLESS this is a // an exception handling continuation. In that case, we break the chain with a _None. That continuation is never canceled unless the user // explicitly passes the same token. // if (_PTokenState == nullptr) { if (_Function_type_traits::_Takes_task::value) { _PTokenState = details::_CancellationTokenState::_None(); } else { _PTokenState = _GetImpl()->_M_pTokenState; } } task<_TaskType> _ContinuationTask; _ContinuationTask._CreateImpl(_PTokenState, _Scheduler); _ContinuationTask._GetImpl()->_M_fFromAsync = (_GetImpl()->_M_fFromAsync || _Async_type_traits::_IsAsyncTask); _ContinuationTask._GetImpl()->_M_fUnwrappedTask = _Async_type_traits::_IsUnwrappedTaskOrAsync; _ContinuationTask._SetTaskCreationCallstack(_CreationStack); _GetImpl()->_ScheduleContinuation(new _ContinuationTaskHandle<_InternalReturnType, _TaskType, _Function, typename _Function_type_traits::_Takes_task, typename _Async_type_traits::_AsyncKind>( _GetImpl(), _ContinuationTask._GetImpl(), _Func, _ContinuationContext, _InliningMode)); return _ContinuationTask; } // The underlying implementation for this task typename details::_Task_ptr<_ReturnType>::_Type _M_Impl; }; /// /// The Parallel Patterns Library (PPL) task class. A task object represents work that can be executed asynchronously, /// and concurrently with other tasks and parallel work produced by parallel algorithms in the Concurrency Runtime. It produces /// a result of type on successful completion. Tasks of type task<void> produce no result. /// A task can be waited upon and canceled independently of other tasks. It can also be composed with other tasks using /// continuations(then), and join(when_all) and choice(when_any) patterns. /// /// /// For more information, see . /// /**/ template<> class task { public: /// /// The type of the result an object of this class produces. /// /**/ typedef void result_type; /// /// Constructs a task object. /// /// /// The default constructor for a task is only present in order to allow tasks to be used within containers. /// A default constructed task cannot be used until you assign a valid task to it. Methods such as get, wait or then /// will throw an invalid_argument exception when called on a default constructed task. /// A task that is created from a task_completion_event will complete (and have its continuations scheduled) when the task /// completion event is set. /// The version of the constructor that takes a cancellation token creates a task that can be canceled using the /// cancellation_token_source the token was obtained from. Tasks created without a cancellation token are not cancelable. /// Tasks created from a Windows::Foundation::IAsyncInfo interface or a lambda that returns an IAsyncInfo interface /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created /// from a lamda that returns a task<result_type> reach their terminal state when the inner task reaches its terminal state, /// and not when the lamda returns. /// task behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads /// without the need for locks. /// The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available /// to Windows Store apps. /// For more information, see . /// /**/ task() : _M_unitTask() { // The default constructor should create a task with a nullptr impl. This is a signal that the // task is not usable and should throw if any wait(), get() or then() APIs are used. } /// /// Constructs a task object. /// /// /// The type of the parameter from which the task is to be constructed. /// /// /// The parameter from which the task is to be constructed. This could be a lambda, a function object, a task_completion_event<result_type> /// object, or a Windows::Foundation::IAsyncInfo if you are using tasks in your Windows Store app. The lambda or function /// object should be a type equivalent to std::function<X(void)>, where X can be a variable of type result_type, /// task<result_type>, or a Windows::Foundation::IAsyncInfo in Windows Store apps. /// /// /// The default constructor for a task is only present in order to allow tasks to be used within containers. /// A default constructed task cannot be used until you assign a valid task to it. Methods such as get, wait or then /// will throw an invalid_argument exception when called on a default constructed task. /// A task that is created from a task_completion_event will complete (and have its continuations scheduled) when the task /// completion event is set. /// The version of the constructor that takes a cancellation token creates a task that can be canceled using the /// cancellation_token_source the token was obtained from. Tasks created without a cancellation token are not cancelable. /// Tasks created from a Windows::Foundation::IAsyncInfo interface or a lambda that returns an IAsyncInfo interface /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created /// from a lamda that returns a task<result_type> reach their terminal state when the inner task reaches its terminal state, /// and not when the lamda returns. /// task behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads /// without the need for locks. /// The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available /// to Windows Store apps. /// For more information, see . /// /**/ template __declspec(noinline) // Ask for no inlining so that the _CAPTURE_CALLSTACK gives us the expected result explicit task(_Ty _Param, const task_options& _TaskOptions = task_options()) { details::_ValidateTaskConstructorArgs(_Param); _M_unitTask._CreateImpl(_TaskOptions.get_cancellation_token()._GetImplValue(), _TaskOptions.get_scheduler()); // Do not move the next line out of this function. It is important that _CAPTURE_CALLSTACK() evaluate to the the call site of the task constructor. _M_unitTask._SetTaskCreationCallstack(details::_get_internal_task_options(_TaskOptions)._M_hasPresetCreationCallstack ? details::_get_internal_task_options(_TaskOptions)._M_presetCreationCallstack : _CAPTURE_CALLSTACK()); _TaskInitMaybeFunctor(_Param, details::_IsCallable(_Param,0)); } /// /// Constructs a task object. /// /// /// The source task object. /// /// /// The default constructor for a task is only present in order to allow tasks to be used within containers. /// A default constructed task cannot be used until you assign a valid task to it. Methods such as get, wait or then /// will throw an invalid_argument exception when called on a default constructed task. /// A task that is created from a task_completion_event will complete (and have its continuations scheduled) when the task /// completion event is set. /// The version of the constructor that takes a cancellation token creates a task that can be canceled using the /// cancellation_token_source the token was obtained from. Tasks created without a cancellation token are not cancelable. /// Tasks created from a Windows::Foundation::IAsyncInfo interface or a lambda that returns an IAsyncInfo interface /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created /// from a lamda that returns a task<result_type> reach their terminal state when the inner task reaches its terminal state, /// and not when the lamda returns. /// task behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads /// without the need for locks. /// The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available /// to Windows Store apps. /// For more information, see . /// /**/ task(const task& _Other): _M_unitTask(_Other._M_unitTask){} /// /// Constructs a task object. /// /// /// The source task object. /// /// /// The default constructor for a task is only present in order to allow tasks to be used within containers. /// A default constructed task cannot be used until you assign a valid task to it. Methods such as get, wait or then /// will throw an invalid_argument exception when called on a default constructed task. /// A task that is created from a task_completion_event will complete (and have its continuations scheduled) when the task /// completion event is set. /// The version of the constructor that takes a cancellation token creates a task that can be canceled using the /// cancellation_token_source the token was obtained from. Tasks created without a cancellation token are not cancelable. /// Tasks created from a Windows::Foundation::IAsyncInfo interface or a lambda that returns an IAsyncInfo interface /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created /// from a lamda that returns a task<result_type> reach their terminal state when the inner task reaches its terminal state, /// and not when the lamda returns. /// task behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads /// without the need for locks. /// The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available /// to Windows Store apps. /// For more information, see . /// /**/ task(task&& _Other) : _M_unitTask(std::move(_Other._M_unitTask)) {} /// /// Replaces the contents of one task object with another. /// /// /// The source task object. /// /// /// As task behaves like a smart pointer, after a copy assignment, this task objects represents the same /// actual task as does. /// /**/ task& operator=(const task& _Other) { if (this != &_Other) { _M_unitTask = _Other._M_unitTask; } return *this; } /// /// Replaces the contents of one task object with another. /// /// /// The source task object. /// /// /// As task behaves like a smart pointer, after a copy assignment, this task objects represents the same /// actual task as does. /// /**/ task& operator=(task&& _Other) { if (this != &_Other) { _M_unitTask = std::move(_Other._M_unitTask); } return *this; } /// /// Adds a continuation task to this task. /// /// /// The type of the function object that will be invoked by this task. /// /// /// The continuation function to execute when this task completes. This continuation function must take as input /// a variable of either result_type or task<result_type>, where result_type is the type /// of the result this task produces. /// /// /// The cancellation token to associate with the continuation task. A continuation task that is created without a cancellation token will inherit /// the token of its antecedent task. /// /// /// The newly created continuation task. The result type of the returned task is determined by what returns. /// /// /// The overloads of then that take a lambda or functor that returns a Windows::Foundation::IAsyncInfo interface, are only available /// to Windows Store apps. /// For more information on how to use task continuations to compose asynchronous work, see . /// /**/ template __declspec(noinline) // Ask for no inlining so that the _CAPTURE_CALLSTACK gives us the expected result auto then(const _Function& _Func, task_options _TaskOptions = task_options()) const -> typename details::_ContinuationTypeTraits<_Function, void>::_TaskOfType { details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(_CAPTURE_CALLSTACK()); return _M_unitTask._ThenImpl(_Func, _TaskOptions); } /// /// Adds a continuation task to this task. /// /// /// The type of the function object that will be invoked by this task. /// /// /// The continuation function to execute when this task completes. This continuation function must take as input /// a variable of either result_type or task<result_type>, where result_type is the type /// of the result this task produces. /// /// /// The cancellation token to associate with the continuation task. A continuation task that is created without a cancellation token will inherit /// the token of its antecedent task. /// /// /// A variable that specifies where the continuation should execute. This variable is only useful when used in a Windows Store /// style app. For more information, see task_continuation_context /// /// /// The newly created continuation task. The result type of the returned task is determined by what returns. /// /// /// The overloads of then that take a lambda or functor that returns a Windows::Foundation::IAsyncInfo interface, are only available /// to Windows Store apps. /// For more information on how to use task continuations to compose asynchronous work, see . /// /**/ template __declspec(noinline) // Ask for no inlining so that the _CAPTURE_CALLSTACK gives us the expected result auto then(const _Function& _Func, cancellation_token _CancellationToken, task_continuation_context _ContinuationContext) const -> typename details::_ContinuationTypeTraits<_Function, void>::_TaskOfType { task_options _TaskOptions(_CancellationToken, _ContinuationContext); details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(_CAPTURE_CALLSTACK()); return _M_unitTask._ThenImpl(_Func, _TaskOptions); } /// /// Waits for this task to reach a terminal state. It is possible for wait to execute the task inline, if all of the tasks /// dependencies are satisfied, and it has not already been picked up for execution by a background worker. /// /// /// A task_status value which could be either completed or canceled. If the task encountered an exception /// during execution, or an exception was propagated to it from an antecedent task, wait will throw that exception. /// /**/ task_status wait() const { return _M_unitTask.wait(); } /// /// Returns the result this task produced. If the task is not in a terminal state, a call to get will wait for the task to /// finish. This method does not return a value when called on a task with a result_type of void. /// /// /// If the task is canceled, a call to get will throw a task_canceled exception. If the task /// encountered an different exception or an exception was propagated to it from an antecedent task, a call to get will throw that exception. /// /**/ void get() const { _M_unitTask.get(); } /// /// Determines if the task is completed. /// /// /// True if the task has completed, false otherwise. /// /// /// The function returns true if the task is completed or canceled (with or without user exception). /// bool is_done() const { return _M_unitTask.is_done(); } /// /// Returns the scheduler for this task /// /// /// A pointer to the scheduler /// scheduler_ptr scheduler() const { return _M_unitTask.scheduler(); } /// /// Determines whether the task unwraps a Windows Runtime IAsyncInfo interface or is descended from such a task. /// /// /// true if the task unwraps an IAsyncInfo interface or is descended from such a task, false otherwise. /// /**/ bool is_apartment_aware() const { return _M_unitTask.is_apartment_aware(); } /// /// Determines whether two task objects represent the same internal task. /// /// /// true if the objects refer to the same underlying task, and false otherwise. /// /**/ bool operator==(const task& _Rhs) const { return (_M_unitTask == _Rhs._M_unitTask); } /// /// Determines whether two task objects represent different internal tasks. /// /// /// true if the objects refer to different underlying tasks, and false otherwise. /// /**/ bool operator!=(const task& _Rhs) const { return !operator==(_Rhs); } /// /// Create an underlying task implementation. /// void _CreateImpl(details::_CancellationTokenState * _Ct, scheduler_ptr _Scheduler) { _M_unitTask._CreateImpl(_Ct, _Scheduler); } /// /// Return the underlying implementation for this task. /// const details::_Task_ptr::_Type & _GetImpl() const { return _M_unitTask._M_Impl; } /// /// Set the implementation of the task to be the supplied implementaion. /// void _SetImpl(const details::_Task_ptr::_Type & _Impl) { _M_unitTask._SetImpl(_Impl); } /// /// Set the implementation of the task to be the supplied implementaion using a move instead of a copy. /// void _SetImpl(details::_Task_ptr::_Type && _Impl) { _M_unitTask._SetImpl(std::move(_Impl)); } /// /// Sets a property determining whether the task is apartment aware. /// void _SetAsync(bool _Async = true) { _M_unitTask._SetAsync(_Async); } /// /// Sets a field in the task impl to the return callstack for calls to the task constructors and the then method. /// void _SetTaskCreationCallstack(const details::_TaskCreationCallstack &_callstack) { _M_unitTask._SetTaskCreationCallstack(_callstack); } /// /// An internal version of then that takes additional flags and executes the continuation inline. Used for runtime internal continuations only. /// template auto _Then(const _Function& _Func, details::_CancellationTokenState *_PTokenState, details::_TaskInliningMode_t _InliningMode = details::_ForceInline) const -> typename details::_ContinuationTypeTraits<_Function, void>::_TaskOfType { // inherit from antecedent auto _Scheduler = _GetImpl()->_GetScheduler(); return _M_unitTask._ThenImpl(_Func, _PTokenState, task_continuation_context::use_default(), _Scheduler, _CAPTURE_CALLSTACK(), _InliningMode); } private: template friend class task; template friend class task_completion_event; /// /// Initializes a task using a task completion event. /// void _TaskInitNoFunctor(task_completion_event& _Event) { _M_unitTask._TaskInitNoFunctor(_Event._M_unitEvent); } #if defined (__cplusplus_winrt) /// /// Initializes a task using an asynchronous action IAsyncAction^ /// void _TaskInitNoFunctor(Windows::Foundation::IAsyncAction^ _AsyncAction) { _M_unitTask._TaskInitAsyncOp(ref new details::_IAsyncActionToAsyncOperationConverter(_AsyncAction)); } /// /// Initializes a task using an asynchronous action with progress IAsyncActionWithProgress<_P>^ /// template void _TaskInitNoFunctor(Windows::Foundation::IAsyncActionWithProgress<_P>^ _AsyncActionWithProgress) { _M_unitTask._TaskInitAsyncOp(ref new details::_IAsyncActionWithProgressToAsyncOperationConverter<_P>(_AsyncActionWithProgress)); } #endif /* defined (__cplusplus_winrt) */ /// /// Initializes a task using a callable object. /// template void _TaskInitMaybeFunctor(_Function & _Func, std::true_type) { _M_unitTask._TaskInitWithFunctor(_Func); } /// /// Initializes a task using a non-callable object. /// template void _TaskInitMaybeFunctor(_T & _Param, std::false_type) { _TaskInitNoFunctor(_Param); } // The void task contains a task of a dummy type so common code can be used for tasks with void and non-void results. task _M_unitTask; }; namespace details { /// /// The following type traits are used for the create_task function. /// #if defined (__cplusplus_winrt) // Unwrap functions for asyncOperations template _Ty _GetUnwrappedType(Windows::Foundation::IAsyncOperation<_Ty>^); void _GetUnwrappedType(Windows::Foundation::IAsyncAction^); template _Ty _GetUnwrappedType(Windows::Foundation::IAsyncOperationWithProgress<_Ty, _Progress>^); template void _GetUnwrappedType(Windows::Foundation::IAsyncActionWithProgress<_Progress>^); #endif /* defined (__cplusplus_winrt) */ // Unwrap task template _Ty _GetUnwrappedType(task<_Ty>); // Unwrap all supportted types template auto _GetUnwrappedReturnType(_Ty _Arg, int) -> decltype(_GetUnwrappedType(_Arg)); // fallback template _Ty _GetUnwrappedReturnType(_Ty, ...); /// /// _GetTaskType functions will retrieve task type T in task[T](Arg), /// for given constructor argument Arg and its property "callable". /// It will automatically unwrap argument to get the final return type if necessary. /// // Non-Callable template _Ty _GetTaskType(task_completion_event<_Ty>, std::false_type); // Non-Callable template auto _GetTaskType(_Ty _NonFunc, std::false_type) -> decltype(_GetUnwrappedType(_NonFunc)); // Callable template auto _GetTaskType(_Ty _Func, std::true_type) -> decltype(_GetUnwrappedReturnType(_Func(), 0)); // Special callable returns void void _GetTaskType(std::function, std::true_type); struct _BadArgType{}; template auto _FilterValidTaskType(_Ty _Param, int) -> decltype(_GetTaskType(_Param, _IsCallable(_Param, 0))); template _BadArgType _FilterValidTaskType(_Ty _Param, ...); template struct _TaskTypeFromParam { typedef decltype(_FilterValidTaskType(stdx::declval<_Ty>(), 0)) _Type; }; } // namespace details /// /// Creates a PPL task object. create_task can be used anywhere you would have used a task constructor. /// It is provided mainly for convenience, because it allows use of the auto keyword while creating tasks. /// /// /// The type of the parameter from which the task is to be constructed. /// /// /// The parameter from which the task is to be constructed. This could be a lambda or function object, a task_completion_event /// object, a different task object, or a Windows::Foundation::IAsyncInfo interface if you are using tasks in your Windows Store app. /// /// /// A new task of type T, that is inferred from . /// /// /// The first overload behaves like a task constructor that takes a single parameter. /// The second overload associates the cancellation token provided with the newly created task. If you use this overload you are not /// allowed to pass in a different task object as the first parameter. /// The type of the returned task is inferred from the first parameter to the function. If is a task_completion_event<T>, /// a task<T>, or a functor that returns either type T or task<T>, the type of the created task is task<T>. /// In a Windows Store app, if is of type Windows::Foundation::IAsyncOperation<T>^ or /// Windows::Foundation::IAsyncOperationWithProgress<T,P>^, or a functor that returns either of those types, the created task will be of type task<T>. /// If is of type Windows::Foundation::IAsyncAction^ or Windows::Foundation::IAsyncActionWithProgress<P>^, or a functor /// that returns either of those types, the created task will have type task<void>. /// /// /// /**/ template __declspec(noinline) auto create_task(_Ty _Param, task_options _TaskOptions = task_options()) -> task::_Type> { static_assert(!std::is_same::_Type,details::_BadArgType>::value, #if defined (__cplusplus_winrt) "incorrect argument for create_task; can be a callable object, an asynchronous operation, or a task_completion_event" #else /* defined (__cplusplus_winrt) */ "incorrect argument for create_task; can be a callable object or a task_completion_event" #endif /* defined (__cplusplus_winrt) */ ); details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(_CAPTURE_CALLSTACK()); task::_Type> _CreatedTask(_Param, _TaskOptions); return _CreatedTask; } /// /// Creates a PPL task object. create_task can be used anywhere you would have used a task constructor. /// It is provided mainly for convenience, because it allows use of the auto keyword while creating tasks. /// /// /// The type of the parameter from which the task is to be constructed. /// /// /// The parameter from which the task is to be constructed. This could be a lambda or function object, a task_completion_event /// object, a different task object, or a Windows::Foundation::IAsyncInfo interface if you are using tasks in your Windows Store app. /// /// /// The cancellation token to associate with the task. When the source for this token is canceled, cancellation will be requested on the task. /// /// /// A new task of type T, that is inferred from . /// /// /// The first overload behaves like a task constructor that takes a single parameter. /// The second overload associates the cancellation token provided with the newly created task. If you use this overload you are not /// allowed to pass in a different task object as the first parameter. /// The type of the returned task is inferred from the first parameter to the function. If is a task_completion_event<T>, /// a task<T>, or a functor that returns either type T or task<T>, the type of the created task is task<T>. /// In a Windows Store app, if is of type Windows::Foundation::IAsyncOperation<T>^ or /// Windows::Foundation::IAsyncOperationWithProgress<T,P>^, or a functor that returns either of those types, the created task will be of type task<T>. /// If is of type Windows::Foundation::IAsyncAction^ or Windows::Foundation::IAsyncActionWithProgress<P>^, or a functor /// that returns either of those types, the created task will have type task<void>. /// /// /// /**/ template __declspec(noinline) task<_ReturnType> create_task(const task<_ReturnType>& _Task) { task<_ReturnType> _CreatedTask(_Task); return _CreatedTask; } #if defined (__cplusplus_winrt) namespace details { template task<_T> _To_task_helper(Windows::Foundation::IAsyncOperation<_T>^ op) { return task<_T>(op); } template task<_T> _To_task_helper(Windows::Foundation::IAsyncOperationWithProgress<_T, _Progress>^ op) { return task<_T>(op); } inline task _To_task_helper(Windows::Foundation::IAsyncAction^ op) { return task(op); } template task _To_task_helper(Windows::Foundation::IAsyncActionWithProgress<_Progress>^ op) { return task(op); } template class _ProgressDispatcherBase { public: virtual ~_ProgressDispatcherBase() { } virtual void _Report(const _ProgressType& _Val) = 0; }; template class _ProgressDispatcher : public _ProgressDispatcherBase<_ProgressType> { public: virtual ~_ProgressDispatcher() { } _ProgressDispatcher(_ClassPtrType _Ptr) : _M_ptr(_Ptr) { } virtual void _Report(const _ProgressType& _Val) { _M_ptr->_FireProgress(_Val); } private: _ClassPtrType _M_ptr; }; class _ProgressReporterCtorArgType{}; } // namespace details /// /// The progress reporter class allows reporting progress notifications of a specific type. Each progress_reporter object is bound /// to a particular asynchronous action or operation. /// /// /// The payload type of each progress notification reported through the progress reporter. /// /// /// This type is only available to Windows Store apps. /// /// /**/ template class progress_reporter { typedef std::shared_ptr> _PtrType; public: /// /// Sends a progress report to the asynchronous action or operation to which this progress reporter is bound. /// /// /// The payload to report through a progress notification. /// /**/ void report(const _ProgressType& _Val) const { _M_dispatcher->_Report(_Val); } template static progress_reporter _CreateReporter(_ClassPtrType _Ptr) { progress_reporter _Reporter; details::_ProgressDispatcherBase<_ProgressType> *_PDispatcher = new details::_ProgressDispatcher<_ProgressType, _ClassPtrType>(_Ptr); _Reporter._M_dispatcher = _PtrType(_PDispatcher); return _Reporter; } progress_reporter() {} private: progress_reporter(details::_ProgressReporterCtorArgType); _PtrType _M_dispatcher; }; namespace details { // // maps internal definitions for AsyncStatus and defines states that are not client visible // enum _AsyncStatusInternal { _AsyncCreated = -1, // externally invisible // client visible states (must match AsyncStatus exactly) _AsyncStarted = 0, // Windows::Foundation::AsyncStatus::Started, _AsyncCompleted = 1, // Windows::Foundation::AsyncStatus::Completed, _AsyncCanceled = 2, // Windows::Foundation::AsyncStatus::Canceled, _AsyncError = 3, // Windows::Foundation::AsyncStatus::Error, // non-client visible internal states _AsyncCancelPending, _AsyncClosed, _AsyncUndefined }; // // designates whether the "GetResults" method returns a single result (after complete fires) or multiple results // (which are progressively consumable between Start state and before Close is called) // enum _AsyncResultType { SingleResult = 0x0001, MultipleResults = 0x0002 }; // *************************************************************************** // Template type traits and helpers for async production APIs: // struct _ZeroArgumentFunctor { }; struct _OneArgumentFunctor { }; struct _TwoArgumentFunctor { }; // **************************************** // CLASS TYPES: // ******************** // TWO ARGUMENTS: // non-void arg: template _Arg1 _Arg1ClassHelperThunk(_ReturnType (_Class::*)(_Arg1, _Arg2) const); // non-void arg: template _Arg2 _Arg2ClassHelperThunk(_ReturnType (_Class::*)(_Arg1, _Arg2) const); template _ReturnType _ReturnTypeClassHelperThunk(_ReturnType (_Class::*)(_Arg1, _Arg2) const); template _TwoArgumentFunctor _ArgumentCountHelper(_ReturnType (_Class::*)(_Arg1, _Arg2) const); // ******************** // ONE ARGUMENT: // non-void arg: template _Arg1 _Arg1ClassHelperThunk(_ReturnType (_Class::*)(_Arg1) const); // non-void arg: template void _Arg2ClassHelperThunk(_ReturnType (_Class::*)(_Arg1) const); template _ReturnType _ReturnTypeClassHelperThunk(_ReturnType (_Class::*)(_Arg1) const); template _OneArgumentFunctor _ArgumentCountHelper(_ReturnType (_Class::*)(_Arg1) const); // ******************** // ZERO ARGUMENT: // void arg: template void _Arg1ClassHelperThunk(_ReturnType (_Class::*)() const); // void arg: template void _Arg2ClassHelperThunk(_ReturnType (_Class::*)() const); // void arg: template _ReturnType _ReturnTypeClassHelperThunk(_ReturnType (_Class::*)() const); template _ZeroArgumentFunctor _ArgumentCountHelper(_ReturnType (_Class::*)() const); // **************************************** // POINTER TYPES: // ******************** // TWO ARGUMENTS: template _Arg1 _Arg1PFNHelperThunk(_ReturnType(__cdecl *)(_Arg1, _Arg2)); template _Arg2 _Arg2PFNHelperThunk(_ReturnType(__cdecl *)(_Arg1, _Arg2)); template _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__cdecl *)(_Arg1, _Arg2)); template _TwoArgumentFunctor _ArgumentCountHelper(_ReturnType(__cdecl *)(_Arg1, _Arg2)); template _Arg1 _Arg1PFNHelperThunk(_ReturnType(__stdcall *)(_Arg1, _Arg2)); template _Arg2 _Arg2PFNHelperThunk(_ReturnType(__stdcall *)(_Arg1, _Arg2)); template _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__stdcall *)(_Arg1, _Arg2)); template _TwoArgumentFunctor _ArgumentCountHelper(_ReturnType(__stdcall *)(_Arg1, _Arg2)); template _Arg1 _Arg1PFNHelperThunk(_ReturnType(__fastcall *)(_Arg1, _Arg2)); template _Arg2 _Arg2PFNHelperThunk(_ReturnType(__fastcall *)(_Arg1, _Arg2)); template _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__fastcall *)(_Arg1, _Arg2)); template _TwoArgumentFunctor _ArgumentCountHelper(_ReturnType(__fastcall *)(_Arg1, _Arg2)); // ******************** // ONE ARGUMENT: template _Arg1 _Arg1PFNHelperThunk(_ReturnType(__cdecl *)(_Arg1)); template void _Arg2PFNHelperThunk(_ReturnType(__cdecl *)(_Arg1)); template _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__cdecl *)(_Arg1)); template _OneArgumentFunctor _ArgumentCountHelper(_ReturnType(__cdecl *)(_Arg1)); template _Arg1 _Arg1PFNHelperThunk(_ReturnType(__stdcall *)(_Arg1)); template void _Arg2PFNHelperThunk(_ReturnType(__stdcall *)(_Arg1)); template _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__stdcall *)(_Arg1)); template _OneArgumentFunctor _ArgumentCountHelper(_ReturnType(__stdcall *)(_Arg1)); template _Arg1 _Arg1PFNHelperThunk(_ReturnType(__fastcall *)(_Arg1)); template void _Arg2PFNHelperThunk(_ReturnType(__fastcall *)(_Arg1)); template _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__fastcall *)(_Arg1)); template _OneArgumentFunctor _ArgumentCountHelper(_ReturnType(__fastcall *)(_Arg1)); // ******************** // ZERO ARGUMENT: template void _Arg1PFNHelperThunk(_ReturnType(__cdecl *)()); template void _Arg2PFNHelperThunk(_ReturnType(__cdecl *)()); template _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__cdecl *)()); template _ZeroArgumentFunctor _ArgumentCountHelper(_ReturnType(__cdecl *)()); template void _Arg1PFNHelperThunk(_ReturnType(__stdcall *)()); template void _Arg2PFNHelperThunk(_ReturnType(__stdcall *)()); template _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__stdcall *)()); template _ZeroArgumentFunctor _ArgumentCountHelper(_ReturnType(__stdcall *)()); template void _Arg1PFNHelperThunk(_ReturnType(__fastcall *)()); template void _Arg2PFNHelperThunk(_ReturnType(__fastcall *)()); template _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__fastcall *)()); template _ZeroArgumentFunctor _ArgumentCountHelper(_ReturnType(__fastcall *)()); template struct _FunctorArguments { static const size_t _Count = 0; }; template<> struct _FunctorArguments<_OneArgumentFunctor> { static const size_t _Count = 1; }; template<> struct _FunctorArguments<_TwoArgumentFunctor> { static const size_t _Count = 2; }; template struct _FunctorTypeTraits { typedef decltype(_ArgumentCountHelper(&(_T::operator()))) _ArgumentCountType; static const size_t _ArgumentCount = _FunctorArguments<_ArgumentCountType>::_Count; typedef decltype(_ReturnTypeClassHelperThunk(&(_T::operator()))) _ReturnType; typedef decltype(_Arg1ClassHelperThunk(&(_T::operator()))) _Argument1Type; typedef decltype(_Arg2ClassHelperThunk(&(_T::operator()))) _Argument2Type; }; template struct _FunctorTypeTraits<_T *> { typedef decltype(_ArgumentCountHelper(stdx::declval<_T*>())) _ArgumentCountType; static const size_t _ArgumentCount = _FunctorArguments<_ArgumentCountType>::_Count; typedef decltype(_ReturnTypePFNHelperThunk(stdx::declval<_T*>())) _ReturnType; typedef decltype(_Arg1PFNHelperThunk(stdx::declval<_T*>())) _Argument1Type; typedef decltype(_Arg2PFNHelperThunk(stdx::declval<_T*>())) _Argument2Type; }; template struct _ProgressTypeTraits { static const bool _TakesProgress = false; typedef void _ProgressType; }; template struct _ProgressTypeTraits> { static const bool _TakesProgress = true; typedef typename _T _ProgressType; }; template::_ArgumentCount> struct _CAFunctorOptions { static const bool _TakesProgress = false; static const bool _TakesToken = false; typedef void _ProgressType; }; template struct _CAFunctorOptions<_T, 1> { private: typedef typename _FunctorTypeTraits<_T>::_Argument1Type _Argument1Type; public: static const bool _TakesProgress = _ProgressTypeTraits<_Argument1Type>::_TakesProgress; static const bool _TakesToken = !_TakesProgress; typedef typename _ProgressTypeTraits<_Argument1Type>::_ProgressType _ProgressType; }; template struct _CAFunctorOptions<_T, 2> { private: typedef typename _FunctorTypeTraits<_T>::_Argument1Type _Argument1Type; public: static const bool _TakesProgress = true; static const bool _TakesToken = true; typedef typename _ProgressTypeTraits<_Argument1Type>::_ProgressType _ProgressType; }; ref class _Zip { }; // *************************************************************************** // Async Operation Task Generators // // // Functor returns an IAsyncInfo - result needs to be wrapped in a task: // template struct _SelectorTaskGenerator { template static task<_ReturnType> _GenerateTask_0(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) { task_options _taskOptinos(_Cts.get_token()); details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); return task<_ReturnType>(_Func(), _taskOptinos); } template static task<_ReturnType> _GenerateTask_1C(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) { task_options _taskOptinos(_Cts.get_token()); details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); return task<_ReturnType>(_Func(_Cts.get_token()), _taskOptinos); } template static task<_ReturnType> _GenerateTask_1P(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) { task_options _taskOptinos(_Cts.get_token()); details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); return task<_ReturnType>(_Func(_Progress), _taskOptinos); } template static task<_ReturnType> _GenerateTask_2PC(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) { task_options _taskOptinos(_Cts.get_token()); details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); return task<_ReturnType>(_Func(_Progress, _Cts.get_token()), _taskOptinos); } }; template struct _SelectorTaskGenerator<_AsyncSelector, void> { template static task _GenerateTask_0(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) { task_options _taskOptinos(_Cts.get_token()); details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); return task(_Func(), _taskOptinos); } template static task _GenerateTask_1C(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) { task_options _taskOptinos(_Cts.get_token()); details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); return task(_Func(_Cts.get_token()), _taskOptinos); } template static task _GenerateTask_1P(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) { task_options _taskOptinos(_Cts.get_token()); details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); return task(_Func(_Progress), _taskOptinos); } template static task _GenerateTask_2PC(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) { task_options _taskOptinos(_Cts.get_token()); details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); return task(_Func(_Progress, _Cts.get_token()), _taskOptinos); } }; // // Functor returns a result - it needs to be wrapped in a task: // template struct _SelectorTaskGenerator<_TypeSelectorNoAsync, _ReturnType> { #pragma warning(push) #pragma warning(disable: 4702) template static task<_ReturnType> _GenerateTask_0(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) { task_options _taskOptinos(_Cts.get_token()); details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); return task<_ReturnType>( [=]() -> _ReturnType { _Task_generator_oversubscriber_t _Oversubscriber; (_Oversubscriber); return _Func(); }, _taskOptinos); } #pragma warning(pop) template static task<_ReturnType> _GenerateTask_1C(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) { task_options _taskOptinos(_Cts.get_token()); details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); return task<_ReturnType>( [=]() -> _ReturnType { _Task_generator_oversubscriber_t _Oversubscriber; (_Oversubscriber); return _Func(_Cts.get_token()); }, _taskOptinos); } template static task<_ReturnType> _GenerateTask_1P(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) { task_options _taskOptinos(_Cts.get_token()); details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); return task<_ReturnType>( [=]() -> _ReturnType { _Task_generator_oversubscriber_t _Oversubscriber; (_Oversubscriber); return _Func(_Progress); }, _taskOptinos); } template static task<_ReturnType> _GenerateTask_2PC(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) { task_options _taskOptinos(_Cts.get_token()); details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); return task<_ReturnType>( [=]() -> _ReturnType { _Task_generator_oversubscriber_t _Oversubscriber; (_Oversubscriber); return _Func(_Progress, _Cts.get_token()); }, _taskOptinos); } }; template<> struct _SelectorTaskGenerator<_TypeSelectorNoAsync, void> { template static task _GenerateTask_0(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) { task_options _taskOptinos(_Cts.get_token()); details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); return task( [=]() { _Task_generator_oversubscriber_t _Oversubscriber; (_Oversubscriber); _Func(); }, _taskOptinos); } template static task _GenerateTask_1C(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) { task_options _taskOptinos(_Cts.get_token()); details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); return task( [=]() { _Task_generator_oversubscriber_t _Oversubscriber; (_Oversubscriber); _Func(_Cts.get_token()); }, _taskOptinos); } template static task _GenerateTask_1P(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) { task_options _taskOptinos(_Cts.get_token()); details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); return task( [=]() { _Task_generator_oversubscriber_t _Oversubscriber; (_Oversubscriber); _Func(_Progress); }, _taskOptinos); } template static task _GenerateTask_2PC(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) { task_options _taskOptinos(_Cts.get_token()); details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); return task( [=]() { _Task_generator_oversubscriber_t _Oversubscriber; (_Oversubscriber); _Func(_Progress, _Cts.get_token()); }, _taskOptinos); } }; // // Functor returns a task - the task can directly be returned: // template struct _SelectorTaskGenerator<_TypeSelectorAsyncTask, _ReturnType> { template static task<_ReturnType> _GenerateTask_0(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) { return _Func(); } template static task<_ReturnType> _GenerateTask_1C(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) { return _Func(_Cts.get_token()); } template static task<_ReturnType> _GenerateTask_1P(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) { return _Func(_Progress); } template static task<_ReturnType> _GenerateTask_2PC(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) { return _Func(_Progress, _Cts.get_token()); } }; template<> struct _SelectorTaskGenerator<_TypeSelectorAsyncTask, void> { template static task _GenerateTask_0(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) { return _Func(); } template static task _GenerateTask_1C(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) { return _Func(_Cts.get_token()); } template static task _GenerateTask_1P(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) { return _Func(_Progress); } template static task _GenerateTask_2PC(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) { return _Func(_Progress, _Cts.get_token()); } }; template struct _TaskGenerator { }; template struct _TaskGenerator<_Generator, false, false> { template static auto _GenerateTask(const _Function& _Func, _ClassPtr _Ptr, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) -> decltype(_Generator::_GenerateTask_0(_Func, _Cts, _callstack)) { return _Generator::_GenerateTask_0(_Func, _Cts, _callstack); } }; template struct _TaskGenerator<_Generator, true, false> { template static auto _GenerateTask(const _Function& _Func, _ClassPtr _Ptr, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) -> decltype(_Generator::_GenerateTask_0(_Func, _Cts, _callstack)) { return _Generator::_GenerateTask_1C(_Func, _Cts, _callstack); } }; template struct _TaskGenerator<_Generator, false, true> { template static auto _GenerateTask(const _Function& _Func, _ClassPtr _Ptr, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) -> decltype(_Generator::_GenerateTask_0(_Func, _Cts, _callstack)) { return _Generator::_GenerateTask_1P(_Func, progress_reporter<_ProgressType>::_CreateReporter(_Ptr), _Cts, _callstack); } }; template struct _TaskGenerator<_Generator, true, true> { template static auto _GenerateTask(const _Function& _Func, _ClassPtr _Ptr, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) -> decltype(_Generator::_GenerateTask_0(_Func, _Cts, _callstack)) { return _Generator::_GenerateTask_2PC(_Func, progress_reporter<_ProgressType>::_CreateReporter(_Ptr), _Cts, _callstack); } }; // *************************************************************************** // Async Operation Attributes Classes // // These classes are passed through the hierarchy of async base classes in order to hold multiple attributes of a given async construct in // a single container. An attribute class must define: // // Mandatory: // ------------------------- // // _AsyncBaseType : The Windows Runtime interface which is being implemented. // _CompletionDelegateType : The Windows Runtime completion delegate type for the interface. // _ProgressDelegateType : If _TakesProgress is true, the Windows Runtime progress delegate type for the interface. If it is false, an empty Windows Runtime type. // _ReturnType : The return type of the async construct (void for actions / non-void for operations) // // _TakesProgress : An indication as to whether or not // // _Generate_Task : A function adapting the user's function into what's necessary to produce the appropriate task // // Optional: // ------------------------- // template struct _AsyncAttributes { }; template struct _AsyncAttributes<_Function, _ProgressType, _ReturnType, _TaskTraits, _TakesToken, true> { typedef typename Windows::Foundation::IAsyncOperationWithProgress<_ReturnType, _ProgressType> _AsyncBaseType; typedef typename Windows::Foundation::AsyncOperationProgressHandler<_ReturnType, _ProgressType> _ProgressDelegateType; typedef typename Windows::Foundation::AsyncOperationWithProgressCompletedHandler<_ReturnType, _ProgressType> _CompletionDelegateType; typedef typename _ReturnType _ReturnType; typedef typename _ProgressType _ProgressType; typedef typename _TaskTraits::_AsyncKind _AsyncKind; typedef typename _SelectorTaskGenerator<_AsyncKind, _ReturnType> _SelectorTaskGenerator; typedef typename _TaskGenerator<_SelectorTaskGenerator, _TakesToken, true> _TaskGenerator; static const bool _TakesProgress = true; static const bool _TakesToken = _TakesToken; template static task<_ReturnType> _Generate_Task(const _Function& _Func, _ClassPtr _Ptr, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) { return _TaskGenerator::_GenerateTask<_Function, _ClassPtr, _ProgressType>(_Func, _Ptr, _Cts, _callstack); } }; template struct _AsyncAttributes<_Function, _ProgressType, _ReturnType, _TaskTraits, _TakesToken, false> { typedef typename Windows::Foundation::IAsyncOperation<_ReturnType> _AsyncBaseType; typedef _Zip _ProgressDelegateType; typedef typename Windows::Foundation::AsyncOperationCompletedHandler<_ReturnType> _CompletionDelegateType; typedef typename _ReturnType _ReturnType; typedef typename _TaskTraits::_AsyncKind _AsyncKind; typedef typename _SelectorTaskGenerator<_AsyncKind, _ReturnType> _SelectorTaskGenerator; typedef typename _TaskGenerator<_SelectorTaskGenerator, _TakesToken, false> _TaskGenerator; static const bool _TakesProgress = false; static const bool _TakesToken = _TakesToken; template static task<_ReturnType> _Generate_Task(const _Function& _Func, _ClassPtr _Ptr, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) { return _TaskGenerator::_GenerateTask<_Function, _ClassPtr, _ProgressType>(_Func, _Ptr, _Cts, _callstack); } }; template struct _AsyncAttributes<_Function, _ProgressType, void, _TaskTraits, _TakesToken, true> { typedef typename Windows::Foundation::IAsyncActionWithProgress<_ProgressType> _AsyncBaseType; typedef typename Windows::Foundation::AsyncActionProgressHandler<_ProgressType> _ProgressDelegateType; typedef typename Windows::Foundation::AsyncActionWithProgressCompletedHandler<_ProgressType> _CompletionDelegateType; typedef void _ReturnType; typedef typename _ProgressType _ProgressType; typedef typename _TaskTraits::_AsyncKind _AsyncKind; typedef typename _SelectorTaskGenerator<_AsyncKind, _ReturnType> _SelectorTaskGenerator; typedef typename _TaskGenerator<_SelectorTaskGenerator, _TakesToken, true> _TaskGenerator; static const bool _TakesProgress = true; static const bool _TakesToken = _TakesToken; template static task<_ReturnType> _Generate_Task(const _Function& _Func, _ClassPtr _Ptr, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) { return _TaskGenerator::_GenerateTask<_Function, _ClassPtr, _ProgressType>(_Func, _Ptr, _Cts, _callstack); } }; template struct _AsyncAttributes<_Function, _ProgressType, void, _TaskTraits, _TakesToken, false> { typedef typename Windows::Foundation::IAsyncAction _AsyncBaseType; typedef _Zip _ProgressDelegateType; typedef typename Windows::Foundation::AsyncActionCompletedHandler _CompletionDelegateType; typedef void _ReturnType; typedef typename _TaskTraits::_AsyncKind _AsyncKind; typedef typename _SelectorTaskGenerator<_AsyncKind, _ReturnType> _SelectorTaskGenerator; typedef typename _TaskGenerator<_SelectorTaskGenerator, _TakesToken, false> _TaskGenerator; static const bool _TakesProgress = false; static const bool _TakesToken = _TakesToken; template static task<_ReturnType> _Generate_Task(const _Function& _Func, _ClassPtr _Ptr, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) { return _TaskGenerator::_GenerateTask<_Function, _ClassPtr, _ProgressType>(_Func, _Ptr, _Cts, _callstack); } }; template struct _AsyncLambdaTypeTraits { typedef typename _FunctorTypeTraits<_Function>::_ReturnType _ReturnType; typedef typename _FunctorTypeTraits<_Function>::_Argument1Type _Argument1Type; typedef typename _CAFunctorOptions<_Function>::_ProgressType _ProgressType; static const bool _TakesProgress = _CAFunctorOptions<_Function>::_TakesProgress; static const bool _TakesToken = _CAFunctorOptions<_Function>::_TakesToken; typedef typename _TaskTypeTraits<_ReturnType> _TaskTraits; typedef typename _AsyncAttributes<_Function, _ProgressType, typename _TaskTraits::_TaskRetType, _TaskTraits, _TakesToken, _TakesProgress> _AsyncAttributes; }; // *************************************************************************** // AsyncInfo (and completion) Layer: // // // Internal base class implementation for async operations (based on internal Windows representation for ABI level async operations) // template < typename _Attributes, _AsyncResultType resultType = SingleResult > ref class _AsyncInfoBase abstract : _Attributes::_AsyncBaseType { internal: _AsyncInfoBase() : _M_currentStatus(_AsyncStatusInternal::_AsyncCreated), _M_errorCode(S_OK), _M_completeDelegate(nullptr), _M_CompleteDelegateAssigned(0), _M_CallbackMade(0) { _M_id = ::pplx::details::platform::GetNextAsyncId(); } public: virtual typename _Attributes::_ReturnType GetResults() { throw ::Platform::Exception::CreateException(E_UNEXPECTED); } virtual property unsigned int Id { unsigned int get() { _CheckValidStateForAsyncInfoCall(); return _M_id; } void set(unsigned int id) { _CheckValidStateForAsyncInfoCall(); if (id == 0) { throw ::Platform::Exception::CreateException(E_INVALIDARG); } else if (_M_currentStatus != _AsyncStatusInternal::_AsyncCreated) { throw ::Platform::Exception::CreateException(E_ILLEGAL_METHOD_CALL); } _M_id = id; } } virtual property Windows::Foundation::AsyncStatus Status { Windows::Foundation::AsyncStatus get() { _CheckValidStateForAsyncInfoCall(); _AsyncStatusInternal _Current = _M_currentStatus; // // Map our internal cancel pending to cancelled. This way "pending cancelled" looks to the outside as "cancelled" but // can still transition to "completed" if the operation completes without acknowledging the cancellation request // switch(_Current) { case _AsyncCancelPending: _Current = _AsyncCanceled; break; case _AsyncCreated: _Current = _AsyncStarted; break; default: break; } return static_cast(_Current); } } virtual property Windows::Foundation::HResult ErrorCode { Windows::Foundation::HResult get() { _CheckValidStateForAsyncInfoCall(); Windows::Foundation::HResult _Hr; _Hr.Value = _M_errorCode; return _Hr; } } virtual property typename _Attributes::_ProgressDelegateType^ Progress { typename typename _Attributes::_ProgressDelegateType^ get() { return _GetOnProgress(); } void set(typename _Attributes::_ProgressDelegateType^ _ProgressHandler) { _PutOnProgress(_ProgressHandler); } } virtual void Cancel() { if (_TransitionToState(_AsyncCancelPending)) { _OnCancel(); } } virtual void Close() { if (_TransitionToState(_AsyncClosed)) { _OnClose(); } else { if (_M_currentStatus != _AsyncClosed) // Closed => Closed transition is just ignored { throw ::Platform::Exception::CreateException(E_ILLEGAL_STATE_CHANGE); } } } virtual property typename _Attributes::_CompletionDelegateType^ Completed { typename _Attributes::_CompletionDelegateType^ get() { _CheckValidStateForDelegateCall(); return _M_completeDelegate; } void set(typename _Attributes::_CompletionDelegateType^ _CompleteHandler) { _CheckValidStateForDelegateCall(); // this delegate property is "write once" if (InterlockedIncrement(&_M_CompleteDelegateAssigned) == 1) { _M_completeDelegateContext = _ContextCallback::_CaptureCurrent(); _M_completeDelegate = _CompleteHandler; // Guarantee that the write of _M_completeDelegate is ordered with respect to the read of state below // as perceived from _FireCompletion on another thread. MemoryBarrier(); if (_IsTerminalState()) { _FireCompletion(); } } else { throw ::Platform::Exception::CreateException(E_ILLEGAL_DELEGATE_ASSIGNMENT); } } } protected private: // _Start - this is not externally visible since async operations "hot start" before returning to the caller void _Start() { if (_TransitionToState(_AsyncStarted)) { _OnStart(); } else { throw ::Platform::Exception::CreateException(E_ILLEGAL_STATE_CHANGE); } } void _FireCompletion() { _TryTransitionToCompleted(); // we guarantee that completion can only ever be fired once if (_M_completeDelegate != nullptr && InterlockedIncrement(&_M_CallbackMade) == 1) { _M_completeDelegateContext._CallInContext([=] { _M_completeDelegate((_Attributes::_AsyncBaseType^)this, this->Status); _M_completeDelegate = nullptr; }); } } virtual typename _Attributes::_ProgressDelegateType^ _GetOnProgress() { throw ::Platform::Exception::CreateException(E_UNEXPECTED); } virtual void _PutOnProgress(typename _Attributes::_ProgressDelegateType^ _ProgressHandler) { throw ::Platform::Exception::CreateException(E_UNEXPECTED); } bool _TryTransitionToCompleted() { return _TransitionToState(_AsyncStatusInternal::_AsyncCompleted); } bool _TryTransitionToCancelled() { return _TransitionToState(_AsyncStatusInternal::_AsyncCanceled); } bool _TryTransitionToError(const HRESULT error) { _InterlockedCompareExchange(reinterpret_cast(&_M_errorCode), error, S_OK); return _TransitionToState(_AsyncStatusInternal::_AsyncError); } // This method checks to see if the delegate properties can be // modified in the current state and generates the appropriate // error hr in the case of violation. inline void _CheckValidStateForDelegateCall() { if (_M_currentStatus == _AsyncClosed) { throw ::Platform::Exception::CreateException(E_ILLEGAL_METHOD_CALL); } } // This method checks to see if results can be collected in the // current state and generates the appropriate error hr in // the case of a violation. inline void _CheckValidStateForResultsCall() { _AsyncStatusInternal _Current = _M_currentStatus; if (_Current == _AsyncError) { throw ::Platform::Exception::CreateException(_M_errorCode); } #pragma warning(push) #pragma warning(disable: 4127) // Conditional expression is constant // single result illegal before transition to Completed or Cancelled state if (resultType == SingleResult) #pragma warning(pop) { if (_Current != _AsyncCompleted) { throw ::Platform::Exception::CreateException(E_ILLEGAL_METHOD_CALL); } } // multiple results can be called after Start has been called and before/after Completed else if (_Current != _AsyncStarted && _Current != _AsyncCancelPending && _Current != _AsyncCanceled && _Current != _AsyncCompleted) { throw ::Platform::Exception::CreateException(E_ILLEGAL_METHOD_CALL); } } // This method can be called by derived classes periodically to determine // whether the asynchronous operation should continue processing or should // be halted. inline bool _ContinueAsyncOperation() { return (_M_currentStatus == _AsyncStarted); } // These two methods are used to allow the async worker implementation do work on // state transitions. No real "work" should be done in these methods. In other words // they should not block for a long time on UI timescales. virtual void _OnStart() = 0; virtual void _OnClose() = 0; virtual void _OnCancel() = 0; private: // This method is used to check if calls to the AsyncInfo properties // (id, status, errorcode) are legal in the current state. It also // generates the appropriate error hr to return in the case of an // illegal call. inline void _CheckValidStateForAsyncInfoCall() { _AsyncStatusInternal _Current = _M_currentStatus; if (_Current == _AsyncClosed) { throw ::Platform::Exception::CreateException(E_ILLEGAL_METHOD_CALL); } else if (_Current == _AsyncCreated) { throw ::Platform::Exception::CreateException(E_ASYNC_OPERATION_NOT_STARTED); } } inline bool _TransitionToState(const _AsyncStatusInternal _NewState) { _AsyncStatusInternal _Current = _M_currentStatus; // This enforces the valid state transitions of the asynchronous worker object // state machine. switch(_NewState) { case _AsyncStatusInternal::_AsyncStarted: if (_Current != _AsyncCreated) { return false; } break; case _AsyncStatusInternal::_AsyncCompleted: if (_Current != _AsyncStarted && _Current != _AsyncCancelPending) { return false; } break; case _AsyncStatusInternal::_AsyncCancelPending: if (_Current != _AsyncStarted) { return false; } break; case _AsyncStatusInternal::_AsyncCanceled: if (_Current != _AsyncStarted && _Current != _AsyncCancelPending) { return false; } break; case _AsyncStatusInternal::_AsyncError: if (_Current != _AsyncStarted && _Current != _AsyncCancelPending) { return false; } break; case _AsyncStatusInternal::_AsyncClosed: if (!_IsTerminalState(_Current)) { return false; } break; default: return false; break; } // attempt the transition to the new state // Note: if currentStatus_ == _Current, then there was no intervening write // by the async work object and the swap succeeded. _AsyncStatusInternal _RetState = static_cast<_AsyncStatusInternal>( _InterlockedCompareExchange(reinterpret_cast(&_M_currentStatus), _NewState, static_cast(_Current))); // ICE returns the former state, if the returned state and the // state we captured at the beginning of this method are the same, // the swap succeeded. return (_RetState == _Current); } inline bool _IsTerminalState() { return _IsTerminalState(_M_currentStatus); } inline bool _IsTerminalState(_AsyncStatusInternal status) { return (status == _AsyncError || status == _AsyncCanceled || status == _AsyncCompleted || status == _AsyncClosed); } private: _ContextCallback _M_completeDelegateContext; typename _Attributes::_CompletionDelegateType^ volatile _M_completeDelegate; _AsyncStatusInternal volatile _M_currentStatus; HRESULT volatile _M_errorCode; unsigned int _M_id; long volatile _M_CompleteDelegateAssigned; long volatile _M_CallbackMade; }; // *************************************************************************** // Progress Layer (optional): // template< typename _Attributes, bool _HasProgress, _AsyncResultType _ResultType = SingleResult > ref class _AsyncProgressBase abstract : _AsyncInfoBase<_Attributes, _ResultType> { }; template< typename _Attributes, _AsyncResultType _ResultType> ref class _AsyncProgressBase<_Attributes, true, _ResultType> abstract : _AsyncInfoBase<_Attributes, _ResultType> { internal: _AsyncProgressBase() : _AsyncInfoBase<_Attributes, _ResultType>(), _M_progressDelegate(nullptr) { } virtual typename _Attributes::_ProgressDelegateType^ _GetOnProgress() override { _CheckValidStateForDelegateCall(); return _M_progressDelegate; } virtual void _PutOnProgress(typename _Attributes::_ProgressDelegateType^ _ProgressHandler) override { _CheckValidStateForDelegateCall(); _M_progressDelegate = _ProgressHandler; _M_progressDelegateContext = _ContextCallback::_CaptureCurrent(); } void _FireProgress(const typename _Attributes::_ProgressType& _ProgressValue) { if (_M_progressDelegate != nullptr) { _M_progressDelegateContext._CallInContext([=] { _M_progressDelegate((_Attributes::_AsyncBaseType^)this, _ProgressValue); }); } } private: _ContextCallback _M_progressDelegateContext; typename _Attributes::_ProgressDelegateType^ _M_progressDelegate; }; template ref class _AsyncBaseProgressLayer abstract : _AsyncProgressBase<_Attributes, _Attributes::_TakesProgress, _ResultType> { }; // *************************************************************************** // Task Adaptation Layer: // // // _AsyncTaskThunkBase provides a bridge between IAsync and task. // template ref class _AsyncTaskThunkBase abstract : _AsyncBaseProgressLayer<_Attributes> { public: virtual _ReturnType GetResults() override { _CheckValidStateForResultsCall(); return _M_task.get(); } internal: typedef task<_ReturnType> _TaskType; _AsyncTaskThunkBase(const _TaskType& _Task) : _M_task(_Task) { } _AsyncTaskThunkBase() { } protected: virtual void _OnStart() override { _M_task.then( [=](_TaskType _Antecedent) { try { _Antecedent.get(); } catch(task_canceled&) { _TryTransitionToCancelled(); } catch(::Platform::Exception^ _Ex) { _TryTransitionToError(_Ex->HResult); } catch(...) { _TryTransitionToError(E_FAIL); } _FireCompletion(); }); } internal: _TaskType _M_task; cancellation_token_source _M_cts; }; template ref class _AsyncTaskThunk : _AsyncTaskThunkBase<_Attributes, typename _Attributes::_ReturnType> { internal: _AsyncTaskThunk(const _TaskType& _Task) : _AsyncTaskThunkBase(_Task) { } _AsyncTaskThunk() { } protected: virtual void _OnClose() override { } virtual void _OnCancel() override { _M_cts.cancel(); } }; // *************************************************************************** // Async Creation Layer: // template ref class _AsyncTaskGeneratorThunk sealed : _AsyncTaskThunk::_AsyncAttributes> { internal: typedef typename _AsyncLambdaTypeTraits<_Function>::_AsyncAttributes _Attributes; typedef typename _AsyncTaskThunk<_Attributes> _Base; typedef typename _Attributes::_AsyncBaseType _AsyncBaseType; _AsyncTaskGeneratorThunk(const _Function& _Func, const _TaskCreationCallstack &_callstack) : _M_func(_Func), _M_creationCallstack(_callstack) { // Virtual call here is safe as the class is declared 'sealed' _Start(); } protected: // // The only thing we must do different from the base class is we must spin the hot task on transition from Created->Started. Otherwise, // let the base thunk handle everything. // virtual void _OnStart() override { // // Call the appropriate task generator to actually produce a task of the expected type. This might adapt the user lambda for progress reports, // wrap the return result in a task, or allow for direct return of a task depending on the form of the lambda. // _M_task = _Attributes::_Generate_Task(_M_func, this, _M_cts, _M_creationCallstack); _Base::_OnStart(); } virtual void _OnCancel() override { _Base::_OnCancel(); } private: _TaskCreationCallstack _M_creationCallstack; _Function _M_func; }; } // namespace details /// /// Creates a Windows Runtime asynchronous construct based on a user supplied lambda or function object. The return type of create_async is /// one of either IAsyncAction^, IAsyncActionWithProgress<TProgress>^, IAsyncOperation<TResult>^, or /// IAsyncOperationWithProgress<TResult, TProgress>^ based on the signature of the lambda passed to the method. /// /// /// The lambda or function object from which to create a Windows Runtime asynchronous construct. /// /// /// An asynchronous construct represented by an IAsyncAction^, IAsyncActionWithProgress<TProgress>^, IAsyncOperation<TResult>^, or an /// IAsyncOperationWithProgress<TResult, TProgress>^. The interface returned depends on the signature of the lambda passed into the function. /// /// /// The return type of the lambda determines whether the construct is an action or an operation. /// Lambdas that return void cause the creation of actions. Lambdas that return a result of type TResult cause the creation of /// operations of TResult. /// The lambda may also return a task<TResult> which encapsulates the aysnchronous work within itself or is the continuation of /// a chain of tasks that represent the asynchronous work. In this case, the lambda itself is executed inline, since the tasks are the ones that /// execute asynchronously, and the return type of the lambda is unwrapped to produce the asynchronous construct returned by create_async. /// This implies that a lambda that returns a task<void> will cause the creation of actions, and a lambda that returns a task<TResult> will /// cause the creation of operations of TResult. /// The lambda may take either zero, one or two arguments. The valid arguments are progress_reporter<TProgress> and /// cancellation_token, in that order if both are used. A lambda without arguments causes the creation of an asynchronous construct without /// the capability for progress reporting. A lambda that takes a progress_reporter<TProgress> will cause create_async to return an asynchronous /// construct which reports progress of type TProgress each time the report method of the progress_reporter object is called. A lambda that /// takes a cancellation_token may use that token to check for cancellation, or pass it to tasks that it creates so that cancellation of the /// asynchronous construct causes cancellation of those tasks. /// If the body of the lambda or function object returns a result (and not a task<TResult>), the lamdba will be executed /// asynchronously within the process MTA in the context of a task the Runtime implicitly creates for it. The IAsyncInfo::Cancel method will /// cause cancellation of the implicit task. /// If the body of the lambda returns a task, the lamba executes inline, and by declaring the lambda to take an argument of type /// cancellation_token you can trigger cancellation of any tasks you create within the lambda by passing that token in when you create them. /// You may also use the register_callback method on the token to cause the Runtime to invoke a callback when you call IAsyncInfo::Cancel on /// the async operation or action produced.. /// This function is only available to Windows Store apps. /// /// /// /// /**/ template __declspec(noinline) details::_AsyncTaskGeneratorThunk<_Function> ^create_async(const _Function& _Func) { static_assert(std::is_same::value, "argument to create_async must be a callable object taking zero, one or two arguments"); return ref new details::_AsyncTaskGeneratorThunk<_Function>(_Func, _CAPTURE_CALLSTACK()); } #endif /* defined (__cplusplus_winrt) */ namespace details { // Helper struct for when_all operators to know when tasks have completed template struct _RunAllParam { _RunAllParam() : _M_completeCount(0), _M_numTasks(0) { } void _Resize(size_t _Len, bool _SkipVector = false) { _M_numTasks = _Len; if (!_SkipVector) { _M_vector._Result.resize(_Len); } } task_completion_event<_Unit_type> _M_completed; _ResultHolder > _M_vector; _ResultHolder<_Type> _M_mergeVal; atomic_size_t _M_completeCount; size_t _M_numTasks; }; template struct _RunAllParam > { _RunAllParam() : _M_completeCount(0), _M_numTasks(0) { } void _Resize(size_t _Len, bool _SkipVector = false) { _M_numTasks = _Len; if (!_SkipVector) { _M_vector.resize(_Len); } } task_completion_event<_Unit_type> _M_completed; std::vector<_ResultHolder > > _M_vector; atomic_size_t _M_completeCount; size_t _M_numTasks; }; // Helper struct specialization for void template<> struct _RunAllParam<_Unit_type> { _RunAllParam() : _M_completeCount(0), _M_numTasks(0) { } void _Resize(size_t _Len) { _M_numTasks = _Len; } task_completion_event<_Unit_type> _M_completed; atomic_size_t _M_completeCount; size_t _M_numTasks; }; inline void _JoinAllTokens_Add(const cancellation_token_source& _MergedSrc, _CancellationTokenState *_PJoinedTokenState) { if (_PJoinedTokenState != nullptr && _PJoinedTokenState != _CancellationTokenState::_None()) { cancellation_token _T = cancellation_token::_FromImpl(_PJoinedTokenState); _T.register_callback( [=](){ _MergedSrc.cancel(); }); } } template void _WhenAllContinuationWrapper(_RunAllParam<_ElementType>* _PParam, _Function _Func, task<_TaskType>& _Task) { if (_Task._GetImpl()->_IsCompleted()) { _Func(); if (atomic_increment(_PParam->_M_completeCount) == _PParam->_M_numTasks) { // Inline execute its direct continuation, the _ReturnTask _PParam->_M_completed.set(_Unit_type()); // It's safe to delete it since all usage of _PParam in _ReturnTask has been finished. delete _PParam; } } else { _ASSERTE(_Task._GetImpl()->_IsCanceled()); if (_Task._GetImpl()->_HasUserException()) { // _Cancel will return false if the TCE is already canceled with or without exception _PParam->_M_completed._Cancel(_Task._GetImpl()->_GetExceptionHolder()); } else { _PParam->_M_completed._Cancel(); } if (atomic_increment(_PParam->_M_completeCount) == _PParam->_M_numTasks) { delete _PParam; } } } template struct _WhenAllImpl { static task> _Perform(const task_options& _TaskOptions, _Iterator _Begin, _Iterator _End) { _CancellationTokenState *_PTokenState = _TaskOptions.has_cancellation_token() ? _TaskOptions.get_cancellation_token()._GetImplValue() : nullptr; auto _PParam = new _RunAllParam<_ElementType>(); cancellation_token_source _MergedSource; // Step1: Create task completion event. task_options _Options(_TaskOptions); _Options.set_cancellation_token(_MergedSource.get_token()); task<_Unit_type> _All_tasks_completed(_PParam->_M_completed, _Options); // The return task must be created before step 3 to enforce inline execution. auto _ReturnTask = _All_tasks_completed._Then([=](_Unit_type) -> std::vector<_ElementType> { return _PParam->_M_vector.Get(); }, nullptr); // Step2: Combine and check tokens, and count elements in range. if (_PTokenState) { _JoinAllTokens_Add(_MergedSource, _PTokenState); _PParam->_Resize(static_cast(std::distance(_Begin, _End))); } else { size_t _TaskNum = 0; for (auto _PTask = _Begin; _PTask != _End; ++_PTask) { _TaskNum++; _JoinAllTokens_Add(_MergedSource, _PTask->_GetImpl()->_M_pTokenState); } _PParam->_Resize(_TaskNum); } // Step3: Check states of previous tasks. if( _Begin == _End ) { _PParam->_M_completed.set(_Unit_type()); delete _PParam; } else { size_t _Index = 0; for (auto _PTask = _Begin; _PTask != _End; ++_PTask) { if (_PTask->is_apartment_aware()) { _ReturnTask._SetAsync(); } _PTask->_Then([_PParam, _Index](task<_ElementType> _ResultTask) { auto _PParamCopy = _PParam; auto _IndexCopy = _Index; auto _Func = [_PParamCopy, _IndexCopy, &_ResultTask](){ _PParamCopy->_M_vector._Result[_IndexCopy] = _ResultTask._GetImpl()->_GetResult(); }; _WhenAllContinuationWrapper(_PParam, _Func, _ResultTask); }, _CancellationTokenState::_None()); _Index++; } } return _ReturnTask; } }; template struct _WhenAllImpl, _Iterator> { static task> _Perform(const task_options& _TaskOptions, _Iterator _Begin, _Iterator _End) { _CancellationTokenState *_PTokenState = _TaskOptions.has_cancellation_token() ? _TaskOptions.get_cancellation_token()._GetImplValue() : nullptr; auto _PParam = new _RunAllParam>(); cancellation_token_source _MergedSource; // Step1: Create task completion event. task_options _Options(_TaskOptions); _Options.set_cancellation_token(_MergedSource.get_token()); task<_Unit_type> _All_tasks_completed(_PParam->_M_completed, _Options); // The return task must be created before step 3 to enforce inline execution. auto _ReturnTask = _All_tasks_completed._Then([=](_Unit_type) -> std::vector<_ElementType> { _ASSERTE(_PParam->_M_completeCount == _PParam->_M_numTasks); std::vector<_ElementType> _Result; for(size_t _I = 0; _I < _PParam->_M_numTasks; _I++) { const std::vector<_ElementType>& _Vec = _PParam->_M_vector[_I].Get(); _Result.insert(_Result.end(), _Vec.begin(), _Vec.end()); } return _Result; }, nullptr); // Step2: Combine and check tokens, and count elements in range. if (_PTokenState) { _JoinAllTokens_Add(_MergedSource, _PTokenState); _PParam->_Resize(static_cast(std::distance(_Begin, _End))); } else { size_t _TaskNum = 0; for (auto _PTask = _Begin; _PTask != _End; ++_PTask) { _TaskNum++; _JoinAllTokens_Add(_MergedSource, _PTask->_GetImpl()->_M_pTokenState); } _PParam->_Resize(_TaskNum); } // Step3: Check states of previous tasks. if( _Begin == _End ) { _PParam->_M_completed.set(_Unit_type()); delete _PParam; } else { size_t _Index = 0; for (auto _PTask = _Begin; _PTask != _End; ++_PTask) { if (_PTask->is_apartment_aware()) { _ReturnTask._SetAsync(); } _PTask->_Then([_PParam, _Index](task> _ResultTask) { auto _PParamCopy = _PParam; auto _IndexCopy = _Index; auto _Func = [_PParamCopy, _IndexCopy, &_ResultTask]() { _PParamCopy->_M_vector[_IndexCopy].Set(_ResultTask._GetImpl()->_GetResult()); }; _WhenAllContinuationWrapper(_PParam, _Func, _ResultTask); }, _CancellationTokenState::_None()); _Index++; } } return _ReturnTask; } }; template struct _WhenAllImpl { static task _Perform(const task_options& _TaskOptions, _Iterator _Begin, _Iterator _End) { _CancellationTokenState *_PTokenState = _TaskOptions.has_cancellation_token() ? _TaskOptions.get_cancellation_token()._GetImplValue() : nullptr; auto _PParam = new _RunAllParam<_Unit_type>(); cancellation_token_source _MergedSource; // Step1: Create task completion event. task_options _Options(_TaskOptions); _Options.set_cancellation_token(_MergedSource.get_token()); task<_Unit_type> _All_tasks_completed(_PParam->_M_completed, _Options); // The return task must be created before step 3 to enforce inline execution. auto _ReturnTask = _All_tasks_completed._Then([=](_Unit_type) { }, nullptr); // Step2: Combine and check tokens, and count elements in range. if (_PTokenState) { _JoinAllTokens_Add(_MergedSource, _PTokenState); _PParam->_Resize(static_cast(std::distance(_Begin, _End))); } else { size_t _TaskNum = 0; for (auto _PTask = _Begin; _PTask != _End; ++_PTask) { _TaskNum++; _JoinAllTokens_Add(_MergedSource, _PTask->_GetImpl()->_M_pTokenState); } _PParam->_Resize(_TaskNum); } // Step3: Check states of previous tasks. if( _Begin == _End ) { _PParam->_M_completed.set(_Unit_type()); delete _PParam; } else { for (auto _PTask = _Begin; _PTask != _End; ++_PTask) { if (_PTask->is_apartment_aware()) { _ReturnTask._SetAsync(); } _PTask->_Then([_PParam](task _ResultTask) { auto _Func = [](){}; _WhenAllContinuationWrapper(_PParam, _Func, _ResultTask); }, _CancellationTokenState::_None()); } } return _ReturnTask; } }; template task> _WhenAllVectorAndValue(const task>& _VectorTask, const task<_ReturnType>& _ValueTask, bool _OutputVectorFirst) { auto _PParam = new _RunAllParam<_ReturnType>(); cancellation_token_source _MergedSource; // Step1: Create task completion event. task<_Unit_type> _All_tasks_completed(_PParam->_M_completed, _MergedSource.get_token()); // The return task must be created before step 3 to enforce inline execution. auto _ReturnTask = _All_tasks_completed._Then([=](_Unit_type) -> std::vector<_ReturnType> { _ASSERTE(_PParam->_M_completeCount == 2); auto _Result = _PParam->_M_vector.Get(); // copy by value auto _mergeVal = _PParam->_M_mergeVal.Get(); if (_OutputVectorFirst == true) { _Result.push_back(_mergeVal); } else { _Result.insert(_Result.begin(), _mergeVal); } return _Result; }, nullptr); // Step2: Combine and check tokens. _JoinAllTokens_Add(_MergedSource, _VectorTask._GetImpl()->_M_pTokenState); _JoinAllTokens_Add(_MergedSource, _ValueTask._GetImpl()->_M_pTokenState); // Step3: Check states of previous tasks. _PParam->_Resize(2, true); if (_VectorTask.is_apartment_aware() || _ValueTask.is_apartment_aware()) { _ReturnTask._SetAsync(); } _VectorTask._Then([_PParam](task> _ResultTask) { auto _PParamCopy = _PParam; auto _Func = [_PParamCopy, &_ResultTask]() { auto _ResultLocal = _ResultTask._GetImpl()->_GetResult(); _PParamCopy->_M_vector.Set(_ResultLocal); }; _WhenAllContinuationWrapper(_PParam, _Func, _ResultTask); }, _CancellationTokenState::_None()); _ValueTask._Then([_PParam](task<_ReturnType> _ResultTask) { auto _PParamCopy = _PParam; auto _Func = [_PParamCopy, &_ResultTask]() { auto _ResultLocal = _ResultTask._GetImpl()->_GetResult(); _PParamCopy->_M_mergeVal.Set(_ResultLocal); }; _WhenAllContinuationWrapper(_PParam, _Func, _ResultTask); }, _CancellationTokenState::_None()); return _ReturnTask; } } // namespace details /// /// Creates a task that will complete successfully when all of the tasks supplied as arguments complete successfully. /// /// /// The type of the input iterator. /// /// /// The position of the first element in the range of elements to be combined into the resulting task. /// /// /// The position of the first element beyond the range of elements to be combined into the resulting task. /// /// /// A task that completes sucessfully when all of the input tasks have completed successfully. If the input tasks are of type T, /// the output of this function will be a task<std::vector<T>>. If the input tasks are of type void the output /// task will also be a task<void>. /// /// /// If one of the tasks is canceled or throws an exception, the returned task will complete early, in the canceled state, and the exception, /// if one is encoutered, will be thrown if you call get() or wait() on that task. /// /// /**/ template auto when_all(_Iterator _Begin, _Iterator _End, const task_options& _TaskOptions = task_options()) -> decltype (details::_WhenAllImpl::value_type::result_type, _Iterator>::_Perform(_TaskOptions, _Begin, _End)) { typedef typename std::iterator_traits<_Iterator>::value_type::result_type _ElementType; return details::_WhenAllImpl<_ElementType, _Iterator>::_Perform(_TaskOptions, _Begin, _End); } /// /// Creates a task that will complete succesfully when both of the tasks supplied as arguments complete successfully. /// /// /// The type of the returned task. /// /// /// The first task to combine into the resulting task. /// /// /// The second task to combine into the resulting task. /// /// /// A task that completes successfully when both of the input tasks have completed successfully. If the input tasks are of type T, /// the output of this function will be a task<std::vector<T>>. If the input tasks are of type void the output /// task will also be a task<void>. /// To allow for a construct of the sort taskA && taskB && taskC, which are combined in pairs, the && operator /// produces a task<std::vector<T>> if either one or both of the tasks are of type task<std::vector<T>>. /// /// /// If one of the tasks is canceled or throws an exception, the returned task will complete early, in the canceled state, and the exception, /// if one is encoutered, will be thrown if you call get() or wait() on that task. /// /// /**/ template task> operator&&(const task<_ReturnType> & _Lhs, const task<_ReturnType> & _Rhs) { task<_ReturnType> _PTasks[2] = {_Lhs, _Rhs}; return when_all(_PTasks, _PTasks+2); } /// /// Creates a task that will complete succesfully when both of the tasks supplied as arguments complete successfully. /// /// /// The type of the returned task. /// /// /// The first task to combine into the resulting task. /// /// /// The second task to combine into the resulting task. /// /// /// A task that completes successfully when both of the input tasks have completed successfully. If the input tasks are of type T, /// the output of this function will be a task<std::vector<T>>. If the input tasks are of type void the output /// task will also be a task<void>. /// To allow for a construct of the sort taskA && taskB && taskC, which are combined in pairs, the && operator /// produces a task<std::vector<T>> if either one or both of the tasks are of type task<std::vector<T>>. /// /// /// If one of the tasks is canceled or throws an exception, the returned task will complete early, in the canceled state, and the exception, /// if one is encoutered, will be thrown if you call get() or wait() on that task. /// /// /**/ template task> operator&&(const task> & _Lhs, const task<_ReturnType> & _Rhs) { return details::_WhenAllVectorAndValue(_Lhs, _Rhs, true); } /// /// Creates a task that will complete succesfully when both of the tasks supplied as arguments complete successfully. /// /// /// The type of the returned task. /// /// /// The first task to combine into the resulting task. /// /// /// The second task to combine into the resulting task. /// /// /// A task that completes successfully when both of the input tasks have completed successfully. If the input tasks are of type T, /// the output of this function will be a task<std::vector<T>>. If the input tasks are of type void the output /// task will also be a task<void>. /// To allow for a construct of the sort taskA && taskB && taskC, which are combined in pairs, the && operator /// produces a task<std::vector<T>> if either one or both of the tasks are of type task<std::vector<T>>. /// /// /// If one of the tasks is canceled or throws an exception, the returned task will complete early, in the canceled state, and the exception, /// if one is encoutered, will be thrown if you call get() or wait() on that task. /// /// /**/ template task> operator&&(const task<_ReturnType> & _Lhs, const task> & _Rhs) { return details::_WhenAllVectorAndValue(_Rhs, _Lhs, false); } /// /// Creates a task that will complete succesfully when both of the tasks supplied as arguments complete successfully. /// /// /// The type of the returned task. /// /// /// The first task to combine into the resulting task. /// /// /// The second task to combine into the resulting task. /// /// /// A task that completes successfully when both of the input tasks have completed successfully. If the input tasks are of type T, /// the output of this function will be a task<std::vector<T>>. If the input tasks are of type void the output /// task will also be a task<void>. /// To allow for a construct of the sort taskA && taskB && taskC, which are combined in pairs, the && operator /// produces a task<std::vector<T>> if either one or both of the tasks are of type task<std::vector<T>>. /// /// /// If one of the tasks is canceled or throws an exception, the returned task will complete early, in the canceled state, and the exception, /// if one is encoutered, will be thrown if you call get() or wait() on that task. /// /// /**/ template task> operator&&(const task> & _Lhs, const task> & _Rhs) { task> _PTasks[2] = {_Lhs, _Rhs}; return when_all(_PTasks, _PTasks+2); } /// /// Creates a task that will complete succesfully when both of the tasks supplied as arguments complete successfully. /// /// /// The type of the returned task. /// /// /// The first task to combine into the resulting task. /// /// /// The second task to combine into the resulting task. /// /// /// A task that completes successfully when both of the input tasks have completed successfully. If the input tasks are of type T, /// the output of this function will be a task<std::vector<T>>. If the input tasks are of type void the output /// task will also be a task<void>. /// To allow for a construct of the sort taskA && taskB && taskC, which are combined in pairs, the && operator /// produces a task<std::vector<T>> if either one or both of the tasks are of type task<std::vector<T>>. /// /// /// If one of the tasks is canceled or throws an exception, the returned task will complete early, in the canceled state, and the exception, /// if one is encoutered, will be thrown if you call get() or wait() on that task. /// /// /**/ inline task operator&&(const task & _Lhs, const task & _Rhs) { task _PTasks[2] = {_Lhs, _Rhs}; return when_all(_PTasks, _PTasks+2); } namespace details { // Helper struct for when_any operators to know when tasks have completed template struct _RunAnyParam { _RunAnyParam() : _M_exceptionRelatedToken(nullptr), _M_completeCount(0), _M_numTasks(0), _M_fHasExplicitToken(false) { } ~_RunAnyParam() { if (_CancellationTokenState::_IsValid(_M_exceptionRelatedToken)) _M_exceptionRelatedToken->_Release(); } task_completion_event<_CompletionType> _M_Completed; cancellation_token_source _M_cancellationSource; _CancellationTokenState * _M_exceptionRelatedToken; atomic_size_t _M_completeCount; size_t _M_numTasks; bool _M_fHasExplicitToken; }; template void _WhenAnyContinuationWrapper(_RunAnyParam<_CompletionType> * _PParam, const _Function & _Func, task<_TaskType>& _Task) { bool _IsTokenCancled = !_PParam->_M_fHasExplicitToken && _Task._GetImpl()->_M_pTokenState != _CancellationTokenState::_None() && _Task._GetImpl()->_M_pTokenState->_IsCanceled(); if (_Task._GetImpl()->_IsCompleted() && !_IsTokenCancled) { _Func(); if (atomic_increment(_PParam->_M_completeCount) == _PParam->_M_numTasks) { delete _PParam; } } else { _ASSERTE(_Task._GetImpl()->_IsCanceled() || _IsTokenCancled); if (_Task._GetImpl()->_HasUserException() && !_IsTokenCancled) { if (_PParam->_M_Completed._StoreException(_Task._GetImpl()->_GetExceptionHolder())) { // This can only enter once. _PParam->_M_exceptionRelatedToken = _Task._GetImpl()->_M_pTokenState; _ASSERTE(_PParam->_M_exceptionRelatedToken); // Deref token will be done in the _PParam destructor. if (_PParam->_M_exceptionRelatedToken != _CancellationTokenState::_None()) { _PParam->_M_exceptionRelatedToken->_Reference(); } } } if (atomic_increment(_PParam->_M_completeCount) == _PParam->_M_numTasks) { // If no one has be completed so far, we need to make some final cancellation decision. if (!_PParam->_M_Completed._IsTriggered()) { // If we already explicit token, we can skip the token join part. if (!_PParam->_M_fHasExplicitToken) { if (_PParam->_M_exceptionRelatedToken) { _JoinAllTokens_Add(_PParam->_M_cancellationSource, _PParam->_M_exceptionRelatedToken); } else { // If haven't captured any exception token yet, there was no exception for all those tasks, // so just pick a random token (current one) for normal cancellation. _JoinAllTokens_Add(_PParam->_M_cancellationSource, _Task._GetImpl()->_M_pTokenState); } } // Do exception cancellation or normal cancellation based on whether it has stored exception. _PParam->_M_Completed._Cancel(); } delete _PParam; } } } template struct _WhenAnyImpl { static task> _Perform(const task_options& _TaskOptions, _Iterator _Begin, _Iterator _End) { if( _Begin == _End ) { throw invalid_operation("when_any(begin, end) cannot be called on an empty container."); } _CancellationTokenState *_PTokenState = _TaskOptions.has_cancellation_token() ? _TaskOptions.get_cancellation_token()._GetImplValue() : nullptr; auto _PParam = new _RunAnyParam, _CancellationTokenState *>>(); if (_PTokenState) { _JoinAllTokens_Add(_PParam->_M_cancellationSource, _PTokenState); _PParam->_M_fHasExplicitToken = true; } task_options _Options(_TaskOptions); _Options.set_cancellation_token(_PParam->_M_cancellationSource.get_token()); task, _CancellationTokenState *>> _Any_tasks_completed(_PParam->_M_Completed, _Options); // Keep a copy ref to the token source auto _CancellationSource = _PParam->_M_cancellationSource; _PParam->_M_numTasks = static_cast(std::distance(_Begin, _End)); size_t _Index = 0; for (auto _PTask = _Begin; _PTask != _End; ++_PTask) { if (_PTask->is_apartment_aware()) { _Any_tasks_completed._SetAsync(); } _PTask->_Then([_PParam, _Index](task<_ElementType> _ResultTask) { auto _PParamCopy = _PParam; // Dev10 auto _IndexCopy = _Index; // Dev10 auto _Func = [&_ResultTask, _PParamCopy, _IndexCopy]() { _PParamCopy->_M_Completed.set(std::make_pair(std::make_pair(_ResultTask._GetImpl()->_GetResult(), _IndexCopy), _ResultTask._GetImpl()->_M_pTokenState)); }; _WhenAnyContinuationWrapper(_PParam, _Func, _ResultTask); }, _CancellationTokenState::_None()); _Index++; } // All _Any_tasks_completed._SetAsync() must be finished before this return continuation task being created. return _Any_tasks_completed._Then([=](std::pair, _CancellationTokenState *> _Result) -> std::pair<_ElementType, size_t> { _ASSERTE(_Result.second); if (!_PTokenState) { _JoinAllTokens_Add(_CancellationSource, _Result.second); } return _Result.first; }, nullptr); } }; template struct _WhenAnyImpl { static task _Perform(const task_options& _TaskOptions, _Iterator _Begin, _Iterator _End) { if( _Begin == _End ) { throw invalid_operation("when_any(begin, end) cannot be called on an empty container."); } _CancellationTokenState *_PTokenState = _TaskOptions.has_cancellation_token() ? _TaskOptions.get_cancellation_token()._GetImplValue() : nullptr; auto _PParam = new _RunAnyParam>(); if (_PTokenState) { _JoinAllTokens_Add(_PParam->_M_cancellationSource, _PTokenState); _PParam->_M_fHasExplicitToken = true; } task_options _Options(_TaskOptions); _Options.set_cancellation_token(_PParam->_M_cancellationSource.get_token()); task> _Any_tasks_completed(_PParam->_M_Completed, _Options); // Keep a copy ref to the token source auto _CancellationSource = _PParam->_M_cancellationSource; _PParam->_M_numTasks = static_cast(std::distance(_Begin, _End)); size_t _Index = 0; for (auto _PTask = _Begin; _PTask != _End; ++_PTask) { if (_PTask->is_apartment_aware()) { _Any_tasks_completed._SetAsync(); } _PTask->_Then([_PParam, _Index](task _ResultTask) { auto _PParamCopy = _PParam; // Dev10 auto _IndexCopy = _Index; // Dev10 auto _Func = [&_ResultTask, _PParamCopy, _IndexCopy]() { _PParamCopy->_M_Completed.set(std::make_pair(_IndexCopy, _ResultTask._GetImpl()->_M_pTokenState)); }; _WhenAnyContinuationWrapper(_PParam, _Func, _ResultTask); }, _CancellationTokenState::_None()); _Index++; } // All _Any_tasks_completed._SetAsync() must be finished before this return continuation task being created. return _Any_tasks_completed._Then([=](std::pair _Result) -> size_t { _ASSERTE(_Result.second); if (!_PTokenState) { _JoinAllTokens_Add(_CancellationSource, _Result.second); } return _Result.first; }, nullptr); } }; } // namespace details /// /// Creates a task that will complete successfully when any of the tasks supplied as arguments completes successfully. /// /// /// The type of the input iterator. /// /// /// The position of the first element in the range of elements to be combined into the resulting task. /// /// /// The position of the first element beyond the range of elements to be combined into the resulting task. /// /// /// A task that completes successfully when any one of the input tasks has completed successfully. If the input tasks are of type T, /// the output of this function will be a task<std::pair<T, size_t>>>, where the first element of the pair is the result /// of the completing task, and the second element is the index of the task that finished. If the input tasks are of type void /// the output is a task<size_t>, where the result is the index of the completing task. /// /// /**/ template auto when_any(_Iterator _Begin, _Iterator _End, const task_options& _TaskOptions = task_options()) -> decltype (details::_WhenAnyImpl::value_type::result_type, _Iterator>::_Perform(_TaskOptions, _Begin, _End)) { typedef typename std::iterator_traits<_Iterator>::value_type::result_type _ElementType; return details::_WhenAnyImpl<_ElementType, _Iterator>::_Perform(_TaskOptions, _Begin, _End); } /// /// Creates a task that will complete successfully when any of the tasks supplied as arguments completes successfully. /// /// /// The type of the input iterator. /// /// /// The position of the first element in the range of elements to be combined into the resulting task. /// /// /// The position of the first element beyond the range of elements to be combined into the resulting task. /// /// /// The cancellation token which controls cancellation of the returned task. If you do not provide a cancellation token, the resulting /// task will receive the cancellation token of the task that causes it to complete. /// /// /// A task that completes successfully when any one of the input tasks has completed successfully. If the input tasks are of type T, /// the output of this function will be a task<std::pair<T, size_t>>>, where the first element of the pair is the result /// of the completing task, and the second element is the index of the task that finished. If the input tasks are of type void /// the output is a task<size_t>, where the result is the index of the completing task. /// /// /**/ template auto when_any(_Iterator _Begin, _Iterator _End, cancellation_token _CancellationToken) -> decltype (details::_WhenAnyImpl::value_type::result_type, _Iterator>::_Perform(_CancellationToken._GetImplValue(), _Begin, _End)) { typedef typename std::iterator_traits<_Iterator>::value_type::result_type _ElementType; return details::_WhenAnyImpl<_ElementType, _Iterator>::_Perform(_CancellationToken._GetImplValue(), _Begin, _End); } /// /// Creates a task that will complete successfully when either of the tasks supplied as arguments completes successfully. /// /// /// The type of the returned task. /// /// /// The first task to combine into the resulting task. /// /// /// The second task to combine into the resulting task. /// /// /// A task that completes sucessfully when either of the input tasks has completed successfully. If the input tasks are of type T, /// the output of this function will be a task<std::vector<T>. If the input tasks are of type void the output task /// will also be a task<void>. /// To allow for a construct of the sort taskA || taskB && taskC, which are combined in pairs, with && taking precedence /// over ||, the operator|| produces a task<std::vector<T>> if one of the tasks is of type task<std::vector<T>> /// and the other one is of type task<T>. /// /// /// If both of the tasks are canceled or throw exceptions, the returned task will complete in the canceled state, and one of the exceptions, /// if any are encountered, will be thrown when you call get() or wait() on that task. /// /// /**/ template task<_ReturnType> operator||(const task<_ReturnType> & _Lhs, const task<_ReturnType> & _Rhs) { auto _PParam = new details::_RunAnyParam>(); task> _Any_tasks_completed(_PParam->_M_Completed, _PParam->_M_cancellationSource.get_token()); // Chain the return continuation task here to ensure it will get inline execution when _M_Completed.set is called, // So that _PParam can be used before it getting deleted. auto _ReturnTask = _Any_tasks_completed._Then([=](std::pair<_ReturnType, size_t> _Ret) -> _ReturnType { _ASSERTE(_Ret.second); _JoinAllTokens_Add(_PParam->_M_cancellationSource, reinterpret_cast(_Ret.second)); return _Ret.first; }, nullptr); if (_Lhs.is_apartment_aware() || _Rhs.is_apartment_aware()) { _ReturnTask._SetAsync(); } _PParam->_M_numTasks = 2; auto _Continuation = [_PParam](task<_ReturnType> _ResultTask) { // Dev10 compiler bug auto _PParamCopy = _PParam; auto _Func = [&_ResultTask, _PParamCopy]() { _PParamCopy->_M_Completed.set(std::make_pair(_ResultTask._GetImpl()->_GetResult(), reinterpret_cast(_ResultTask._GetImpl()->_M_pTokenState))); }; _WhenAnyContinuationWrapper(_PParam, _Func, _ResultTask); }; _Lhs._Then(_Continuation, details::_CancellationTokenState::_None()); _Rhs._Then(_Continuation, details::_CancellationTokenState::_None()); return _ReturnTask; } /// /// Creates a task that will complete successfully when any of the tasks supplied as arguments completes successfully. /// /// /// The type of the returned task. /// /// /// The first task to combine into the resulting task. /// /// /// The second task to combine into the resulting task. /// /// /// A task that completes sucessfully when either of the input tasks has completed successfully. If the input tasks are of type T, /// the output of this function will be a task<std::vector<T>. If the input tasks are of type void the output task /// will also be a task<void>. /// To allow for a construct of the sort taskA || taskB && taskC, which are combined in pairs, with && taking precedence /// over ||, the operator|| produces a task<std::vector<T>> if one of the tasks is of type task<std::vector<T>> /// and the other one is of type task<T>. /// /// /// If both of the tasks are canceled or throw exceptions, the returned task will complete in the canceled state, and one of the exceptions, /// if any are encountered, will be thrown when you call get() or wait() on that task. /// /// /**/ template task> operator||(const task> & _Lhs, const task<_ReturnType> & _Rhs) { auto _PParam = new details::_RunAnyParam, details::_CancellationTokenState *>>(); task, details::_CancellationTokenState *>> _Any_tasks_completed(_PParam->_M_Completed, _PParam->_M_cancellationSource.get_token()); // Chain the return continuation task here to ensure it will get inline execution when _M_Completed.set is called, // So that _PParam can be used before it getting deleted. auto _ReturnTask = _Any_tasks_completed._Then([=](std::pair, details::_CancellationTokenState *> _Ret) -> std::vector<_ReturnType> { _ASSERTE(_Ret.second); _JoinAllTokens_Add(_PParam->_M_cancellationSource, _Ret.second); return _Ret.first; }, nullptr); if (_Lhs.is_apartment_aware() || _Rhs.is_apartment_aware()) { _ReturnTask._SetAsync(); } _PParam->_M_numTasks = 2; _Lhs._Then([_PParam](task> _ResultTask) { // Dev10 compiler bug auto _PParamCopy = _PParam; auto _Func = [&_ResultTask, _PParamCopy]() { auto _Result = _ResultTask._GetImpl()->_GetResult(); _PParamCopy->_M_Completed.set(std::make_pair(_Result, _ResultTask._GetImpl()->_M_pTokenState)); }; _WhenAnyContinuationWrapper(_PParam, _Func, _ResultTask); }, details::_CancellationTokenState::_None()); _Rhs._Then([_PParam](task<_ReturnType> _ResultTask) { auto _PParamCopy = _PParam; auto _Func = [&_ResultTask, _PParamCopy]() { auto _Result = _ResultTask._GetImpl()->_GetResult(); std::vector<_ReturnType> _Vec; _Vec.push_back(_Result); _PParamCopy->_M_Completed.set(std::make_pair(_Vec, _ResultTask._GetImpl()->_M_pTokenState)); }; _WhenAnyContinuationWrapper(_PParam, _Func, _ResultTask); }, details::_CancellationTokenState::_None()); return _ReturnTask; } /// /// Creates a task that will complete successfully when any of the tasks supplied as arguments completes successfully. /// /// /// The type of the returned task. /// /// /// The first task to combine into the resulting task. /// /// /// The second task to combine into the resulting task. /// /// /// A task that completes sucessfully when either of the input tasks has completed successfully. If the input tasks are of type T, /// the output of this function will be a task<std::vector<T>. If the input tasks are of type void the output task /// will also be a task<void>. /// To allow for a construct of the sort taskA || taskB && taskC, which are combined in pairs, with && taking precedence /// over ||, the operator|| produces a task<std::vector<T>> if one of the tasks is of type task<std::vector<T>> /// and the other one is of type task<T>. /// /// /// If both of the tasks are canceled or throw exceptions, the returned task will complete in the canceled state, and one of the exceptions, /// if any are encountered, will be thrown when you call get() or wait() on that task. /// /// /**/ template task> operator||(const task<_ReturnType> & _Lhs, const task> & _Rhs) { return _Rhs || _Lhs; } /// /// Creates a task that will complete successfully when any of the tasks supplied as arguments completes successfully. /// /// /// The type of the returned task. /// /// /// The first task to combine into the resulting task. /// /// /// The second task to combine into the resulting task. /// /// /// A task that completes sucessfully when either of the input tasks has completed successfully. If the input tasks are of type T, /// the output of this function will be a task<std::vector<T>. If the input tasks are of type void the output task /// will also be a task<void>. /// To allow for a construct of the sort taskA || taskB && taskC, which are combined in pairs, with && taking precedence /// over ||, the operator|| produces a task<std::vector<T>> if one of the tasks is of type task<std::vector<T>> /// and the other one is of type task<T>. /// /// /// If both of the tasks are canceled or throw exceptions, the returned task will complete in the canceled state, and one of the exceptions, /// if any are encountered, will be thrown when you call get() or wait() on that task. /// /// /**/ inline task operator||(const task & _Lhs, const task & _Rhs) { auto _PParam = new details::_RunAnyParam>(); task> _Any_task_completed(_PParam->_M_Completed, _PParam->_M_cancellationSource.get_token()); // Chain the return continuation task here to ensure it will get inline execution when _M_Completed.set is called, // So that _PParam can be used before it getting deleted. auto _ReturnTask = _Any_task_completed._Then([=](std::pair _Ret) { _ASSERTE(_Ret.second); details::_JoinAllTokens_Add(_PParam->_M_cancellationSource, _Ret.second); }, nullptr); if (_Lhs.is_apartment_aware() || _Rhs.is_apartment_aware()) { _ReturnTask._SetAsync(); } _PParam->_M_numTasks = 2; auto _Continuation = [_PParam](task _ResultTask) mutable { // Dev10 compiler needs this. auto _PParam1 = _PParam; auto _Func = [&_ResultTask, _PParam1]() { _PParam1->_M_Completed.set(std::make_pair(details::_Unit_type(), _ResultTask._GetImpl()->_M_pTokenState)); }; _WhenAnyContinuationWrapper(_PParam, _Func, _ResultTask); }; _Lhs._Then(_Continuation, details::_CancellationTokenState::_None()); _Rhs._Then(_Continuation, details::_CancellationTokenState::_None()); return _ReturnTask; } template task<_Ty> task_from_result(_Ty _Param, const task_options& _TaskOptions = task_options()) { task_completion_event<_Ty> _Tce; _Tce.set(_Param); return create_task(_Tce, _TaskOptions); } // Work around VS 2010 compiler bug #if _MSC_VER == 1600 inline task task_from_result(bool _Param) { task_completion_event _Tce; _Tce.set(_Param); return create_task(_Tce, task_options()); } #endif inline task task_from_result(const task_options& _TaskOptions = task_options()) { task_completion_event _Tce; _Tce.set(); return create_task(_Tce, _TaskOptions); } template task<_TaskType> task_from_exception(_ExType _Exception, const task_options& _TaskOptions = task_options()) { task_completion_event<_TaskType> _Tce; _Tce.set_exception(_Exception); return create_task(_Tce, _TaskOptions); } namespace details { /// /// A convenient extension to Concurrency: loop until a condition is no longer met /// /// /// A function representing the body of the loop. It will be invoked at least once and /// then repetitively as long as it returns true. /// inline task do_while(std::function(void)> func) { task first = func(); return first.then([=](bool guard) -> task { if (guard) return do_while(func); else return first; }); } } // namespace details } // namespace Concurrency #pragma pop_macro("new") #if defined(_MSC_VER) #pragma warning(pop) #endif #pragma pack(pop) #endif // (defined(_MSC_VER) && (_MSC_VER >= 1800)) #ifndef _CONCRT_H #ifndef _LWRCASE_CNCRRNCY #define _LWRCASE_CNCRRNCY // Note to reader: we're using lower-case namespace names everywhere, but the 'Concurrency' namespace // is capitalized for historical reasons. The alias let's us pretend that style issue doesn't exist. namespace Concurrency {} namespace concurrency = Concurrency; #endif #endif #endif // _PPLXTASKS_H #endif // !XSAPI_NO_PPL ================================================ FILE: Include/cpprestinclude/pplx/pplxtasks.h ================================================ #if !XSAPI_NO_PPL /*** * ==++== * * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * ==--== * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ * * Parallel Patterns Library - PPLx Tasks * * For the latest on this and related APIs, please see http://casablanca.codeplex.com. * * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ****/ #pragma once #if (defined(_MSC_VER) && (_MSC_VER >= 1800)) #include "pplxtasks.140.h" #else #include "pplxtasks.110.h" #endif #endif ================================================ FILE: Include/cpprestinclude/pplx/pplxwin.h ================================================ #if !XSAPI_NO_PPL /*** * ==++== * * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * ==--== * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ * * Windows specific pplx implementations * * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk * * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ****/ #pragma once #if !defined(_WIN32) || _MSC_VER < 1800 || CPPREST_FORCE_PPLX #include "cpprest/details/cpprest_compat.h" #include "pplx/pplxinterface.h" namespace pplx { namespace details { namespace platform { /// /// Returns a unique identifier for the execution thread where this routine in invoked /// _PPLXIMP long __cdecl GetCurrentThreadId(); /// /// Yields the execution of the current execution thread - typically when spin-waiting /// _PPLXIMP void __cdecl YieldExecution(); /// /// Captures the callstack /// __declspec(noinline) _PPLXIMP size_t __cdecl CaptureCallstack(void **, size_t, size_t); #if defined(__cplusplus_winrt) /// // Internal API which retrieves the next async id. /// _PPLXIMP unsigned int __cdecl GetNextAsyncId(); #endif } /// /// Manual reset event /// class event_impl { public: static const unsigned int timeout_infinite = 0xFFFFFFFF; _PPLXIMP event_impl(); _PPLXIMP ~event_impl(); _PPLXIMP void set(); _PPLXIMP void reset(); _PPLXIMP unsigned int wait(unsigned int timeout); unsigned int wait() { return wait(event_impl::timeout_infinite); } private: // Windows events void * _M_impl; event_impl(const event_impl&); // no copy constructor event_impl const & operator=(const event_impl&); // no assignment operator }; /// /// Mutex - lock for mutual exclusion /// class critical_section_impl { public: _PPLXIMP critical_section_impl(); _PPLXIMP ~critical_section_impl(); _PPLXIMP void lock(); _PPLXIMP void unlock(); private: typedef void * _PPLX_BUFFER; // Windows critical section _PPLX_BUFFER _M_impl[8]; critical_section_impl(const critical_section_impl&); // no copy constructor critical_section_impl const & operator=(const critical_section_impl&); // no assignment operator }; #if _WIN32_WINNT >= _WIN32_WINNT_VISTA /// /// Reader writer lock /// class reader_writer_lock_impl { public: class scoped_lock_read { public: explicit scoped_lock_read(reader_writer_lock_impl &_Reader_writer_lock) : _M_reader_writer_lock(_Reader_writer_lock) { _M_reader_writer_lock.lock_read(); } ~scoped_lock_read() { _M_reader_writer_lock.unlock(); } private: reader_writer_lock_impl& _M_reader_writer_lock; scoped_lock_read(const scoped_lock_read&); // no copy constructor scoped_lock_read const & operator=(const scoped_lock_read&); // no assignment operator }; _PPLXIMP reader_writer_lock_impl(); _PPLXIMP void lock(); _PPLXIMP void lock_read(); _PPLXIMP void unlock(); private: // Windows slim reader writer lock void * _M_impl; // Slim reader writer lock doesn't have a general 'unlock' method. // We need to track how it was acquired and release accordingly. // true - lock exclusive // false - lock shared bool m_locked_exclusive; }; #endif // _WIN32_WINNT >= _WIN32_WINNT_VISTA /// /// Recursive mutex /// class recursive_lock_impl { public: recursive_lock_impl() : _M_owner(-1), _M_recursionCount(0) { } ~recursive_lock_impl() { _ASSERTE(_M_owner == -1); _ASSERTE(_M_recursionCount == 0); } void recursive_lock_impl::lock() { auto id = ::pplx::details::platform::GetCurrentThreadId(); if ( _M_owner == id ) { _M_recursionCount++; } else { _M_cs.lock(); _M_owner = id; _M_recursionCount = 1; } } void recursive_lock_impl::unlock() { _ASSERTE(_M_owner == ::pplx::details::platform::GetCurrentThreadId()); _ASSERTE(_M_recursionCount >= 1); _M_recursionCount--; if ( _M_recursionCount == 0 ) { _M_owner = -1; _M_cs.unlock(); } } private: pplx::details::critical_section_impl _M_cs; long _M_recursionCount; volatile long _M_owner; }; class windows_scheduler : public pplx::scheduler_interface { public: _PPLXIMP virtual void schedule( TaskProc_t proc, _In_ void* param); }; } // namespace details /// /// A generic RAII wrapper for locks that implement the critical_section interface /// std::lock_guard /// template class scoped_lock { public: explicit scoped_lock(_Lock& _Critical_section) : _M_critical_section(_Critical_section) { _M_critical_section.lock(); } ~scoped_lock() { _M_critical_section.unlock(); } private: _Lock& _M_critical_section; scoped_lock(const scoped_lock&); // no copy constructor scoped_lock const & operator=(const scoped_lock&); // no assignment operator }; // The extensibility namespace contains the type definitions that are used internally namespace extensibility { typedef ::pplx::details::event_impl event_t; typedef ::pplx::details::critical_section_impl critical_section_t; typedef scoped_lock scoped_critical_section_t; #if _WIN32_WINNT >= _WIN32_WINNT_VISTA typedef ::pplx::details::reader_writer_lock_impl reader_writer_lock_t; typedef scoped_lock scoped_rw_lock_t; typedef reader_writer_lock_t::scoped_lock_read scoped_read_lock_t; #endif // _WIN32_WINNT >= _WIN32_WINNT_VISTA typedef ::pplx::details::recursive_lock_impl recursive_lock_t; typedef scoped_lock scoped_recursive_lock_t; } /// /// Default scheduler type /// typedef details::windows_scheduler default_scheduler_t; namespace details { /// /// Terminate the process due to unhandled exception /// #ifndef _REPORT_PPLTASK_UNOBSERVED_EXCEPTION #define _REPORT_PPLTASK_UNOBSERVED_EXCEPTION() do { \ __debugbreak(); \ std::terminate(); \ } while(false) #endif // _REPORT_PPLTASK_UNOBSERVED_EXCEPTION } // namespace details } // namespace pplx #endif #endif // !XSAPI_NO_PPL ================================================ FILE: Include/cpprestinclude/pplx/threadpool.h ================================================ /*** * ==++== * * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * ==--== * * Simple Linux implementation of a static thread pool. * * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk * * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ ***/ #pragma once #if defined(__clang__) #include #endif #include #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wconversion" #pragma clang diagnostic ignored "-Wunreachable-code" #pragma clang diagnostic ignored "-Winfinite-recursion" #endif #ifdef ASIO_STANDALONE #include #endif #if defined(__clang__) #pragma clang diagnostic pop #endif #if (defined(ANDROID) || defined(__ANDROID__)) #include #include #include "pplx/pplx.h" #include "asio/io_service.hpp" #endif namespace crossplat { #if (defined(ANDROID) || defined(__ANDROID__)) // IDEA: Break this section into a separate android/jni header extern std::atomic JVM; JNIEnv* get_jvm_env(); struct java_local_ref_deleter { void operator()(jobject lref) const { crossplat::get_jvm_env()->DeleteLocalRef(lref); } }; template using java_local_ref = std::unique_ptr::type, java_local_ref_deleter>; #endif class threadpool { public: threadpool(size_t n) : m_service(n), m_work(m_service) { for (size_t i = 0; i < n; i++) add_thread(); } static threadpool& shared_instance(); ~threadpool() { m_service.stop(); for (auto iter = m_threads.begin(); iter != m_threads.end(); ++iter) { pthread_t t = *iter; void* res; pthread_join(t, &res); } } template void schedule(T task) { m_service.post(task); } asio::io_service& service() { return m_service; } private: struct _cancel_thread { }; void add_thread() { pthread_t t; auto result = pthread_create(&t, nullptr, &thread_start, this); if (result == 0) m_threads.push_back(t); } void remove_thread() { schedule([]() -> void { throw _cancel_thread(); }); } #if (defined(ANDROID) || defined(__ANDROID__)) static void detach_from_java(void*) { JVM.load()->DetachCurrentThread(); } #endif static void* thread_start(void *arg) { #if (defined(ANDROID) || defined(__ANDROID__)) // Calling get_jvm_env() here forces the thread to be attached. get_jvm_env(); pthread_cleanup_push(detach_from_java, nullptr); #endif threadpool* _this = reinterpret_cast(arg); try { _this->m_service.run(); } catch (const _cancel_thread&) { // thread was cancelled } catch (...) { // Something bad happened #if (defined(ANDROID) || defined(__ANDROID__)) // Reach into the depths of the 'droid! // NOTE: Uses internals of the bionic library // Written against android ndk r9d, 7/26/2014 __pthread_cleanup_pop(&__cleanup, true); throw; #endif } #if (defined(ANDROID) || defined(__ANDROID__)) pthread_cleanup_pop(true); #endif return arg; } std::vector m_threads; asio::io_service m_service; asio::io_service::work m_work; }; } ================================================ FILE: Include/xsapi-c/achievements_c.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #if !defined(__cplusplus) #error C++11 required #endif #pragma once extern "C" { /// /// Enumeration values that indicate the achievement type. /// /// /// enum class XblAchievementType : uint32_t { /// /// The achievement type is unknown. /// Unknown, /// /// Gets all achievements regardless of type. /// All, /// /// A persistent achievement that may be unlocked at any time. /// Persistent achievements can give Gamerscore as a reward. /// Persistent, /// /// A challenge achievement that may only be unlocked within a certain time period. /// Challenge achievements can't give Gamerscore as a reward. /// Challenge }; /// /// Enumeration values that indicate the achievement sort order. /// /// enum class XblAchievementOrderBy : uint32_t { /// /// Default order does not guarantee sort order. /// DefaultOrder, /// /// Sort by title id. /// TitleId, /// /// Sort by achievement unlock time. /// UnlockTime }; /// /// Enumeration values that indicate the state of a player's progress towards unlocking an achievement. /// /// enum class XblAchievementProgressState : uint32_t { /// /// Achievement progress is unknown. /// Unknown, /// /// Achievement has been earned. /// Achieved, /// /// Achievement progress has not been started. /// NotStarted, /// /// Achievement progress has started. /// InProgress }; /// /// Enumeration values that indicate the media asset type associated with the achievement. /// /// enum class XblAchievementMediaAssetType : uint32_t { /// /// The media asset type is unknown. /// Unknown, /// /// An icon media asset. /// Icon, /// /// An art media asset. /// Art }; /// /// Enumeration values that indicate the participation type for an achievement. /// /// enum class XblAchievementParticipationType : uint32_t { /// /// The participation type is unknown. /// Unknown, /// /// An achievement that can be earned as an individual participant. /// Individual, /// /// An achievement that can be earned as a group participant. /// Group }; /// /// Enumeration values that indicate the reward type for an achievement. /// /// enum class XblAchievementRewardType : uint32_t { /// /// The reward type is unknown. /// Unknown, /// /// A Gamerscore reward. /// Gamerscore, /// /// An in-app reward, defined and delivered by the title. /// InApp, /// /// A digital art reward. /// Art }; /// /// Enumeration values that indicate the rarity category for an achievement. /// enum class XblAchievementRarityCategory : uint32_t { /// /// The rarity is incalculable (e.g. no one has played the title yet, denominator is 0). /// Unset = 0, /// /// The number of global users that have unlocked this achievement is between 0 - 10.9. /// Rare = 1, /// /// The number of global users that have unlocked this achievement is between 11.0 - 100.0. /// Common = 2 }; /// /// Represents the association between a title and achievements. /// /// typedef struct XblAchievementTitleAssociation { /// /// The UTF-8 encoded localized name of the title. /// _Field_z_ const char* name; /// /// The title ID. /// uint32_t titleId; } XblAchievementTitleAssociation; /// /// Represents requirements for unlocking the achievement. /// /// typedef struct XblAchievementRequirement { /// /// The UTF-8 encoded achievement requirement ID. /// _Field_z_ const char* id; /// /// A UTF-8 encoded value that indicates the current progress of the player towards meeting the requirement. /// _Field_z_ const char* currentProgressValue; /// /// The UTF-8 encoded target progress value that the player must reach in order to meet /// the requirement. /// _Field_z_ const char* targetProgressValue; } XblAchievementRequirement; /// /// Represents progress details about the achievement, including requirements. /// /// typedef struct XblAchievementProgression { /// /// The actions and conditions that are required to unlock the achievement. /// XblAchievementRequirement* requirements; /// /// The size of **requirements**. /// size_t requirementsCount; /// /// The timestamp when the achievement was first unlocked. /// time_t timeUnlocked; } XblAchievementProgression; /// /// Represents an interval of time during which an achievement can be unlocked. /// /// /// This class is only used when the achievement_type enumeration is set to challenge. /// /// typedef struct XblAchievementTimeWindow { /// /// The start date and time of the achievement time window. /// time_t startDate; /// /// The end date and time of the achievement time window. /// time_t endDate; } XblAchievementTimeWindow; /// /// Represents a media asset for an achievement. /// /// /// typedef struct XblAchievementMediaAsset { /// /// The UTF-8 encoded name of the media asset, such as "tile01". /// _Field_z_ const char* name; /// /// The type of media asset. /// XblAchievementMediaAssetType mediaAssetType; /// /// The UTF-8 encoded URL of the media asset. /// _Field_z_ const char* url; } XblAchievementMediaAsset; /// /// Represents a reward that is associated with the achievement. /// /// typedef struct XblAchievementReward { /// /// The UTF-8 encoded localized reward name. /// _Field_z_ const char* name; /// /// The UTF-8 encoded description of the reward. /// _Field_z_ const char* description; /// /// The UTF-8 encoded title-defined reward value (data type and content varies by reward type). /// _Field_z_ const char* value; /// /// The reward type. /// XblAchievementRewardType rewardType; /// /// The UTF-8 encoded property type of the reward value string. /// _Field_z_ const char* valueType; /// /// The media asset associated with the reward. /// If the reward type is gamerscore, this will be nullptr. /// If the reward type is in_app, this will be a media asset. /// If the reward type is art, this may be a media asset or nullptr. /// XblAchievementMediaAsset* mediaAsset; } XblAchievementReward; /// /// An entry for XblAchievementProgressChangeEventArgs representing a progress update for a single achievement. /// typedef struct XblAchievementProgressChangeEntry { /// /// The UTF-8 encoded achievement ID. Represents a uint. /// _Field_z_ const char* achievementId; /// /// The state of a user's progress towards the earning of the achievement. /// XblAchievementProgressState progressState; /// /// The progression object containing progress details about the achievement, /// including requirements. /// XblAchievementProgression progression; } XblAchievementProgressChangeEntry; /// /// Event arguments for achievement progress changes. /// typedef struct XblAchievementProgressChangeEventArgs { /// /// Array of objects detailing the progress changes of each achievement that has been updated. /// XblAchievementProgressChangeEntry* updatedAchievementEntries; /// /// The count of **updatedAchievementEntries**. /// size_t entryCount; } XblAchievementProgressChangeEventArgs; /// /// Represents an achievement, a system-wide mechanism for directing and /// rewarding users' in-game actions consistently across all games. /// /// typedef struct XblAchievement { /// /// The UTF-8 encoded achievement ID. Represents a uint. /// _Field_z_ const char* id; /// /// The Service Configuration ID (SCID) that is associated with the achievement. The SCID is considered case sensitive so paste it directly from the Partner Center /// _Field_z_ const char* serviceConfigurationId; /// /// The UTF-8 encoded localized achievement name. /// _Field_z_ const char* name; /// /// The game/app titles associated with the achievement. /// XblAchievementTitleAssociation* titleAssociations; /// /// The size of **titleAssociations**. /// size_t titleAssociationsCount; /// /// The state of a user's progress towards the earning of the achievement. /// XblAchievementProgressState progressState; /// /// The progression object containing progress details about the achievement, including requirements. /// XblAchievementProgression progression; /// /// The media assets associated with the achievement, such as image IDs. /// XblAchievementMediaAsset* mediaAssets; /// /// The size of **mediaAssets**. /// size_t mediaAssetsCount; /// /// The UTF-8 encoded collection of platforms that the achievement is available on. /// _Field_z_ const char** platformsAvailableOn; /// /// The size of **platformsAvailableOn**. /// size_t platformsAvailableOnCount; /// /// Whether or not the achievement is secret. /// bool isSecret; /// /// The UTF-8 encoded description of the unlocked achievement. /// _Field_z_ const char* unlockedDescription; /// /// The UTF-8 encoded description of the locked achievement. /// _Field_z_ const char* lockedDescription; /// /// The UTF-8 encoded product_id the achievement was released with. /// This is a globally unique identifier that may correspond to an application, downloadable content, etc. /// _Field_z_ const char* productId; /// /// The type of achievement, such as a challenge achievement. /// XblAchievementType type; /// /// The participation type for the achievement, such as group or individual. /// XblAchievementParticipationType participationType; /// /// The time window during which the achievement is available. Applies to Challenges. /// XblAchievementTimeWindow available; /// /// The collection of rewards that the player earns when the achievement is unlocked. /// XblAchievementReward* rewards; /// /// The size of **rewards**. /// size_t rewardsCount; /// /// The estimated time that the achievement takes to be earned. /// uint64_t estimatedUnlockTime; /// /// A UTF-8 encoded deep link for clients that enables the title to launch at a desired /// starting point for the achievement. /// _Field_z_ const char* deepLink; /// /// A value that indicates whether or not the achievement is revoked by enforcement. /// bool isRevoked; } XblAchievement; /// /// Represents an Achievement Unlock notification received from the notification service. /// typedef struct XblAchievementUnlockEvent { /// /// The name of the achievement in the locale of the DeviceEndpoint to which it's being sent (current char limit: 44). /// _Field_z_ const char* achievementName; /// /// The description of the achievement in the locale of the DeviceEndpoint to which it's being sent. /// _Field_z_ const char* achievementDescription; /// /// The URL to the image associated to the achievement (max length: 2048). /// _Field_z_ const char* achievementIcon; /// /// The UTF-8 encoded achievement ID. /// _Field_z_ const char* achievementId; /// /// The amount of gamerscore earned for unlocking the achievement (can be 0 - challenges cannot have gamerscore). /// uint64_t gamerscore; /// /// The base 10 ID of the title the achievement is defined for. /// uint32_t titleId; /// /// The person's Xbox user identifier. /// uint64_t xboxUserId; /// /// The deep link set on the achievement. /// _Field_z_ const char* deepLink; /// /// The ratio of the count of users who have unlocked the achievement / the total number unique users of that title expressed as a fractional value >= 0.0 and <= 1.0 rounded to 2 decimal places. /// float rarityPercentage; /// /// "Rare" or "Common" - where Rare achievements are those with a rarityPercentage <= 9% (or 0.9) and "Common" is everything else. (This string is not localized). /// XblAchievementRarityCategory rarityCategory; /// /// The timestamp when the achievement was first unlocked for this user. /// time_t timeUnlocked; } XblAchievementUnlockEvent; /// /// A handle to an achievement result. /// /// /// This handle is used by other APIs to get the achievement objects and to get the /// next page of achievements from the service if there is one. /// The handle must be closed using /// when the result is no longer needed. /// /// /// /// /// /// /// /// /// typedef struct XblAchievementsResult* XblAchievementsResultHandle; /// /// Get a list of XblAchievement objects. /// /// Achievement result handle. /// Pointer to an array of XblAchievement objects. /// The memory for the returned pointer will remain valid for the life of the /// XblAchievementsResultHandle object until it is closed. /// The count of objects in the returned array. /// HRESULT return code for this API operation. /// /// The returned array of XblAchievement objects is freed when all outstanding handles /// to the object have been closed with . /// STDAPI XblAchievementsResultGetAchievements( _In_ XblAchievementsResultHandle resultHandle, _Out_ const XblAchievement** achievements, _Out_ size_t* achievementsCount ) XBL_NOEXCEPT; /// /// Checks if there are more pages of achievements to retrieve from the service. /// /// Achievement result handle. /// Passes back true if there are more results to retrieve, false otherwise. /// HRESULT return code for this API operation. STDAPI XblAchievementsResultHasNext( _In_ XblAchievementsResultHandle resultHandle, _Out_ bool* hasNext ) XBL_NOEXCEPT; /// /// Gets the result of next page of achievements for a player of the specified title. /// /// Handle to the achievement result. /// The maximum number of items that the result can contain. /// Pass 0 to attempt to retrieve all items. /// Caller allocated AsyncBlock. /// HRESULT return code for this API operation. /// /// To get the result, call inside the AsyncBlock callback /// or after the AsyncBlock is complete. /// /// V2 GET /users/xuid({xuid})/achievements STDAPI XblAchievementsResultGetNextAsync( _In_ XblAchievementsResultHandle resultHandle, _In_ uint32_t maxItems, _In_ XAsyncBlock* async ) XBL_NOEXCEPT; /// /// Get XblAchievementsResultHandle from an XblAchievementsResultGetNextAsync call. /// /// The same AsyncBlock that passed to XblAchievementsResultGetNextAsync. /// /// Returns the next achievement result handle. /// Note that this is a separate handle than the one passed to the XblAchievementsResultGetNextAsync API. /// Each result handle must be closed separately. /// /// HRESULT return code for this API operation. STDAPI XblAchievementsResultGetNextResult( _In_ XAsyncBlock* async, _Out_ XblAchievementsResultHandle* result ) XBL_NOEXCEPT; /// /// Gets the first page of achievements for a player of the specified title. /// /// An xbox live context handle created with XblContextCreateHandle. /// The Xbox User ID of the player. /// The title ID. /// The achievement type to retrieve. /// Indicates whether to return unlocked achievements only. /// Controls how the list of achievements is ordered. /// The number of achievements to skip. /// The maximum number of achievements the result can contain. /// Pass 0 to attempt to retrieve all items. /// Caller allocated AsyncBlock. /// HRESULT return code for this API operation. /// /// To get the result, call inside the AsyncBlock callback /// or after the AsyncBlock is complete. /// /// V2 GET /users/xuid({xuid})/achievements STDAPI XblAchievementsGetAchievementsForTitleIdAsync( _In_ XblContextHandle xboxLiveContext, _In_ uint64_t xboxUserId, _In_ uint32_t titleId, _In_ XblAchievementType type, _In_ bool unlockedOnly, _In_ XblAchievementOrderBy orderBy, _In_ uint32_t skipItems, _In_ uint32_t maxItems, _In_ XAsyncBlock* async ) XBL_NOEXCEPT; /// /// Get XblAchievementsResultHandle from an XblAchievementsGetAchievementsForTitleIdAsync call. /// /// The same AsyncBlock that passed to XblAchievementsGetAchievementsForTitleIdAsync. /// Achievement result handle. /// HRESULT return code for this API operation. /// /// Use to get the list. /// STDAPI XblAchievementsGetAchievementsForTitleIdResult( _In_ XAsyncBlock* async, _Out_ XblAchievementsResultHandle* result ) XBL_NOEXCEPT; /// /// Allow achievement progress to be updated and achievements to be unlocked. /// /// An xbox live context handle created with XblContextCreateHandle. /// The Xbox User ID of the player. /// The UTF-8 encoded achievement ID as defined by XDP or Dev Center. /// The completion percentage of the achievement to indicate progress. /// Valid values are from 1 to 100. Set to 100 to unlock the achievement. /// Progress will be set by the server to the highest value sent /// Caller allocated AsyncBlock. /// HRESULT return code for this API operation. /// /// This API will work even when offline on PC and Xbox One. Offline updates will be /// posted by the system when connection is re-established even if the title isn't running. /// The result of the asynchronous operation can be obtained by calling /// inside the AsyncBlock callback or after the AsyncBlock is complete. /// /// If the achievement has already been unlocked or the progress value is less than or /// equal to what is currently recorded on the server, then XAsyncGetStatus() inside the callback /// will return HTTP_E_STATUS_NOT_MODIFIED (0x80190130L). /// /// V2 POST /users/xuid({xuid})/achievements/{scid}/update STDAPI XblAchievementsUpdateAchievementAsync( _In_ XblContextHandle xboxLiveContext, _In_ uint64_t xboxUserId, _In_z_ const char* achievementId, _In_ uint32_t percentComplete, _In_ XAsyncBlock* async ) XBL_NOEXCEPT; /// /// Allow achievement progress to be updated and achievements to be unlocked. /// /// An xbox live context handle created with XblContextCreateHandle. /// The Xbox User ID of the player. /// The title ID. /// The Service Configuration ID (SCID) for the title. The SCID is considered case sensitive so paste it directly from the Partner Center. /// The UTF-8 encoded achievement ID as defined by XDP or Dev Center. /// The completion percentage of the achievement to indicate progress. /// Valid values are from 1 to 100. Set to 100 to unlock the achievement. /// Progress will be set by the server to the highest value sent. /// Caller allocated AsyncBlock. /// HRESULT return code for this API operation. /// /// This API will work even when offline on PC and Xbox One. /// Offline updates will be posted by the system when connection is re-established even if the title isn't running. /// The result of the asynchronous operation can be obtained by calling /// inside the AsyncBlock callback or after the AsyncBlock is complete. /// If this unexpectedly fails, ensure the SCID is correctly in the XblInitArgs as it is case-sensitive. /// /// V2 POST /users/xuid({xuid})/achievements/{scid}/update STDAPI XblAchievementsUpdateAchievementForTitleIdAsync( _In_ XblContextHandle xboxLiveContext, _In_ uint64_t xboxUserId, _In_ const uint32_t titleId, _In_z_ const char* serviceConfigurationId, _In_z_ const char* achievementId, _In_ uint32_t percentComplete, _In_ XAsyncBlock* async ) XBL_NOEXCEPT; /// /// Gets an achievement for a player with a specific achievement ID. /// /// An xbox live context handle created with XblContextCreateHandle. /// The Xbox User ID of the player. /// The Service Configuration ID (SCID) for the title. The SCID is considered case sensitive so paste it directly from the Partner Center. /// The UTF-8 encoded unique identifier of the Achievement as defined by XDP or Dev Center. /// Caller allocated AsyncBlock. /// HRESULT return code for this API operation. /// /// If multiple achievements are required, use the batch API instead: /// To get the result, call inside the AsyncBlock callback /// or after the AsyncBlock is complete. /// /// V2 GET /users/xuid({xuid})/achievements/{scid}/{achievementId} STDAPI XblAchievementsGetAchievementAsync( _In_ XblContextHandle xboxLiveContext, _In_ uint64_t xboxUserId, _In_z_ const char* serviceConfigurationId, _In_z_ const char* achievementId, _In_ XAsyncBlock* async ) XBL_NOEXCEPT; /// /// Get the result handle from an XblAchievementsGetAchievementAsync call. /// /// The same AsyncBlock that passed to XblAchievementsGetAchievementAsync. /// The achievement result handle. /// This handle is used by other APIs to get the achievement objects /// and to get the next page of achievements from the service if there is one. /// The handle must be closed using when the result is no longer needed. /// /// HRESULT return code for this API operation. STDAPI XblAchievementsGetAchievementResult( _In_ XAsyncBlock* async, _Out_ XblAchievementsResultHandle* result ) XBL_NOEXCEPT; /// /// Duplicates a XblAchievementsResultHandle. /// /// The XblAchievementsResultHandle to duplicate. /// The duplicated handle. /// HRESULT return code for this API operation. STDAPI XblAchievementsResultDuplicateHandle( _In_ XblAchievementsResultHandle handle, _Out_ XblAchievementsResultHandle* duplicatedHandle ) XBL_NOEXCEPT; /// /// Closes the XblAchievementsResultHandle. /// /// The XblAchievementsResultHandle to close. /// /// /// When all outstanding handles have been closed, the memory associated with the achievement result will be freed. /// STDAPI_(void) XblAchievementsResultCloseHandle( _In_ XblAchievementsResultHandle handle ) XBL_NOEXCEPT; #if HC_PLATFORM == HC_PLATFORM_WIN32 || HC_PLATFORM_IS_EXTERNAL /// /// Handle for Function handling achievement unlock events. /// /// An achievement unlock event. /// Caller context to be passed the handler. /// /// The lifetime of the XblAchievementUnlockEvent object is limited to the callback. typedef void CALLBACK XblAchievementUnlockHandler( _In_ const XblAchievementUnlockEvent* args, _In_opt_ void* context ); /// /// Registers an event handler for achievement unlock notifications. /// /// Xbox live context for the local user. /// The callback function that receives notifications. /// Caller context to be passed the handler. /// An XblFunctionContext object that can be used to unregister the event handler. STDAPI_(XblFunctionContext) XblAchievementUnlockAddNotificationHandler( _In_ XblContextHandle xblContextHandle, _In_ XblAchievementUnlockHandler* handler, _In_opt_ void* handlerContext ) XBL_NOEXCEPT; /// /// Unregisters an event handler for achievement unlock notifications. /// /// Xbox live context for the local user. /// The XblFunctionContext that was returned when the event handler was registered. /// STDAPI_(void) XblAchievementUnlockRemoveNotificationHandler( _In_ XblContextHandle xblContextHandle, _In_ XblFunctionContext functionContext ) XBL_NOEXCEPT; #endif /// /// A callback invoked when a progress is made on an achievement. /// /// The arguments associated with the change in achievement state. /// The fields of the struct are only valid during the callback. /// Context provided by when the handler is added. /// /// /// For the callback to work properly, you must be subscribed to achievement progress changes for at least one user. /// typedef void CALLBACK XblAchievementsProgressChangeHandler( _In_ const XblAchievementProgressChangeEventArgs* eventArgs, _In_opt_ void* context ); /// /// Registers an event handler for achievement progress change notifications. /// /// An xbox live context handle created with XblContextCreateHandle. /// The callback function that receives notifications. /// Client context pointer to be passed back to the handler. /// A XblFunctionContext used to remove the handler. /// /// Call to un-register event handler. /// STDAPI_(XblFunctionContext) XblAchievementsAddAchievementProgressChangeHandler( _In_ XblContextHandle xblContextHandle, _In_ XblAchievementsProgressChangeHandler handler, _In_opt_ void* handlerContext ) XBL_NOEXCEPT; /// /// Removes an event handler for achievement progress change notifications. /// /// An xbox live context handle created with XblContextCreateHandle. /// XblFunctionContext for the handler to remove. /// /// /// /// Call this API only if was used to register an event handler. /// STDAPI XblAchievementsRemoveAchievementProgressChangeHandler( _In_ XblContextHandle xblContextHandle, _In_ XblFunctionContext functionContext ) XBL_NOEXCEPT; } ================================================ FILE: Include/xsapi-c/achievements_manager_c.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #if !defined(__cplusplus) #error C++11 required #endif #pragma once #include "pal.h" #include "achievements_c.h" extern "C" { /// /// Enumeration values that specify the order we display the results in. /// enum class XblAchievementsManagerSortOrder : uint32_t { /// /// Unsorted sort order will skip the sort operation. /// Unsorted = 0, /// /// Elements in the response are in ascending order of the field specified by XblAchievementsManagerSortValue. /// Ascending = 1, /// /// Elements in the response are in descending order of the field specified by XblAchievementsManagerSortValue. /// Descending = 2 }; /// /// Defines values used to indicate event types for an achievement. /// enum class XblAchievementsManagerEventType : uint32_t { /// /// Indicates that a local user was added and has been initialized. /// LocalUserInitialStateSynced = 0, /// /// Indicates the achievement was unlocked. /// AchievementUnlocked = 1, /// /// Indicates progress has been made on (a requirement of) an achievement. /// AchievementProgressUpdated = 2 }; /// /// An achievement event that will be returned from . /// typedef struct XblAchievementsManagerEvent { /// /// Current state of progress for an achievement for AchievementUnlocked and /// AchievementProgressUpdated events. The values of this struct are not /// populated for LocalUserInitialStateSynced events. /// XblAchievementProgressChangeEntry progressInfo; /// /// The ID for the user that has made progress on an achievement. /// uint64_t xboxUserId; /// /// Type of the event triggered. /// XblAchievementsManagerEventType eventType; } XblAchievementsManagerEvent; /// /// A handle to an achievement manager result. /// /// /// This handle is used by other APIs to get the achievement objects and to get the /// next page of achievements from the service if there is one. /// The handle must be closed using /// when the result is no longer needed. /// /// /// /// typedef struct XblAchievementsManagerResult* XblAchievementsManagerResultHandle; /// /// Get a list of XblAchievement objects. /// /// Achievement result handle. /// Pointer to an array of XblAchievement objects. /// The memory for the returned pointer will remain valid for the life of the /// XblAchievementsManagerResultHandle object until it is closed. /// The count of objects in the returned array. /// HRESULT return code for this API operation. /// /// The returned array of XblAchievement objects is freed when all outstanding handles /// to the object have been closed with . /// However the data might become stale. /// STDAPI XblAchievementsManagerResultGetAchievements( _In_ XblAchievementsManagerResultHandle resultHandle, _Out_ const XblAchievement** achievements, _Out_ uint64_t* achievementsCount ) XBL_NOEXCEPT; /// /// Duplicates a XblAchievementsManagerResultHandle. /// /// The XblAchievementsManagerResultHandle to duplicate. /// The duplicated handle. /// HRESULT return code for this API operation. STDAPI XblAchievementsManagerResultDuplicateHandle( _In_ XblAchievementsManagerResultHandle handle, _Out_ XblAchievementsManagerResultHandle* duplicatedHandle ) XBL_NOEXCEPT; /// /// Closes the XblAchievementsManagerResultHandle. /// /// The XblAchievementsManagerResultHandle to close. /// /// /// When all outstanding handles have been closed, the memory associated with the achievement result will be freed. /// STDAPI_(void) XblAchievementsManagerResultCloseHandle( _In_ XblAchievementsManagerResultHandle handle ) XBL_NOEXCEPT; /// /// Generate a local cache of achievements for a user. /// /// Xbox Live User to fetch achievements for. /// Queue to be used for background operation for this user (Optional). /// HRESULT return code for this API operation. STDAPI XblAchievementsManagerAddLocalUser( _In_ XblUserHandle user, _In_opt_ XTaskQueueHandle queue ) XBL_NOEXCEPT; /// /// Immediately remove the local cache of achievements for a user. /// /// Xbox Live User to clear the cache for. /// HRESULT return code for this API operation. STDAPI XblAchievementsManagerRemoveLocalUser( _In_ XblUserHandle user ) XBL_NOEXCEPT; /// /// Checks whether a specific user has had its initial state synced. /// /// Xbox Live User to check. /// HRESULT return code for this API operation. If the user is not /// initialized, this function will return E_FAIL. STDAPI XblAchievementsManagerIsUserInitialized( _In_ uint64_t xboxUserId ) XBL_NOEXCEPT; /// /// Called whenever the title wants to update the state of achievements and get list of change events. /// /// Passes back a pointer to the array of achievement events that have occurred since the last call to XblAchievementsManagerDoWork. /// Passes back the number of events in the achievement events array. /// HRESULT return code for this API operation. /// /// Must be called every frame for data to be up to date. /// The array of achievement events that is sent back is only valid until the next call to . /// Make sure to check if there were achievement events sent back. /// If the achievement events array is null, no results. /// If the achievement events count is 0, no results. /// If there were achievement events sent back then handle each /// by their respective . /// STDAPI XblAchievementsManagerDoWork( _Outptr_result_maybenull_ const XblAchievementsManagerEvent** achievementsEvents, _Out_ size_t* achievementsEventsCount ) XBL_NOEXCEPT; /// /// Gets the current local state of an achievement for a local player with a specific achievement ID. /// /// The Xbox User ID of the player. /// The UTF-8 encoded unique identifier of the Achievement as defined by Dev Center. /// The handle to the result of AchievementsManager API calls. /// This handle is used by other APIs to get the achievement objects matching the /// API that was called. /// The handle must be closed using when /// the result is no longer needed. /// /// HRESULT return code for this API operation. STDAPI XblAchievementsManagerGetAchievement( _In_ uint64_t xboxUserId, _In_ const char* achievementId, _Outptr_result_maybenull_ XblAchievementsManagerResultHandle* achievementResult ) XBL_NOEXCEPT; /// /// Gets a list of all achievements for a player. /// /// The Xbox User ID of the player. /// /// The field to sort the list of achievements on. /// /// The direction by which to sort the list of achievements. /// The handle to the result of AchievementsManager API calls. /// This handle is used by other APIs to get the achievement objects matching the /// API that was called. /// The handle must be closed using when /// the result is no longer needed. /// /// /// Passing in XblAchievementOrderBy::TitleId for sortField yields the same results /// as passing in XblAchievementOrderBy::DefaultOrder since all achievements tracked /// by achievement manager will have the same TitleId. /// /// HRESULT return code for this API operation. STDAPI XblAchievementsManagerGetAchievements( _In_ uint64_t xboxUserId, _In_ XblAchievementOrderBy sortField, _In_ XblAchievementsManagerSortOrder sortOrder, _Outptr_result_maybenull_ XblAchievementsManagerResultHandle* achievementsResult ) XBL_NOEXCEPT; /// /// Gets a list of achievements in a specific progress state for a player. /// /// The Xbox User ID of the player. /// /// The field to sort the list of achievements on. TitleId will behave /// the same as DefaultOrder, as AchievementsManager only handles one title /// at a time. /// /// The direction by which to sort the list of achievements. /// The achievement state to include in the results. /// The handle to the result of AchievementsManager API calls. /// This handle is used by other APIs to get the achievement objects matching the /// API that was called. /// The handle must be closed using when /// the result is no longer needed. /// /// HRESULT return code for this API operation. STDAPI XblAchievementsManagerGetAchievementsByState( _In_ uint64_t xboxUserId, _In_ XblAchievementOrderBy sortField, _In_ XblAchievementsManagerSortOrder sortOrder, _In_ XblAchievementProgressState achievementState, _Outptr_result_maybenull_ XblAchievementsManagerResultHandle* achievementsResult ) XBL_NOEXCEPT; /// /// Allow achievement progress to be updated and achievements to be unlocked. /// /// The Xbox User ID of the player. /// The UTF-8 encoded achievement ID as defined by Dev Center. /// The completion percentage of the achievement to indicate progress. /// Valid values are from 1 to 100. Set to 100 to unlock the achievement. /// Progress will be set by the server to the highest value sent /// HRESULT return code for this API operation. /// /// This API will work even when offline on PC and Xbox consoles. Offline updates will be /// posted by the system when connection is re-established even if the title isn't running. /// Note that in order for this API to work offline, the title must have previously /// called for the user while online otherwise this API /// will return an error. If this is not desired, consider calling /// if this API fails to handle 'offline during init' scenarios. /// /// The result of the operation will not be represented locally immediately. The /// earliest the update will be reflected will be after the next frame's call to /// DoWork. Once the change is reflected, the array returned by DoWork /// will contain a of with /// an event type of AchievementProgressUpdated, and potentially an /// additional event of type AchievementUnlocked if the new progress /// resulted in unlocking the achievement. /// /// If the achievement has already been unlocked or the progress value is less than or /// equal to what is cached from the server, this function will return E_NOT_VALID_STATE /// or E_INVALIDARG respectively. /// /// Only title based achievements may be updated with this function. Event based /// achievements cannot be updated with this function. /// /// V2 POST /users/xuid({xuid})/achievements/{scid}/update STDAPI XblAchievementsManagerUpdateAchievement( _In_ uint64_t xboxUserId, _In_ const char* achievementId, _In_ uint8_t currentProgress ) XBL_NOEXCEPT; } //end extern "C" ================================================ FILE: Include/xsapi-c/errors_c.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #if !defined(__cplusplus) #error C++11 required #endif #pragma once extern "C" { /// /// Enumeration values that define the Xbox Live API error conditions. /// /// /// A best practice is to test the returned HRESULT against these error conditions using . /// enum class XblErrorCondition : uint32_t { /// /// No error. /// NoError = 0, /// /// A generic error condition. /// GenericError, /// /// An error condition related to an object being out of range. /// GenericOutOfRange, /// /// An error condition related to attempting to authenticate. /// Auth, /// /// An error condition related to network connectivity. /// Network, /// /// An error condition related to an HTTP method call. /// HttpGeneric, /// /// The requested resource was not modified. /// Http304NotModified, /// /// The requested resource was not found. /// Http404NotFound, /// /// The precondition given in one or more of the request-header fields evaluated /// to false when it was tested on the server. /// Http412PreconditionFailed, /// /// Client is sending too many requests /// Http429TooManyRequests, /// /// The service timed out while attempting to process the request. /// HttpServiceTimeout, /// /// An error related to real time activity. /// Rta }; /// /// Groups HRESULT values returned from Xbl APIs in to error condition buckets that are actionable. /// /// HRESULT value returned from an Xbl API. /// The corresponding XblErrorCondition. STDAPI_(XblErrorCondition) XblGetErrorCondition( _In_ HRESULT hr ) XBL_NOEXCEPT; } ================================================ FILE: Include/xsapi-c/events_c.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #if !defined(__cplusplus) #error C++11 required #endif #pragma once #ifdef XSAPI_EVENTS_SERVICE extern "C" { /// /// Write an in-game event that includes "dimensions" and "measurement" data fields. /// /// Xbox Live context handle. /// Event name. Must be contain only alphanumeric characters. /// Dimensions data fields. /// Measurement data fields. /// HRESULT return code for this API operation. /// /// Dimensions include event fields with a finite number of defined numeric or string values. /// Examples of dimensions: map id, difficulty level, character or weapon class, game mode, boolean settings, etc. /// /// Measurements include event fields that represent scalar numeric metrics. /// Examples of measurements: score, time, counters, position, etc. /// /// Example: for an in-game event that tracks the highest match score for a particular difficulty level: /// The difficulty level should be included in dimensions, and the score should be included in measurements. /// /// The name of the event, and the names of the event fields (both dimensions and measurements), must match /// the names declared in the title's service configuration. The names are case insensitive. /// If the API writes an event with a name that does not match a name in the service configuration, the /// service drops the event with no notification. /// /// When using the GDK PC version, a GRTS runtime with the XGameEvent feature must be installed or this will E_NOTIMPL. /// STDAPI XblEventsWriteInGameEvent( _In_ XblContextHandle xboxLiveContext, _In_z_ const char* eventName, _In_opt_z_ const char* dimensionsJson, _In_opt_z_ const char* measurementsJson ) XBL_NOEXCEPT; #ifdef XSAPI_INTERNAL_EVENTS_SERVICE /// /// Set the maximum amount of disk space that Xsapi can use to store the events pending retry and upload before /// it starts deleting the oldest files. /// /// Maximum storage space (in bytes) that will be used to store pending events. /// HRESULT return code for this API operation. /// /// When the maximum storage space is exceeded, the oldest file will be silently deleted. /// Note that this is a global setting and will apply to all Xbox Live contexts. /// The default value is approximately 20MB. /// STDAPI XblEventsSetStorageAllotment( uint64_t storageAllotmentInBytes ) XBL_NOEXCEPT; /// /// Set the maximum file size for pending events files. /// /// The maximum size (in bytes) for pending events files. /// HRESULT return code for this API operation. /// /// The files will be read into memory all at once. /// Note that this is a global setting and will apply to all Xbox Live contexts. /// The default value is 128KB. /// STDAPI XblEventsSetMaxFileSize( uint64_t maxFileSizeInByes ) XBL_NOEXCEPT; #endif // XSAPI_INTERNAL_EVENTS_SERVICE } #endif // XSAPI_EVENTS_SERVICE ================================================ FILE: Include/xsapi-c/game_invite_c.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #if !defined(__cplusplus) #error C++11 required #endif #pragma once #include "multiplayer_c.h" #include "real_time_activity_c.h" #if HC_PLATFORM == HC_PLATFORM_WIN32 || HC_PLATFORM_IS_EXTERNAL extern "C" { /// /// Contains information about received game invite notifications. /// typedef struct XblGameInviteNotificationEventArgs { /// /// The Xbox user ID of the player. /// uint64_t invitedXboxUserId; /// /// The Xbox user ID of the player. /// uint64_t senderXboxUserId; /// /// The UTF-8 encoded gamertag of the player. /// char senderGamertag[XBL_GAMERTAG_CHAR_SIZE]; /// /// The UTF-8 encoded modern gamertag for the player. /// Not guaranteed to be unique. /// char senderModernGamertag[XBL_MODERN_GAMERTAG_CHAR_SIZE]; /// /// The UTF-8 encoded suffix appended to modern gamertag to ensure uniqueness. /// May be empty in some cases. /// char senderModernGamertagSuffix[XBL_MODERN_GAMERTAG_SUFFIX_CHAR_SIZE]; /// /// The UTF-8 encoded unique modern gamertag and suffix. /// Format will be "modernGamertag#suffix". /// Guaranteed to be no more than 16 rendered characters. /// char senderUniqueModernGamertag[XBL_UNIQUE_MODERN_GAMERTAG_CHAR_SIZE]; /// /// Invite Handle ID. /// The memory for the returned string pointer only remains valid inside the XblGameInviteHandler, /// so deep copy the string if you need to refer to it outside the handler. /// _Field_z_ const char* inviteHandleId; /// /// Invite Protocol. /// The memory for the returned string pointer only remains valid inside the XblGameInviteHandler, /// so deep copy the string if you need to refer to it outside the handler. /// _Field_z_ const char* inviteProtocol; /// /// Invite Context. /// The memory for the returned string pointer only remains valid inside the XblGameInviteHandler, /// so deep copy the string if you need to refer to it outside the handler. /// _Field_z_ const char* inviteContext; /// /// Sender Image URL. /// The memory for the returned string pointer only remains valid inside the XblGameInviteHandler, /// so deep copy the string if you need to refer to it outside the handler. /// _Field_z_ const char* senderImageUrl; /// /// Expiration Date. /// time_t expiration; /// /// Multiplayer Session Reference. /// XblMultiplayerSessionReference sessionReference; } XblGameInviteNotificationEventArgs; /// /// Handle for Function handling Game Invite Event Notifications. /// /// The game invite notifications that are passed in. /// Caller context to be passed the handler. /// /// The lifetime of the XblGameInviteNotifcationEventArgs object is limited to the callback. typedef void CALLBACK XblGameInviteHandler( _In_ const XblGameInviteNotificationEventArgs* args, _In_opt_ void* context ); /// /// Registers the title to receive notifications for game invites. /// Call XblGameInviteRegisterForEventResult() to get the result. /// DEPRECATED. Calling this API is no longer required and it will be removed in a future release. Registration with appropriate /// service endpoints is done automatically by XSAPI as are added and removed. /// /// Xbox live context for the local user. /// The AsyncBlock for this operation. /// HRESULT return code for this API operation. STDAPI_XBL_DEPRECATED XblGameInviteRegisterForEventAsync( _In_ XblContextHandle xboxLiveContext, _In_ XAsyncBlock* async ) XBL_NOEXCEPT; /// /// Gets the result of a XblGameInviteRegisterForEventAsync operation. /// /// The AsyncBlock for this operation. /// Handle for the subscription to be registered. /// HRESULT return code for this API operation. STDAPI_XBL_DEPRECATED XblGameInviteRegisterForEventResult( _In_ XAsyncBlock* async, _Out_ XblRealTimeActivitySubscriptionHandle* subscriptionHandle ) XBL_NOEXCEPT; /// /// Unregisters the title to stop receiving notifications for game invites. /// DEPRECATED. Calling this API is no longer required and it will be removed in a future release. Registration with appropriate /// service endpoints is done automatically by XSAPI as are added and removed. /// /// Xbox live context for the local user. /// Handle for the subscription to be unregistered. /// The AsyncBlock for this operation. /// HRESULT return code for this API operation. /// E_FAIL will be returned if this API before a Game Invite has been registered. STDAPI_XBL_DEPRECATED XblGameInviteUnregisterForEventAsync( _In_ XblContextHandle xblContextHandle, _In_ XblRealTimeActivitySubscriptionHandle subscriptionHandle, _In_ XAsyncBlock* async ) XBL_NOEXCEPT; /// /// Registers an event handler for game invite notifications. /// /// Xbox live context for the local user. /// The callback function that receives notifications. /// Caller context to be passed the handler. /// An XblFunctionContext object that can be used to unregister the event handler. STDAPI_(XblFunctionContext) XblGameInviteAddNotificationHandler( _In_ XblContextHandle xblContextHandle, _In_ XblGameInviteHandler* handler, _In_opt_ void* context ) XBL_NOEXCEPT; /// /// Unregisters an event handler for game invite notifications. /// /// Xbox live context for the local user. /// The XblFunctionContext object that was returned when the event handler was registered. /// STDAPI_(void) XblGameInviteRemoveNotificationHandler( _In_ XblContextHandle xblContextHandle, _In_ XblFunctionContext token ) XBL_NOEXCEPT; } // end extern C #endif ================================================ FILE: Include/xsapi-c/http_call_c.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #if !defined(__cplusplus) #error C++11 required #endif #pragma once extern "C" { /// /// Defines the response body type when reading the results of an HTTP call. /// enum class XblHttpCallResponseBodyType : uint32_t { /// /// Read results as string. /// String, /// /// Read results as vector. /// Vector }; /// /// A handle to an HTTP call. /// typedef struct XblHttpCall* XblHttpCallHandle; // Http APIs // /// /// Creates an HTTP call handle. /// /// Xbox Live context that provides user context for authorizing the call. /// UTF-8 encoded method for the HTTP call. /// UTF-8 encoded URL for the HTTP call. /// The handle of the HTTP call. /// Result code for this API operation. Possible values are S_OK, E_INVALIDARG, or E_FAIL. /// /// First create an HTTP handle using XblHttpCallCreate(). /// Then call XblHttpCallRequestSet*() to prepare the XblHttpCallHandle. /// Then call XblHttpCallPerformAsync() to perform HTTP call using the XblHttpCallHandle. /// This call is asynchronous, so the work will be done on a background thread and will return via the callback. /// /// The perform call is asynchronous, so the work will be done on a background thread which calls /// XTaskQueueDispatch( ..., XTaskQueuePort::Work ). /// /// The results will return to the callback on the thread that calls /// XTaskQueueDispatch( ..., XTaskQueuePort::Completion ), then get the result of the HTTP call by calling /// XblHttpCallResponseGet*() to get the HTTP response of the XblHttpCallHandle. /// /// When the XblHttpCallHandle is no longer needed, call to free the /// memory associated with the XblHttpCallHandle. /// STDAPI XblHttpCallCreate( _In_ XblContextHandle xblContext, _In_z_ const char* method, _In_z_ const char* url, _Out_ XblHttpCallHandle* call ) XBL_NOEXCEPT; /// /// Perform an HTTP call using the XblHttpCallHandle. /// /// The handle of the HTTP call. /// The response body type to read the results of this HTTP call. /// Note: this does not influence the content-type header, which must be /// supplied by calling . /// The XAsyncBlock that defines the async operation. /// Result code for this API operation. Possible values are S_OK, E_INVALIDARG, E_OUTOFMEMORY, or E_FAIL. /// /// First create a HTTP handle using XblHttpCallCreate(). /// Then call XblHttpCallRequestSet*() to prepare the XblHttpCallHandle. /// Then call XblHttpCallPerformAsync() to perform HTTP call using the XblHttpCallHandle. /// This call is asynchronous, so the work will be done on a background thread and will return via the callback. /// /// The perform call is asynchronous, so the work will be done on a background thread which calls /// XTaskQueueDispatch( ..., XTaskQueuePort::Work ). /// /// The results will return to the callback on the thread that calls /// XTaskQueueDispatch( ..., XTaskQueuePort::Completion ), then get the result of the HTTP call by calling /// XblHttpCallResponseGet*() to get the HTTP response of the XblHttpCallHandle. /// /// When the XblHttpCallHandle is no longer needed, call XblHttpCallCloseHandle() to free the /// memory associated with the XblHttpCallHandle. /// /// XblHttpCallPerformAsync can only be called once. Create new XblHttpCallHandle to repeat the call. /// STDAPI XblHttpCallPerformAsync( _In_ XblHttpCallHandle call, _In_ XblHttpCallResponseBodyType type, _Inout_ XAsyncBlock* asyncBlock ) XBL_NOEXCEPT; /// /// Duplicates the XblHttpCallHandle object. /// /// The handle of the HTTP call. /// The duplicated handle. /// Result code for this API operation. /// /// Use XblHttpCallCloseHandle to close it. /// STDAPI XblHttpCallDuplicateHandle( _In_ XblHttpCallHandle call, _Out_ XblHttpCallHandle* duplicateHandle ) XBL_NOEXCEPT; /// /// Decrements the reference count on the call object. /// /// The handle of the HTTP call. /// /// /// When the XblHttpCallHandle ref count is 0, XblHttpCallCloseHandle() will /// free the memory associated with the XblHttpCallHandle. /// STDAPI_(void) XblHttpCallCloseHandle( _In_ XblHttpCallHandle call ) XBL_NOEXCEPT; /// /// Enables or disables tracing for this specific HTTP call. /// /// The handle of the HTTP call. /// Trace this call. /// Result code for this API operation. Possible values are S_OK, E_INVALIDARG, or E_FAIL. /// /// Defaults to true. /// STDAPI XblHttpCallSetTracing( _In_ XblHttpCallHandle call, _In_ bool traceCall ) XBL_NOEXCEPT; /// /// Gets the request url for the HTTP call. /// /// The handle of the HTTP call. /// The UTF-8 encoded url body string of the HTTP call. /// The memory for the returned string pointer remains valid for the life of /// the XblHttpCallHandle object until XblHttpCallCloseHandle() is called on it. /// Result code for this API operation. STDAPI XblHttpCallGetRequestUrl( _In_ XblHttpCallHandle call, _Out_ const char** url ) XBL_NOEXCEPT; ///////////////////////////////////////////////////////////////////////////////////////// // HttpCallRequest Set APIs // /// /// Set the request body bytes of the HTTP call. /// /// The handle of the HTTP call. /// The request body bytes of the HTTP call. /// The length in bytes of the body being set. /// Result code for this API operation. Possible values are S_OK, E_INVALIDARG, E_OUTOFMEMORY, or E_FAIL. /// /// This must be called prior to calling . /// STDAPI XblHttpCallRequestSetRequestBodyBytes( _In_ XblHttpCallHandle call, _In_reads_bytes_(requestBodySize) const uint8_t* requestBodyBytes, _In_ uint32_t requestBodySize ) XBL_NOEXCEPT; /// /// Set the request body string of the HTTP call. /// /// The handle of the HTTP call. /// The UTF-8 encoded request body string of the HTTP call. /// Result code for this API operation. Possible values are S_OK, E_INVALIDARG, E_OUTOFMEMORY, or E_FAIL. /// /// This must be called prior to calling . /// STDAPI XblHttpCallRequestSetRequestBodyString( _In_ XblHttpCallHandle call, _In_z_ const char* requestBodyString ) XBL_NOEXCEPT; /// /// Set a request header for the HTTP call. /// /// The handle of the HTTP call. /// UTF-8 encoded request header name for the HTTP call. /// UTF-8 encoded request header value for the HTTP call. /// Set to false to skip tracing this request header, for example if it contains private information. /// Result code for this API operation. Possible values are S_OK, E_INVALIDARG, E_OUTOFMEMORY, or E_FAIL. /// /// This must be called prior to calling . /// STDAPI XblHttpCallRequestSetHeader( _In_ XblHttpCallHandle call, _In_z_ const char* headerName, _In_z_ const char* headerValue, _In_ bool allowTracing ) XBL_NOEXCEPT; /// /// Sets if retry is allowed for this HTTP call. /// /// The handle of the HTTP call. /// Pass nullptr to set the default for future calls. /// If retry is allowed for this HTTP call. /// Result code for this API operation. Possible values are S_OK, or E_FAIL. /// /// Defaults to true. /// This must be called prior to calling . /// STDAPI XblHttpCallRequestSetRetryAllowed( _In_ XblHttpCallHandle call, _In_ bool retryAllowed ) XBL_NOEXCEPT; /// /// ID number of this REST endpoint used to cache the Retry-After header for fast fail. /// /// The handle of the HTTP call. /// Pass nullptr to set the default for future calls. /// ID number of this REST endpoint used to cache the Retry-After header for fast fail. 1-1000 are reserved for XSAPI. /// Result code for this API operation. Possible values are S_OK, E_INVALIDARG, or E_FAIL. /// /// This must be called prior to calling . /// STDAPI XblHttpCallRequestSetRetryCacheId( _In_ XblHttpCallHandle call, _In_ uint32_t retryAfterCacheId ) XBL_NOEXCEPT; /// /// Sets a flag which defines the HTTP call as long or not. /// /// The handle of the HTTP call. /// Pass nullptr to set the default for future calls. /// The boolean to set the HTTP call to long or not. /// Result code for this API operation. Possible values are S_OK, or E_FAIL. /// /// Defaults to false. /// This must be called prior to calling . /// STDAPI XblHttpCallRequestSetLongHttpCall( _In_ XblHttpCallHandle call, _In_ bool longHttpCall ) XBL_NOEXCEPT; ///////////////////////////////////////////////////////////////////////////////////////// // HttpCallResponse Get APIs // /// /// Get the response body string of the HTTP call. /// /// The handle of the HTTP call. /// /// The UTF-8 encoded response body string of the HTTP call. /// The memory for the returned string pointer remains valid for the life of the XblHttpCallHandle object /// until XblHttpCallCloseHandle() is called on it. /// /// Result code for this API operation. Possible values are S_OK, E_INVALIDARG, or E_FAIL. /// /// This can only be called after calling when the HTTP task is completed. /// This will only be valid if the responsetype passed to PerformAsync was String. /// STDAPI XblHttpCallGetResponseString( _In_ XblHttpCallHandle call, _Out_ const char** responseString ) XBL_NOEXCEPT; /// /// Get the response body buffer size of the HTTP call. /// /// The handle of the HTTP call. /// The response body buffer size of the HTTP call. /// Result code for this API operation. Possible values are S_OK, E_INVALIDARG, or E_FAIL. /// /// This can only be called after calling when the HTTP task is completed. /// This will only be valid if the responsetype passed to PerformAsync was Vector. /// STDAPI XblHttpCallGetResponseBodyBytesSize( _In_ XblHttpCallHandle call, _Out_ size_t* bufferSize ) XBL_NOEXCEPT; /// /// Get the response body buffer of the HTTP call. /// /// The handle of the HTTP call. /// The response body buffer size being passed in. /// The buffer to be written to. /// The actual number of bytes written to the buffer. /// Result code for this API operation. Possible values are S_OK, E_INVALIDARG, or E_FAIL. /// /// This can only be called after calling when the HTTP task is completed. /// This will only be valid if the responsetype passed to PerformAsync was Vector. /// STDAPI XblHttpCallGetResponseBodyBytes( _In_ XblHttpCallHandle call, _In_ size_t bufferSize, _Out_writes_bytes_to_(bufferSize, *bufferUsed) uint8_t* buffer, _Out_opt_ size_t* bufferUsed ) XBL_NOEXCEPT; /// /// Get the HTTP status code of the HTTP call response. /// /// The handle of the HTTP call. /// the HTTP status code of the HTTP call response. /// Result code for this API operation. /// /// This can only be called after calling when the HTTP task is completed. /// STDAPI XblHttpCallGetStatusCode( _In_ XblHttpCallHandle call, _Out_ uint32_t* statusCode ) XBL_NOEXCEPT; /// /// Get the network error code of the HTTP call. /// /// The handle of the HTTP call. /// The network error code of the HTTP call. Possible values are S_OK, or E_FAIL. /// The platform specific network error code of the HTTP call to be used for tracing / debugging. /// Result code for this API operation. Possible values are S_OK, E_INVALIDARG, or E_FAIL. /// /// This can only be called after calling when the HTTP task is completed. /// STDAPI XblHttpCallGetNetworkErrorCode( _In_ XblHttpCallHandle call, _Out_ HRESULT* networkErrorCode, _Out_ uint32_t* platformNetworkErrorCode ) XBL_NOEXCEPT; /// /// Get the platform network error message of the HTTP call. /// /// The handle of the HTTP call. /// The platform specific network error message of the HTTP call to be used for tracing / debugging. /// Result code for this API operation. Possible values are S_OK, E_INVALIDARG, or E_FAIL. /// /// This can only be called after calling when the HTTP task is completed. /// STDAPI XblHttpCallGetPlatformNetworkErrorMessage( _In_ XblHttpCallHandle call, _Out_ const char** platformNetworkErrorMessage ) XBL_NOEXCEPT; /// /// Get a response header for the HTTP call for a given header name. /// /// The handle of the HTTP call. /// UTF-8 encoded response header name for the HTTP call. /// The memory for the returned string pointer remains valid for the life of /// the XblHttpCallHandle object until XblHttpCallCloseHandle() is called on it. /// UTF-8 encoded response header value for the HTTP call. /// Returns nullptr if the header doesn't exist. /// The memory for the returned string pointer remains valid for the life of /// the XblHttpCallHandle object until XblHttpCallCloseHandle() is called on it. /// Result code for this API operation. Possible values are S_OK, E_INVALIDARG, or E_FAIL. /// /// This can only be called after calling when the HTTP task is completed. /// STDAPI XblHttpCallGetHeader( _In_ XblHttpCallHandle call, _In_z_ const char* headerName, _Out_ const char** headerValue ) XBL_NOEXCEPT; /// /// Gets the number of response headers in the HTTP call. /// /// The handle of the HTTP call. /// The number of response headers in the HTTP call. /// Result code for this API operation. Possible values are S_OK, E_INVALIDARG, or E_FAIL. /// /// This can only be called after calling when the HTTP task is completed. /// STDAPI XblHttpCallGetNumHeaders( _In_ XblHttpCallHandle call, _Out_ uint32_t* numHeaders ) XBL_NOEXCEPT; /// /// Gets the response headers at specific zero based index in the HTTP call. /// /// The handle of the HTTP call. /// Specific zero based index of the response header. /// UTF-8 encoded response header name for the HTTP call. /// The memory for the returned string pointer remains valid for the life of /// the XblHttpCallHandle object until XblHttpCallCloseHandle() is called on it. /// UTF-8 encoded response header value for the HTTP call. /// The memory for the returned string pointer remains valid for the life of /// the XblHttpCallHandle object until XblHttpCallCloseHandle() is called on it. /// Result code for this API operation. Possible values are S_OK, E_INVALIDARG, or E_FAIL. /// /// This can only be called after calling when the HTTP task is completed. /// Use to know how many response headers there are in the HTTP call. /// STDAPI XblHttpCallGetHeaderAtIndex( _In_ XblHttpCallHandle call, _In_ uint32_t headerIndex, _Out_ const char** headerName, _Out_ const char** headerValue ) XBL_NOEXCEPT; } ================================================ FILE: Include/xsapi-c/leaderboard_c.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #if !defined(__cplusplus) #error C++11 required #endif #pragma once extern "C" { /// /// Enumerates the data type of a leaderboard statistic. /// /// enum class XblLeaderboardStatType : uint32_t { /// /// Unsigned 64 bit integer. /// Uint64, /// /// Boolean. /// Boolean, /// /// Double. /// Double, /// /// String. /// String, /// /// Unknown. /// Other }; /// /// The order to sort the leaderboard in. /// /// enum class XblLeaderboardSortOrder : uint32_t { /// /// Sorting the leaderboard highest to lowest. /// Descending, /// /// Sorting the leaderboard lowest to highest. /// Ascending }; /// /// Predefined Xbox Live social groups. /// /// enum class XblSocialGroupType : uint32_t { /// /// No social group. /// None, /// /// Social group for the people followed. /// People, /// /// Social group for the people tagged as favorites. /// Favorites }; /// /// Enum used to specify the type of leaderboard in a Leaderboard query. /// /// enum class XblLeaderboardQueryType : uint32_t { /// /// A leaderboard based an event based user stat. /// UserStatBacked = 0, /// /// A global leaderboard backed by a title managed stat. /// TitleManagedStatBackedGlobal = 1, /// /// A social leaderboard backed by a title managed stat. /// TitleManagedStatBackedSocial = 2, }; /// /// Represents a column in a collection of leaderboard items. /// /// typedef struct XblLeaderboardColumn { /// /// The UTF-8 encoded name the statistic displayed in the column. /// _Field_z_ const char* statName; /// /// The property type of the statistic in the column. /// XblLeaderboardStatType statType; } XblLeaderboardColumn; /// /// Represents a row in a collection of leaderboard items. /// /// typedef struct XblLeaderboardRow { /// /// The UTF-8 encoded gamertag of the player. /// _Field_z_ char gamertag[XBL_GAMERTAG_CHAR_SIZE]; /// /// The UTF-8 encoded modern gamertag for the player. /// Not guaranteed to be unique. /// char modernGamertag[XBL_MODERN_GAMERTAG_CHAR_SIZE]; /// /// The UTF-8 encoded suffix appended to modern gamertag to ensure uniqueness. /// May be empty in some cases. /// char modernGamertagSuffix[XBL_MODERN_GAMERTAG_SUFFIX_CHAR_SIZE]; /// /// The UTF-8 encoded unique modern gamertag and suffix. Format will be "modernGamertag#suffix". /// Guaranteed to be no more than 16 rendered characters. /// char uniqueModernGamertag[XBL_UNIQUE_MODERN_GAMERTAG_CHAR_SIZE]; /// /// The Xbox user ID of the player. /// uint64_t xboxUserId; /// /// The percentile rank of the player. /// double percentile; /// /// The rank of the player. /// uint32_t rank; /// /// The global rank of the player. If globalrank is 0, then the user has no global rank. /// uint32_t globalRank; /// /// UTF-8 encoded JSON values for each column in the leaderboard row for the player. /// _Field_z_ const char** columnValues; /// /// The count of string in socialNetworks array. /// size_t columnValuesCount; } XblLeaderboardRow; /// /// Represents the parameters for submitting a leaderboard query using event-based or title-based stats. /// /// /// typedef struct XblLeaderboardQuery { /// /// Optional Xbox user ID of the requesting user. /// If doing a global leaderboard, then set to 0. /// uint64_t xboxUserId; /// /// The UTF-8 encoded service configuration ID (SCID) of the title. /// _Field_z_ char scid[XBL_SCID_LENGTH]; /// /// Optional UTF-8 encoded leaderboard name to get a leaderboard for. /// Set to nullptr if querying a social leaderboard or a title managed stat backed leaderboard. /// _Field_z_ const char* leaderboardName; /// /// Optional UTF-8 encoded statistic name to get a leaderboard for. /// Used when querying a social leaderboard or title managed stat backed leaderboard. /// _Field_z_ const char* statName; /// /// Optional the social group of users to get leaderboard results. /// For example, to get a "friends only" leaderboard. /// Set to XblSocialGroupType_None to get a global leaderboard. /// XblSocialGroupType socialGroup; /// /// Optional UTF-8 encoded array of names of stats for the additional columns. /// _Field_z_ const char** additionalColumnleaderboardNames; /// /// Optional count of additionalColumnleaderboardNames passed in. /// size_t additionalColumnleaderboardNamesCount; /// /// Set sort order for the resulting leaderboard. /// XblLeaderboardSortOrder order; /// /// Set maximum items that the resulting leaderboard will contain. /// Set to 0 to let the service return the default number of max items. /// uint32_t maxItems; /// /// Set the resulting leaderboard will start with the /// user that requested the leaderboard. /// Set to 0 to not skip to a user. /// uint64_t skipToXboxUserId; /// /// Set which rank the resulting leaderboard will start at. /// Set 0 to not skip to a specific rank. /// uint32_t skipResultToRank; /// /// The UTF-8 encoded continuationToken used to get the next set of leaderboard data. /// _Field_z_ const char* continuationToken; /// /// The type of leaderboard to query for. /// XblLeaderboardQueryType queryType; } XblLeaderboardQuery; /// /// Represents the results of a leaderboard request. /// /// /// /// typedef struct XblLeaderboardResult { /// /// The total number of rows in the leaderboard results. /// uint32_t totalRowCount; /// /// The collection of columns in the leaderboard results. /// XblLeaderboardColumn* columns; /// /// The number of **columns**. /// size_t columnsCount; /// /// The collection of rows in the leaderboard results. /// XblLeaderboardRow* rows; /// /// The number of **rows**. /// size_t rowsCount; /// /// Indicates whether there is a next page of results. /// bool hasNext; /// /// The next query. /// Internal use only. /// XblLeaderboardQuery nextQuery; } XblLeaderboardResult; /// /// Get a leaderboard for a title using event-based or title-based stats. /// /// An xbox live context handle created with XblContextCreateHandle. /// The query parameters of the leaderboard request. /// Caller allocated AsyncBlock. /// HRESULT return code for this API operation. /// V4 GET https://leaderboards.xboxlive.com/users/xuid({xuid})/scids/{scid}/stats/{statname}/people/{all|favorites}[?sort=descending|ascending]&skipToUser={skipToUser} STDAPI XblLeaderboardGetLeaderboardAsync( _In_ XblContextHandle xboxLiveContext, _In_ XblLeaderboardQuery leaderboardQuery, _In_ XAsyncBlock* async ) XBL_NOEXCEPT; /// /// Get the result size for an XblLeaderboardGetLeaderboardAsync call. /// /// The AsyncBlock for this operation. /// The size in bytes required to store the user statistics result. /// HRESULT return code for this API operation. STDAPI XblLeaderboardGetLeaderboardResultSize( _In_ XAsyncBlock* async, _Out_ size_t* resultSizeInBytes ) XBL_NOEXCEPT; /// /// Get the result for an XblLeaderboardGetLeaderboardAsync call. /// /// The AsyncBlock for this operation. /// The size of the provided buffer. /// Use to get the size required. /// A caller allocated byte buffer to write result into. /// Strongly typed pointer that points into buffer. /// Do not free this as its lifecycle is tied to buffer. /// Number of bytes written to the buffer. /// HRESULT return code for this API operation. STDAPI XblLeaderboardGetLeaderboardResult( _In_ XAsyncBlock* async, _In_ size_t bufferSize, _Out_writes_bytes_to_(bufferSize, *bufferUsed) void* buffer, _Outptr_ XblLeaderboardResult** ptrToBuffer, _Out_opt_ size_t* bufferUsed ) XBL_NOEXCEPT; /// /// Gets the result of next page of the leaderboard for a player of the specified title. /// /// An xbox live context handle created with XblContextCreateHandle. /// The leaderboard result from a previous call to XblLeaderboardGetLeaderboardResult. /// The maximum number of items that the result can contain. /// Pass 0 to attempt to retrieve all items. /// Caller allocated AsyncBlock. /// HRESULT return code for this API operation. /// /// To get the result, call inside the AsyncBlock callback /// or after the AsyncBlock is complete. /// STDAPI XblLeaderboardResultGetNextAsync( _In_ XblContextHandle xboxLiveContext, _In_ XblLeaderboardResult* leaderboardResult, _In_ uint32_t maxItems, _In_ XAsyncBlock* async ) XBL_NOEXCEPT; /// /// Get the result size for an XblLeaderboardResultGetNextAsync call. /// /// The AsyncBlock for this operation. /// The size in bytes required to store the user statistics result. /// HRESULT return code for this API operation. STDAPI XblLeaderboardResultGetNextResultSize( _In_ XAsyncBlock* async, _Out_ size_t* resultSizeInBytes ) XBL_NOEXCEPT; /// /// Get the result for an XblLeaderboardResultGetNextAsync call. /// /// The AsyncBlock for this operation. /// The size of the provided buffer. /// Use to get the size required. /// A caller allocated byte buffer to write result into. /// Strongly typed pointer that points into buffer. /// Do not free this as its lifecycle is tied to buffer. /// Number of bytes written to the buffer. /// HRESULT return code for this API operation. STDAPI XblLeaderboardResultGetNextResult( _In_ XAsyncBlock* async, _In_ size_t bufferSize, _Out_writes_bytes_to_(bufferSize, *bufferUsed) void* buffer, _Outptr_ XblLeaderboardResult** ptrToBuffer, _Out_opt_ size_t* bufferUsed ) XBL_NOEXCEPT; } // end extern c ================================================ FILE: Include/xsapi-c/matchmaking_c.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #if !defined(__cplusplus) #error C++11 required #endif #pragma once #include "multiplayer_c.h" extern "C" { /// /// Defines values used to indicate whether a match ticket is for a new /// game session or an existing session. /// enum class XblPreserveSessionMode : uint32_t { /// /// The server returned an unrecognized response. /// Unknown, /// /// Always use an existing game session. /// This is for matching more players for a game session /// that is already created or in progress. /// Always, /// /// Never use an existing game session. /// This is for matching players for a new game session. /// Never }; /// /// Defines values used to indicate the status of the match request. /// enum class XblTicketStatus : uint32_t { /// /// The status of the match request has not been returned by the server yet /// or the server returned an unrecognized response. /// Unknown, /// /// Matchmaking has not found a match and the search /// request has expired according to its give up duration. /// Expired, /// /// Matchmaking has not found a match yet and it is /// still searching. /// Searching, /// /// Matchmaking has found a match and the ticket contains a /// reference to the session that is to be created. /// Found, /// /// Matchmaking has been canceled for this ticket. /// Canceled }; /// /// Contains information about server response to a create match ticket request. /// typedef struct XblCreateMatchTicketResponse { /// /// Ticket ID of a match request. /// _Field_z_ char matchTicketId[XBL_SCID_LENGTH]; /// /// Estimated wait time for a match request to be matched with other players. /// int64_t estimatedWaitTime; } XblCreateMatchTicketResponse; /// /// Represents a server response to a request for match ticket details. /// typedef struct XblMatchTicketDetailsResponse { /// /// Status of a match request. /// XblTicketStatus matchStatus; /// /// Estimated wait time for a match request to be matched with other players. /// int64_t estimatedWaitTime; /// /// An enum value to specify whether the match should preserve the session on which the match has been requested. /// XblPreserveSessionMode preserveSession; /// /// The session on which the match was requested. /// XblMultiplayerSessionReference ticketSession; /// /// The session on which a match request has been found. /// XblMultiplayerSessionReference targetSession; /// /// The attributes of a match request. /// char* ticketAttributes; } XblMatchTicketDetailsResponse; /// /// Represents a server response to a hopper statistics request. /// typedef struct XblHopperStatisticsResponse { /// /// Name of the hopper in which a match was requested. /// _Field_z_ char* hopperName; /// /// Estimated wait time for a match request to be matched with other players. /// int64_t estimatedWaitTime; /// /// The number of players in the hopper waiting to be matched. /// uint32_t playersWaitingToMatch; } XblHopperStatisticsResponse; /// /// Sends a matchmaking request to the server and returns the match ticket with a ticket id. /// Call XblMatchmakingCreateMatchTicketResult upon completion to get the result. /// /// Xbox live context for the local user. /// The multiplayer session to use for the match. /// The service configuration ID for the match. /// The name of the hopper. /// The maximum time to wait for players to join the session. /// Indicates if the session should be preserved. /// The ticket attributes for the session. (Optional) /// The AsyncBlock for this operation. /// HRESULT return code for this API operation. /// Calls V103 POST /serviceconfigs/{serviceConfigId}/hoppers/{hopperName} /// /// The match ticket object contains server returned information such as ticket id and wait /// time, and also contains copies of the title specified data from the ticket data object. /// STDAPI XblMatchmakingCreateMatchTicketAsync( _In_ XblContextHandle xboxLiveContext, _In_ XblMultiplayerSessionReference ticketSessionReference, _In_ const char* matchmakingServiceConfigurationId, _In_ const char* hopperName, _In_ const uint64_t ticketTimeout, _In_ XblPreserveSessionMode preserveSession, _In_ const char* ticketAttributesJson, _In_ XAsyncBlock* asyncBlock ) XBL_NOEXCEPT; /// /// Get the result for an XblMatchmakingCreateMatchTicketAsync call. /// /// The AsyncBlock for this operation. /// A caller allocated struct to be filled with the match ticket results. /// HRESULT return code for this API operation. STDAPI XblMatchmakingCreateMatchTicketResult( _In_ XAsyncBlock* asyncBlock, _Out_ XblCreateMatchTicketResponse* resultPtr ) XBL_NOEXCEPT; /// /// Deletes a the match ticket on the server. /// /// Xbox live context for the local user. /// The Service Configuration ID (SCID) for the title. The SCID is considered case sensitive so paste it directly from the Partner Center. /// The name of the hopper where the match ticket is located. /// The id of the ticket to delete on the server. /// The AsyncBlock for this operation. /// HRESULT return code for this API operation. /// Calls V103 DELETE /serviceconfigs/{serviceConfigId}/hoppers/{hopperName}/tickets/{ticketId} STDAPI XblMatchmakingDeleteMatchTicketAsync( _In_ XblContextHandle xboxLiveContext, _In_ const char* serviceConfigurationId, _In_ const char* hopperName, _In_ const char* ticketId, _In_ XAsyncBlock* asyncBlock ) XBL_NOEXCEPT; /// /// Retrieves the properties of a match ticket from the server. /// /// Xbox live context for the local user. /// The Service Configuration ID (SCID) for the title. The SCID is considered case sensitive so paste it directly from the Partner Center. /// The name of the hopper where the match ticket is located. /// The ticket id of the match ticket to retrieve. /// The AsyncBlock for this operation. /// HRESULT return code for this API operation. /// Calls V103 GET /serviceconfigs/{serviceConfigId}/hoppers/{hopperName}/tickets/{ticketId} STDAPI XblMatchmakingGetMatchTicketDetailsAsync( _In_ XblContextHandle xboxLiveContext, _In_ const char* serviceConfigurationId, _In_ const char* hopperName, _In_ const char* ticketId, _In_ XAsyncBlock* asyncBlock ) XBL_NOEXCEPT; /// /// Get the result size for an XblMatchmakingGetMatchTicketDetailsAsync call. /// /// The AsyncBlock for this operation. /// The size in bytes required to store the Create Match Ticket result. /// HRESULT return code for this API operation. STDAPI XblMatchmakingGetMatchTicketDetailsResultSize( _In_ XAsyncBlock* asyncBlock, _Out_ size_t* resultSizeInBytes ) XBL_NOEXCEPT; /// /// Get the result for an XblMatchmakingGetMatchTicketDetailsAsync call. /// /// The AsyncBlock for this operation. /// The size of the provided buffer. /// Use to get the size required. /// A caller allocated byte buffer to write result into. /// Strongly typed pointer that points into buffer. /// This is a pointer within buffer and should not be freed separately. /// Number of bytes written to the buffer. /// HRESULT return code for this API operation. STDAPI XblMatchmakingGetMatchTicketDetailsResult( _In_ XAsyncBlock* asyncBlock, _In_ size_t bufferSize, _Out_writes_bytes_to_(bufferSize, *bufferUsed) void* buffer, _Outptr_ XblMatchTicketDetailsResponse** ptrToBuffer, _Out_opt_ size_t* bufferUsed ) XBL_NOEXCEPT; /// /// Gets statistics about a hopper such as how many players are in it. /// /// Xbox live context for the local user. /// The Service Configuration ID (SCID) for the title. The SCID is considered case sensitive so paste it directly from the Partner Center. /// The name of the hopper to query stats for. /// The AsyncBlock for this operation. /// HRESULT return code for this API operation. /// Calls V103 GET /serviceconfigs/{serviceConfigId}/hoppers/{hopperName}/stats STDAPI XblMatchmakingGetHopperStatisticsAsync( _In_ XblContextHandle xboxLiveContext, _In_ const char* serviceConfigurationId, _In_ const char* hopperName, _In_ XAsyncBlock* asyncBlock ) XBL_NOEXCEPT; /// /// Get the result size for an XblMatchmakingGetHopperStatisticsAsync call. /// /// The AsyncBlock for this operation. /// The size in bytes required to store the Create Match Ticket result. /// HRESULT return code for this API operation. STDAPI XblMatchmakingGetHopperStatisticsResultSize( _In_ XAsyncBlock* asyncBlock, _Out_ size_t* resultSizeInBytes ) XBL_NOEXCEPT; /// /// Get the result for an XblMatchmakingGetHopperStatisticsAsync call. /// /// The AsyncBlock for this operation. /// The size of the provided buffer. /// Use to get the size required. /// A caller allocated byte buffer to write result into. /// Strongly typed pointer that points into buffer. /// This is a pointer within buffer and should not be freed separately. /// Number of bytes written to the buffer. /// HRESULT return code for this API operation. STDAPI XblMatchmakingGetHopperStatisticsResult( _In_ XAsyncBlock* asyncBlock, _In_ size_t bufferSize, _Out_writes_bytes_to_(bufferSize, *bufferUsed) void* buffer, _Outptr_ XblHopperStatisticsResponse** ptrToBuffer, _Out_opt_ size_t* bufferUsed ) XBL_NOEXCEPT; } // end extern c ================================================ FILE: Include/xsapi-c/multiplayer_activity_c.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #if !defined(__cplusplus) #error C++11 required #endif #pragma once #include "xsapi-c/pal.h" extern "C" { /// /// Enumerates the platforms on which a title can be activated. /// /// /// enum class XblMultiplayerActivityPlatform : uint32_t { /// /// Unknown device. /// Unknown = 0, /// /// Xbox One device. /// XboxOne = 1, /// /// Windows OneCore (Universal Windows Platform [UWP] and {% term projname %} on PC). /// WindowsOneCore = 2, /// /// Win32-based device. /// Win32 = 3, /// /// {% term scarlett %} device. /// Scarlett = 4, /// /// iOS device. /// iOS = 20, /// /// Android device. /// Android = 30, /// /// Nintendo device. /// Nintendo = 40, /// /// PlayStation device. /// PlayStation = 50, /// /// Activity is joinable on all platforms supported by the title. /// All = 60 }; /// /// Enumerates who can join a player's current activity. /// /// /// /// /// To see how this enumeration is used, see "Setting an activity" in /// the Activities section /// of Example code for Multiplayer Activity. /// enum class XblMultiplayerActivityJoinRestriction : uint32_t { /// /// Everyone. /// Public = 0, /// /// Only invited players. /// InviteOnly = 1, /// /// Only followed players. /// Followed = 2 }; /// /// Enumerates types of recent player encounters. /// /// /// /// To see how this enumeration is used, see "Setting an activity" in /// the Activities section /// of Example code for Multiplayer Activity. /// enum class XblMultiplayerActivityEncounterType : uint32_t { /// /// No inherent meaning. The title interprets this value as appropriate. /// Default = 0, /// /// Teammate. /// Teammate = 1, /// /// Opponent. /// Opponent = 2 }; /// /// Information about a player's activity while playing a title. /// /// /// /// /// To see how this enumeration is used, see "Setting an activity" and "Getting activities" in /// the Activities section /// of Example code for Multiplayer Activity. /// typedef struct XblMultiplayerActivityInfo { /// /// The Xbox user ID to which the activity info belongs. /// uint64_t xuid; /// /// The connection string passed to the connecting client to join a game; /// typically contains information such as the server IP address. /// When querying activities, this field is populated only if the activity is public /// or the player is following the caller. /// _Field_z_ const char* connectionString; /// /// Specifies who can join the player's current activity. /// XblMultiplayerActivityJoinRestriction joinRestriction; /// /// The maximum number of players who can join the player's current activity. /// A value of 0 indicates that no players can join. /// size_t maxPlayers; /// /// The number of players already playing with the player in a multiplayer activity. /// A value of 0 indicates that no other players are currently playing. /// size_t currentPlayers; /// /// A unique identifier to group all users playing as part of the same activity. /// The title sets this identifier when it creates the activity. /// _Field_z_ const char* groupId; /// /// The platform on which the activity is happening. /// When setting an activity, the platform is automatically inferred; this field is ignored. /// XblMultiplayerActivityPlatform platform; } XblMultiplayerActivityInfo; /// /// Describes a recent player encounter. /// /// /// /// To see how this enumeration is used, see "Updating recent players" in /// the Recent players section /// of Example code for Multiplayer Activity. /// typedef struct XblMultiplayerActivityRecentPlayerUpdate { /// /// Xbox user ID of the encountered user. /// uint64_t xuid; /// /// The type of encounter. /// XblMultiplayerActivityEncounterType encounterType; } XblMultiplayerActivityRecentPlayerUpdate; /// /// Appends to a player's list of recent players. /// If an encountered user is already in the list, it updates the existing recent-player entry. /// /// {% term xbox-live %} context for the local user. /// List of objects to append to the recent players list. /// Size of the `updates` array. /// HRESULT return code for this API operation. /// /// This call is unidirectional; it only affects the caller's recent-players list.

/// Recent-player updates are batched and uploaded by XSAPI by using the background queue provided during `XblInitialize`.

/// To force an immediate flush, call . ///
STDAPI XblMultiplayerActivityUpdateRecentPlayers( _In_ XblContextHandle xblContext, _In_reads_(updatesCount) const XblMultiplayerActivityRecentPlayerUpdate* updates, _In_ size_t updatesCount ) XBL_NOEXCEPT; /// /// Immediately writes any pending recent-players updates to {% term xbox-live %}. /// /// {% term xbox-live %} context for the local user. /// The `XAsyncBlock` for this operation. /// HRESULT return code for this API operation. /// /// Calling this API is optional; updates are periodically uploaded from a background task queue.

/// When this API is used, the upload happens on the task queue supplied in this call.

/// To get the result of the asynchronous operation, call /// inside the `XAsyncBlock` callback or after `XAsyncBlock` is complete.

/// To see how this enumeration is used, see the /// Recent players section /// of Example code for Multiplayer Activity. ///
/// Calls POST /titles/{titleId}/recentplayers STDAPI XblMultiplayerActivityFlushRecentPlayersAsync( _In_ XblContextHandle xblContext, _In_ XAsyncBlock* async ) XBL_NOEXCEPT; /// /// Sets or updates the multiplayer activity for a local user. /// /// {% term xbox-live %} context for the local user. /// Information about the activity. /// The `maxPlayers` and `currentPlayers` fields are optional; they are ignored if set to 0. /// The value of the `platform` field is ignored; XSAPI automatically sets the activity /// on the appropriate local platform. /// /// True if the activity should be joinable on other platforms supported by the title. /// /// The `XAsyncBlock` for this operation. /// HRESULT return code for this API operation. /// /// When a title starts or joins a multiplayer experience, it should create an /// activity. Doing this lets both the shell and other players in your title /// see the player's activity. Your title can let other players join the game /// in progress. If a player wants to join an activity for your title and it is not /// running, it is activated and the connection string is passed to it.

/// To see how this function is used, see "Setting an activity" in /// the Activities section /// of Example code for Multiplayer Activity. ///
/// Calls PUT /titles/{titleId}/users/{xuid}/activites STDAPI XblMultiplayerActivitySetActivityAsync( _In_ XblContextHandle xblContext, _In_ const XblMultiplayerActivityInfo* activityInfo, _In_ bool allowCrossPlatformJoin, _In_ XAsyncBlock* async ) XBL_NOEXCEPT; /// /// Get the multiplayer activity for a set of users. You can query at most 30 users with each call. /// /// {% term xbox-live %} context for the local user. /// List of Xbox user IDs for whom to get multiplayer activity. /// Size of the `xuids` array. /// The `XAsyncBlock` for this operation. /// HRESULT return code for this API operation. /// /// To get the result, call and /// inside the `XAsyncBlock` callback or /// after the async operation is complete.

/// To see how this function is used, see "Getting activities" in /// the Activities section /// of Example code for Multiplayer Activity.

/// For more information about multiplayer activities, see /// Activities. ///
/// Calls POST /titles/{titleId}/activities/query STDAPI XblMultiplayerActivityGetActivityAsync( _In_ XblContextHandle xblContext, _In_reads_(xuidsCount) const uint64_t* xuids, _In_ size_t xuidsCount, _In_ XAsyncBlock* async ) XBL_NOEXCEPT; /// /// Gets the buffer size needed to store the results of a get activity call. /// /// The `XAsyncBlock` for this operation. /// The size in bytes for the result buffer. /// HRESULT return code for this API operation. /// /// To see how this function is used, see "Getting activities" in /// the Activities section /// of Example code for Multiplayer Activity.

/// For more information about multiplayer activities, see /// Activities. ///
/// /// STDAPI XblMultiplayerActivityGetActivityResultSize( _In_ XAsyncBlock* async, _Out_ size_t* resultSizeInBytes ) XBL_NOEXCEPT; /// /// Gets the results for a successful get activity call. /// /// The `XAsyncBlock` for this operation. /// The size of the result buffer. /// Use to get the required buffer size. /// A caller-allocated byte buffer that receives the result. /// Strongly typed array of that /// points into `buffer`. Do not free this array. Its lifecycle is tied to `buffer`. /// /// The number of entries in the `ptrToBufferResults` array. /// The number of bytes in `buffer` that were used. /// HRESULT return code for this API operation. /// /// To get the size of the buffer that you need to store the results, call the /// function.

/// To see how this function is used, see "Getting activities" in /// the Activities section /// of Example code for Multiplayer Activity.

/// For more information about multiplayer activities, see /// Activities. ///
STDAPI XblMultiplayerActivityGetActivityResult( _In_ XAsyncBlock* async, _In_ size_t bufferSize, _Out_writes_bytes_to_(bufferSize, *bufferUsed) void* buffer, _Outptr_ XblMultiplayerActivityInfo** ptrToBufferResults, _Out_ size_t* resultCount, _Out_opt_ size_t* bufferUsed ) XBL_NOEXCEPT; /// /// Clears the multiplayer activity for the local user. /// /// {% term xbox-live %} context for the local user. /// The `XAsyncBlock` for this operation. /// HRESULT return code for this API operation. /// /// Titles should delete the activity for a user as soon as they leave the multiplayer activity.

/// If the title does not delete a user's activity, it is automatically cleared by a presence check.

/// To get the result of the asynchronous operation, call /// inside the `XAsyncBlock` callback or after `XAsyncBlock` is complete.

/// To see how this function is used, see "Deleting an activity" in /// the Activities section /// of Example code for Multiplayer Activity.

/// For more information about multiplayer activities, see /// Activities. ///
/// Calls DELETE /titles/{titleId}/users/{xuid}/activites STDAPI XblMultiplayerActivityDeleteActivityAsync( _In_ XblContextHandle xblContext, _In_ XAsyncBlock* async ) XBL_NOEXCEPT; /// /// Sends invites to Xbox user IDs to join the caller's current activity. /// /// {% term xbox-live %} context for the local user. /// List of Xbox user IDs to invite. /// Size of the `xuids` array. /// If the title is configured for cross-platform invites, /// setting this parameter to true sends an invite to all platform endpoints that the title supports. /// If set to false, the invite is sent to the sender's platform platform only. /// If cross-platform invites are not configured, the invite is always sent to the sender's platform only. /// (Optional) Connection string that the peer uses to join the game. /// The `XAsyncBlock` for this operation. /// HRESULT return code for this API operation. /// /// To get the result of the asynchronous operation, call /// inside the `XAsyncBlock` callback or after `XAsyncBlock` is complete.

/// To see how this function is used, see "Sending invites" in /// the Invites section /// of Example code for Multiplayer Activity.

/// For more information about multiplayer activities, see /// Activities. ///
/// Calls POST /titles/{titleId}/invites STDAPI XblMultiplayerActivitySendInvitesAsync( _In_ XblContextHandle xblContext, _In_ const uint64_t* xuids, _In_ size_t xuidsCount, _In_ bool allowCrossPlatformJoin, _In_opt_z_ const char* connectionString, _In_ XAsyncBlock* async ) XBL_NOEXCEPT; #if HC_PLATFORM == HC_PLATFORM_WIN32 || HC_PLATFORM_IS_EXTERNAL /// /// Describes multiplayer activity invites. /// /// /// /// For more information about multiplayer activities, see /// Activities. /// typedef struct XblMultiplayerActivityInviteData { /// /// The Xbox user ID of the invited user. /// uint64_t invitedXuid; /// /// The Xbox user ID of the user sending the invite. /// uint64_t senderXuid; /// /// URL of the sender's gamerpic. /// _Field_z_ const char* senderImageUrl; /// /// The UTF-8 encoded gamertag of the player. /// char senderGamertag[XBL_GAMERTAG_CHAR_SIZE]; /// /// The UTF-8 encoded modern gamertag of the player. /// Not guaranteed to be unique. /// char senderModernGamertag[XBL_MODERN_GAMERTAG_CHAR_SIZE]; /// /// The UTF-8 encoded suffix appended to the modern gamertag to ensure uniqueness. /// Can be empty in some cases. /// char senderModernGamertagSuffix[XBL_MODERN_GAMERTAG_SUFFIX_CHAR_SIZE]; /// /// The UTF-8 encoded unique modern gamertag and suffix. /// Format is "modernGamertag#suffix". /// Guaranteed to be no more than 16 rendered characters. /// char senderUniqueModernGamertag[XBL_UNIQUE_MODERN_GAMERTAG_CHAR_SIZE]; /// /// The name of the title for which the invite is being sent. /// _Field_z_ const char* titleName; /// /// URL of the title image used in the invite. /// _Field_z_ const char* titleImageUrl; /// /// Connection string to pass to the connecting client so that it can join a game. /// Typically contains information such as the server IP address. /// _Field_z_ const char* connectionString; /// /// Expiration time. /// time_t expirationTime; } XblMultiplayerActivityInviteData; /// /// Event handler for multiplayer activity invites. /// /// Data needed by the invitee to respond to a game invite. /// Client context provided when the handler was added. /// /// /// The lifetime of the object is limited to the callback.

/// For more information about multiplayer activities, see /// Activities. ///
typedef void CALLBACK XblMultiplayerActivityInviteHandler( _In_ const XblMultiplayerActivityInviteData* data, _In_opt_ void* context ); /// /// Registers an event handler for multiplayer activity invites. /// /// {% term xbox-live %} context for the local user. /// The callback function that receives notifications. /// Caller context to be passed to the handler. /// An `XblFunctionContext` object that can be used to unregister the event handler. /// /// To unregister an event handler for multiplayer activity invites, call the /// function.

/// For more information about multiplayer activities, see /// Activities. ///
STDAPI_(XblFunctionContext) XblMultiplayerActivityAddInviteHandler( _In_ XblContextHandle xblContextHandle, _In_ XblMultiplayerActivityInviteHandler* handler, _In_opt_ void* context ) XBL_NOEXCEPT; /// /// Unregisters an event handler for multiplayer activity invites. /// /// {% term xbox-live %} context for the local user. /// The `XblFunctionContext` object that was returned when the event handler was registered. /// /// /// To register an event handler for multiplayer activity invites, call the /// function.

/// For more information about multiplayer activities, see /// Activities. ///
STDAPI XblMultiplayerActivityRemoveInviteHandler( _In_ XblContextHandle xblContextHandle, _In_ XblFunctionContext token ) XBL_NOEXCEPT; #endif } ================================================ FILE: Include/xsapi-c/multiplayer_c.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #if !defined(__cplusplus) #error C++11 required #endif #pragma once #include "pal.h" extern "C" { /// /// Defines values that indicate the state of a tournament game. /// DEPRECATED. This enumeration will be removed in a future release. /// /// enum class XblTournamentGameResult : uint32_t { /// /// No game. /// NoContest, /// /// Win. /// Win, /// /// Loss. /// Loss, /// /// Draw. /// Draw, /// /// Rank. /// Rank, /// /// Didn't show up. /// NoShow, }; /// /// Defines values that indicate the arbitration state of a tournament game. /// DEPRECATED. This enumeration will be removed in a future release. /// /// enum class XblTournamentArbitrationState : uint32_t { /// /// No arbitration state is set. /// None, /// /// Results were fully uploaded and complete. /// Completed, /// /// The match was canceled, such as in the case of forfeiting. /// Canceled, /// /// The match began, but no players or servers reported results before the arbitration deadline. /// NoResults, /// /// Some results were received, and results were compiled based on the incomplete data. /// PartialResults }; /// /// Defines values that indicate the source for a tournament game state. /// DEPRECATED. This enumeration will be removed in a future release. /// /// /// enum class XblTournamentGameResultSource : uint32_t { /// /// No game result source. /// None, /// /// Game result is determined by client arbitration. /// Arbitration, /// /// Game result is determined by game servers. /// Server, /// /// Game result is adjusted by tournament administrator. /// Adjusted, }; /// /// Defines values that indicate the status of a tournament game result. /// DEPRECATED. This enumeration will be removed in a future release. /// /// /// enum class XblTournamentArbitrationStatus : uint32_t { /// /// The system time is before the arbitration start time (`ArbitrationStartTime`), which is also the start time for the match. /// Waiting, /// /// The system time is after the arbitration start time (`ArbitrationStartTime`), and at least one player has become active. /// InProgress, /// /// The player reported results, so the player's role in the arbitration process is now done. /// Occurs when arbitration succeeds, after the arbitration forfeit time (`ArbitrationStartTime` /// plus the `ForfeitTimeout` delta) if no players have joined, or after arbitration time-out /// (`ArbitrationStartTime` plus the `ArbitrationTimeout` delta). /// Complete, /// /// The player has become active at least once and is now participating in the match. /// Playing, /// /// The player was not able to upload results before arbitration time-out (`ArbitrationStartTime` plus the `ArbitrationTimeout` delta). /// Incomplete, /// /// The system time is after the arbitration start time (`ArbitrationStartTime`), but the player is not yet active. /// Joining }; /// /// Defines values that indicate the team session registration state for a tournament. /// DEPRECATED. This enumeration will be removed in a future release. /// /// enum class XblTournamentRegistrationState : uint32_t { /// /// The team registration state is unknown. /// Unknown, /// /// Registration was successfully received by the Tournament service and will be eventually processed. /// Pending, /// /// Registration for the team was withdrawn. /// Withdrawn, /// /// Registration could not be performed for the team. /// Rejected, /// /// Registration has been confirmed by the Tournament service. /// Registered, /// /// The team completed its participation in the tournament. /// Completed }; /// /// Defines values that indicate reasons why the team is under selected tournament registration state. /// DEPRECATED. It will be removed in a future release /// /// enum class XblTournamentRegistrationReason : uint32_t { /// /// The reason is unknown. /// Unknown, /// /// The registration for this tournament is closed. /// RegistrationClosed, /// /// One of the team members is already registered for this tournament. /// MemberAlreadyRegistered, /// /// The tournament has reached its team registration limit. /// TournamentFull, /// /// The team has been eliminated from the tournament. /// TeamEliminated, /// /// The tournament is completed. /// TournamentCompleted }; /// /// Defines values that indicate the visibility or accessibility of a session. /// /// /// /// /// /// /// /// /// /// For more information, /// see Game session visibility and joinability /// and the Visibility and joinability section /// of Multiplayer Session advanced topics. /// enum class XblMultiplayerSessionVisibility : uint32_t { /// /// The status is unknown. /// Unknown, /// /// Ignore the session visibility filter. /// Any, /// /// The session is private and is not visible to players who aren't in the session. /// Attempting to join causes the service to return HTTP_E_STATUS_FORBIDDEN (403). /// PrivateSession, /// /// The session is visible to players who aren't in the session, but the session is /// read-only to them and they can't join. /// Attempting to join causes the service to return HTTP_E_STATUS_BAD_REQUEST (400). /// Visible, /// /// The session is full and cannot be joined by anyone. /// Attempting to join causes the service to return HTTP_E_STATUS_BAD_REQUEST (400). /// Full, /// /// The session is open and can be joined by anyone. /// Open }; /// /// Defines values that indicate the initialization stage of a session during managed initialization. /// /// /// /// For more information about managed initialization, see the "Managed initialization" section /// of Target session initialization and QoS. /// enum class XblMultiplayerInitializationStage : uint32_t { /// /// Stage not known. /// Unknown, /// /// Stage not set. /// None, /// /// Joining stage. Typically, matchmaking creates a session and adds users to the session. /// The client has until the joining timeout to join the session during this stage. /// Joining, /// /// Measuring stage. Quality of Service (QoS) measurement happens during this stage. /// If the title is manually managing QoS, the title handles this stage. /// Otherwise, the {% term xbox-live-party %} system handles this stage /// when calling `RegisterGameSession` or `RegisterMatchSession`. /// Measuring, /// /// Evaluating stage. If `externalEvaluation` is false, this stage is skipped. /// Otherwise, the title does its own evaluation. /// Evaluating, /// /// Failed stage. If the first initialization episode didn't succeed, the session can't be initialized. /// Failed }; /// /// Defines values that indicate the type of metric used to measure matchmaking Quality of Service (QoS) for a session. /// /// /// /// For more information, see Target session initialization and QoS. /// enum class XblMultiplayerMetrics : uint32_t { /// /// Unknown. /// Unknown, /// /// Upstream (peer-to-host) bandwidth. /// BandwidthUp, /// /// Downstream (host-to-peer) bandwidth. /// BandwidthDown, /// /// Combined bandwidth. /// Bandwidth, /// /// Upstream (peer-to-host) latency. /// Latency }; /// /// Defines values that indicate the current network address translation (NAT) settings for /// a console connecting to {% term xbox-live %}. /// /// enum class XblNetworkAddressTranslationSetting : uint32_t { /// /// The server returned an unrecognized response. /// Unknown, /// /// Can connect with any consoles regardless of their NAT settings. /// Open, /// /// Can connect only with consoles that use Moderate or Open settings. /// Moderate, /// /// Can connect only with consoles that use Open NAT settings. /// Strict }; /// /// Defines values that indicate why Quality of Service (QoS) measurement failed during session initialization. /// /// /// /// /// For more information, see Target session initialization and QoS. /// enum class XblMultiplayerMeasurementFailure : uint32_t { /// /// Unknown measurement failure. /// Unknown, /// /// No measurement failure. /// None, /// /// Measurement timed out. /// Timeout, /// /// Measurement of latency failed. /// Latency, /// /// Measurement of upstream (peer-to-host) bandwidth failed. /// BandwidthUp, /// /// Measurement of downstream (host-to-peer) bandwidth failed. /// BandwidthDown, /// /// Measurement failed for this player failed because measurement failed for another player in the group. /// Group, /// /// Measurement failed due to a network error; for example, the player was unreachable. /// Network, /// /// Measurement failed because the initialization episode failed. /// This likely happened because not enough users were in the session. /// Episode }; /// /// Defines values that indicate the current status of a session. /// /// /// /// For more information, see /// the Session user states section /// of Multiplayer Session advanced topics. /// enum class XblMultiplayerSessionStatus : uint32_t { /// /// The server returned an unrecognized response. /// Unknown, /// /// At least one player is active in the session. /// Active, /// /// No players are active in the session or all players left the session. /// Inactive, /// /// One or more players have not accepted the session invite. /// Reserved }; /// /// Defines values that indicate restrictions on the users who can join a session. /// enum class XblMultiplayerSessionRestriction : uint32_t { /// /// Unknown restriction type. /// Unknown, /// /// No restrictions. /// None, /// /// Only players whose token `DeviceId` values match the `DeviceId` of a player /// who is already in the session and active. /// Local, /// /// Only local players (as defined for `Local`) and players who are followed by an /// existing (not reserved) member of the session can join without a reservation. /// Followed }; /// /// Defines values that indicate the status of a matchmaking request for a session. /// /// enum class XblMatchmakingStatus : uint32_t { /// /// The server returned an unrecognized response. /// Unknown, /// /// The matchmaking search is not specified. /// This status is optional and requires the `clientMatchmaking` capability. /// None, /// /// The matchmaking search is still searching. /// Searching, /// /// The matchmaking search has expired. /// Expired, /// /// The matchmaking search found a session. /// Found, /// /// The matchmaking search was canceled. /// Canceled }; /// /// Defines values that indicate the status of a member of a session. /// /// /// /// enum class XblMultiplayerSessionMemberStatus : uint32_t { /// /// The member is reserved for a specific Xbox user ID. /// The specified member must join the session to fill the reservation. /// If a reserved member doesn't join before the end of `JoinTimeout` (in /// the structure), the member is removed from the session. /// Reserved, /// /// The member is inactive in the current title. /// The member may be active in another title, as specified by `ActiveTitleId` in /// the structure. /// If an inactive member doesn't mark themselves as active before the end of `MemberInactiveTimeout` /// (in the structure), the member is removed from the session. /// Inactive, /// /// When the shell launches the title to start a multiplayer game, the member is marked as ready. /// If a ready member doesn't mark themselves as active before the end of `MemberReadyTimeout` (in /// the structure), the member is marked as inactive. /// Ready, /// /// The member is active in the current title. /// Active }; /// /// Defines values that indicate the mode used when creating or writing to a multiplayer session. /// /// /// enum class XblMultiplayerSessionWriteMode : uint32_t { /// /// Create a multiplayer session. /// Fails if the session already exists. /// CreateNew, /// /// Either update or create a session. /// Doesn't care whether the session exists. /// UpdateOrCreateNew, /// /// Updates an existing multiplayer session. /// Fails if the session doesn't exist. /// UpdateExisting, /// /// Updates an existing multiplayer session. /// Fails with HTTP_E_STATUS_PRECOND_FAILED (HTTP status 412) if the ETag on the local session doesn't match the ETag on the server. /// Fails if the session does not exist. /// SynchronizedUpdate, }; /// /// Defines values that indicate the write status of a multiplayer session. /// enum class XblWriteSessionStatus : uint32_t { /// /// Unknown write result. /// Unknown, /// /// User does not have permission to write to the session (HTTP status 403). /// AccessDenied, /// /// The write operation created the session (HTTP status 201). /// Created, /// /// A conflict occurred during the write operation (HTTP status 409). /// Conflict, /// /// The session was not found (HTTP status 404). /// HandleNotFound, /// /// The session was updated by another user (HTTP status 412). /// OutOfSync, /// /// The session was deleted successfully (HTTP status 204). /// SessionDeleted, /// /// The session was updated successfully (HTTP status 200). /// Updated }; /// /// Defines values that indicate change types for a multiplayer session. /// /// enum class XblMultiplayerSessionChangeTypes : uint32_t { /// /// None. /// None = 0x0000, /// /// Changes to anything in the session. /// Everything = 0x0001, /// /// Changes to the host device token. /// HostDeviceTokenChange = 0x0002, /// /// Changes to the stage of initialization. /// InitializationStateChange = 0x0004, /// /// Changes to the matchmaking status, such as match found or match expired. /// MatchmakingStatusChange = 0x0008, /// /// A member joined the session. /// MemberListChange = 0x0010, /// /// A member left the session. /// MemberStatusChange = 0x0020, /// /// Changes to the joinability () of the session. /// SessionJoinabilityChange = 0x0040, /// /// Changes in the custom properties of the session. /// CustomPropertyChange = 0x0080, /// /// Changes in the custom properties of any of the members. /// MemberCustomPropertyChange = 0x0100, /// /// Changes in tournament server properties, such as next game, last game, or registration. /// DEPRECATED. This value will be removed in a future release. /// TournamentPropertyChange = 0x0200, /// /// Changes in arbitration server properties, such as game results. /// DEPRECATED. This value will be removed in a future release. /// ArbitrationPropertyChange = 0x0400 }; DEFINE_ENUM_FLAG_OPERATORS(XblMultiplayerSessionChangeTypes); /// /// Defines values that indicate which multiplayer role settings are mutable. /// /// /// /// Only the session owner can modify role settings and only those that are set /// in `XblMultiplayerRoleType::MutableRoleSettings`. /// You can set `XblMutableRoleSettings` in the session template. /// enum class XblMutableRoleSettings : uint32_t { /// /// None of the role settings are mutable. /// None = 0x0, /// /// Allows you to set the maximum number of players that can fill the role. /// Max = 0x1, /// /// Allows you to set the target number of players that should fill the role. /// Target = 0x2 }; DEFINE_ENUM_FLAG_OPERATORS(XblMutableRoleSettings); #define XBL_TOURNAMENT_REFERENCE_DEFINITION_NAME_MAX_LENGTH 100 #define XBL_TOURNAMENT_REFERENCE_ORGANIZER_LENGTH 100 /// /// Represents a reference to a tournament reference. /// DEPRECATED. This structure will be removed in a future release. /// /// typedef struct XBL_DEPRECATED XblTournamentReference { /// /// The definition name of the tournament. /// _Null_terminated_ char DefinitionName[XBL_TOURNAMENT_REFERENCE_DEFINITION_NAME_MAX_LENGTH]; /// /// The tournament ID specific to the tournament. /// _Null_terminated_ char TournamentId[XBL_GUID_LENGTH]; /// /// The name of the tournament organizer. /// _Null_terminated_ char Organizer[XBL_TOURNAMENT_REFERENCE_ORGANIZER_LENGTH]; /// /// The service configuration ID specific to the tournament. /// _Null_terminated_ char Scid[XBL_SCID_LENGTH]; } XblTournamentReference; /// /// Represents the result of a multiplayer game. /// DEPRECATED. This structure will be removed in a future release. /// typedef struct XBL_DEPRECATED XblTournamentGameResultWithRank { /// /// The result for the team. /// XblTournamentGameResult Result; /// /// The ranking of the result. Applies only when `Result` is `XblTournamentGameResult::Rank`. /// uint64_t Ranking; } XblTournamentGameResultWithRank; /// /// Represents a team result for a multiplayer game. /// DEPRECATED. This structure will be removed in a future release. /// typedef struct XBL_DEPRECATED XblTournamentTeamResult { /// /// Name of the team. /// const char* Team; XBL_WARNING_PUSH XBL_WARNING_DISABLE_DEPRECATED /// /// The game result. /// XblTournamentGameResultWithRank GameResult; XBL_WARNING_POP } XblTournamentTeamResult; /// /// Represents requirements for each connection between a host candidate and session members. /// /// /// /// /// For more information, see Target session initialization and QoS. /// typedef struct XblMultiplayerPeerToHostRequirements { /// /// The maximum latency, in milliseconds, of the upstream (peer-to-host) connection. /// uint64_t LatencyMaximum; /// /// The minimum bandwidth, in kilobits per second, of the downstream (host-to-peer) connection. /// uint64_t BandwidthDownMinimumInKbps; /// /// The minimum bandwidth, in kilobits per second, of the upstream (peer-to-host) connection. /// uint64_t BandwidthUpMinimumInKbps; /// /// The metric used to select the host. /// XblMultiplayerMetrics HostSelectionMetric; } XblMultiplayerPeerToHostRequirements; /// /// Represents requirements for a connection between session members. /// /// /// typedef struct XblMultiplayerPeerToPeerRequirements { /// /// The maximum latency, in milliseconds, for the peer-to-peer connection. /// uint64_t LatencyMaximum; /// /// The minimum bandwidth, in kilobits per second, for the peer-to-peer connection. /// uint64_t BandwidthMinimumInKbps; } XblMultiplayerPeerToPeerRequirements; /// /// Represents requirements for a new Multiplayer service session. /// /// /// typedef struct XblMultiplayerMemberInitialization { /// /// Maximum time, in milliseconds, for the joining stage of the Quality of Service (QoS) process. /// uint64_t JoinTimeout; /// /// Maximum time, in milliseconds, for the measurement stage of the QoS process. /// uint64_t MeasurementTimeout; /// /// Maximum time, in milliseconds, for the evaluation stage of the QoS process. /// uint64_t EvaluationTimeout; /// /// When set to true, indicates that the title performs the evaluation stage. /// bool ExternalEvaluation; /// /// Minimum number of members for the session. Defaults to 2. Must be between 1 and `maxMemberCount`. /// Applies only to the joining stage. /// uint32_t MembersNeededToStart; } XblMultiplayerMemberInitialization; /// /// Represents the capabilities of a Multiplayer service session. /// /// /// /// /// Session capabilities are optional Boolean values that are set in the session template. /// If no capabilities are needed, an empty `SessionCapabilities` object should be in the template /// to prevent capabilities from being specified at session creation, unless the title /// requires dynamic session capabilities.

/// For more information, see the Session capabilities /// section of Multiplayer Session advanced topics. ///
typedef struct XblMultiplayerSessionCapabilities { /// /// Indicates whether a session can enable metrics and session members can set `secureDeviceAddress`. /// If false, the session can't enable any metrics, and session members can't set `secureDeviceAddress`. /// bool Connectivity; /// /// If true, team capability is set on the session for a tournament. /// DEPRECATED. This member will be removed in a future release. /// XBL_DEPRECATED bool Team; /// /// If true, arbitration capability is set on the session for a tournament. /// DEPRECATED. This member will be removed in a future release. /// XBL_DEPRECATED bool Arbitration; /// /// If false (the default value), active users are required to remain online playing the title. /// If they don't, they are demoted to inactive status. /// Set this flag to true to enable session members to stay active indefinitely. /// bool SuppressPresenceActivityCheck; /// /// Indicates whether the session represents actual gameplay rather than time in setup or /// a menu, such a lobby or during matchmaking. /// If true, the session is in gameplay mode. /// bool Gameplay; /// /// If true, the session can host 101 to 1000 users, which affects other session features. /// If false, the session can host 1 to 100 users. For more information, see /// the Session size section /// of Multiplayer Session advanced topics. /// bool Large; /// /// If true, a connection is required for a member to be marked as active. To enable session notifications /// and detect disconnections, this member must be set to true. For more information, see /// the Subscribe for MPSD session change notifications section /// of Multiplayer tasks. /// bool ConnectionRequiredForActiveMembers; /// /// If true, the session supports calls from platforms without strong title identity. /// This capability can't be set on large sessions. /// bool UserAuthorizationStyle; /// /// If true, the session supports crossplay between {% term platform_windows %} PC and Xbox. /// bool Crossplay; /// /// If true, the session can be linked to a search handle for searching. /// bool Searchable; /// /// If true, the session has owners. For a session to be searchable when `UserAuthorizationStyle` is /// true, the session must have owners. /// bool HasOwners; } XblMultiplayerSessionCapabilities; /// /// Represents constants for a multiplayer session. /// /// /// Session constants are set by the creator or by the session template only when a session is created. /// typedef struct XblMultiplayerSessionConstants { /// /// The maximum number of members in the session. /// uint32_t MaxMembersInSession; /// /// The visibility of the session. /// XblMultiplayerSessionVisibility Visibility; /// /// A collection of Xbox user IDs indicating who initiated the session. (Optional) /// uint64_t* InitiatorXuids; /// /// The number of entries in the `InitiatorXuids` array. /// size_t InitiatorXuidsCount; /// /// Any custom constants for the session, specified in a JSON string. /// These constants can't be changed after the session is created. (Optional) /// const char* CustomJson; /// /// The Cloud Compute package constants for the session, specified in a JSON string. /// These constants can't be changed after the session is created. (Optional) /// const char* SessionCloudComputePackageConstantsJson; /// /// Maximum time, in milliseconds, for a member with a reservation to join the session. /// If the member doesn't join within this time, the reservation is removed. /// uint64_t MemberReservedTimeout; /// /// Maximum time, in milliseconds, for an inactive member to become active. /// If an inactive member doesn't become active within this time, the member is removed from the session. /// uint64_t MemberInactiveTimeout; /// /// Maximum time, in milliseconds, for a member who is marked as ready to become active. /// When the shell launches the title to start a multiplayer game, the member is marked as ready. /// If a member who is marked as ready doesn't become active within this time, the member becomes inactive. /// uint64_t MemberReadyTimeout; /// /// Maximum time, in milliseconds, that the session can remain empty. /// If no members join the session within this time, the session is deleted. /// uint64_t SessionEmptyTimeout; /// /// Delta, in milliseconds, from start time that represents the time at which results are finalized. /// If no one (client or server) has reported at this time, we declare the match results incomplete. /// DEPRECATED. This member will be removed in a future release. /// XBL_DEPRECATED uint64_t ArbitrationTimeout; /// /// Delta, in milliseconds, from start time that represents the time at which, if the session has /// no active users, the match is canceled. /// DEPRECATED. This member will be removed in a future release. /// XBL_DEPRECATED uint64_t ForfeitTimeout; /// /// If true, indicates that the title wants latency measured to help determine connectivity. /// Requires `capabilities.connectivity` to be true. /// bool EnableMetricsLatency; /// /// If true, indicates that the title wants downstream (host-to-peer) bandwidth measured to help /// determine connectivity. Requires `capabilities.connectivity` to be true. /// bool EnableMetricsBandwidthDown; /// /// If true, indicates that the title wants upstream (peer-to-host) bandwidth measured to help /// determine connectivity. Requires `capabilities.connectivity` to be true. /// bool EnableMetricsBandwidthUp; /// /// If true, indicates that the title wants a custom measurement to help determine connectivity. /// Requires `capabilities.connectivity` to be true. /// bool EnableMetricsCustom; /// /// If set, the session expects the client system or title to perform initialization after session creation. /// Timeouts and initialization stages are automatically tracked by the session, including /// initial Quality of Service (QoS) measurements if any metrics are set. /// XblMultiplayerMemberInitialization* MemberInitialization; /// /// QoS requirements for a connection between session members. /// XblMultiplayerPeerToPeerRequirements PeerToPeerRequirements; /// /// QoS requirements for a connection between a host candidate and session members. /// XblMultiplayerPeerToHostRequirements PeerToHostRequirements; /// /// The set of potential server connection strings that should be evaluated. /// const char* MeasurementServerAddressesJson; /// /// Indicates whether the matchmaking status fields can be written to. /// bool ClientMatchmakingCapable; /// /// The capabilities of the session. /// XblMultiplayerSessionCapabilities SessionCapabilities; } XblMultiplayerSessionConstants; #define XBL_MULTIPLAYER_DEVICE_TOKEN_MAX_LENGTH 40 #define XBL_MULTIPLAYER_SESSION_TEMPLATE_NAME_MAX_LENGTH 100 #define XBL_MULTIPLAYER_SESSION_NAME_MAX_LENGTH XBL_MULTIPLAYER_SESSION_TEMPLATE_NAME_MAX_LENGTH /// /// Represents a handle ID of a multiplayer session. /// /// /// /// Multiplayer Session Directory (MPSD) can create various handles that refer to a session. /// They are immutable and can only be created, read, and deleted. /// Note that this handle ID references to a service side object. /// typedef struct XblMultiplayerSessionHandleId { /// /// The ID of the handle that MSPD created. /// _Null_terminated_ char value[XBL_GUID_LENGTH]; } XblMultiplayerSessionHandleId; /// /// Represents a reference to a multiplayer session. /// typedef struct XblMultiplayerSessionReference { /// /// The service configuration ID (SCID) specific to the title. /// _Null_terminated_ char Scid[XBL_SCID_LENGTH]; /// /// The name of the template for the session. /// _Null_terminated_ char SessionTemplateName[XBL_MULTIPLAYER_SESSION_TEMPLATE_NAME_MAX_LENGTH]; /// /// The name of the session. /// _Null_terminated_ char SessionName[XBL_MULTIPLAYER_SESSION_NAME_MAX_LENGTH]; } XblMultiplayerSessionReference; #define XBL_MULTIPLAYER_SESSION_REFERENCE_URI_MAX_LENGTH (44 + XBL_SCID_LENGTH + XBL_MULTIPLAYER_SESSION_TEMPLATE_NAME_MAX_LENGTH + XBL_MULTIPLAYER_SESSION_NAME_MAX_LENGTH) /// /// Represents a URI path representation of a session reference. /// /// /// The format of the URI path /// is `/serviceconfigs/{scid}/sessiontemplates/{session-template-name}/sessions/{session-name}`, where /// `{scid}` is the service configuration ID specific to the title. /// /// typedef struct XblMultiplayerSessionReferenceUri { /// /// The URI path. /// _Null_terminated_ char value[XBL_MULTIPLAYER_SESSION_REFERENCE_URI_MAX_LENGTH]; } XblMultiplayerSessionReferenceUri; /// /// Creates an object from a service configuration ID (SCID), session /// template name, and session name. /// /// The SCID that the session is a part of. The SCID is case-sensitive, so paste it directly /// from Partner Center. /// The name of the session template. /// The name of the session. /// A reference to the multiplayer session. STDAPI_(XblMultiplayerSessionReference) XblMultiplayerSessionReferenceCreate( _In_z_ const char* scid, _In_z_ const char* sessionTemplateName, _In_z_ const char* sessionName ) XBL_NOEXCEPT; /// /// Returns the session reference parsed from a URI. /// /// The URI path. /// Passes back the session reference. /// HRESULT return code for this API operation. STDAPI XblMultiplayerSessionReferenceParseFromUriPath( _In_ const char* path, _Out_ XblMultiplayerSessionReference* sessionReference ) XBL_NOEXCEPT; /// /// Returns the URI path representation of a session reference. /// /// A session reference. /// Passes back the URI representation of the session reference. /// HRESULT return code for this API operation. STDAPI XblMultiplayerSessionReferenceToUriPath( _In_ const XblMultiplayerSessionReference* sessionReference, _Out_ XblMultiplayerSessionReferenceUri* sessionReferenceUri ) XBL_NOEXCEPT; /// /// Verifies whether an object is well formed. /// /// The session reference. /// Returns true if session is well formed, false if session is not well formed. /// /// An object is considered to be well formed if none /// of the fields are empty strings. /// STDAPI_(bool) XblMultiplayerSessionReferenceIsValid( _In_ const XblMultiplayerSessionReference* sessionReference ) XBL_NOEXCEPT; /// /// Represents the matchmaking server that supports a multiplayer session. /// typedef struct XblMultiplayerMatchmakingServer { /// /// The status of the matchmaking server. /// XblMatchmakingStatus Status; /// /// The status details, if any, of the matchmaking server. /// const char* StatusDetails; /// /// The typical wait time, in seconds. /// uint32_t TypicalWaitInSeconds; /// /// A reference to the target session. /// XblMultiplayerSessionReference TargetSessionRef; } XblMultiplayerMatchmakingServer; /// /// Represents the arbitration server that supports a multiplayer session. /// DEPRECATED. This structure will be removed in a future release. /// typedef struct XBL_DEPRECATED XblMultiplayerArbitrationServer { /// /// The start time of the match. This time is also when arbitration starts. /// time_t ArbitrationStartTime; /// /// The state of the result. /// XblTournamentArbitrationState ResultState; /// /// The source of the result. /// XblTournamentGameResultSource ResultSource; /// /// A value from 0 through 100 that indicates the confidence level of the result. /// uint32_t ResultConfidenceLevel; XBL_WARNING_PUSH XBL_WARNING_DISABLE_DEPRECATED /// /// The results of the game. /// XblTournamentTeamResult* Results; XBL_WARNING_POP /// /// The number of results. /// size_t ResultsCount; } XblMultiplayerArbitrationServer; /// /// Represents a team in a tournament. /// DEPRECATED. This structure will be removed in a future release. /// typedef struct XBL_DEPRECATED XblMultiplayerTournamentTeam { /// /// ID of the team. /// const char* TeamId; /// /// Session reference of the session the team is in. /// XblMultiplayerSessionReference TeamSessionReference; } XblMultiplayerTournamentTeam; /// /// Represents the tournament server that supports a multiplayer session. /// DEPRECATED. This structure will be removed in a future release. /// typedef struct XBL_DEPRECATED XblMultiplayerTournamentsServer { XBL_WARNING_PUSH XBL_WARNING_DISABLE_DEPRECATED /// /// The tournament reference. /// XblTournamentReference TournamentReference; XBL_WARNING_POP XBL_WARNING_PUSH XBL_WARNING_DISABLE_DEPRECATED /// /// The teams in the tournament. /// XblMultiplayerTournamentTeam* Teams; XBL_WARNING_POP /// /// The number of teams in the tournament. /// size_t TeamsCount; /// /// The registration state of the team. /// XblTournamentRegistrationState RegistrationState; /// /// The reason for the registration state. /// XblTournamentRegistrationReason RegistrationReason; /// /// The start time of the next game in the tournament. /// time_t NextGameStartTime; /// /// The session reference of the next game in the tournament. /// XblMultiplayerSessionReference NextGameSessionReference; /// /// The end time of the last game in the tournament. /// time_t LastGameEndTime; XBL_WARNING_PUSH XBL_WARNING_DISABLE_DEPRECATED /// /// The result of the last game in the tournament. /// XblTournamentTeamResult LastTeamResult; XBL_WARNING_POP /// /// The source of the result of the last game in the tournament. /// XblTournamentGameResultSource LastGameResultSource; } XblMultiplayerTournamentsServer; /// /// Represents a category of roles for a multiplayer session. /// /// /// /// /// For more information, see Multiplayer roles. /// typedef struct XblMultiplayerRoleType { /// /// Name of the role type. /// const char* Name; /// /// If true, only the owner of the session can assign this role to members. /// bool OwnerManaged; /// /// The settings (for roles in this role type) that can be modified throughout the life of the session. /// Exclude role settings to lock them. /// XblMutableRoleSettings MutableRoleSettings; /// /// A collection of roles for this role type. /// struct XblMultiplayerRole* Roles; /// /// The number of roles in the `Roles` array. /// size_t RoleCount; } XblMultiplayerRoleType; /// /// Represents role info for a multiplayer role. /// typedef struct XblMultiplayerRole { /// /// The role type that this role belongs too. /// XblMultiplayerRoleType* RoleType; /// /// Name of the role. /// Unique with a role type. /// const char* Name; /// /// Member xbox_user_ids currently assigned for this role. /// uint64_t* MemberXuids; /// /// Number of slots occupied for this role. /// uint32_t MemberCount; /// /// Number of target slots assigned for this role. /// uint32_t TargetCount; /// /// Maximum number of slots available for this role. /// uint32_t MaxMemberCount; } XblMultiplayerRole; /// /// Represents a session member's role in the session. /// typedef struct XblMultiplayerSessionMemberRole { /// /// Role type this role belongs to. /// const char* roleTypeName; /// /// Name of the role. /// const char* roleName; } XblMultiplayerSessionMemberRole; /// /// Represents a users current multiplayer activity, along with some details about the corresponding session. /// typedef struct XblMultiplayerActivityDetails { /// /// Session reference containing identifying information for the session. /// XblMultiplayerSessionReference SessionReference; /// /// HandleId corresponding to this activity. /// char HandleId[XBL_GUID_LENGTH]; /// /// TitleId that should be launched in order to join this activity. /// uint32_t TitleId; /// /// The visibility state of the session. Whether other users can see, or join, etc. /// XblMultiplayerSessionVisibility Visibility; /// /// The join restriction of the session, which applies if visibility is "open". /// XblMultiplayerSessionRestriction JoinRestriction; /// /// Indicates whether the session is temporarily closed for joining. /// bool Closed; /// /// Xbox User ID of the member whose activity this is. /// uint64_t OwnerXuid; /// /// Number of total slots. /// uint32_t MaxMembersCount; /// /// Number of slots occupied. /// uint32_t MembersCount; /// /// String containing custom session properties JSON blob. /// const char* CustomSessionPropertiesJson; } XblMultiplayerActivityDetails; /// /// Token that represents a unique device participating in the session. /// It's a case-insensitive string that can be used for equality comparisons. /// typedef struct XblDeviceToken { /// /// The unique device. /// _Null_terminated_ char Value[XBL_MULTIPLAYER_DEVICE_TOKEN_MAX_LENGTH]; } XblDeviceToken; /// /// Represents a read only reference to member in a multiplayer session. /// typedef struct XblMultiplayerSessionMember { /// /// Id for this member. /// Unique within the context of the session which this member is part of. /// uint32_t MemberId; /// /// Id of this members' team in a tournament. /// DEPRECATED. It will be removed in a future release /// XBL_DEPRECATED const char* TeamId; /// /// Initial team assignment from SmartMatch. /// const char* InitialTeam; /// /// Arbitration Status of a member in a tournament. /// DEPRECATED. It will be removed in a future release /// XBL_DEPRECATED XblTournamentArbitrationStatus ArbitrationStatus; /// /// Xbox User ID of the member. /// Only known if the member has accepted. /// uint64_t Xuid; /// /// JSON string that specify the custom constants for the member. /// const char* CustomConstantsJson; /// /// The base64 encoded secure device address of the member. (Optional) /// const char* SecureDeviceBaseAddress64; /// /// An array of roles for this member. (Optional) /// const XblMultiplayerSessionMemberRole* Roles; /// /// Number of entries in the Roles array. /// size_t RolesCount; /// /// JSON string that specify the custom properties for the member. /// const char* CustomPropertiesJson; /// /// The Gamertag of the member. (Optional) /// Only known if the member has accepted. /// char Gamertag[XBL_GAMERTAG_CHAR_SIZE]; /// /// The status of this member. /// XblMultiplayerSessionMemberStatus Status; /// /// Only true if this member is ready for turn. /// bool IsTurnAvailable; /// /// Indicates if this MultiplayerSessionMember is for the current user. /// bool IsCurrentUser; /// /// Indicates to run QoS initialization for this user. /// Defaults to false. /// Ignored if there is not a "memberInitialization" section for the session. /// bool InitializeRequested; /// /// When match adds a user to a session, it can provide some context around how and why they were matched into the session. /// This is a copy of the user's serverMeasurements from the matchmaking session. /// const char* MatchmakingResultServerMeasurementsJson; /// /// QoS measurements by game-server connection string. /// Like all fields, "serverMeasurements" must be updated as a whole, so it should be set once when measurement is complete. /// If empty, it means that none of the measurements completed within the "serverMeasurementTimeout". /// const char* ServerMeasurementsJson; /// /// A collection of memberIds in my group. /// If a "initializationGroup" list is set, the member's own index will always be added if it isn't already present. /// During managed initialization, if any members in the list fail, this member will also fail. /// const uint32_t* MembersInGroupIds; /// /// The number of entries in the MembersInGroupIds array. /// size_t MembersInGroupCount; /// /// QoS measurements by secure device address. /// Like all fields, "measurements" must be updated as a whole. /// It should be set once when measurement is complete, not incrementally. /// If a "measurements" object is set, it can't contain an entry for the member's own address. /// const char* QosMeasurementsJson; /// /// This is set when the member uploads a secure device address. /// It's a case-insensitive string that can be used for equality comparisons. /// XblDeviceToken DeviceToken; /// /// This is the device's NAT setting when the member uploads a secure device address. /// XblNetworkAddressTranslationSetting Nat; /// /// If the member is active, this is the title ID in which they are active. /// uint32_t ActiveTitleId; /// /// This value is only useful to read when the title is manually managing their own QoS. /// If the "memberInitialization" section is set and the member was added with "initialize":true, /// this is set to the initialization episode that the member will participate in otherwise it is 0. /// Users join sessions in batches. /// The initialization episode number indicates a set of users that QoS needs to be performed against. /// Initialization episode 1 is a special value used for the members added to a new session at create time. /// uint32_t InitializationEpisode; /// /// The time the user joined the session. /// If "reserved" is true, this is the time the reservation was made. /// time_t JoinTime; /// /// The cause of why the initialization failed, or XblMultiplayerMeasurementFailure::None if there was no failure. /// Set when transitioning out of the "joining" or "measuring" stage if this member doesn't pass. /// XblMultiplayerMeasurementFailure InitializationFailureCause; /// /// An array of group names for the current user indicating which groups that user was part of during a multiplayer session. /// const char** Groups; /// /// The number of items in the Groups array. /// size_t GroupsCount; /// /// Gets a list of group names for the current user indicating which groups that user encountered during a multiplayer session. /// const char** Encounters; /// /// The number of items in the Groups array. /// size_t EncountersCount; /// /// The tournament team session reference. /// DEPRECATED. It will be removed in a future release /// XBL_DEPRECATED XblMultiplayerSessionReference TournamentTeamSessionReference; /// /// Internal use only. /// void* Internal; } XblMultiplayerSessionMember; /// /// A set of properties associated with this session. /// Any player can modify these properties. /// typedef struct XblMultiplayerSessionProperties { /// /// A collection of keywords associated with the session (Optional, might be empty). /// const char** Keywords; /// /// The number of keywords. /// size_t KeywordCount; /// /// Restricts who can join "open" sessions. (Has no effect on reservations, which means it has no impact on "private" and "visible" sessions.) /// Defaults to "none". /// If "local", only users whose token's DeviceId matches someone else already in the session and "active": true. /// If "followed", only local users (as defined above) and users who are followed by an existing (not reserved) member of the session can join without a reservation. /// XblMultiplayerSessionRestriction JoinRestriction; /// /// Restricts who can read "open" sessions. (Has no effect on reservations, which means it has no impact on "private" and "visible" sessions.) /// Defaults to "none". /// If "local", only users whose token's DeviceId matches someone else already in the session and "active": true. /// If "followed", only local users (as defined above) and users who are followed by an existing (not reserved) member of the session can read without a reservation. /// The read restriction applies to sessions with "open" or "visible" visibility and determines who can read the session without an invite. /// The read restriction must be at least as accessible as the join restriction, i.e. 'joinRestriction' can't be set to "followed" without also setting 'readRestriction'." /// XblMultiplayerSessionRestriction ReadRestriction; /// /// A collection of session MemberIds indicating whose turn it is. /// uint32_t* TurnCollection; /// /// The number of entries in the TurnCollection array. /// size_t TurnCollectionCount; /// /// A JSON string representing the target session constants. /// const char* MatchmakingTargetSessionConstantsJson; /// /// JSON string that specify the custom properties for the session. /// These can be changed anytime. /// When changing, call multiplayer_service::write_session to write the changes to the service. /// const char* SessionCustomPropertiesJson; /// /// Force a specific connection string to be used. /// This is useful for session in progress join scenarios. /// const char* MatchmakingServerConnectionString; /// /// The ordered list of connection strings that the session could use to connect to a game server. /// Generally titles should use the first on the list, but sophisticated titles could use /// a custom mechanism for choosing one of the others (e.g. based on load). /// const char** ServerConnectionStringCandidates; /// /// The number of entries in the ServerConnectionStringCandidates array. /// size_t ServerConnectionStringCandidatesCount; /// /// Session MemberIds of owners of the session. /// uint32_t* SessionOwnerMemberIds; /// /// The number of entries in the SessionOwnerMemberIds array. /// size_t SessionOwnerMemberIdsCount; /// /// Device token of the host. /// Must match the "deviceToken" of at least one member, otherwise this field is deleted. /// If "peerToHostRequirements" is set and "host" is set, the measurement stage assumes the given host is the correct host and only measures metrics to that host. /// XblDeviceToken HostDeviceToken; /// /// Controls whether a session is joinable, independent of visibility, join restriction, and available space in the session. /// Does not affect reservations. /// Defaults to false. /// bool Closed; /// /// If true, it would allow the members of the session to be locked, such that if a user leaves they are able to /// come back into the session but no other user could take that spot. Defaults to false. /// bool Locked; /// /// Setting to true by a client triggers a Xbox Live Compute allocation attempt by MPSD. /// Defaults to false. /// bool AllocateCloudCompute; /// /// True if the match that was found didn't work out and needs to be resubmitted. /// Set to false to signal that the match did work, and the matchmaking service can release the session. /// bool MatchmakingResubmit; } XblMultiplayerSessionProperties; /// /// Basic info about a local multiplayer session. /// typedef struct XblMultiplayerSessionInfo { /// /// The contract version of the session. /// uint32_t ContractVersion; /// /// The branch of the session used to scope change numbers. /// char Branch[XBL_GUID_LENGTH]; /// /// The change number of the session. /// uint64_t ChangeNumber; /// /// A unique ID to the session used to query trace logs for entries that relate to the session. /// char CorrelationId[XBL_GUID_LENGTH]; /// /// The time that the session began. /// time_t StartTime; /// /// If any timeouts are in progress, this is the date when the next timer will fire. /// time_t NextTimer; /// /// A unique search handle ID to the session. /// char SearchHandleId[XBL_GUID_LENGTH]; } XblMultiplayerSessionInfo; /// /// Present during member initialization. /// typedef struct XblMultiplayerSessionInitializationInfo { /// /// The 'stage' goes from "joining" to "measuring" to "evaluating". /// If episode #1 fails, then 'stage' is set to "failed" and the session cannot be initialized. /// Otherwise, when an initialization episode completes, the 'initializing' object is removed. /// If 'autoEvaluate' is set, "evaluating" is skipped. If neither 'metrics' nor 'measurementServerAddresses' is set, "measuring" is skipped. /// XblMultiplayerInitializationStage Stage; /// /// The time with the initialization stage started. /// time_t StageStartTime; /// /// If member_initialization set and Initialize is true on the member, then the member gets assigned to an InitializingEpisode. /// An episode is a set of users that need to have QoS metrics applied to them. /// Will be 0 when the InitializingEpisode is not set. /// This value is only useful when manually managing QoS. /// uint32_t Episode; } XblMultiplayerSessionInitializationInfo; /// /// Arguments passed to the event handler when a session change occurs. /// typedef struct XblMultiplayerSessionChangeEventArgs { /// /// The session that triggered this event. /// XblMultiplayerSessionReference SessionReference; /// /// The branch of the session used to scope change numbers. /// char Branch[XBL_GUID_LENGTH]; /// /// The change number of the session. /// uint64_t ChangeNumber; } XblMultiplayerSessionChangeEventArgs; /// /// Queries the visible multiplayer sessions based on the configuration of this request. /// typedef struct XblMultiplayerSessionQuery { /// /// The service configuration id that the session is a part of. /// char Scid[XBL_SCID_LENGTH]; /// /// The maximum number of items to return. /// uint32_t MaxItems; /// /// Include private sessions to the result. /// bool IncludePrivateSessions; /// /// Include sessions that the user hasn't accepted. /// Must specify xboxUserIdFilter to use. /// bool IncludeReservations; /// /// Include inactive sessions to the result. /// Must specify xboxUserIdFilter to use. /// bool IncludeInactiveSessions; /// /// Filter result to just sessions these Xbox User IDs in it. (Optional) /// You must specify at least one Xuid filter OR a keyword filter. /// uint64_t* XuidFilters; /// /// The number of Xuids in the XuidsFilters array. /// size_t XuidFiltersCount; /// /// Filter result to just sessions with this keyword. (Optional) /// You must specify at least one Xuid filter OR a keyword filter. /// const char* KeywordFilter; /// /// The name of the template for the multiplayer session to filter on. /// char SessionTemplateNameFilter[XBL_MULTIPLAYER_SESSION_TEMPLATE_NAME_MAX_LENGTH]; /// /// Filter result to just sessions with the specified visibility. /// XblMultiplayerSessionVisibility VisibilityFilter; /// /// Filter result to just sessions with this major version or less of the contract. /// Use 0 to ignore. /// uint32_t ContractVersionFilter; } XblMultiplayerSessionQuery; /// /// Session information returned from a XblMultiplayerQuerySessionsAsync call. /// typedef struct XblMultiplayerSessionQueryResult { /// /// The time that the session began. /// time_t StartTime; /// /// Session reference for the session. /// XblMultiplayerSessionReference SessionReference; /// /// The current status of the session. /// XblMultiplayerSessionStatus Status; /// /// The visibility state of the session. /// Whether other users can see, or join, etc. /// XblMultiplayerSessionVisibility Visibility; /// /// Indicates if it is my turn. /// bool IsMyTurn; /// /// Xbox User ID of the member. /// uint64_t Xuid; /// /// Approximate number of non-reserved members. /// uint32_t AcceptedMemberCount; /// /// Join restriction for the session. /// XblMultiplayerSessionRestriction JoinRestriction; } XblMultiplayerSessionQueryResult; /// /// A handle to an search details object that is associated with a MPSD sessions. /// The object has filterable and queryable attributes about the session. /// typedef struct XblMultiplayerSearchHandleDetails* XblMultiplayerSearchHandle; #define XBL_MULTIPLAYER_SEARCH_HANDLE_MAX_FIELD_LENGTH 100 /// /// A searchable tag that can be attached to a multiplayer session search handle when it is created. /// Tags must be alphanumeric and start with a letter. /// They're case-insensitive. /// typedef struct XblMultiplayerSessionTag { /// /// The multiplayer search handle. /// char value[XBL_MULTIPLAYER_SEARCH_HANDLE_MAX_FIELD_LENGTH]; } XblMultiplayerSessionTag; /// /// An associative attribute that can be attached to a multiplayer session search handle when it is created. /// Attribute names be lower-case alphanumeric, and start with a letter. /// typedef struct XblMultiplayerSessionStringAttribute { /// /// Name of the attribute. /// char name[XBL_MULTIPLAYER_SEARCH_HANDLE_MAX_FIELD_LENGTH]; /// /// Attribute value. /// char value[XBL_MULTIPLAYER_SEARCH_HANDLE_MAX_FIELD_LENGTH]; } XblMultiplayerSessionStringAttribute; /// /// An associative attribute that can be attached to a multiplayer session search handle when it is created. /// Attribute names be lower-case alphanumeric, and start with a letter. /// typedef struct XblMultiplayerSessionNumberAttribute { /// /// Name of the attribute. /// Attribute names be lower-case alphanumeric, and start with a letter. /// char name[XBL_MULTIPLAYER_SEARCH_HANDLE_MAX_FIELD_LENGTH]; /// /// Attribute value. /// double value; } XblMultiplayerSessionNumberAttribute; /// /// Handle to a local multiplayer session. /// This handle will be used to query and change the local session object. /// Anytime the changes made to the local session object will not be reflected in the multiplayer service /// until a subsequent call to . /// typedef struct XblMultiplayerSession* XblMultiplayerSessionHandle; /// /// Optional args when creating a new local multiplayer session. /// typedef struct XblMultiplayerSessionInitArgs { /// /// The maximum number of members in this session. /// This value can only be set if the maximum is not specified in the title's multiplayer session template. /// If the maximum is specified in the title's multiplayer session template, then set to 0 to ignore this parameter. /// uint32_t MaxMembersInSession; /// /// The visibility of this session. /// XblMultiplayerSessionVisibility Visibility; /// /// A collection of Xbox User IDs indicating who initiated the session. (Optional) /// const uint64_t* InitiatorXuids; /// /// The number of Xuids in the Initiator XuidsArray. /// size_t InitiatorXuidsCount; /// /// JSON that specifies the custom constants for the session.These can not be changed after the session is created. (Optional) /// _Null_terminated_ const char* CustomJson; } XblMultiplayerSessionInitArgs; /// /// Creates a new local multiplayer session. /// /// The Xbox User ID of the user who is creating this session. /// A reference that uniquely identifies the session. /// Additional args used to initialize the session. Must also include the sessionReference if these are included. /// Handle to a local multiplayer session. /// /// You must call after this to write batched local changes to the service. /// If this is called without XblMultiplayerWriteSessionAsync, this will only create a local session object, but does not commit it to the service. /// When the local session object is no longer needed, call . /// STDAPI_(XblMultiplayerSessionHandle) XblMultiplayerSessionCreateHandle( _In_ uint64_t xuid, _In_opt_ const XblMultiplayerSessionReference* sessionReference, _In_opt_ const XblMultiplayerSessionInitArgs* initArgs ) XBL_NOEXCEPT; /// /// Increments the reference count to a local session object. /// /// Handle to the multiplayer session. /// Passes back the duplicated handle. /// HRESULT return code for this API operation. STDAPI XblMultiplayerSessionDuplicateHandle( _In_ XblMultiplayerSessionHandle handle, _Out_ XblMultiplayerSessionHandle* duplicatedHandle ) XBL_NOEXCEPT; /// /// Decrements the reference count to a local session object. /// /// Handle to the multiplayer session. /// STDAPI_(void) XblMultiplayerSessionCloseHandle( _In_ XblMultiplayerSessionHandle handle ) XBL_NOEXCEPT; /// /// Arbitration status of a tournament. /// /// Handle to the multiplayer session. /// Returns the status of a tournament. STDAPI_XBL_DEPRECATED_(XblTournamentArbitrationStatus) XblMultiplayerSessionArbitrationStatus( _In_ XblMultiplayerSessionHandle handle ) XBL_NOEXCEPT; /// /// The time when the server returned the session. /// /// Handle to the multiplayer session. /// The time when the server returned the session. /// /// Note that this is not part of the remote session state, it only indicates when the local session was created. /// STDAPI_(time_t) XblMultiplayerSessionTimeOfSession( _In_ XblMultiplayerSessionHandle handle ) XBL_NOEXCEPT; /// /// Get the info about session initialization. /// /// Handle to the multiplayer session. /// The initialization info used to create the session. /// /// If the session is not doing member initialization, nullptr will be returned. /// STDAPI_(const XblMultiplayerSessionInitializationInfo*) XblMultiplayerSessionGetInitializationInfo( _In_ XblMultiplayerSessionHandle handle ) XBL_NOEXCEPT; /// /// Returns an OR'd set of XblMultiplayerSessionChangeTypes values representing the aspects of the session /// that the current xboxlivecontext is subscribed to, of XblMultiplayerSessionChangeTypes::None if there is none. /// /// Handle to the multiplayer session. /// An OR'd set of XblMultiplayerSessionChangeTypes values representing the aspects of the session. STDAPI_(XblMultiplayerSessionChangeTypes) XblMultiplayerSessionSubscribedChangeTypes( _In_ XblMultiplayerSessionHandle handle ) XBL_NOEXCEPT; /// /// Host candidates are an ordered list of device tokens, ordered by preference as specified by XblMultiplayerMetrics /// in the session constants. /// /// Handle to the multiplayer session. /// Passes back a pointer to the array of candidate device tokens. /// The memory for the pointer will remain valid for the life of the XblMultiplayerSessionHandle object until it is closed. /// Passes back the size of the deviceTokens array. /// HRESULT return code for this API operation. STDAPI XblMultiplayerSessionHostCandidates( _In_ XblMultiplayerSessionHandle handle, _Out_ const XblDeviceToken** deviceTokens, _Out_ size_t* deviceTokensCount ) XBL_NOEXCEPT; /// /// The uniquely identifying information for the session. /// /// Handle to the multiplayer session. /// A pointer to the multiplayer session reference. /// The memory for the returned pointer will remain valid for the life of the XblMultiplayerSessionHandle object until it is closed. STDAPI_(const XblMultiplayerSessionReference*) XblMultiplayerSessionSessionReference( _In_ XblMultiplayerSessionHandle handle ) XBL_NOEXCEPT; /// /// A set of constants associated with this session. /// These can only be set when creating the session. /// /// Handle to the multiplayer session. /// A pointer to the constant values used in the multiplayer session. STDAPI_(const XblMultiplayerSessionConstants*) XblMultiplayerSessionSessionConstants( _In_ XblMultiplayerSessionHandle handle ) XBL_NOEXCEPT; /// /// Sets the maximum number of members in this session. /// /// Handle to the multiplayer session. /// Value to set for max members. /// STDAPI_(void) XblMultiplayerSessionConstantsSetMaxMembersInSession( _In_ XblMultiplayerSessionHandle handle, uint32_t maxMembersInSession ) XBL_NOEXCEPT; /// /// Sets the visibility of this session. /// /// Handle to the multiplayer session. /// New visibility value. /// STDAPI_(void) XblMultiplayerSessionConstantsSetVisibility( _In_ XblMultiplayerSessionHandle handle, _In_ XblMultiplayerSessionVisibility visibility ) XBL_NOEXCEPT; /// /// Sets the timeouts for the session. /// This can only be set when creating a new session. /// /// Handle to the multiplayer session. /// The timeout for a member reservation, in milliseconds. /// A value of 0 is allowed and indicates an immediate timeout. If the timeout is not specified, it is considered infinite. /// The timeout for a member to be considered inactive, in milliseconds. /// A value of 0 is allowed and indicates an immediate timeout. If the timeout is not specified, it is considered infinite. /// The timeout for a member to be considered ready, in milliseconds. /// A value of 0 is allowed and indicates an immediate timeout. If the timeout is not specified, it is considered infinite. /// The timeout for an empty session, in milliseconds. /// A value of 0 is allowed and indicates an immediate timeout. If the timeout is not specified, it is considered infinite. /// HRESULT return code for this API operation. STDAPI XblMultiplayerSessionConstantsSetTimeouts( _In_ XblMultiplayerSessionHandle handle, _In_ uint64_t memberReservedTimeout, _In_ uint64_t memberInactiveTimeout, _In_ uint64_t memberReadyTimeout, _In_ uint64_t sessionEmptyTimeout ) XBL_NOEXCEPT; /// /// Sets the arbitration timeouts for the session. /// This can only be set when creating a new session. /// /// Handle to the multiplayer session. /// The timeout for arbitration, in milliseconds representing the point at which results are finalized. /// The timeout for forfeit, in milliseconds representing the point at which, /// if the session has no active users, the match is canceled. /// HRESULT return code for this API operation. STDAPI_XBL_DEPRECATED XblMultiplayerSessionConstantsSetArbitrationTimeouts( _In_ XblMultiplayerSessionHandle handle, _In_ uint64_t arbitrationTimeout, _In_ uint64_t forfeitTimeout ) XBL_NOEXCEPT; /// /// Enables or disables connectivity metrics for the session. /// This can only be set when creating a new session. /// /// Handle to the multiplayer session. /// True to enable the measuring of latency, and false to disable latency measurement. /// True to enable the measuring of bandwidth down, and false to disable bandwidth down measurement. /// True to enable the measuring of bandwidth up, and false to disable bandwidth up measurement. /// True to enable custom metrics, and false to disable them. /// HRESULT return code for this API operation. /// /// For ones that are enabled, they must be sufficient to satisfy the QoS requirements. /// STDAPI XblMultiplayerSessionConstantsSetQosConnectivityMetrics( _In_ XblMultiplayerSessionHandle handle, _In_ bool enableLatencyMetric, _In_ bool enableBandwidthDownMetric, _In_ bool enableBandwidthUpMetric, _In_ bool enableCustomMetric ) XBL_NOEXCEPT; /// /// If a 'memberInitialization' object is set, the session expects the client system or title to perform /// initialization following session creation and/or as new members join the session. /// This can only be set when creating a new session. /// /// Handle to the multiplayer session. /// Object specifying member initialization parameters. /// HRESULT return code for this API operation. /// /// The timeouts and initialization stages are automatically tracked by the session, including QoS measurements if any metrics are set. /// These timeouts override the session's reservation and ready timeouts for members that have 'initializationEpisode' set. /// STDAPI XblMultiplayerSessionConstantsSetMemberInitialization( _In_ XblMultiplayerSessionHandle handle, _In_ XblMultiplayerMemberInitialization memberInitialization ) XBL_NOEXCEPT; /// /// These thresholds apply to each pairwise connection for all members in a session. /// This can only be set when creating a new session. /// /// Handle to the multiplayer session. /// Object specifying the peer to peer requirements for the session. /// HRESULT return code for this API operation. STDAPI XblMultiplayerSessionConstantsSetPeerToPeerRequirements( _In_ XblMultiplayerSessionHandle handle, _In_ XblMultiplayerPeerToPeerRequirements requirements ) XBL_NOEXCEPT; /// /// These thresholds apply to each connection from a host candidate. /// This can only be set when creating a new session. /// /// Handle to the multiplayer session. /// Object specifying the peer to host requirements for the session. /// HRESULT return code for this API operation. STDAPI XblMultiplayerSessionConstantsSetPeerToHostRequirements( _In_ XblMultiplayerSessionHandle handle, _In_ XblMultiplayerPeerToHostRequirements requirements ) XBL_NOEXCEPT; /// /// The set of potential server connection strings that should be evaluated. /// /// Handle to the multiplayer session. /// The Json that represent the measurement server addresses. /// Example JSON: /// { /// "server farm a": { /// "secureDeviceAddress": "r5Y=" // Base-64 encoded secure-device-address /// }, /// "datacenter b" : { /// "secureDeviceAddress": "rwY=" /// } /// } /// /// HRESULT return code for this API operation. STDAPI XblMultiplayerSessionConstantsSetMeasurementServerAddressesJson( _In_ XblMultiplayerSessionHandle handle, _In_ const char* measurementServerAddressesJson ) XBL_NOEXCEPT; /// /// Sets the capabilities constants for the session. /// This can only be set when creating a new session. /// /// Handle to the multiplayer session. /// A collection of MultiplayerSessionCapabilities flags that apply to the MultiplayerSessionConstant's capabilities JSON object. /// HRESULT return code for this API operation. STDAPI XblMultiplayerSessionConstantsSetCapabilities( _In_ XblMultiplayerSessionHandle handle, _In_ XblMultiplayerSessionCapabilities capabilities ) XBL_NOEXCEPT; /// /// This can only be set when creating a new session. /// Can only be specified if the 'cloudCompute' capability is set. /// Enables clients to request that a cloud compute instance be allocated on behalf of the session. /// /// Handle to the multiplayer session. /// Cloud compute instance to be allocated on behalf of the session. /// Example Json: /// { /// "crossSandbox": true, // True if the cloud compute resources are provisioned to be sandbox-agnostic, false if they are provisioned per-sandbox. /// "titleId" : "4567", //The title ID and GSI set of the cloud compute package to allocate. /// "gsiSet" : "128ce92a-45d0-4319-8a7e-bd8e940114ec" /// } /// /// HRESULT return code for this API operation. STDAPI XblMultiplayerSessionConstantsSetCloudComputePackageJson( _In_ XblMultiplayerSessionHandle handle, _In_ const char* sessionCloudComputePackageConstantsJson ) XBL_NOEXCEPT; /// /// Get the properties associated with the session. /// Any player can modify these properties. /// /// Handle to the multiplayer session. /// A pointer to the set of properties associated with this session. STDAPI_(const XblMultiplayerSessionProperties*) XblMultiplayerSessionSessionProperties( _In_ XblMultiplayerSessionHandle handle ) XBL_NOEXCEPT; /// /// A collection of keywords associated with the session. (Optional, might be empty) /// /// Handle to the multiplayer session. /// The keywords to associate with the session. Overwrites existing keywords. /// The size of the keywords array. /// HRESULT return code for this API operation. STDAPI XblMultiplayerSessionPropertiesSetKeywords( _In_ XblMultiplayerSessionHandle handle, _In_ const char** keywords, _In_ size_t keywordsCount ) XBL_NOEXCEPT; /// /// Restricts who can join "open" sessions. (Has no effect on reservations, which means it has no impact on "private" and "visible" sessions.) /// /// Handle to the multiplayer session. /// New join restriction value. /// /// /// Defaults to "none". /// If "local", only users whose token's DeviceId matches someone else already in the session and "active": true. /// If "followed", only local users (as defined above) and users who are followed by an existing (not reserved) member of the session can join without a reservation. /// STDAPI_(void) XblMultiplayerSessionPropertiesSetJoinRestriction( _In_ XblMultiplayerSessionHandle handle, _In_ XblMultiplayerSessionRestriction joinRestriction ) XBL_NOEXCEPT; /// /// Restricts who can read "open" sessions. (Has no effect on reservations, which means it has no impact on "private" and "visible" sessions.) /// /// Handle to the multiplayer session. /// New read restriction value. /// /// /// Defaults to "none". /// If "local", only users whose token's DeviceId matches someone else already in the session and "active": true. /// If "followed", only local users (as defined above) and users who are followed by an existing (not reserved) member of the session can read without a reservation. /// STDAPI_(void) XblMultiplayerSessionPropertiesSetReadRestriction( _In_ XblMultiplayerSessionHandle handle, _In_ XblMultiplayerSessionRestriction readRestriction ) XBL_NOEXCEPT; /// /// Sets the collection of session MemberIds indicating whose turn it is. /// /// Handle to the multiplayer session. /// Collection of MemberIds whose turn it is. /// The size of the turnCollectionMemberIds array. /// HRESULT return code for this API operation. STDAPI XblMultiplayerSessionPropertiesSetTurnCollection( _In_ XblMultiplayerSessionHandle handle, _In_ const uint32_t* turnCollectionMemberIds, _In_ size_t turnCollectionMemberIdsCount ) XBL_NOEXCEPT; /// /// A set of role types associated with this session. /// /// Handle to the multiplayer session. /// Passes back a pointer to an array of roleTypes. /// The memory for the pointer remains valid for the life of the XblMultiplayerSessionHandle object until it is closed. /// Passes back the number of roles types in the roleTypes array. /// HRESULT return code for this API operation. STDAPI XblMultiplayerSessionRoleTypes( _In_ XblMultiplayerSessionHandle handle, _Out_ const XblMultiplayerRoleType** roleTypes, _Out_ size_t* roleTypesCount ) XBL_NOEXCEPT; /// /// Fetches the XblMultiplayerRole object by role type name and role name. /// /// Handle to the multiplayer session. /// Name of the role type the role belongs to. /// Name of the role. /// Passes back a pointer to the role object with info about the role. /// The memory for the pointer will remain valid for the life of the XblMultiplayerSessionHandle object until it is closed. /// If no such role exists, nullptr will be passed back. /// HRESULT return code for this API operation. /// /// Note that newly created sessions will not have role info populated until the XblMultiplayerWriteSessionAsync is called. /// STDAPI XblMultiplayerSessionGetRoleByName( _In_ XblMultiplayerSessionHandle handle, _In_z_ const char* roleTypeName, _In_z_ const char* roleName, _Out_ const XblMultiplayerRole** role ) XBL_NOEXCEPT; /// /// Sets the max member count and/or target member counts for a role. /// /// Handle to the multiplayer session. /// Name of the role type for the role being modified. /// Name of the role to modify. /// The maximum number of members that can have the role. /// The target number of members for the role. /// HRESULT return code for this API operation. /// /// Only the session owner can modify role settings and only those that are mutable (see XblMultiplayerRoleType.MutableRoleSettings). /// In your session template, you also need to set 'hasOwners' capability and 'ownerManaged' to true for the specific role type /// that you want to modify. /// STDAPI XblMultiplayerSessionSetMutableRoleSettings( _In_ XblMultiplayerSessionHandle handle, _In_z_ const char* roleTypeName, _In_z_ const char* roleName, _In_opt_ uint32_t* maxMemberCount, _In_opt_ uint32_t* targetMemberCount ) XBL_NOEXCEPT; /// /// Gets the collection of members that are in the session or entering the session together. /// /// Handle to the multiplayer session. /// Passes back a pointer to array of session member objects. /// The memory for the returned pointer will remain valid for the life of the XblMultiplayerSessionHandle object until it is closed. /// Passes back the size of the returned array. /// HRESULT return code for this API operation. /// /// Call XblMultiplayerSessionJoin or XblMultiplayerSessionLeave to add or remove yourself from this list. /// Call XblMultiplayerSessionAddMemberReservation to add a reservation for another user on this list. /// STDAPI XblMultiplayerSessionMembers( _In_ XblMultiplayerSessionHandle handle, _Out_ const XblMultiplayerSessionMember** members, _Out_ size_t* membersCount ) XBL_NOEXCEPT; /// /// Gets the session member with a specified MemberId. /// /// Handle to the multiplayer session. /// The Id of the member to fetch. /// A pointer to a member in a multiplayer session. (Read Only) /// The memory for the returned pointer will remain valid for the life of the XblMultiplayerSessionHandle object until it is closed. /// /// If there is no member with the specified Id, nullptr is returned. /// STDAPI_(const XblMultiplayerSessionMember*) XblMultiplayerSessionGetMember( _In_ XblMultiplayerSessionHandle handle, _In_ uint32_t memberId ) XBL_NOEXCEPT; /// /// A multiplayer session server that contains properties associated with a target session reference. /// /// Handle to the multiplayer session. /// The matchmaking server supporting the multiplayer session. /// The memory for the returned pointer will remain valid for the life of the XblMultiplayerSessionHandle object until it is closed. STDAPI_(const XblMultiplayerMatchmakingServer*) XblMultiplayerSessionMatchmakingServer( _In_ XblMultiplayerSessionHandle handle ) XBL_NOEXCEPT; /// /// A tournament session servers that contains properties associated with a tournament reference. /// /// Handle to the multiplayer session. /// The tournament server supporting the multiplayer session. /// The memory for the returned pointer will remain valid for the life of the XblMultiplayerSessionHandle object until it is closed. XBL_WARNING_PUSH XBL_WARNING_DISABLE_DEPRECATED STDAPI_XBL_DEPRECATED_(const XblMultiplayerTournamentsServer*) XblMultiplayerSessionTournamentsServer( _In_ XblMultiplayerSessionHandle handle ) XBL_NOEXCEPT; XBL_WARNING_POP /// /// An arbitration server that contains properties associated with a tournament games results. /// /// Handle to the multiplayer session. /// The arbitration server supporting the multiplayer session. /// The memory for the returned pointer will remain valid for the life of the XblMultiplayerSessionHandle object until it is closed. XBL_WARNING_PUSH XBL_WARNING_DISABLE_DEPRECATED STDAPI_XBL_DEPRECATED_(const XblMultiplayerArbitrationServer*) XblMultiplayerSessionArbitrationServer( _In_ XblMultiplayerSessionHandle handle ) XBL_NOEXCEPT; XBL_WARNING_POP /// /// The number of members that have accepted and are added to the session and are no longer pending. /// /// Handle to the multiplayer session. /// The number of members that have accepted. STDAPI_(uint32_t) XblMultiplayerSessionMembersAccepted( _In_ XblMultiplayerSessionHandle handle ) XBL_NOEXCEPT; /// /// A JSON string containing a collection of servers for this multiplayer session. /// This Json object has the info that is parsed to create the XblMultiplayerArbitrationServer, /// XblMultiplayerMatchmakingServer, and XblMultiplayerTournamentsServer objects. /// /// Handle to the multiplayer session. /// The JSON string containing a collection of servers for this multiplayer session. /// The memory for the returned pointer will remain valid for the life of the XblMultiplayerSessionHandle object until it is closed. STDAPI_(const char*) XblMultiplayerSessionRawServersJson( _In_ XblMultiplayerSessionHandle handle ) XBL_NOEXCEPT; /// /// Sets the JSON string containing a collection of servers for this multiplayer session. /// /// Handle to the multiplayer session. /// Json describing the session servers. /// HRESULT return code for this API operation. STDAPI XblMultiplayerSessionSetRawServersJson( _In_ XblMultiplayerSessionHandle handle, _In_z_ const char* rawServersJson ) XBL_NOEXCEPT; /// /// The ETag returned with this session. /// /// Handle to the multiplayer session. /// The ETag. /// The memory for the returned pointer will remain valid for the life of the XblMultiplayerSessionHandle object until it is closed. STDAPI_(const char*) XblMultiplayerSessionEtag( _In_ XblMultiplayerSessionHandle handle ) XBL_NOEXCEPT; /// /// Returns the current User in the session. /// /// Handle to the multiplayer session. /// The current User in the session. /// The memory for the returned pointer will remain valid for the life of the XblMultiplayerSessionHandle object until it is closed. /// /// A nullptr will be returned if there is no current user in the session. /// STDAPI_(const XblMultiplayerSessionMember*) XblMultiplayerSessionCurrentUser( _In_ XblMultiplayerSessionHandle handle ) XBL_NOEXCEPT; /// /// Gets some basic info about the session. /// Represents the info in the root of the MPSD session document. /// /// Handle to the multiplayer session. /// A pointer to the basic info about the local multiplayer session. /// The memory for the returned pointer will remain valid for the life of the XblMultiplayerSessionHandle object until it is closed. STDAPI_(const XblMultiplayerSessionInfo*) XblMultiplayerSessionGetInfo( _In_ XblMultiplayerSessionHandle handle ) XBL_NOEXCEPT; /// /// After call XblMultiplayerWriteSessionAsync, the status of the write. /// /// Handle to the multiplayer session. /// The write status of the Multiplayer session. STDAPI_(XblWriteSessionStatus) XblMultiplayerSessionWriteStatus( _In_ XblMultiplayerSessionHandle handle ) XBL_NOEXCEPT; /// /// Add a new member reservation on the session for the specified xuid and member constants. /// /// Handle to the multiplayer session. /// The xuid to add a reservation for. /// The custom constants to set for this member. /// This is the only time the member's constants can be set. (Optional) /// True if the system should perform managed initialization, and false otherwise. /// HRESULT return code for this API operation. STDAPI XblMultiplayerSessionAddMemberReservation( _In_ XblMultiplayerSessionHandle handle, _In_ uint64_t xuid, _In_opt_z_ const char* memberCustomConstantsJson, _In_ bool initializeRequested ) XBL_NOEXCEPT; /// /// Joins the local user to the session, sets the user to active. /// /// Handle to the multiplayer session. /// The custom constants to set for this member. /// This is the only time the member's constants can be set. /// True if the system should perform managed initialization, and false otherwise. /// True if player should join with active status. /// HRESULT return code for this API operation. STDAPI XblMultiplayerSessionJoin( _In_ XblMultiplayerSessionHandle handle, _In_opt_z_ const char* memberCustomConstantsJson, _In_ bool initializeRequested, _In_ bool joinWithActiveStatus ) XBL_NOEXCEPT; /// /// This can only be set when creating a new session. /// /// Handle to the multiplayer session. /// True if initialization succeeded, and false otherwise. /// STDAPI_(void) XblMultiplayerSessionSetInitializationSucceeded( _In_ XblMultiplayerSessionHandle handle, _In_ bool initializationSucceeded ) XBL_NOEXCEPT; /// /// Sets the device token of the host. /// /// Handle to the multiplayer session. /// The host device token. /// /// /// If "peerToHostRequirements" is set and this is set, the measurement stage assumes the given host is the correct host and only measures metrics to that host. /// Note that host device tokens are generated from a session member's secure device address, so ensure that the secure device address is set /// (see ) for the desired host prior to calling this method. /// STDAPI_(void) XblMultiplayerSessionSetHostDeviceToken( _In_ XblMultiplayerSessionHandle handle, _In_ XblDeviceToken hostDeviceToken ) XBL_NOEXCEPT; /// /// Forces a specific server connection string to be used, useful in preserveSession=always cases. /// /// Handle to the multiplayer session. /// The server connection path. /// Setting this path can be useful when the session is preserved. /// STDAPI_(void) XblMultiplayerSessionSetMatchmakingServerConnectionPath( _In_ XblMultiplayerSessionHandle handle, _In_z_ const char* serverConnectionPath ) XBL_NOEXCEPT; /// /// If set to true, makes the session "closed", meaning that new users will not be able to join unless they already have a reservation. /// /// Handle to the multiplayer session. /// True if the session should be closed, false otherwise. /// STDAPI_(void) XblMultiplayerSessionSetClosed( _In_ XblMultiplayerSessionHandle handle, _In_ bool closed ) XBL_NOEXCEPT; /// /// Sets if a session is locked or not. /// If locked, members that leave the session will be able to come back into the session, allowing no additional users to take that spot. /// /// Handle to the multiplayer session. /// Set true, if the session should be lock, false otherwise. /// /// /// If the session is locked, it must also be set to closed. /// STDAPI_(void) XblMultiplayerSessionSetLocked( _In_ XblMultiplayerSessionHandle handle, _In_ bool locked ) XBL_NOEXCEPT; /// /// If this property is set an allocation of the 'cloudComputePackage' will be attempted. /// /// Handle to the multiplayer session. /// True if a cloud compute package should be allocated and false otherwise. /// STDAPI_(void) XblMultiplayerSessionSetAllocateCloudCompute( _In_ XblMultiplayerSessionHandle handle, _In_ bool allocateCloudCompute ) XBL_NOEXCEPT; /// /// Sets if a match needs to be resubmitted or not. /// /// Handle to the multiplayer session. /// Set true, if the match that was found didn't work out and needs to be resubmitted. /// Set false, if the match did work and the matchmaking service can release the session. /// STDAPI_(void) XblMultiplayerSessionSetMatchmakingResubmit( _In_ XblMultiplayerSessionHandle handle, _In_ bool matchResubmit ) XBL_NOEXCEPT; /// /// The ordered list of case-insensitive connection strings that the session could use to connect to a game server. /// Generally titles should use the first on the list, but sophisticated titles could use /// a custom mechanism for choosing one of the others (e.g. based on load). /// /// Handle to the multiplayer session. /// The collection of connection paths. /// The size of the candidates array. /// HRESULT return code for this API operation. STDAPI XblMultiplayerSessionSetServerConnectionStringCandidates( _In_ XblMultiplayerSessionHandle handle, _In_reads_(serverConnectionStringCandidatesCount) const char** serverConnectionStringCandidates, _In_ size_t serverConnectionStringCandidatesCount ) XBL_NOEXCEPT; /// /// Configures the set of session changes that this client will be subscribed to. /// /// Handle to the multiplayer session. /// Or'd set of XblMultiplayerSessionChangeType enum values representing the change types to subscribe to. /// HRESULT return code for this API operation. /// /// Set to MultiplayerSessionChangeTypes::None to clear the subscription. /// STDAPI XblMultiplayerSessionSetSessionChangeSubscription( _In_ XblMultiplayerSessionHandle handle, _In_ XblMultiplayerSessionChangeTypes changeTypes ) XBL_NOEXCEPT; /// /// Call if the user who either created or got the session leaves the session. /// /// Handle to the multiplayer session. /// HRESULT return code for this API operation. /// /// If the session is deleted as a result of this action, a 204 response will be returned. /// STDAPI XblMultiplayerSessionLeave( _In_ XblMultiplayerSessionHandle handle ) XBL_NOEXCEPT; /// /// Set the current user to active or inactive. /// The member must first be joined to the session. /// /// Handle to the multiplayer session. /// Indicates the current user status. /// HRESULT return code for this API operation. /// /// You cannot set the user to reserved or ready in this manner. /// Use to add a member reservation. /// STDAPI XblMultiplayerSessionCurrentUserSetStatus( _In_ XblMultiplayerSessionHandle handle, _In_ XblMultiplayerSessionMemberStatus status ) XBL_NOEXCEPT; /// /// Set the base64 encoded secure device address of the member. /// The member must first be joined to the session. /// /// Handle to the multiplayer session. /// Indicates the value of the current user's secure device address encoded in base64. /// HRESULT return code for this API operation. /// /// On platforms that don't have a secure device address, call XblFormatSecureDeviceAddress /// to generate a value that can be used by this function. Note that setting a secure device address is required /// to manually set a session host. /// STDAPI XblMultiplayerSessionCurrentUserSetSecureDeviceAddressBase64( _In_ XblMultiplayerSessionHandle handle, _In_ const char* value ) XBL_NOEXCEPT; #if HC_PLATFORM != HC_PLATFORM_XDK && HC_PLATFORM != HC_PLATFORM_UWP /// /// A formatted secure device address. /// typedef struct XblFormattedSecureDeviceAddress { /// /// The secure device address. /// char value[4096]; } XblFormattedSecureDeviceAddress; /// /// Formats a secure device address given a unique device id for platforms that don't support SDAs. /// /// A unique id to represent this device for the lifetime of the local game process/instance. /// Passes back the formatted secure device address. /// HRESULT return code for this API operation. /// Formats the deviceId string according to the following recommendation: /// /// Generate 16 random bytes(e.g., a GUID) to be used for the lifetime of the local game process/instance /// to uniquely represent the device. Format the 'secureDeviceAddress' to be a string with the prefix "AAAAAAAA" /// (8 capital letter As) followed by the 32 character hexadecimal representation of the random bytes(upper /// or lower case letters don't matter, nor does the byte order if the bytes are an actual structured GUID). /// For example, "AAAAAAAA00112233445566778899AABBCCDDEEFF". /// /// Call XblMultiplayerSessionCurrentUserSetSecureDeviceAddressBase64 using the formatted SDA to set the SDA for the user. /// STDAPI XblFormatSecureDeviceAddress( _In_ const char* deviceId, _Inout_ XblFormattedSecureDeviceAddress* address ) XBL_NOEXCEPT; #endif /// /// Set the role info of the local member. /// The member must first be joined to the session. /// /// Handle to the multiplayer session. /// Indicates a collection of roles for the user. /// The size of the role array. /// HRESULT return code for this API operation. STDAPI XblMultiplayerSessionCurrentUserSetRoles( _In_ XblMultiplayerSessionHandle handle, _In_ const XblMultiplayerSessionMemberRole* roles, _In_ size_t rolesCount ) XBL_NOEXCEPT; /// /// Set a collection of members in the group. /// The member must first be joined to the session. /// /// Handle to the multiplayer session. /// Array of memberIds that are in the local users group. /// Size of the memberIds array. /// HRESULT return code for this API operation. STDAPI XblMultiplayerSessionCurrentUserSetMembersInGroup( _In_ XblMultiplayerSessionHandle session, _In_reads_(memberIdsCount) uint32_t* memberIds, _In_ size_t memberIdsCount ) XBL_NOEXCEPT; /// /// Sets a string vector of group names for the current user indicating which groups that user was part of during a multiplayer session. /// /// Handle to the multiplayer session. /// Array of group names. /// Size of the groups array. /// HRESULT return code for this API operation. STDAPI XblMultiplayerSessionCurrentUserSetGroups( _In_ XblMultiplayerSessionHandle handle, _In_reads_(groupsCount) const char** groups, _In_ size_t groupsCount ) XBL_NOEXCEPT; /// /// Sets a list of group names for the current user indicating which groups that user encountered during a multiplayer session. /// An encounter is a brief interaction with another group. /// /// Handle to the multiplayer session. /// Array of encounter strings. /// Size of the encounters array. /// HRESULT return code for this API operation. STDAPI XblMultiplayerSessionCurrentUserSetEncounters( _In_ XblMultiplayerSessionHandle handle, _In_reads_(encountersCount) const char** encounters, _In_ size_t encountersCount ) XBL_NOEXCEPT; /// /// Sets a collection of XblMultiplayerQosMeasurements for the members. /// This is only useful when the title is manually managing QoS. (If the platform is automatically performing QoS, this does not need to be called) /// /// Handle to the multiplayer session. /// Json representing the QoS measurements of members. /// Example Json: /// { /// "e69c43a8": { // Device token of peer /// "latency": 5953, // Milliseconds /// "bandwidthDown" : 19342, // Kilobits per second /// "bandwidthUp" : 944, // Kilobits per second /// "custom" : { } /// }, /// ... // additional device tokens /// } /// /// HRESULT return code for this API operation. STDAPI XblMultiplayerSessionCurrentUserSetQosMeasurements( _In_ XblMultiplayerSessionHandle handle, _In_z_ const char* measurements ) XBL_NOEXCEPT; /// /// Sets measurements JSON for the servers. /// This is only useful when the title is manually managing QoS. (If the platform is automatically performing QoS, this does not need to be called) /// /// Handle to the multiplayer session. /// The JSON that represents the server measurements. /// Example Json: /// { /// "server farm a": { /// "latency": 233 // Milliseconds /// } /// } /// /// HRESULT return code for this API operation. STDAPI XblMultiplayerSessionCurrentUserSetServerQosMeasurements( _In_ XblMultiplayerSessionHandle handle, _In_z_ const char* measurements ) XBL_NOEXCEPT; /// /// Set a custom property on the current user to the specified JSON string. /// The member must first be joined to the session. /// /// Handle to the multiplayer session. /// The name of the property to set. /// The JSON value to assign to the property. (Optional) /// HRESULT return code for this API operation. STDAPI XblMultiplayerSessionCurrentUserSetCustomPropertyJson( _In_ XblMultiplayerSessionHandle handle, _In_z_ const char* name, _In_z_ const char* valueJson ) XBL_NOEXCEPT; /// /// Delete a custom property on the current user. /// /// Handle to the multiplayer session. /// The name of the property to delete. /// HRESULT return code for this API operation. STDAPI XblMultiplayerSessionCurrentUserDeleteCustomPropertyJson( _In_ XblMultiplayerSessionHandle handle, _In_z_ const char* name ) XBL_NOEXCEPT; /// /// Sets the properties of the matchmaking. /// This should only be set by a client acting as a matchmaking service. /// /// Handle to the multiplayer session. /// A JSON string representing the target session constants. /// HRESULT return code for this API operation. STDAPI XblMultiplayerSessionSetMatchmakingTargetSessionConstantsJson( _In_ XblMultiplayerSessionHandle handle, _In_ const char* matchmakingTargetSessionConstantsJson ) XBL_NOEXCEPT; /// /// Set a session custom property to the specified JSON string. /// /// Handle to the multiplayer session. /// The name of the property to set. /// /// The JSON value to assign to the property. This must be a valid JSON string. /// Examples include "\"JsonString\"", "{\"name\":\"A\"}", or "true". /// /// HRESULT return code for this API operation. STDAPI XblMultiplayerSessionSetCustomPropertyJson( _In_ XblMultiplayerSessionHandle handle, _In_z_ const char* name, _In_z_ const char* valueJson ) XBL_NOEXCEPT; /// /// Deletes a session custom property. /// /// Handle to the multiplayer session. /// The name of the property to delete. /// HRESULT return code for this API operation. STDAPI XblMultiplayerSessionDeleteCustomPropertyJson( _In_ XblMultiplayerSessionHandle handle, _In_z_ const char* name ) XBL_NOEXCEPT; /// /// Checks the deltas between 2 sessions and returns an Or'ed MultiplayerSessionChangeType. /// Useful to compare a session object passed to XblMultiplayerWriteSessionAsync with the session object returned. /// /// A session to compare to the other. /// A session to compare to the other. /// The Or'ed change types for a multiplayer session. STDAPI_(XblMultiplayerSessionChangeTypes) XblMultiplayerSessionCompare( _In_ XblMultiplayerSessionHandle currentSessionHandle, _In_ XblMultiplayerSessionHandle oldSessionHandle ) XBL_NOEXCEPT; /// /// Increments the reference count to a local search handle details object. /// /// Handle to the search handle details. /// Passes back the duplicated handle. /// HRESULT return code for this API operation. STDAPI XblMultiplayerSearchHandleDuplicateHandle( _In_ XblMultiplayerSearchHandle handle, _Out_ XblMultiplayerSearchHandle* duplicatedHandle ) XBL_NOEXCEPT; /// /// Decrements the reference count to a local search handle details object. /// /// Handle to the search handle details. /// STDAPI_(void) XblMultiplayerSearchHandleCloseHandle( _In_ XblMultiplayerSearchHandle handle ) XBL_NOEXCEPT; /// /// Get the session reference for the session the search handle is associated with. /// /// Handle to the search handle details. /// Passes back the associated session reference. /// HRESULT return code for this API operation. STDAPI XblMultiplayerSearchHandleGetSessionReference( _In_ XblMultiplayerSearchHandle handle, _Out_ XblMultiplayerSessionReference* sessionRef ) XBL_NOEXCEPT; /// /// Get the Id for the search handle object. /// /// Handle to the search handle details. /// Passes back a search handle Id. /// The memory for the pointer will remain valid for the life of the XblMultiplayerSessionHandle object until it is closed. /// HRESULT return code for this API operation. STDAPI XblMultiplayerSearchHandleGetId( _In_ XblMultiplayerSearchHandle handle, _Out_ const char** id ) XBL_NOEXCEPT; /// /// Get a pointer to an array of Xuids who own the session associated with the search handle. /// /// Handle to the search handle details. /// Passes back a pointer to array of xuids. /// The memory for the pointer will remain valid for the life of the XblMultiplayerSessionHandle object until it is closed. /// Passes back the count of xuids in the array. /// HRESULT return code for this API operation. STDAPI XblMultiplayerSearchHandleGetSessionOwnerXuids( _In_ XblMultiplayerSearchHandle handle, _Out_ const uint64_t** xuids, _Out_ size_t* xuidsCount ) XBL_NOEXCEPT; /// /// Get a pointer to an array of tags attached with the search handle. /// /// Handle to the search handle details. /// Passes back a pointer to array of tags. /// The memory for the pointer will remain valid for the life of the XblMultiplayerSessionHandle object until it is closed. /// Passes back the count of tags in the array. /// HRESULT return code for this API operation. STDAPI XblMultiplayerSearchHandleGetTags( _In_ XblMultiplayerSearchHandle handle, _Out_ const XblMultiplayerSessionTag** tags, _Out_ size_t* tagsCount ) XBL_NOEXCEPT; /// /// Get a pointer to an array of attributes attached with the search handle. /// /// Handle to the search handle details. /// Passes back a pointer to array of attributes. /// The memory for the pointer will remain valid for the life of the XblMultiplayerSessionHandle object until it is closed. /// Passes back the count of attributes in the array. /// HRESULT return code for this API operation. STDAPI XblMultiplayerSearchHandleGetStringAttributes( _In_ XblMultiplayerSearchHandle handle, _Out_ const XblMultiplayerSessionStringAttribute** attributes, _Out_ size_t* attributesCount ) XBL_NOEXCEPT; /// /// Get a pointer to an array of attributes attached with the search handle. /// /// Handle to the search handle details. /// Passes back a pointer to array of attributes. /// The memory for the returned pointer will remain valid for the life of the XblMultiplayerSessionHandle object until it is closed. /// Passes back the count of attributes in the attributes array. /// HRESULT return code for this API operation. STDAPI XblMultiplayerSearchHandleGetNumberAttributes( _In_ XblMultiplayerSearchHandle handle, _Out_ const XblMultiplayerSessionNumberAttribute** attributes, _Out_ size_t* attributesCount ) XBL_NOEXCEPT; /// /// Get visibility of the session associated with the search handle. /// /// Handle to the search handle details. /// Passes back the visibility of the associated session. /// HRESULT return code for this API operation. STDAPI XblMultiplayerSearchHandleGetVisibility( _In_ XblMultiplayerSearchHandle handle, _Out_ XblMultiplayerSessionVisibility* visibility ) XBL_NOEXCEPT; /// /// Get join restriction of the session associated with the search handle. /// /// Handle to the search handle details. /// Passes back the join restriction of the associated session. /// HRESULT return code for this API operation. STDAPI XblMultiplayerSearchHandleGetJoinRestriction( _In_ XblMultiplayerSearchHandle handle, _Out_ XblMultiplayerSessionRestriction* joinRestriction ) XBL_NOEXCEPT; /// /// Get whether or not the session associated with the search handle is temporarily closed for joining. /// /// Handle to the search handle details. /// Passes back whether the session is closed or not. /// HRESULT return code for this API operation. STDAPI XblMultiplayerSearchHandleGetSessionClosed( _In_ XblMultiplayerSearchHandle handle, _Out_ bool* closed ) XBL_NOEXCEPT; /// /// Get the current and max number of members in the associated session. /// /// Handle to the search handle details. /// Passes back the max members allowed in session. /// Passes back the current number of members in session. /// HRESULT return code for this API operation. STDAPI XblMultiplayerSearchHandleGetMemberCounts( _In_ XblMultiplayerSearchHandle handle, _Out_opt_ size_t* maxMembers, _Out_opt_ size_t* currentMembers ) XBL_NOEXCEPT; /// /// Get the creation time of the search handle. /// /// Handle to the search handle details. /// Passes back the time the search handle was created in MPSD (not the local object). /// HRESULT return code for this API operation. STDAPI XblMultiplayerSearchHandleGetCreationTime( _In_ XblMultiplayerSearchHandle handle, _Out_ time_t* creationTime ) XBL_NOEXCEPT; /// /// Get the custom session properties for the associated session. /// /// Handle to the search handle details. /// Passes back the custom properties JSON string. /// The memory for the returned pointer will remain valid for the life of the XblMultiplayerSessionHandle object until it is closed. /// HRESULT return code for this API operation. STDAPI XblMultiplayerSearchHandleGetCustomSessionPropertiesJson( _In_ XblMultiplayerSearchHandle handle, _Out_ const char** customPropertiesJson ) XBL_NOEXCEPT; /// /// Writes a new or updated multiplayer session to the service. /// The session must have a valid session reference. /// If it was not created with one, use XblMultiplayerWriteSessionByHandleAsync instead. /// /// Xbox live context for the local user. /// A MultiplayerSession object that has been modified with the changes to write. /// The type of write operation. /// The AsyncBlock for this operation. /// HRESULT return code for this API operation. /// /// In the async callback, call XblMultiplayerWriteSessionResult() to get a XblMultiplayerSessionHandle handle. /// Use that handle to call XblMultiplayerSessionWriteStatus() to get the write status. /// The call to XblMultiplayerWriteSessionAsync() will only fail if the args passed to it are invalid or in very rare /// cases where it could not start the async task. /// /// Calls V105 PUT /serviceconfigs/{serviceConfigurationId}/sessionTemplates/{sessiontemplateName}/sessions/{sessionName} STDAPI XblMultiplayerWriteSessionAsync( _In_ XblContextHandle xblContext, _In_ XblMultiplayerSessionHandle multiplayerSession, _In_ XblMultiplayerSessionWriteMode writeMode, _Inout_ XAsyncBlock* async ) XBL_NOEXCEPT; /// /// Gets the result of a XblMultiplayerWriteSessionAsync operation. /// /// The AsyncBlock for this operation. /// Passes back a handle to a new instance of a local multiplayer session object. /// The XblMultiplayerSessionHandle must be released by the caller by calling . /// Use XblMultiplayerSession* APIs to get session data from the handle. /// If the updated session object is not needed, passing nullptr will cause the new multiplayer /// session object to be cleaned up immediately. /// /// HRESULT return code for this API operation. /// It will be a failure HRESULT if there was a network error or failure HTTP status code unless its a 412 (Precondition Failed). /// A 412 returns success since the service also returns latest session state, so you must call XblMultiplayerSessionWriteStatus() to get the /// write status and call XblMultiplayerSession* APIs to get session data from the handle. /// /// /// Note that if you leave a session that you are the last member of and the sessionEmptyTimeout /// is equal to 0, then the session will be deleted immediately. /// Call XblMultiplayerSessionWriteStatus() to get the write status. /// STDAPI XblMultiplayerWriteSessionResult( _Inout_ XAsyncBlock* async, _Out_ XblMultiplayerSessionHandle* handle ) XBL_NOEXCEPT; /// /// Writes a new or updated multiplayer session to the service, using the specified handle to the session. /// /// Xbox live context for the local user. /// A MultiplayerSession object that has been modified with the changes to write. /// The type of write operation. /// The ID of the handle that should be used when writing the session. /// The AsyncBlock for this operation. /// HRESULT return code for this API operation. /// /// A handle is a service-side pointer to a session. The handle ID is a GUID identifier of the handle. /// Callers will usually get the handleId either from another player's XblMultiplayerActivityDetails via /// the XblMultiplayerGetActivitiesForUsersAsync() API, or from an invite. /// /// Use this method only if your multiplayer session object doesn't have a valid XblMultiplayerSessionReference, as /// a handle's lifetime may be shorter than that of the session it points to. /// STDAPI XblMultiplayerWriteSessionByHandleAsync( _In_ XblContextHandle xblContext, _In_ XblMultiplayerSessionHandle multiplayerSession, _In_ XblMultiplayerSessionWriteMode writeMode, _In_ const char* handleId, _In_ XAsyncBlock* async ) XBL_NOEXCEPT; /// /// Gets the result of a XblMultiplayerWriteSessionAsync operation. /// /// The AsyncBlock for this operation. /// Passes back a handle to a new instance of a local multiplayer session object. /// It must be release by the caller with . /// If the updated session object is not needed, passing nullptr will cause the new multiplayer session object to be cleaned up immediately. /// HRESULT return code for this API operation. /// /// Note that if you leave a session that you are the last member of and the sessionEmptyTimeout /// is equal to 0, then the session will be deleted immediately and a nullptr will be returned. /// STDAPI XblMultiplayerWriteSessionByHandleResult( _Inout_ XAsyncBlock* async, _Out_ XblMultiplayerSessionHandle* handle ) XBL_NOEXCEPT; /// /// Gets an existing session object with all its attributes from the server. /// /// Xbox live context for the local user. /// A XblMultiplayerSessionReference object containing identifying information for the session. /// The AsyncBlock for this operation. /// HRESULT return code for this API operation. /// Calls V102 GET /serviceconfigs/{serviceConfigurationId}/sessionTemplates/{sessiontemplateName}/sessions/{sessionName} STDAPI XblMultiplayerGetSessionAsync( _In_ XblContextHandle xblContext, _In_ const XblMultiplayerSessionReference* sessionReference, _In_ XAsyncBlock* async ) XBL_NOEXCEPT; /// /// Gets the result of an XblMultiplayerGetSessionResult call. /// /// The AsyncBlock for this operation. /// Passes back a handle to a new instance of a local multiplayer session object. /// It must be release by the caller with . /// HRESULT return code for this API operation. /// /// If the session does not exist, this API will return __HRESULT_FROM_WIN32(ERROR_RESOURCE_DATA_NOT_FOUND) and a null /// XblMultiplayerSessionHandle. /// STDAPI XblMultiplayerGetSessionResult( _In_ XAsyncBlock* async, _Out_ XblMultiplayerSessionHandle* handle ) XBL_NOEXCEPT; /// /// Gets a session object with all its attributes from the server, given a session handle id. /// /// Xbox live context for the local user. /// A multiplayer handle id, which uniquely identifies the session. /// The AsyncBlock for this operation. /// HRESULT return code for this API operation. /// /// A handle is a service-side pointer to a session. The handleId is a GUID identifier of the handle. /// Callers will usually get the handleId either from another player's XblMultiplayerActivityDetails, or from an invite. /// /// Calls GET /handles/{handleId}/session STDAPI XblMultiplayerGetSessionByHandleAsync( _In_ XblContextHandle xblContext, _In_ const char* handleId, _In_ XAsyncBlock* async ) XBL_NOEXCEPT; /// /// Gets the result of an XblMultiplayerGetSessionByHandleAsync call. /// /// The AsyncBlock for this operation. /// Passes back a handle to a new instance of a local multiplayer session object. /// It must be release by the caller with . /// HRESULT return code for this API operation. /// /// If the session does not exist, this API will return __HRESULT_FROM_WIN32(ERROR_RESOURCE_DATA_NOT_FOUND) and a null /// XblMultiplayerSessionHandle. /// STDAPI XblMultiplayerGetSessionByHandleResult( _In_ XAsyncBlock* async, _Out_ XblMultiplayerSessionHandle* handle ) XBL_NOEXCEPT; /// /// Retrieve a list of sessions with various filters. /// /// Xbox live context for the local user. /// A session query object that defines the parameters for the query. /// The AsyncBlock for this operation. /// HRESULT return code for this API operation. /// Calls V102 GET /serviceconfigs/{scid}/sessions or /serviceconfigs/{scid}/sessiontemplates/{session-template-name}/sessions STDAPI XblMultiplayerQuerySessionsAsync( _In_ XblContextHandle xblContext, _In_ const XblMultiplayerSessionQuery* sessionQuery, _In_ XAsyncBlock* async ) XBL_NOEXCEPT; /// /// Gets the number of sessions that matched a session query. /// /// The AsyncBlock for this operation. /// Passes back the number of matching sessions. /// HRESULT return code for this API operation. STDAPI XblMultiplayerQuerySessionsResultCount( _In_ XAsyncBlock* async, _Out_ size_t* sessionCount ) XBL_NOEXCEPT; /// /// Gets the results of a session query. /// /// The AsyncBlock for this operation. /// The number of sessions that matched the query. /// Use to get the count required. /// A caller allocated array to pass back the session query results. /// HRESULT return code for this API operation. STDAPI XblMultiplayerQuerySessionsResult( _In_ XAsyncBlock* async, _In_ size_t sessionCount, _Out_writes_(sessionCount) XblMultiplayerSessionQueryResult* sessions ) XBL_NOEXCEPT; /// /// Sets the passed session as the user's current activity, which will be displayed in Xbox dashboard user experiences /// (e.g. friends and gamercard) as associated with the currently running title. /// If the session is joinable, it may also be displayed as such in those user experiences. /// /// Xbox live context for the local user. /// An XblMultiplayerSessionReference for the session of the activity. /// The AsyncBlock for this operation. /// HRESULT return code for this API operation. STDAPI XblMultiplayerSetActivityAsync( _In_ XblContextHandle xblContext, _In_ const XblMultiplayerSessionReference* sessionReference, _In_ XAsyncBlock* async ) XBL_NOEXCEPT; /// /// Clears the user's current activity session for the specified serviceConfigurationId. /// /// Xbox live context for the local user. /// The Service Configuration ID (SCID) in which to clear activity. The SCID is considered case sensitive so paste it directly from the Partner Center /// The AsyncBlock for this operation. /// HRESULT return code for this API operation. STDAPI XblMultiplayerClearActivityAsync( _In_ XblContextHandle xblContext, _In_z_ const char* scid, _In_ XAsyncBlock* async ) XBL_NOEXCEPT; /// /// The access rights the caller has to the origin session are extended to the target session. /// /// Xbox live context for the local user. /// Target XblMultiplayerSessionReference for the session you want to extend the access rights for. /// Origin XblMultiplayerSessionReference for the session that grants access to the target session. /// The AsyncBlock for this operation. /// HRESULT return code for this API operation. /// /// For example, in a title with a lobby session and a game session, the title could put a transfer handle /// linking the lobby to the game inside the lobby session. /// Users invited to the lobby can use the handle to join the game session as well. /// The transfer handle is deleted once the target session is deleted. /// STDAPI XblMultiplayerSetTransferHandleAsync( _In_ XblContextHandle xblContext, _In_ XblMultiplayerSessionReference targetSessionReference, _In_ XblMultiplayerSessionReference originSessionReference, _In_ XAsyncBlock* async ) XBL_NOEXCEPT; /// /// Gets the result of a XblMultiplayerSetTransferHandleAsync operation. /// /// The AsyncBlock for this operation. /// Passes back the multiplayer session handle ID. /// This handle holds the unique ID for a MPSD transfer handle. /// HRESULT return code for this API operation. STDAPI XblMultiplayerSetTransferHandleResult( _In_ XAsyncBlock* async, _Out_ XblMultiplayerSessionHandleId* handleId ) XBL_NOEXCEPT; /// /// Create a search handle associated with an existing session. /// This makes the session queryable. /// /// Xbox live context for the local user. /// Session reference to create a search handle for. /// Optional set of tags to attach to search handle. /// Count of tags. /// Optional attributes to attach to search handle. /// Count of number attributes. /// Optional attributes to attach to search handle. /// Count of string attributes. /// The AsyncBlock for this operation. /// HRESULT return code for this API operation. STDAPI XblMultiplayerCreateSearchHandleAsync( _In_ XblContextHandle xblContext, _In_ const XblMultiplayerSessionReference* sessionRef, _In_reads_opt_(tagsCount) const XblMultiplayerSessionTag* tags, _In_ size_t tagsCount, _In_reads_opt_(numberAttributesCount) const XblMultiplayerSessionNumberAttribute* numberAttributes, _In_ size_t numberAttributesCount, _In_reads_opt_(stringAttributesCount) const XblMultiplayerSessionStringAttribute* stringAttributes, _In_ size_t stringAttributesCount, _In_ XAsyncBlock* async ) XBL_NOEXCEPT; /// /// Get the result from an XblMultiplayerCreateSearchHandleAsync. /// /// The AsyncBlock for this operation. /// Passes back a handle to the local search handle object. /// If this parameter is null, the local search handle object will be freed immediately. /// Note that this does not destroy the search handle on the MPSD service side, that must be done with XblMultiplayerDeleteSearchHandleAsync. /// If this is non-null, the handle must later be closed with . /// HRESULT return code for this API operation. STDAPI XblMultiplayerCreateSearchHandleResult( _In_ XAsyncBlock* async, _Out_opt_ XblMultiplayerSearchHandle* handle ) XBL_NOEXCEPT; /// /// Delete a search handle from the MPSD service. /// /// Xbox live context for the local user. /// Id of the search handle to delete. /// Obtained from either the search handle or session handle. /// The AsyncBlock for this operation. /// HRESULT return code for this API operation. /// /// Note that this will not destroy local search handle objects; the lifetime of those objects is still managed by XblMultiplayerSearchHandleCloseHandle. /// Once the search handle object is deleted from service, the associated session will no longer be searchable. /// STDAPI XblMultiplayerDeleteSearchHandleAsync( _In_ XblContextHandle xblContext, _In_ const char* handleId, _In_ XAsyncBlock* async ) XBL_NOEXCEPT; /// /// Search for sessions by their associated search handles. /// /// Xbox live context for the local user. /// The Service Configuration ID (SCID) within which to query for search handles. The SCID is considered case sensitive so paste it directly from the Partner Center /// The name of the template to query for search handles. /// This specifies the attribute to sort the search handles by. Pass empty string to default to ordering by 'Timestamp asc'. /// Pass true to order ascending, false to order descending. /// The query string to get the search handles for. /// The social group to get the search handles for. /// The AsyncBlock for this operation. /// HRESULT return code for this API operation. /// /// **Filtering search handles:** /// /// The query syntax is an OData like syntax with only the following operators supported EQ, NE, GE, GT, LE and LT along with the logical operators of AND and OR. /// /// Example 1: /// To filter for search handles for a specific XboxUserId use /// "MemberXuids/any(d:d eq '12345678')" or "OwnerXuids/any(d:d eq '12345678')" /// /// Example 2: /// To filter for search handles for a title defined string metadata use /// "Strings/stringMetadataType eq 'value'" /// /// Example 3: /// To filter for search handles for a title defined numbers metadata AND a tag type value use /// "Numbers/numberMetadataType eq 53 AND Tags/tagType eq 'value'" /// /// **Empty filter and social group:** /// /// Since searchFilter and socialGroup are optional, please make sure to pass in a nullptr if they aren't needed/used. Passing in an empty string "" will **not** work. /// STDAPI XblMultiplayerGetSearchHandlesAsync( _In_ XblContextHandle xblContext, _In_z_ const char* scid, _In_z_ const char* sessionTemplateName, _In_opt_z_ const char* orderByAttribute, _In_ bool orderAscending, _In_opt_z_ const char* searchFilter, _In_opt_z_ const char* socialGroup, _In_ XAsyncBlock* async ) XBL_NOEXCEPT; /// /// Get the number of search handles returned from an XblMultiplayerGetSearchHandlesAsync call. /// /// The AsyncBlock for this operation. /// Passes back the number of search results. /// HRESULT return code for this API operation. STDAPI XblMultiplayerGetSearchHandlesResultCount( _In_ XAsyncBlock* async, _Out_ size_t* searchHandleCount ) XBL_NOEXCEPT; /// /// Get handles to the local search handle objects returned from XblMultiplayerGetSearchHandlesAsync call. /// /// The AsyncBlock for this operation. /// A caller allocated array to pass back the search handle results. /// Each handle must later be closed with . /// Size of search handles array. /// Use to get the count required. /// HRESULT return code for this API operation. STDAPI XblMultiplayerGetSearchHandlesResult( _In_ XAsyncBlock* async, _Out_writes_(searchHandlesCount) XblMultiplayerSearchHandle* searchHandles, _In_ size_t searchHandlesCount ) XBL_NOEXCEPT; /// /// A handle that corresponds to an outstanding invite that has been sent. /// typedef struct XblMultiplayerInviteHandle { /// /// The outstanding invite GUID. /// _Null_terminated_ char Data[XBL_GUID_LENGTH]; } XblMultiplayerInviteHandle; /// /// Invites the specified users to a session. /// This will result in a notification being shown to each invited user using standard invite text. /// If a user accepts that notification the title will be activated. /// /// Xbox live context for the local user. /// An XblMultiplayerSessionReference object representing the session the target users will be invited to. /// The list of xbox user IDs who will be invited. /// Size of the xuids array. /// The ID of the title that the invited user will activate in order to join the session. /// The custom context string ID. /// This string ID is defined during Xbox Live ingestion to identify the invitation text that is additional to the standard invitation text. /// The ID string must be prefixed with "///". /// Pass nullptr if you don't want a custom string added to the invite. /// The activation context string. A game defined string that is passed to the invited game client and interpreted as desired. (Optional) /// The AsyncBlock for this operation. /// HRESULT return code for this API operation. STDAPI XblMultiplayerSendInvitesAsync( _In_ XblContextHandle xblContext, _In_ const XblMultiplayerSessionReference* sessionReference, _In_ const uint64_t* xuids, _In_ size_t xuidsCount, _In_ uint32_t titleId, _In_opt_z_ const char* contextStringId, _In_opt_z_ const char* customActivationContext, _In_ XAsyncBlock* async ) XBL_NOEXCEPT; /// /// Invites the specified users to a session. /// This will result in a notification being shown to each invited user using standard invite text. /// If a user accepts that notification the title will be activated. /// /// The AsyncBlock for this operation. /// The number of handles in the handles array. Size should be equal to the number of invites requested. /// A caller allocated array to pass back the invite handle results. /// The handle ID strings corresponding to the invites that have been sent. /// HRESULT return code for this API operation. STDAPI XblMultiplayerSendInvitesResult( _In_ XAsyncBlock* async, _In_ size_t handlesCount, _Out_writes_(handlesCount) XblMultiplayerInviteHandle* handles ) XBL_NOEXCEPT; /// /// DEPRECATED - Call , which also /// populates `CustomSessionProperties` in the result.
/// Queries for the current activity for a social group of players associated with a particular "owner" player. ///
/// {% term xbox-live %} context for the local player. /// The service configuration identifier (SCID) within which to query for activities. /// The SCID is case-sensitive, so paste it directly from Partner Center. /// The player whose social group will be used for the query. /// The social group (such as "people" or "favorites") to use to get the list of users. /// The `XAsyncBlock` for this operation. /// HRESULT return code for this API operation. /// /// No paging or continuation is available. The Multiplayer service limits the number of items returned to 100. /// STDAPI XblMultiplayerGetActivitiesForSocialGroupAsync( _In_ XblContextHandle xboxLiveContext, _In_ const char* scid, _In_ uint64_t socialGroupOwnerXuid, _In_ const char* socialGroup, _In_ XAsyncBlock* async ) XBL_NOEXCEPT; /// /// Queries for the current activity for a social group of players associated with a particular "owner" player. /// /// {% term xbox-live %} context for the local player. /// The service configuration identifier (SCID) within which to query for activities. /// The SCID is case-sensitive, so paste it directly from Partner Center. /// The player whose social group will be used for the query. /// The social group (such as "people" or "favorites") to use to get the list of users. /// The `XAsyncBlock` for this operation. /// HRESULT return code for this API operation. /// /// No paging or continuation is available. The Multiplayer service limits the number of items returned to 100. /// STDAPI XblMultiplayerGetActivitiesWithPropertiesForSocialGroupAsync( _In_ XblContextHandle xblContext, _In_ const char* scid, _In_ uint64_t socialGroupOwnerXuid, _In_ const char* socialGroup, _In_ XAsyncBlock* async ) XBL_NOEXCEPT; /// /// Gets the number of objects returned. /// /// The `XAsyncBlock` for this operation. /// The number of activity objects returned. /// HRESULT return code for this API operation. STDAPI XblMultiplayerGetActivitiesForSocialGroupResultCount( _In_ XAsyncBlock* async, _Out_ size_t* activityCount ) XBL_NOEXCEPT; /// /// Gets the result of a call to . /// /// The `XAsyncBlock` for this operation. /// The number of activity objects returned. /// A caller-allocated array for the activity objects returned. /// HRESULT return code for this API operation. STDAPI XblMultiplayerGetActivitiesForSocialGroupResult( _In_ XAsyncBlock* async, _In_ size_t activityCount, _Out_writes_(activityCount) XblMultiplayerActivityDetails* activities ) XBL_NOEXCEPT; /// /// Gets the size of objects returned. /// /// The `XAsyncBlock` for this operation. /// The size in bytes required to store the multiplayer activity details. /// HRESULT return code for this API operation. STDAPI XblMultiplayerGetActivitiesWithPropertiesForSocialGroupResultSize( _In_ XAsyncBlock* async, _Out_ size_t* resultSizeInBytes ) XBL_NOEXCEPT; /// /// Gets the result of a call to . /// /// The `XAsyncBlock` for this operation. /// The size of the provided `buffer`. /// A caller-allocated byte buffer to write results into. /// Strongly typed pointer to `buffer`. /// Do not free; its lifecycle is tied to `buffer`. /// Number of entries in `buffer`. /// Number of bytes actually written to `buffer`. /// HRESULT return code for this API operation. STDAPI XblMultiplayerGetActivitiesWithPropertiesForSocialGroupResult( _In_ XAsyncBlock* async, _In_ size_t bufferSize, _Out_writes_bytes_to_(bufferSize, *bufferUsed) void* buffer, _Outptr_ XblMultiplayerActivityDetails** ptrToBuffer, _Out_ size_t* ptrToBufferCount, _Out_opt_ size_t* bufferUsed ) XBL_NOEXCEPT; /// /// DEPRECATED - Call , which also /// populates `CustomSessionProperties` in the result.
/// Queries for the current activity for a set of players specified by Xbox user ID. ///
/// {% term xbox-live %} context for the local user. /// The service configuration identifier (SCID) within which to query for activities. /// The SCID is case-sensitive, so paste it directly from Partner Center. /// The list of Xbox user IDs to find activities for. /// The size of the `xuids` array. /// The `XAsyncBlock` for this operation. /// HRESULT return code for this API operation. /// /// No paging or continuation is available. The Multiplayer service limits the number of items returned to 100. /// STDAPI XblMultiplayerGetActivitiesForUsersAsync( _In_ XblContextHandle xblContext, _In_ const char* scid, _In_reads_(xuidsCount) const uint64_t* xuids, _In_ size_t xuidsCount, _In_ XAsyncBlock* async ) XBL_NOEXCEPT; /// /// Queries for the current activity for a set of players specified by Xbox user ID. /// /// {% term xbox-live %} context for the local user. /// The service configuration identifier (SCID) within which to query for activities. /// The SCID is case-sensitive, so paste it directly from Partner Center. /// The list of Xbox user IDs to find activities for. /// The size of the `xuids` array. /// The `XAsyncBlock` for this operation. /// HRESULT return code for this API operation. /// /// No paging or continuation is available. The Multiplayer service limits the number of items returned to 100. /// STDAPI XblMultiplayerGetActivitiesWithPropertiesForUsersAsync( _In_ XblContextHandle xblContext, _In_ const char* scid, _In_reads_(xuidsCount) const uint64_t* xuids, _In_ size_t xuidsCount, _In_ XAsyncBlock* async ) XBL_NOEXCEPT; /// /// Gets the number of objects returned. /// /// The `XAsyncBlock` for this operation. /// The number of activity objects returned. /// HRESULT return code for this API operation. STDAPI XblMultiplayerGetActivitiesForUsersResultCount( _In_ XAsyncBlock* async, _Out_ size_t* activityCount ) XBL_NOEXCEPT; /// /// Gets the result of a call to . /// /// The `XAsyncBlock` for this operation. /// The number of activity objects returned. /// A caller-allocated array for the activity objects returned. /// HRESULT return code for this API operation. STDAPI XblMultiplayerGetActivitiesForUsersResult( _In_ XAsyncBlock* async, _In_ size_t activityCount, _Out_writes_(activityCount) XblMultiplayerActivityDetails* activities ) XBL_NOEXCEPT; /// /// Gets the size of objects returned. /// /// The `XAsyncBlock` for this operation. /// Returns the size in bytes required to store the result. /// HRESULT return code for this API operation. STDAPI XblMultiplayerGetActivitiesWithPropertiesForUsersResultSize( _In_ XAsyncBlock* async, _Out_ size_t* resultSizeInBytes ) XBL_NOEXCEPT; /// /// Gets the result of a call to . /// /// The `XAsyncBlock` for this operation. /// The size of the provided `buffer`. /// A caller-allocated byte buffer to write results into. /// Strongly typed pointer to `buffer`. /// Do not free; its lifecycle is tied to `buffer`. /// Number of entries in `buffer`. /// Number of bytes actually written to `buffer`. /// HRESULT return code for this API operation. STDAPI XblMultiplayerGetActivitiesWithPropertiesForUsersResult( _In_ XAsyncBlock* async, _In_ size_t bufferSize, _Out_writes_bytes_to_(bufferSize, *bufferUsed) void* buffer, _Outptr_ XblMultiplayerActivityDetails** ptrToBuffer, _Out_ size_t* ptrToBufferCount, _Out_opt_ size_t* bufferUsed ) XBL_NOEXCEPT; /// /// Explicitly starts or stops the multiplayer service connectivity via RTA. Enabling the RTA subscription enables: /// 1. Callbacks when the local Users's sessions change, using the MultiplayerSession object. Handlers are added /// with . /// 2. Automatic removal of members from sessions when the RTA connection underlying this multiplayer subscription is broken. /// /// Xbox live context for the local user. /// True to enable subscriptions and false to stop them. /// HRESULT return code for this API operation. /// /// This method immediately enables the RTA connection, but the in order to receive session changed callbacks, the session /// must be written again after enabling subscriptions. /// STDAPI XblMultiplayerSetSubscriptionsEnabled( _In_ XblContextHandle xblContext, _In_ bool subscriptionsEnabled ) XBL_NOEXCEPT; /// /// Indicates whether multiplayer subscriptions are currently enabled. Note that subscriptions can be enabled/disabled /// explicitly using , but they will also be enabled automatically /// if a session changed handler is added. /// /// Xbox live context for the local user. /// Returns true if enabled, false if not enabled. STDAPI_(bool) XblMultiplayerSubscriptionsEnabled( _In_ XblContextHandle xblHandle ) XBL_NOEXCEPT; /// /// A callback method to be called when a session changes. /// /// Caller context to be passed the handler. /// Arguments to be passed the handler. /// typedef void CALLBACK XblMultiplayerSessionChangedHandler( _In_opt_ void* context, _In_ XblMultiplayerSessionChangeEventArgs args ); /// /// Registers an event handler for notifications when a multiplayer session changes. If the RTA subscription has not /// been explicitly enabled with , adding session changed handlers will /// enable it automatically. Use the returned XblFunctionContext to unregister the handler. /// /// Xbox live context for the local user. /// The callback function that receives notifications. /// Caller context to be passed the handler. /// The function context token that was registered for the event. STDAPI_(XblFunctionContext) XblMultiplayerAddSessionChangedHandler( _In_ XblContextHandle xblContext, _In_ XblMultiplayerSessionChangedHandler* handler, _In_opt_ void* context ) XBL_NOEXCEPT; /// /// Unregisters an event handler for multiplayer session change notifications. /// /// Xbox live context for the local user. /// The XblFunctionContext object that was returned when the event handler was registered. /// STDAPI_(void) XblMultiplayerRemoveSessionChangedHandler( _In_ XblContextHandle xblContext, _In_ XblFunctionContext token ) XBL_NOEXCEPT; /// /// A callback method to be called when a rta subscription is lost. /// /// Caller context to be passed the handler. /// typedef void CALLBACK XblMultiplayerSessionSubscriptionLostHandler( _In_opt_ void* context ); /// /// Registers an event handler for notifications when a multiplayer subscription is lost. /// Use the returned XblFunctionContext to unregister the handler. /// /// Xbox live context for the local user. /// The callback function that receives notifications. /// Caller context to be passed the handler. /// The function context token that was registered for the event. STDAPI_(XblFunctionContext) XblMultiplayerAddSubscriptionLostHandler( _In_ XblContextHandle xblContext, _In_ XblMultiplayerSessionSubscriptionLostHandler* handler, _In_opt_ void* context ) XBL_NOEXCEPT; /// /// Unregisters an event handler for multiplayer subscription lost notifications. /// /// Xbox live context for the local user. /// The XblFunctionContext object that was returned when the event handler was registered. /// STDAPI_(void) XblMultiplayerRemoveSubscriptionLostHandler( _In_ XblContextHandle xblContext, _In_ XblFunctionContext token ) XBL_NOEXCEPT; /// /// A callback method to be called when the multiplayer connection id changes. /// /// Caller context to be passed the handler. /// typedef void CALLBACK XblMultiplayerConnectionIdChangedHandler( _In_opt_ void* context ); /// /// Registers an event handler for notifications when the multiplayer connection id changes. /// Use the returned XblFunctionContext to unregister the handler. /// /// Xbox live context for the local user. /// The callback function that receives notifications. /// Caller context to be passed the handler. /// The function context token that was registered for the event. STDAPI_(XblFunctionContext) XblMultiplayerAddConnectionIdChangedHandler( _In_ XblContextHandle xblContext, _In_ XblMultiplayerConnectionIdChangedHandler* handler, _In_opt_ void* context ) XBL_NOEXCEPT; /// /// Unregisters an event handler for multiplayer connection id changed notifications. /// /// Xbox live context for the local user. /// The XblFunctionContext object that was returned when the event handler was registered. /// STDAPI_(void) XblMultiplayerRemoveConnectionIdChangedHandler( _In_ XblContextHandle xblContext, _In_ XblFunctionContext token ) XBL_NOEXCEPT; } ================================================ FILE: Include/xsapi-c/multiplayer_manager_c.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #if !defined(__cplusplus) #error C++11 required #endif #pragma once #include "multiplayer_c.h" extern "C" { /// /// Defines values used to indicate who can join your lobby. /// enum class XblMultiplayerJoinability : uint32_t { /// /// Joinability not set or no lobby exists yet. /// None, /// /// Default value. The lobby is joinable by users who are followed by an existing member of the session. /// JoinableByFriends, /// /// The lobby is joinable only via an invite. /// InviteOnly, /// /// This option will close the lobby only when a game is in progress. /// All other times, it will keep the lobby open for InviteOnly so invitees can join when no game is in progress. /// DisableWhileGameInProgress, /// /// This option will close the lobby immediately. /// Closed }; /// /// Defines values used to indicate status for the matchmaking stages. /// enum class XblMultiplayerMatchStatus : uint32_t { /// /// Indicates no matchmaking search has been started. /// None, /// /// Indicates that a match ticket was submitted for matchmaking. /// SubmittingMatchTicket, /// /// Indicates that matchmaking is still searching. /// Searching, /// /// Indicates that matchmaking search has found a match. /// Found, /// /// Joining initialization stage. /// Matchmaking creates the game session and adds users to it. /// The client has up to the joining timeout to join the session during this phase. /// Joining, /// /// Waiting for remote clients to join the game session. /// The client has up to the joining timeout to join the session during this phase. /// WaitingForRemoteClientsToJoin, /// /// Measuring initialization stage. /// Stage where QoS measurement happens. /// The client has up to the measurement timeout to upload qos measurements to the service during this phase. /// Measuring, /// /// Uploading QoS measurement results to the service. /// The client has up to the measurement timeout to upload qos measurements to the service during this phase. /// UploadingQosMeasurements, /// /// Waiting for remote clients to upload QoS measurement results to the service. /// The client has up to the measurement timeout to upload qos measurements to the service during this phase. /// WaitingForRemoteClientsToUploadQos, /// /// Evaluating initialization stage. /// If auto evaluate is true, then this stage is skipped. /// Otherwise the title will do its own evaluation. /// Evaluating, /// /// Match was found and QoS measurement was successful. /// Completed, /// /// If the match that was found was not successful and is resubmitting. /// Resubmitting, /// /// Indicates that matchmaking search has expired. /// Expired, /// /// Indicates that matchmaking is in the process of canceling the search. /// Canceling, /// /// Indicates that matchmaking search has been canceled. /// Canceled, /// /// Failed initialization stage. /// The initialization failed. /// Failed, }; /// /// Defines values used to indicate event types for a multiplayer lobby or game. /// The XblMultiplayerEventArgsHandle can be used to get additional information /// about the event depending on the event type. /// enum class XblMultiplayerEventType : uint32_t { /// /// Indicates the user was added. /// You can call XblMultiplayerEventArgsXuid to get the xuid of the added user. /// UserAdded, /// /// Indicates the user was removed. /// You can call XblMultiplayerEventArgsXuid to get the xuid of the added user. /// UserRemoved, /// /// Indicates a new member has joined the session. /// You can call XblMultiplayerEventArgsMembersCount, XblMultiplayerEventArgsMembers /// to get the members who joined. /// MemberJoined, /// /// Indicates a member has left the session. /// You can call XblMultiplayerEventArgsMembersCount, XblMultiplayerEventArgsMembers /// to get the members who left. /// MemberLeft, /// /// Indicates a member property has changed. /// You can call XblMultiplayerEventArgsMember, XblMultiplayerEventArgsPropertiesJson /// to get the properties that changed and the member. /// MemberPropertyChanged, /// /// Indicates that the XblMultiplayerManagerLobbySessionSetLocalMemberProperties() or /// XblMultiplayerManagerLobbySessionDeleteLocalMemberProperties() operation has completed. /// Upon completion, the game can view the XblMultiplayerEvent::Result to see if the write succeeded. /// A game can write local member properties by calling the XblMultiplayerManagerLobbySessionSetLocalMemberProperties() operation. /// LocalMemberPropertyWriteCompleted, /// /// Indicates that the XblMultiplayerManagerLobbySessionSetLocalMemberConnectionAddress() operation has completed. /// Upon completion, the game can view the XblMultiplayerEvent::Result to see if the write succeeded. /// A game can write local member properties by calling the XblMultiplayerManagerLobbySessionSetLocalMemberConnectionAddress() operation. /// LocalMemberConnectionAddressWriteCompleted, /// /// Indicates a session (lobby or game) property has changed. /// You can call XblMultiplayerEventArgsPropertiesJson for the changed properties. /// SessionPropertyChanged, /// /// Indicates that the set property operation has completed. /// Upon completion, the game can view the XblMultiplayerEvent::Result to see if the write succeeded. /// A game can write synchronized properties by calling the XblMultiplayerManagerLobbySessionSetProperties() or /// XblMultiplayerManagerGameSessionSetProperties() operation. /// SessionPropertyWriteCompleted, /// /// Indicates that the set synchronized property operation has completed. /// Upon completion, the game can view the XblMultiplayerEvent::Result to see if the write succeeded. /// A game can write synchronized properties by calling the XblMultiplayerManagerGameSessionSetSynchronizedProperties() operation. /// SessionSynchronizedPropertyWriteCompleted, /// /// Indicates host has changed. /// You can call XblMultiplayerEventArgsMember for the new host. /// HostChanged, /// /// Indicates that the XblMultiplayerManagerLobbySessionSetSynchronizedHost() operation has completed. /// Upon completion, the game can view the XblMultiplayerEvent::Result to see if the write succeeded. /// A game can write synchronized host by calling the XblMultiplayerManagerLobbySessionSetSynchronizedHost() operation. /// SynchronizedHostWriteCompleted, /// /// Indicates that the XblMultiplayerJoinability value has changed. /// A game can change the state by calling the XblMultiplayerManagerSetJoinability() operation. /// JoinabilityStateChanged, /// /// Fired when a match has been found, and the client has joined the target game session. /// When this event occurs, title should provide QOS measurement results (via XblMultiplayerSessionCurrentUserSetQosMeasurements) /// between itself and a list of remote clients. /// Note: If your title does not require QoS (based on the session template), this event will not be triggered. /// You can call XblMultiplayerEventArgsPerformQoSMeasurements for the measurements. /// PerformQosMeasurements, /// /// Indicates that the XblMultiplayerManagerFindMatch() operation has completed. /// You can call XblMultiplayerEventArgsFindMatchCompleted for more information. /// FindMatchCompleted, /// /// Indicates that the XblMultiplayerManagerJoinGame() operation has completed. /// Once the join succeeds, the member is now part of the game session, and can use /// data in the session to connect to other game members. /// JoinGameCompleted, /// /// Indicates that the XblMultiplayerManagerLeaveGame() operation has completed. /// After receiving this event, the game session object will be set to null. /// You can join another game by calling XblMultiplayerManagerJoinGame() or XblMultiplayerManagerJoinGameFromLobby(). /// LeaveGameCompleted, /// /// Indicates that the operation has completed. /// Once the join succeeds, the member is now part of the lobby session, /// and can use data in the session to connect to other lobby members. /// You can call XblMultiplayerEventArgsXuid for the xuid. /// JoinLobbyCompleted, /// /// Fired when the title's connection to MPSD using the real-time activity service is lost. /// When this event occurs, the title should shut down the multiplayer. /// ClientDisconnectedFromMultiplayerService, /// /// Indicates that the invite API operation has been completed. /// /// /// On Xbox, receiving this event does not necessarily mean an invite has been sent successfully. /// You will receive this event as long as the game invite UI flow was successfully invoked, /// even if the user cancelled the UI flow without sending an invite, or the invite failed to submit to the service for various reasons. /// InviteSent, /// /// Only applicable if you are using Xbox Live Tournaments. /// Triggered when the tournament's team registration state changes. /// You can call XblMultiplayerEventArgsTournamentRegistrationStateChanged for more information. /// DEPRECATED. It will be removed in a future release /// TournamentRegistrationStateChanged, /// /// Only applicable if you are using Xbox Live Tournaments. /// Triggered when a new game has been scheduled. /// You can call XblMultiplayerEventArgsTournamentGameSessionReady for more information. /// DEPRECATED. It will be removed in a future release /// TournamentGameSessionReady, /// /// Only applicable if you are using Xbox Live Tournaments. /// Triggered when arbitration is complete and game results have been written to the game session. /// DEPRECATED. It will be removed in a future release /// ArbitrationComplete }; /// /// Defines values used to indicate types for multiplayer sessions. /// enum class XblMultiplayerSessionType : uint32_t { /// /// The session type is unknown. /// Unknown, /// /// Indicates multiplayer lobby session. /// LobbySession, /// /// Indicates multiplayer game session. /// GameSession, /// /// Indicates multiplayer match session. /// MatchSession }; /// /// Represents a reference to a member in a multiplayer game. /// /// /// The member objects are created and owned by MultiplayerManager. /// The fields of returned XblMultiplayerManagerMember objects are only valid until XblMultiplayerManagerDoWork is called again. /// typedef struct XblMultiplayerManagerMember { /// /// Id for the member. /// uint32_t MemberId; /// /// Only applicable if you are using Xbox Live Tournaments. /// Id of this members' team in a tournament. /// DEPRECATED. It will be removed in a future release /// XBL_DEPRECATED _Field_z_ const char* TeamId; /// /// Only applicable if you are using Team rules with Smart Match. /// Initial team assignment suggested by Smart Match. /// _Field_z_ const char* InitialTeam; /// /// Xbox User ID of the member. /// uint64_t Xuid; /// /// The Gamertag of the member. This is only to be used for debugging purposes as this gamertag may be out of date. /// It is recommended you use social manager's XblSocialManagerCreateSocialUserGroupFromList /// or the profile APIs such as XblProfileGetUserProfileAsync to get this information. /// _Field_z_ const char* DebugGamertag; /// /// Indicates if this member is playing on the local device. /// bool IsLocal; /// /// Indicates if this member is part of the lobby. /// bool IsInLobby; /// /// Indicates if this member is part of the game. /// bool IsInGame; /// /// The status of this member. /// XblMultiplayerSessionMemberStatus Status; /// /// The address used for network connection. /// This can be used for secure socket connection. /// _Field_z_ const char* ConnectionAddress; /// /// JSON value that specify the custom properties of the member. /// _Field_z_ const char* PropertiesJson; /// /// Token that uniquely identifies a device. Used for setting host and QoS measurements. /// _Field_z_ const char* DeviceToken; } XblMultiplayerManagerMember; /// /// Determines whether two members are on the same device. /// /// The first member. /// The second member. /// Returns true if both members are on the same device, false if both members are not on the same device. /// This function compares the device tokens of both members. If the device tokens match, both members are on the same device. /// For more information, see . STDAPI_(bool) XblMultiplayerManagerMemberAreMembersOnSameDevice( _In_ const XblMultiplayerManagerMember* first, _In_ const XblMultiplayerManagerMember* second ) XBL_NOEXCEPT; /// /// A handle to multiplayer event arguments that can be used to retrieve additional information for a multiplayer event, depending on the type of event. /// /// /// /// /// /// /// /// /// /// /// typedef struct XblMultiplayerEventArgs* XblMultiplayerEventArgsHandle; /// /// A multiplayer event that is returned from . /// typedef struct XblMultiplayerEvent { /// /// The error code indicating the result of the operation. /// HRESULT Result; /// /// Call-specific debug information if the API operation fails. /// The debug information is not localized; use only for debugging purposes. /// _Field_z_ const char* ErrorMessage; /// /// A pointer to the application-defined data passed into the initiating method. /// void* Context; /// /// The type of the event triggered. /// XblMultiplayerEventType EventType; /// /// A handle to the event arguments for the multiplayer event. /// XblMultiplayerEventArgsHandle EventArgsHandle; /// /// The multiplayer session type this event was triggered for. /// Depending upon the session type, you can then retrieve the latest lobby or game session. /// XblMultiplayerSessionType SessionType; } XblMultiplayerEvent; /// /// A connection address/device token pair to run QoS measurements on. /// typedef struct XblMultiplayerConnectionAddressDeviceTokenPair { /// /// The connection address. /// _Field_z_ const char* connectionAddress; /// /// The connection device token. /// _Field_z_ XblDeviceToken deviceToken; } XblMultiplayerConnectionAddressDeviceTokenPair; /// /// Event arguments returned for `XblMultiplayerEventType::PerformQosMeasurements` events. /// typedef struct XblMultiplayerPerformQoSMeasurementsArgs { /// /// An array of remote clients to perform QoS measurements on. /// const XblMultiplayerConnectionAddressDeviceTokenPair* remoteClients; /// /// The size of the `remoteClients` array. /// size_t remoteClientsSize; } XblMultiplayerPerformQoSMeasurementsArgs; /// /// Retrieves additional information for `XblMultiplayerEventType::UserAdded`, `XblMultiplayerEventType::UserRemoved`, /// and `XblMultiplayerEventType::JoinLobbyCompleted` events. /// /// The event arguments handle for the multiplayer event. /// The applicable Xbox User ID, depending on the multiplayer event: /// `XblMultiplayerEventType::UserAdded` - The Xbox User ID of the member that was added. /// `XblMultiplayerEventType::UserRemoved` - The Xbox User ID of the member that was removed. /// `XblMultiplayerEventType::JoinLobbyCompleted` - The Xbox User ID of the member that was invited. /// /// HRESULT return code for this API operation. /// Call this function to get more information about multiplayer events returned by /// for which the `EventType` member of the for a multiplayer event is set to either /// `XblMultiplayerEventType::UserAdded`, `XblMultiplayerEventType::UserRemoved`, or `XblMultiplayerEventType::JoinLobbyCompleted`. /// The event arguments handle for a multiplayer event can be retrieved from the `EventArgsHandle` member of /// the structure for that multiplayer event. /// For more information about multiplayer events, see /// Multiplayer Manager API overview. /// /// /// /// /// /// /// /// STDAPI XblMultiplayerEventArgsXuid( _In_ XblMultiplayerEventArgsHandle argsHandle, _Out_ uint64_t* xuid ) XBL_NOEXCEPT; /// /// Retrieves additional information for `XblMultiplayerEventType::MemberJoined` and `XblMultiplayerEventType::MemberLeft` events. /// /// The event arguments handle for the multiplayer event. /// The size of the `members` caller-allocated array for . /// HRESULT return code for this API operation. /// Call this function before you call the function, to return /// the size of the array you must allocate for the `members` parameter of the function. /// The event arguments handle for a multiplayer event can be retrieved from the `EventArgsHandle` member of /// the structure for that multiplayer event. /// For more information about multiplayer events, see /// Multiplayer Manager API overview. /// /// /// /// /// /// /// STDAPI XblMultiplayerEventArgsMembersCount( _In_ XblMultiplayerEventArgsHandle argsHandle, _Out_ size_t* memberCount ) XBL_NOEXCEPT; /// /// Retrieves additional information for `XblMultiplayerEventType::MemberJoined` and `XblMultiplayerEventType::MemberLeft` events. /// /// The event arguments handle for the multiplayer event. /// The size of the `members` array. /// A caller-allocated array that passes back a list of members, depending on the multiplayer event: /// `XblMultiplayerEventType::MemberJoined` - A list of members that joined the game. /// `XblMultiplayerEventType::MemberLeft` - A list of members that left the game. /// /// HRESULT return code for this API operation. /// Call the function before you call this function, to return /// the size of the array you must allocate for the `members` parameter of this function. /// The event arguments handle for a multiplayer event can be retrieved from the `EventArgsHandle` member of /// the structure for that multiplayer event. /// For more information about multiplayer events, see /// Multiplayer Manager API overview. /// /// /// /// /// /// /// STDAPI XblMultiplayerEventArgsMembers( _In_ XblMultiplayerEventArgsHandle argsHandle, _In_ size_t membersCount, _Out_writes_(membersCount) XblMultiplayerManagerMember* members ) XBL_NOEXCEPT; /// /// Retrieves additional information for `XblMultiplayerEventType::HostChanged` and `XblMultiplayerEventType::MemberPropertyChanged` events. /// /// The event arguments handle for the multiplayer event. /// The applicable member, depending on the multiplayer event: /// `XblMultiplayerEventType::HostChanged` - The new host member. If an existing host leaves, there is no new host member to return /// in this parameter. In this case, this function returns `HRESULT_FROM_WIN32(ERROR_RESOURCE_DATA_NOT_FOUND)`. /// `XblMultiplayerEventType::MemberPropertyChanged` - The member whose property changed. /// /// HRESULT return code for this API operation. /// Call this function to get more information about multiplayer events returned by /// for which the `EventType` member of the for a multiplayer event is set to either /// `XblMultiplayerEventType::HostChanged` or `XblMultiplayerEventType::MemberPropertyChanged`. /// The event arguments handle for a multiplayer event can be retrieved from the `EventArgsHandle` member of /// the structure for that multiplayer event. /// For more information about multiplayer events, see /// Multiplayer Manager API overview. /// /// /// /// /// /// /// STDAPI XblMultiplayerEventArgsMember( _In_ XblMultiplayerEventArgsHandle argsHandle, _Out_ XblMultiplayerManagerMember* member ) XBL_NOEXCEPT; /// /// Retrieves additional information for `XblMultiplayerEventType::MemberPropertyChanged` /// and `XblMultiplayerEventType::SessionPropertyChanged` events. /// /// The event arguments handle for the multiplayer event. /// A pointer to a JSON string, depending on the multiplayer event: /// `XblMultiplayerEventType::MemberPropertyChanged` - The JSON string of the member property that changed. /// `XblMultiplayerEventType::SessionPropertyChanged` - The JSON string of the session property that changed. /// The memory for the pointer remains valid for the life of the `XblMultiplayerEventArgsHandle` object, until the handle is closed. /// /// HRESULT return code for this API operation. /// Call this function to get more information about multiplayer events returned by /// for which the `EventType` member of the for a multiplayer event is set to either /// `XblMultiplayerEventType::MemberPropertyChanged` or `XblMultiplayerEventType::SessionPropertyChanged`. /// The event arguments handle for a multiplayer event can be retrieved from the `EventArgsHandle` member of /// the structure for that multiplayer event. /// For more information about multiplayer events, see /// Multiplayer Manager API overview. /// /// /// /// /// /// /// STDAPI XblMultiplayerEventArgsPropertiesJson( _In_ XblMultiplayerEventArgsHandle argsHandle, _Out_ const char** properties ) XBL_NOEXCEPT; /// /// Retrieves additional information for `XblMultiplayerEventType.FindMatchCompleted` multiplayer events. /// /// The event arguments handle for the multiplayer event. /// A caller-allocated structure that describes the current matchmaking status. /// A caller-allocated structure that passes back the cause of why /// the initialization failed, or `XblMultiplayerMeasurementFailure::None` if there was no failure. /// This value is set when transitioning out of the `XblMultiplayerMatchStatus::Joining` or /// `XblMultiplayerMatchStatus::Measuring` initialization stages, if this member doesn't pass the initializaton stage. /// HRESULT return code for this API operation. /// Call this function to get more information about multiplayer events returned by /// for which the `EventType` member of the for a multiplayer event is set to /// `XblMultiplayerEventType.FindMatchCompleted`. /// The event arguments handle for a multiplayer event can be retrieved from the `EventArgsHandle` member of /// the structure for that multiplayer event. /// For more information about multiplayer events, see /// Multiplayer Manager API overview. /// /// /// /// /// /// /// STDAPI XblMultiplayerEventArgsFindMatchCompleted( _In_ XblMultiplayerEventArgsHandle argsHandle, _Out_opt_ XblMultiplayerMatchStatus* matchStatus, _Out_opt_ XblMultiplayerMeasurementFailure* initializationFailureCause ) XBL_NOEXCEPT; /// /// Retrieves additional information for XblMultiplayerEventType::TournamentRegistrationStateChanged events. /// /// The event args handle from the XblMultiplayerEvent. /// A caller allocated struct that passes back the tournament team registration state. /// A caller allocated struct that passes back the tournament team registration reason for the certain registration states. /// HRESULT return code for this API operation. STDAPI_XBL_DEPRECATED XblMultiplayerEventArgsTournamentRegistrationStateChanged( _In_ XblMultiplayerEventArgsHandle argsHandle, _Out_opt_ XblTournamentRegistrationState* registrationState, _Out_opt_ XblTournamentRegistrationReason* registrationReason ) XBL_NOEXCEPT; /// /// Retrieves additional information for XblMultiplayerEventType::TournamentGameSessionReady events. /// /// The event args handle from the XblMultiplayerEvent. /// Passes back the game's start time for the tournament. /// HRESULT return code for this API operation. STDAPI_XBL_DEPRECATED XblMultiplayerEventArgsTournamentGameSessionReady( _In_ XblMultiplayerEventArgsHandle argsHandle, _Out_ time_t* startTime ) XBL_NOEXCEPT; /// /// Retrieves additional information for `XblMultiplayerEventType::PerformQosMeasurements` events. /// /// The event arguments handle for the multiplayer event. /// A caller-allocated structure that passes back the remote clients for which QoS information is needed. /// HRESULT return code for this API operation. /// Call this function to get more information about multiplayer events returned by /// for which the `EventType` member of the for a multiplayer event is set to /// `XblMultiplayerEventType::PerformQosMeasurements`. /// The event arguments handle for a multiplayer event can be retrieved from the `EventArgsHandle` member of /// the structure for that multiplayer event. /// For more information about multiplayer events, see /// Multiplayer Manager API overview. /// /// /// /// /// /// /// STDAPI XblMultiplayerEventArgsPerformQoSMeasurements( _In_ XblMultiplayerEventArgsHandle argsHandle, _Out_ XblMultiplayerPerformQoSMeasurementsArgs* performQoSMeasurementsArgs ) XBL_NOEXCEPT; /// /// Initializes Multiplayer Manager (MPM). /// /// The name of the session template for the lobby session to be based on. /// The task queue where all Multiplayer Manager work should be scheduled. /// HRESULT return code for this API operation. /// /// This function initializes the lobby session with which Multiplayer Manager (MPM) interacts, based on a session template configured for the title. /// You must call this function before calling other MPM functions, otherwise errors may occur. For more information about /// configuring session templates, see Configuring the Multiplayer service. /// /// STDAPI XblMultiplayerManagerInitialize( _In_z_ const char* lobbySessionTemplateName, _In_opt_ XTaskQueueHandle asyncQueue ) XBL_NOEXCEPT; /// /// Maintains game state updates between the title and Multiplayer Manager (MPM). /// /// An array of multiplayer events for the game to handle. /// This is set to null if no multiplayer events occur during this update. /// The size of the `multiplayerEvents` array. /// HRESULT return code for this API operation. /// /// This function sends and receives game state updates between the title and Multiplayer Manager (MPM), returning an array /// of structures that represent significant multiplayer events, such as remote players /// joining or leaving. You must call this function on a regular and frequent basis, such as once per frame, so that MPM can /// properly maintain game state. For more information, see Multiplayer Manager overview. /// The multiplayer events returned by this function are owned by MPM, and remain valid only until `XblMultiplayerManagerDoWork` is called again. /// In addition, the title must be thread-safe when calling `XblMultiplayerManagerDoWork`, because game state changes at the time this function is called. /// For example, if you're iterating through the list of members on a thread other than the one from which you're calling this function, /// the list may change when this function is called. /// STDAPI XblMultiplayerManagerDoWork( _Deref_out_opt_ const XblMultiplayerEvent** multiplayerEvents, _Out_ size_t* multiplayerEventsCount ) XBL_NOEXCEPT; /// /// Retrieves the correlation handle for the lobby session. /// /// Passes back the correlation handle for the lobby session, /// or null if a lobby session doesn't exist. The memory for the returned pointer remains /// valid until the next call to . /// HRESULT return code for this API operation. /// /// This function retrieves the correlation handle for the lobby session in Multiplayer Manager (MPM). The correlation handle serves as /// an alias for the lobby session, allowing a game to refer to a lobby session by using only the correlation handle. /// The correlation handle can be used to query trace logs for entries that relate to the lobby session. /// For more information, see Multiplayer concepts overview. /// /// STDAPI XblMultiplayerManagerLobbySessionCorrelationId( _Out_ XblGuid* correlationId ) XBL_NOEXCEPT; /// /// Retrieves the session reference for the lobby session. /// /// Passes back a pointer to the session reference for the lobby session, /// or null if a lobby session doesn't exist. The memory for the returned pointer remains valid until /// the next call to . /// HRESULT return code for this API operation. /// /// This function retrieves a pointer to the session reference for the lobby session, if the lobby session exists in /// Multiplayer Manager. The session reference contains the service configuration ID (SCID), /// session template, and session name for the lobby session, and uniquely identifies /// the lobby session in Multiplayer Session Directory (MPSD). For more information about session references, see /// Multiplayer Session advanced topics. /// /// /// STDAPI XblMultiplayerManagerLobbySessionSessionReference( _Out_ XblMultiplayerSessionReference* sessionReference ) XBL_NOEXCEPT; /// /// Retrieves the number of local members in the lobby session. /// /// The number of members in the lobby session that are local to the device. /// /// STDAPI_(size_t) XblMultiplayerManagerLobbySessionLocalMembersCount() XBL_NOEXCEPT; /// /// Retrieves member information for the local members in the lobby session. /// /// The size of the `localMembers` array. /// The required size can be obtained by calling . /// A caller-allocated array to write into. /// HRESULT return code for this API operation. /// /// This function returns member information for each member in the lobby session that is local to the device. /// You can also call the function to return member /// information for all members in the lobby session. /// The member information returned by this function is valid only until is called again. /// /// STDAPI XblMultiplayerManagerLobbySessionLocalMembers( _In_ size_t localMembersCount, _Out_writes_(localMembersCount) XblMultiplayerManagerMember* localMembers ) XBL_NOEXCEPT; /// /// Retrieves the number of members in the lobby session. /// /// The number of members that are in the lobby session. /// /// /// STDAPI_(size_t) XblMultiplayerManagerLobbySessionMembersCount() XBL_NOEXCEPT; /// /// Retrieves member information for the members in the lobby session. /// /// The size of the `members` array. /// The required size can be obtained by calling . /// A caller-allocated array to write into. /// HRESULT return code for this API operation. /// /// This function returns member information for each member in the lobby session. /// You can also call the function to return member /// information for each member in the lobby session that is local to the device. /// The member information returned by this function is valid only until is called again. /// /// STDAPI XblMultiplayerManagerLobbySessionMembers( _In_ size_t membersCount, _Out_writes_(membersCount) XblMultiplayerManagerMember* members ) XBL_NOEXCEPT; /// /// Retrieves member information for the host member in the lobby session. /// /// A caller-allocated structure to be populated with member information for the host member. /// HRESULT return code for this API operation. /// Returns `__HRESULT_FROM_WIN32(ERROR_NO_SUCH_USER)` if a host member doesn't exist. /// /// This function retrieves member information about the member that represents the host for a lobby session. /// If a lobby session doesn't exist, or if a host member doesn't exist for the lobby session, the function /// returns `__HRESULT_FROM_WIN32(ERROR_NO_SUCH_USER)`. The host member is defined as the user with the lowest /// index on the host device. /// The information returned by this function is valid only until is called again. /// /// /// /// STDAPI XblMultiplayerManagerLobbySessionHost( _Out_ XblMultiplayerManagerMember* hostMember ) XBL_NOEXCEPT; /// /// Retrieves the custom properties for the lobby session, as a JSON string. /// /// A JSON string that specifies the custom properties for the lobby session, or null if a lobby session doesn't exist. /// The memory for the returned pointer remains valid until the next call to . /// /// This function retrieves the custom properties for the lobby session, represented as a JSON string. These custom properties /// can be changed at any time. If custom properties are shared between devices, or may be updated by several devices /// at the same time, use the function to change custom properties. /// Otherwise, you can use the function to change custom properties. /// /// STDAPI_(const char*) XblMultiplayerManagerLobbySessionPropertiesJson() XBL_NOEXCEPT; /// /// Retrieves the session constants associated with the lobby session. /// /// A pointer to the session constants for the lobby session, or null if a lobby session doesn't exist. /// The memory for the returned pointer remains valid until the next call to . /// /// This function retrieves a pointer to the session constants for a lobby session, if the lobby session exists /// in Multiplayer Manager. The session constants contain constants, such as session visibility /// and session capabilities, defined by the session template used for the lobby session. Unlike session properties, /// session constants can only be set through the session template, and are set at the time the lobby session is created. /// For more information about session constants, see Multiplayer Session advanced topics. /// /// /// STDAPI_(const XblMultiplayerSessionConstants*) XblMultiplayerManagerLobbySessionConstants() XBL_NOEXCEPT; XBL_WARNING_PUSH XBL_WARNING_DISABLE_DEPRECATED /// /// The known last team result of the tournament. /// Only applicable if you are using Xbox Live Tournaments. /// /// A pointer to the team's result for the multiplayer game. /// The pointer returned remains valid until the next call to XblMultiplayerManagerDoWork. STDAPI_XBL_DEPRECATED_(const XblTournamentTeamResult*) XblMultiplayerManagerLobbySessionLastTournamentTeamResult() XBL_NOEXCEPT; XBL_WARNING_POP /// /// Joins an Xbox user to the lobby session. /// /// The user handle of the user joining the lobby session. /// HRESULT return code for this API operation. /// /// This function creates a new lobby session and adds the Xbox user specified in to the session. /// Subsequent users are added to the newly-hosted lobby session as secondary users. You can send /// invites, set session properties, and access members of the lobby session only after the first /// local user is added to the lobby session. /// The result of this function is delivered as a multiplayer event with an event type set to `XblMultiplayerEventType::JoinLobbyCompleted`. /// You can call to retrieve multiplayer events. /// When attempting to join a lobby session, the service returns `HTTP_E_STATUS_BAD_REQUEST` if the server is full. /// After joining, you can set the properties for the lobby session by calling /// , or you can set the host for the lobby session by /// calling if the lobby session doesn't already have a host. /// You can also send an invite to another user by calling either /// or . /// If you don't need a lobby session, and if you haven't added local users by calling this function, /// you can instead call and specify the list of users to join the game. /// /// /// STDAPI XblMultiplayerManagerLobbySessionAddLocalUser( _In_ XblUserHandle user ) XBL_NOEXCEPT; /// /// Removes the local user from both the lobby session and game session. /// /// The local user to be removed. /// HRESULT return code for this API operation. /// /// If there are no local users remaining after this function is called, the title cannot /// perform any further multiplayer operations. Changes are batched and written to the service /// when is called. /// The result of this function is delivered as a multiplayer event with an event type set to /// `XblMultiplayerEventType::UserRemoved`. /// You can call to retrieve multiplayer events. /// After leaving, you can join a different game by calling either or /// , or you can re-add the /// local user by calling . /// /// /// /// STDAPI XblMultiplayerManagerLobbySessionRemoveLocalUser( _In_ XblUserHandle user ) XBL_NOEXCEPT; /// /// Set a custom property for a local member to the specified JSON string. /// /// The user you want to set the property for. /// The name of the property to set. /// Optional. The JSON value to assign to the property. /// Optional. The application-defined data to correlate the to the initiating call. /// HRESULT return code for this API operation. /// /// This function sets the value, represented as a JSON string, of a custom property for a local member in /// the lobby session. Custom properties can be changed at any time. Changes are batched and written to the /// service when is called. /// The result of this function is delivered as a multiplayer event with an event type set /// to `XblMultiplayerEventType::LocalMemberPropertyWriteCompleted`. /// You can call to retrieve multiplayer events. /// /// /// STDAPI XblMultiplayerManagerLobbySessionSetLocalMemberProperties( _In_ XblUserHandle user, _In_z_ const char* name, _In_z_ const char* valueJson, _In_opt_ void* context ) XBL_NOEXCEPT; /// /// Deletes a custom property from a local member of the lobby session. /// /// The user handle of the local member. /// The name of the custom property to delete. /// Optional. The application-defined data to correlate the to the initiating call. /// HRESULT return code for this API operation. /// /// This function deletes a custom property from a local member of the lobby session. Custom properties /// can be changed at any time. Changes are batched and written to the service when is called. /// The result of this function is delivered as a multiplayer event with an event type set to /// `XblMultiplayerEventType::LocalMemberPropertyWriteCompleted`. /// You can call to retrieve multiplayer events. /// /// /// STDAPI XblMultiplayerManagerLobbySessionDeleteLocalMemberProperties( _In_ XblUserHandle user, _In_z_ const char* name, _In_opt_ void* context ) XBL_NOEXCEPT; /// /// Sets the connection address for the local member. /// /// The user you want to set the property for. /// The network connection address to set. /// Optional. The application-defined data to correlate the to the initiating call. /// HRESULT return code for this API operation. /// /// This function sets the network connection address of a local member in the lobby session. You can use /// the connection address for network and secure socket connections to that local member. Changes are batched /// and written to the service when is called. /// The result of this function is delivered as a multiplayer event with an event type set /// to `XblMultiplayerEventType::LocalMemberConnectionAddressWriteCompleted`. /// You can call to retrieve multiplayer events. /// /// /// STDAPI XblMultiplayerManagerLobbySessionSetLocalMemberConnectionAddress( _In_ XblUserHandle user, _In_z_ const char* connectionAddress, _In_opt_ void* context ) XBL_NOEXCEPT; /// /// Indicates whether the specified user is the host for the lobby session. /// /// The Xbox User ID (XUID) of the user. /// Returns true if the XUID is the host of the lobby session; otherwise, false. /// /// This function returns false if a lobby session doesn't exist, or if /// the Xbox User ID (XUID) specified in `xuid` isn't the host for the lobby session. You can /// retrieve the host for a lobby session by calling . /// For more information, see Multiplayer Manager API overview. /// /// STDAPI_(bool) XblMultiplayerManagerLobbySessionIsHost( _In_ uint64_t xuid ) XBL_NOEXCEPT; /// /// Sets the value of a custom property for the lobby session. /// /// The name of the custom property to set. /// The value to assign to the property, as a JSON string. /// Optional. The application-defined data to correlate the to the initiating call. /// HRESULT return code for this API operation. /// /// This function sets the value, represented as a JSON string, of a custom property for the lobby session. Custom properties /// can be changed at any time. Changes are batched and written to the service when is called. /// If custom properties are shared between devices, or may be updated by several devices /// at the same time, use the function to change custom properties. /// Otherwise, you can use this function to change custom properties. /// The result of this function is delivered as a multiplayer event with an event type set to `XblMultiplayerEventType::SessionPropertyWriteCompleted`. /// You can call to retrieve multiplayer events. /// /// /// STDAPI XblMultiplayerManagerLobbySessionSetProperties( _In_z_ const char* name, _In_z_ const char* valueJson, _In_opt_ void* context ) XBL_NOEXCEPT; /// /// Sets the value of a custom property for the lobby session, using `XblMultiplayerSessionWriteMode::SynchronizedUpdate`. /// /// The name of the custom property to set. /// The value to assign to the property, as a JSON string. /// Optional. The application-defined data to correlate the to the initiating call. /// HRESULT return code for this API operation. /// /// This function sets the value, represented as a JSON string, of a custom property for the lobby session. Custom properties /// can be changed at any time. If custom properties are shared between devices, or may be updated by several devices /// at the same time, use this function to ensure atomicity and resolve any conflicts between devices while changing the values of those custom properties. /// If a custom property isn't shared across devices, use the function instead /// to change the value of that custom property. /// The service may reject the request to change the custom property if a race condition occurs due to a conflict. /// If the request is rejected, the service returns `HTTP_E_STATUS_PRECOND_FAILED`. If a conflict occurs, re-evaluate the need to /// change the custom property and, if needed, call this function again to re-submit the request. /// The result of this function is delivered as a multiplayer event with an event type set to `XblMultiplayerEventType::SessionSynchronizedPropertyWriteCompleted`. /// You can call to retrieve multiplayer events. /// /// /// STDAPI XblMultiplayerManagerLobbySessionSetSynchronizedProperties( _In_z_ const char* name, _In_z_ const char* valueJson, _In_opt_ void* context ) XBL_NOEXCEPT; /// /// Sets the host for the lobby session, using `XblMultiplayerSessionWriteMode::SynchronizedUpdate`. /// /// The device token of the host. /// Optional. The application-defined data to correlate the to the initiating call. /// HRESULT return code for this API operation. /// /// This function sets the host for the lobby session. Use this function to ensure atomicity and resolve any conflicts between devices /// trying to set the host at the same time. /// The service may reject the request to set the host if a race condition occurs due to a conflict. /// If the request is rejected, the service returns `HTTP_E_STATUS_PRECOND_FAILED`. If a conflict occurs, re-evaluate the need to /// change the host and, if needed, call this function again to re-submit the request. /// The result of this function is delivered as a multiplayer event with an event type set to `XblMultiplayerEventType::SynchronizedHostWriteCompleted`. /// You can call to retrieve multiplayer events. /// Note that host device tokens are generated from a session member's secure device address, so ensure that the secure device address is set for the /// desired host prior to calling this method. /// /// /// /// STDAPI XblMultiplayerManagerLobbySessionSetSynchronizedHost( _In_ const char* deviceToken, _In_opt_ void* context ) XBL_NOEXCEPT; #if HC_PLATFORM_IS_MICROSOFT /// /// Displays the standard Xbox UI, allowing the user to select friends or recent players and invite them to the game. /// /// The user who is sending the invite. /// Optional. The custom context string ID, a string that is defined /// during Xbox Live ingestion, to identify the custom invitation text /// that is added to the standard invitation text. The ID string must be prefixed with /// three slash characters ("///"). /// Optional. The activation context string, a game-defined string /// that is passed to the invited game client and interpreted as desired by the game. /// HRESULT return code for this API operation. /// /// If this function is invoked, Multiplayer Manager sends invites to the selected players when the user /// confirms the player selection in the standard Xbox UI. If a selected player accepts the invite, the title is notified. /// For GDK-based games, the title is notified by invoking the callback function specified /// when the function was invoked. /// For games based on other platforms, the title is activated. /// For more information, see Receiving invites. /// /// /// /// STDAPI XblMultiplayerManagerLobbySessionInviteFriends( _In_ XblUserHandle requestingUser, _In_opt_z_ const char* contextStringId, _In_opt_z_ const char* customActivationContext ) XBL_NOEXCEPT; #endif /// /// Invites the specified users to the game without displaying additional UI. /// /// The user who is sending the invite. /// The array of Xbox User IDs (XUIDs) to be invited. /// The size of the `xuids` array. /// Optional. The custom context string ID, a string that is defined /// during Xbox Live ingestion, to identify the custom invitation text /// that is added to the standard invitation text. The ID string must be prefixed with /// three slash characters ("///"). /// Optional. The activation context string, a game-defined string /// that is passed to the invited game client and interpreted as desired by the game. /// HRESULT return code for this API operation. /// /// Multiplayer Manager sends invites to the selected players when this function is invoked. If a selected /// player accepts the invite, the title is notified. /// For GDK-based games, the title is notified by invoking the callback function specified /// when the function was invoked. /// For games based on other platforms, the title is activated. /// For more information, see Receiving invites. /// /// /// /// STDAPI XblMultiplayerManagerLobbySessionInviteUsers( _In_ XblUserHandle user, _In_ const uint64_t* xuids, _In_ size_t xuidsCount, _In_opt_z_ const char* contextStringId, _In_opt_z_ const char* customActivationContext ) XBL_NOEXCEPT; /// /// Indicates whether there is an active game session. /// /// Returns true if a game session exists for the lobby session in Multiplayer Manager; otherwise, false. /// /// STDAPI_(bool) XblMultiplayerManagerGameSessionActive() XBL_NOEXCEPT; /// /// Retrieves the correlation handle for the game session. /// /// The correlation handle for the game session, or null if a game session doesn't exist. /// The memory for the returned pointer remains valid until the next call to . /// /// This function retrieves the correlation handle for the game session. The correlation handle serves as /// an alias for the game session, allowing a game to refer to a game session by using only the correlation handle. /// The correlation handle can be used to query trace logs for entries that relate to the game session. /// For more information, see Multiplayer concepts overview. /// /// STDAPI_(const char*) XblMultiplayerManagerGameSessionCorrelationId() XBL_NOEXCEPT; /// /// Retrieves the session reference for the game session. /// /// A pointer to the session reference for the game session, or null if a game session doesn't exist. /// The memory for the returned pointer remains valid until the next call to . /// /// This function retrieves a pointer to the session reference for a game session, if the game session exists for /// the lobby session in Multiplayer Manager. The session reference contains the service configuration ID (SCID), /// session template, and session name for the game session, and uniquely identifies /// the game session in Multiplayer Session Directory (MPSD). For more information about session references, see /// Multiplayer Session advanced topics. /// /// /// STDAPI_(const XblMultiplayerSessionReference*) XblMultiplayerManagerGameSessionSessionReference() XBL_NOEXCEPT; /// /// Retrieves the number of members in the game session. /// /// The number of members that are in the game session. /// /// When a friend accepts a game invite, the corresponding member is added to the lobby and the game session members list. /// /// /// STDAPI_(size_t) XblMultiplayerManagerGameSessionMembersCount() XBL_NOEXCEPT; /// /// Retrieves member information for the members in the game session. /// /// The size of the `members` array. /// The required size can be obtained by calling . /// A caller-allocated array to write into. /// HRESULT return code for this API operation. /// /// This function returns member information for each member in a game session. /// The member information returned by this function is valid only until is called again. /// STDAPI XblMultiplayerManagerGameSessionMembers( _In_ size_t membersCount, _Out_writes_(membersCount) XblMultiplayerManagerMember* members ) XBL_NOEXCEPT; /// /// Retrieves member information for the host member in the game session. /// /// A caller-allocated structure to be populated with member information for the host member. /// HRESULT return code for this API operation. /// Returns `__HRESULT_FROM_WIN32(ERROR_NO_SUCH_USER)` if a host member doesn't exist. /// /// This function retrieves member information about the member that represents the host for a game session. /// If a game session doesn't exist, or if a host member doesn't exist for the game session, the function /// returns `__HRESULT_FROM_WIN32(ERROR_NO_SUCH_USER)`. The host member is defined as the user with the lowest /// index on the host device. /// The information returned by this function is valid only until is called again. /// /// /// STDAPI XblMultiplayerManagerGameSessionHost( _Out_ XblMultiplayerManagerMember* hostMember ) XBL_NOEXCEPT; /// /// Retrieves the custom properties for the game session, as a JSON string. /// /// A JSON string that specifies the custom properties for the game session, or null if a game session doesn't exist. /// The memory for the returned pointer remains valid until the next call to . /// /// This function retrieves the custom properties for the game session, represented as a JSON string. These custom properties /// can be changed at any time. If custom properties are shared between devices, or may be updated by several devices /// at the same time, use the function to change custom properties. /// Otherwise, you can use the function to change custom properties. /// /// STDAPI_(const char*) XblMultiplayerManagerGameSessionPropertiesJson() XBL_NOEXCEPT; /// /// Retrieves the session constants associated with the game session. /// /// A pointer to the session constants for the game session, or null if a game session doesn't exist. /// The memory for the returned pointer remains valid until the next call to . /// /// This function retrieves a pointer to the session constants for a game session, if the game session exists for /// the lobby session in Multiplayer Manager. The session constants contain constants, such as session visibility /// and session capabilities, defined by the session template used for the game session. Unlike session properties, /// session constants can only be set through the session template, and are set at the time the game session is created. /// For more information about session constants, see Multiplayer Session advanced topics. /// /// /// STDAPI_(const XblMultiplayerSessionConstants*) XblMultiplayerManagerGameSessionConstants() XBL_NOEXCEPT; /// /// Indicates whether the specified user is the host for the game session. /// /// The Xbox User ID (XUID) of the user. /// Returns true if the XUID is the host of the game session; otherwise, false. /// /// This function returns false if a game session doesn't exist for the lobby session, or if /// the Xbox User ID (XUID) specified in `xuid` isn't the host for the game session. You can /// retrieve the host for a game session by calling . /// For more information, see Multiplayer Manager API overview. /// /// STDAPI_(bool) XblMultiplayerManagerGameSessionIsHost( _In_ uint64_t xuid ) XBL_NOEXCEPT; /// /// Sets the value of a custom property for the game session. /// /// The name of the custom property to set. /// The value to assign to the property, as a JSON string. /// Optional. The application-defined data to correlate the to the initiating call. /// HRESULT return code for this API operation. /// /// This function sets the value, represented as a JSON string, of a custom property for the game session. Custom properties /// can be changed at any time. Changes are batched and written to the service when is called. /// If custom properties are shared between devices, or may be updated by several devices /// at the same time, use the function to change custom properties. /// Otherwise, you can use this function to change custom properties. /// /// STDAPI XblMultiplayerManagerGameSessionSetProperties( _In_z_ const char* name, _In_z_ const char* valueJson, _In_opt_ void* context ) XBL_NOEXCEPT; /// /// Sets the value of a custom property for the game session, using `XblMultiplayerSessionWriteMode::SynchronizedUpdate`. /// /// The name of the custom property to set. /// The value to assign to the property, as a JSON string. /// Optional. The application-defined data to correlate the to the initiating call. /// HRESULT return code for this API operation. /// /// This function sets the value, represented as a JSON string, of a custom property for the game session. Custom properties /// can be changed at any time. If custom properties are shared between devices, or may be updated by several devices /// at the same time, use this function to ensure atomicity and resolve any conflicts between devices while changing the values of those custom properties. /// If a custom property isn't shared across devices, use the function instead /// to change the value of that custom property. /// The service may reject the request to change the custom property if a race condition occurs due to a conflict. /// If the request is rejected, the service returns `HTTP_E_STATUS_PRECOND_FAILED`. If a conflict occurs, re-evaluate the need to /// change the custom property and, if needed, call this function again to re-submit the request. /// The result of this function is delivered as a multiplayer event with an event type set to `XblMultiplayerEventType::SessionSynchronizedPropertyWriteCompleted`. /// You can call to retrieve multiplayer events. /// /// /// STDAPI XblMultiplayerManagerGameSessionSetSynchronizedProperties( _In_z_ const char* name, _In_z_ const char* valueJson, _In_opt_ void* context ) XBL_NOEXCEPT; /// /// Sets the host for the game session, using `XblMultiplayerSessionWriteMode::SynchronizedUpdate`. /// /// The device token of the host. /// Optional. The application-defined data to correlate the to the initiating call. /// HRESULT return code for this API operation. /// /// This function sets the host for the game session. Use this function to ensure atomicity and resolve any conflicts between devices /// trying to set the host at the same time. /// The service may reject the request to set the host if a race condition occurs due to a conflict. /// If the request is rejected, the service returns `HTTP_E_STATUS_PRECOND_FAILED`. If a conflict occurs, re-evaluate the need to /// change the host and, if needed, call this function again to re-submit the request. /// The result of this function is delivered as a multiplayer event with an event type set to `XblMultiplayerEventType::SynchronizedHostWriteCompleted`. /// You can call to retrieve multiplayer events. /// Note that host device tokens are generated from a session member's secure device address, so ensure that the secure device address is set for the /// desired host prior to calling this method. /// /// /// /// STDAPI XblMultiplayerManagerGameSessionSetSynchronizedHost( _In_ const char* deviceToken, _In_opt_ void* context ) XBL_NOEXCEPT; /// /// Joins an Xbox user to a lobby session. /// /// The activity handle for the lobby session. /// The user handle of the user joining the lobby session. /// HRESULT return code for this API operation. /// /// This function joins the Xbox user specified in user to the lobby session specified in handleId. /// The activity handle for the lobby session is typically retrieved either from a game invite or from the `HandleId` value /// of another user's , by calling . /// For more information about multiplayer activities, see Activities. /// The result of this function is delivered as a multiplayer event with an event type set to `XblMultiplayerEventType::JoinLobbyCompleted`. /// You can call to retrieve multiplayer events. /// When attempting to join a lobby session, the service returns `HTTP_E_STATUS_BAD_REQUEST` if the server is full. /// After joining, you can set the properties for the lobby session by calling /// , or you can set the host for the lobby session by /// calling if the lobby session doesn't already have a host. /// You can also send an invite to another user by calling either /// or . /// If you don't need a lobby session, and if you haven't added local users by calling , /// you can instead call and specify the list of users to join the game. /// /// /// STDAPI XblMultiplayerManagerJoinLobby( _In_z_ const char* handleId, _In_ XblUserHandle user ) XBL_NOEXCEPT; /// /// Creates a new game session for the lobby session, or joins an existing game session if one exists for the lobby session. /// /// The name of the session template for the game session to be based on. /// HRESULT return code for this API operation. /// /// This function creates a new game session and adds the current members of the lobby session to the game session, /// if a game session doesn't already exist. If a new user joins the lobby session after a game session is already /// created, this function finds the existing game session in Multiplayer Session Directory (MPSD) by using a /// transfer handle in the lobby session, and then adds the new user to the game session using that transfer handle. /// For more information, see Multiplayer concepts overview. /// If a lobby session doesn't exist, likely because wasn't called before /// calling this function, an error occurs. An error also occurs if matchmaking is in progress. /// This function does not migrate existing lobby session properties to the game session. /// The result of this function is delivered as a multiplayer event with an event type set to `XblMultiplayerEventType::JoinGameCompleted`. /// You can call to retrieve multiplayer events. /// When attempting to join a lobby session, the service returns `HTTP_E_STATUS_BAD_REQUEST` if the server is full. /// After joining, you can set the properties for the game session by calling /// or , /// or you can set the host for the game session by calling . /// /// /// /// STDAPI XblMultiplayerManagerJoinGameFromLobby( _In_z_ const char* sessionTemplateName ) XBL_NOEXCEPT; /// /// Joins a game session, using the globally unique session name. /// /// The globally unique session name for the game session. /// The name of the session template for the game session to be based on. /// An array of Xbox User IDs (XUIDs) that represents the users you want to be part of the game. /// The number of elements in the array specified for `xuids`. /// HRESULT return code for this API operation. /// /// This function joins a list of Xbox users, specified in `xuids`, to the game session identified by the globally /// unique session name specified in `sessionName.` You can get the globally unique session name from the results of the title's third-party matchmaking, /// and you should call this function for all clients that need to join the game. /// The result of this function is delivered as a multiplayer event with an event type set to `XblMultiplayerEventType::JoinGameCompleted`. /// You can call to retrieve multiplayer events. /// When attempting to join a game session, the service returns `HTTP_E_STATUS_BAD_REQUEST` if the server is full. /// After joining, you can set the properties for the game session by calling /// or , /// or you can set the host for the game session by calling . /// STDAPI XblMultiplayerManagerJoinGame( _In_z_ const char* sessionName, _In_z_ const char* sessionTemplateName, _In_opt_ const uint64_t* xuids, _In_ size_t xuidsCount ) XBL_NOEXCEPT; /// /// Leaves the game session, returning the Xbox user and all other local users to the lobby session. /// /// HRESULT return code for this API operation. /// /// This function removes the Xbox user from the game session and returns the user back to the lobby session. The game session is set to null, /// and all local users are also removed from the game session and returned to the lobby session. Any matchmaking request in progress is /// canceled when this function is called. /// The result of this function is delivered as a multiplayer event with an event type set to `XblMultiplayerEventType::LeaveGameCompleted`. /// You can call to retrieve multiplayer events. /// After leaving, you can join a different game by calling either or /// . /// /// /// STDAPI XblMultiplayerManagerLeaveGame() XBL_NOEXCEPT; /// /// Submits a matchmaking request to the server. /// /// The name of the hopper for this request. /// Optional. The attributes of the match ticket for this request, as a JSON string. /// The maximum time, in seconds, to wait for users to join the match. /// HRESULT return code for this API operation. /// /// This function submits a matchmaking request for the lobby session to Multiplayer Manager (MPM). Before you can use this function, /// you must first configure hoppers in the service configuration for your title. A hopper defines the rules that SmartMatch uses /// to match players. /// For more information about hoppers, see Matchmaking overview. /// If a lobby session doesn't exist, likely because wasn't called, or if local users weren't added /// to the lobby session before calling this function, an error occurs. An error also occurs if matchmaking is already in progress. /// The result of this function is delivered as a multiplayer event with an event type set to `XblMultiplayerEventType::FindMatchCompleted`. /// You can call to retrieve multiplayer events. /// /// STDAPI XblMultiplayerManagerFindMatch( _In_z_ const char* hopperName, _In_opt_z_ const char* attributesJson, _In_ uint32_t timeoutInSeconds ) XBL_NOEXCEPT; /// /// Cancels the match request on the server, if one exists. /// /// /// /// This function cancels a previously submitted match ticket. This function is ignored if the status of the match ticket /// is set to `XblMultiplayerMatchStatus::None`, `XblMultiplayerMatchStatus::Expired`, `XblMultiplayerMatchStatus::Canceled`, or /// `XblMultiplayerMatchStatus::Failed`, or if a different host submitted the match ticket. /// If this function is called, the status of the match ticket is set to `XblMultiplayerMatchStatus::Canceling` until /// the match ticket is canceled on the server. /// For more information about match tickets, see Multiplayer concepts overview. /// /// /// STDAPI_(void) XblMultiplayerManagerCancelMatch() XBL_NOEXCEPT; /// /// Provides the current status of matchmaking. /// /// The current status of matchmaking. 'XblMultiplayerMatchStatus::None' if no matchmaking is in progress. STDAPI_(XblMultiplayerMatchStatus) XblMultiplayerManagerMatchStatus() XBL_NOEXCEPT; /// /// Retrieves the estimated wait time, in seconds, to complete a matchmaking request in progress. /// /// The estimated wait time, in seconds. /// /// Call this function only after the function has been called /// to submit a matchmaking request. The matchmaking request uses SmartMatch to find an existing game that has enough open /// player slots for all the members in the lobby session. If a matchmaking request isn't in progress, /// this function returns zero (0) seconds. For more information about finding a multiplayer game, /// see Enable finding a multiplayer game by using SmartMatch using Multiplayer Manager. /// /// STDAPI_(uint32_t) XblMultiplayerManagerEstimatedMatchWaitTime() XBL_NOEXCEPT; /// /// Indicates whether the game should auto-fill open slots during gameplay. /// /// Returns true if the game should auto-fill open slots during gameplay; otherwise, false. /// Call the function to discover /// whether the game should use matchmaking to auto-fill open slots during gameplay. You can also call /// the function to specify whether the game /// should auto-fill open slots during gameplay. For more information about matchmaking, /// see Matchmaking overview. STDAPI_(bool) XblMultiplayerManagerAutoFillMembersDuringMatchmaking() XBL_NOEXCEPT; /// /// Sets if matchmaking should auto fill open slots during gameplay. /// This can be changed anytime. /// /// Set true, to search for members during matchmaking if the game has open slots. /// Set false, to not allow auto fill. /// STDAPI_(void) XblMultiplayerManagerSetAutoFillMembersDuringMatchmaking( _In_ bool autoFillMembers ) XBL_NOEXCEPT; /// /// Sets json representing QoS measurements between the current user and a list of remote clients. /// This is only used when the title is manually managing QoS. /// /// /// Json representing the QoS measurements. /// Example Json: /// "e69c43a8": { // remote client deviceToken /// "latency": 5953, // Milliseconds /// "bandwidthDown" : 19342, // Kilobits per second /// "bandwidthUp" : 944, // Kilobits per second /// "custom" : { } /// }, /// ... // additional remote client entries /// /// HRESULT return code for this API operation. STDAPI XblMultiplayerManagerSetQosMeasurements( _In_z_ const char* measurementsJson ) XBL_NOEXCEPT; /// /// Indicates which users can join your lobby session. /// /// The joinability setting for your lobby session. STDAPI_(XblMultiplayerJoinability) XblMultiplayerManagerJoinability() XBL_NOEXCEPT; /// /// Restricts who can join the game. /// /// The joinability value you want to set. /// The application-defined data to correlate the XblMultiplayerEvent to the initiating call. (Optional) /// HRESULT return code for this API operation. /// /// Defaults to JoinableByFriends, meaning only local users and users who are followed /// by an existing member of the lobby can join without an invite. /// The result is delivered via XblMultiplayerEvent of type JoinabilityStateChanged /// through XblMultiplayerManagerDoWork(). /// Changes are batched and written to the service on the next XblMultiplayerManagerDoWork. /// All session properties and members contain updated response returned from the server upon /// calling XblMultiplayerManagerDoWork. /// STDAPI XblMultiplayerManagerSetJoinability( _In_ XblMultiplayerJoinability joinability, _In_opt_ void* context ) XBL_NOEXCEPT; } ================================================ FILE: Include/xsapi-c/notification_c.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #if !defined(__cplusplus) #error C++11 required #endif #pragma once extern "C" { #if HC_PLATFORM == HC_PLATFORM_IOS || HC_PLATFORM == HC_PLATFORM_ANDROID /// /// Sets the device/registration token received from GNS or APNS and subscribes the title to push notifications. /// /// An xbox live context handle created with XblContextCreateHandle. /// Caller allocated AsyncBlock. /// The device/registration token received from GNS/APNS /// HRESULT return code for this API operation. STDAPI XblNotificationSubscribeToNotificationsAsync( _In_ XblContextHandle xboxLiveContext, _In_ XAsyncBlock* asyncBlock, _In_ const char* deviceToken ) XBL_NOEXCEPT; #endif #if HC_PLATFORM == HC_PLATFORM_IOS || HC_PLATFORM == HC_PLATFORM_ANDROID || HC_PLATFORM == HC_PLATFORM_UWP /// /// Unsubscribes the title from push notifications. /// /// An xbox live context handle created with XblContextCreateHandle. /// Caller allocated AsyncBlock. /// HRESULT return code for this API operation. STDAPI XblNotificationUnsubscribeFromNotificationsAsync( _In_ XblContextHandle xboxLiveContext, _In_ XAsyncBlock* asyncBlock ) XBL_NOEXCEPT; #endif } ================================================ FILE: Include/xsapi-c/pal.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #if !defined(__cplusplus) #error C++11 required #endif #pragma once #if HC_PLATFORM != HC_PLATFORM_ANDROID #pragma warning(disable: 4062) #endif #include "httpClient/pal.h" #if HC_PLATFORM == HC_PLATFORM_GDK #include #if !(_GRDK_YY == 19 && _GRDK_MM_NUM == 5) // GPEA GDK doesn't have XGameEventWrite API #define XSAPI_BUILD_WITH_1910_GRTS 1 #endif #endif // Events/Notifications services #if !(HC_PLATFORM == HC_PLATFORM_XDK || defined(XSAPI_UNIT_TESTS)) #ifndef XSAPI_EVENTS_SERVICE #define XSAPI_EVENTS_SERVICE 1 #endif #endif #if HC_PLATFORM == HC_PLATFORM_GDK && XSAPI_BUILD_WITH_1910_GRTS #ifndef XSAPI_GRTS_EVENTS_SERVICE #define XSAPI_GRTS_EVENTS_SERVICE 1 #endif #endif #if HC_PLATFORM_IS_MICROSOFT && HC_PLATFORM != HC_PLATFORM_WIN32 && !XSAPI_BUILD_WITH_1910_GRTS #ifndef XSAPI_WRL_EVENTS_SERVICE #define XSAPI_WRL_EVENTS_SERVICE 1 #endif #endif #if !defined(XSAPI_GRTS_EVENTS_SERVICE) && !defined(XSAPI_WRL_EVENTS_SERVICE) #ifndef XSAPI_INTERNAL_EVENTS_SERVICE #define XSAPI_INTERNAL_EVENTS_SERVICE 1 #endif #endif #if !(HC_PLATFORM == HC_PLATFORM_XDK || HC_PLATFORM == HC_PLATFORM_GDK || defined(XSAPI_UNIT_TESTS)) #ifndef XSAPI_NOTIFICATION_SERVICE #define XSAPI_NOTIFICATION_SERVICE 1 #endif #endif // WinRT APIs #ifdef XSAPI_UNIT_TESTS #define XSAPI_WINRT 1 #endif extern "C" { #if HC_PLATFORM_IS_MICROSOFT #ifndef _WIN32_WINNT_WIN10 #define _WIN32_WINNT_WIN10 0x0A00 #endif #endif #if !HC_PLATFORM_IS_MICROSOFT #ifdef _In_ #undef _In_ #endif #define _In_ #ifdef _Ret_maybenull_ #undef _Ret_maybenull_ #endif #define _Ret_maybenull_ #ifdef _Post_writable_byte_size_ #undef _Post_writable_byte_size_ #endif #define _Post_writable_byte_size_(X) #ifdef _Outptr_result_maybenull_ #undef _Outptr_result_maybenull_ #endif #define _Outptr_result_maybenull_ #ifndef ANYSIZE_ARRAY #define ANYSIZE_ARRAY 1 #endif #ifndef FIELD_OFFSET #define FIELD_OFFSET(type, field) ((long)(long)&(((type *)0)->field)) #endif #ifndef UNREFERENCED_PARAMETER #define UNREFERENCED_PARAMETER(P) (P) #endif #endif #if HC_PLATFORM_IS_MICROSOFT #if _MSC_VER >= 1900 #define XBL_DEPRECATED __declspec(deprecated) #else #define XBL_DEPRECATED #endif #define STDAPI_XBL_DEPRECATED EXTERN_C XBL_DEPRECATED HRESULT STDAPICALLTYPE #define STDAPI_XBL_DEPRECATED_(type) EXTERN_C XBL_DEPRECATED type STDAPICALLTYPE #define XBL_WARNING_PUSH __pragma(warning(push)) #define XBL_WARNING_DISABLE_DEPRECATED __pragma(warning(disable:4996)) #define XBL_WARNING_POP __pragma(warning(pop)) #else #define XBL_DEPRECATED #define STDAPI_XBL_DEPRECATED EXTERN_C XBL_DEPRECATED HRESULT STDAPIVCALLTYPE #define STDAPI_XBL_DEPRECATED_(type) EXTERN_C XBL_DEPRECATED type STDAPIVCALLTYPE #define XBL_WARNING_PUSH #define XBL_WARNING_DISABLE_DEPRECATED #define XBL_WARNING_POP #endif #ifndef _T #if HC_PLATFORM_IS_MICROSOFT #define _T(x) L ## x #else #define _T(x) x #endif #endif #ifndef XBL_CALLING_CONV #define XBL_CALLING_CONV __cdecl #endif #ifdef __cplusplus #define XBL_NOEXCEPT noexcept #else #define XBL_NOEXCEPT #endif } // end extern "C" ================================================ FILE: Include/xsapi-c/platform_c.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #if !defined(__cplusplus) #error C++11 required #endif #pragma once extern "C" { /// /// Handle to an operation XSAPI requested the client to perform. /// typedef struct XblClientOperation* XblClientOperationHandle; /// /// Enum defining the results of a client operation. /// enum class XblClientOperationResult : uint32_t { /// /// Client completed the requested operation sucessfully. /// Success, /// /// Client was not able to complete the requested operation for any reason. /// Failure }; //----------------------------------------------------------------------------- // Types related to custom storage hooks. Local storage is needed on Win32, // iOS, and Android platforms but default implementations are provided. /// /// Write mode for a local storage write operation. /// enum class XblLocalStorageWriteMode : uint32_t { /// /// Appends new data to the end of any existing data. /// Append, /// /// Overwrite any existing data. /// Truncate }; /// /// Invoked by XSAPI to request the client to perform a local storage write operation. /// /// Optional pointer to data used by the event handler. /// The handle for this operation. /// The user for which the data is being written. /// The mode to be used for the write operation. /// Identifies the data being written. /// The size (in bytes) of the data. /// The data to write. /// /// /// When the operation is complete, XblLocalStorageWriteComplete should be called. /// Apart from context, all parameters are owned by XSAPI and are guaranteed to be valid /// until the operation is complete. /// typedef void (*XblLocalStorageWriteHandler)( _In_opt_ void* context, _In_ XblClientOperationHandle operation, _In_ XblUserHandle user, _In_ XblLocalStorageWriteMode mode, _In_z_ char const* key, _In_ size_t dataSize, _In_reads_bytes_(dataSize) void const* data ); /// /// Invoked by XSAPI to request the client to perform a local storage read operation. /// /// Optional pointer to data used by the event handler. /// The handle for this operation. /// The user for which XSAPI is requesting the read. /// Identifies the data being read. /// /// /// When the operation is complete, XblLocalStorageReadComplete should be called. /// Apart from context, all parameters are owned by XSAPI and are guaranteed to be valid /// until the operation is complete. /// If the requested key is not found, the client should complete with /// XblClientOperationResult::Success and no data. /// typedef void (*XblLocalStorageReadHandler)( _In_opt_ void* context, _In_ XblClientOperationHandle operation, _In_ XblUserHandle user, _In_z_ const char* key ); /// /// Invoked by XSAPI to request the client to perform a local storage clear operation. /// /// Optional pointer to data used by the event handler. /// The handle for this operation. /// The user for which XSAPI is requesting the clear. /// Identifies the data being read. /// /// /// When the operation is complete, XblLocalStorageClearComplete should be called. /// Apart from context, all parameters are owned by XSAPI and are guaranteed to be valid /// until the operation is complete. /// typedef void (*XblLocalStorageClearHandler)( _In_opt_ void* context, _In_ XblClientOperationHandle operation, _In_ XblUserHandle user, _In_z_ const char* key ); #ifdef XSAPI_INTERNAL_EVENTS_SERVICE /// /// Completes a local storage write operation. /// /// /// The handle for this operation. /// The result of the operation. /// The new size (in bytes) of the data associated with the requested key. /// HRESULT return code for this API operation. /// /// Should only be called by clients after completing (or failing to complete) a requested write operation. /// STDAPI XblLocalStorageWriteComplete( _In_ XblClientOperationHandle operation, _In_ XblClientOperationResult result, _In_ size_t dataSize ) XBL_NOEXCEPT; /// /// Completes a local storage read operation. /// /// /// The handle for this operation. /// The result of the operation. /// The size (in bytes) of the data. /// The data read. /// HRESULT return code for this API operation. /// /// Should only be called by clients after completing (or failing to complete) a requested read operation. /// If the requested key cannot be found, the operation should be completed with /// XblClientOperationResult::Success and dataSize of 0. /// STDAPI XblLocalStorageReadComplete( _In_ XblClientOperationHandle operation, _In_ XblClientOperationResult result, _In_ size_t dataSize, _In_reads_bytes_opt_(dataSize) void const* data ) XBL_NOEXCEPT; /// /// Completes a local storage clear operation. /// /// /// The handle for this operation. /// The result of the operation. /// HRESULT return code for this API operation. /// /// Should only be called by clients after completing (or failing to complete) a requested clear operation. /// If the requested key cannot be found, the operation should be completed with XblClientOperationResult::Success. /// STDAPI XblLocalStorageClearComplete( _In_ XblClientOperationHandle operation, _In_ XblClientOperationResult result ) XBL_NOEXCEPT; /// /// Sets the storage handlers. /// /// The async queue the callbacks should be invoked on. /// Handler to be invoked when XSAPI needs to write to local storage. /// Handler to be invoked when XSAPI needs to read from local storage. /// Handler to be invoked when XSAPI needs to clear local storage. /// Client context to be passed back to the handlers. /// HRESULT return code for this API operation. /// /// Must be called before XblInitialize and all three handlers must be set together. /// STDAPI XblLocalStorageSetHandlers( _In_opt_ XTaskQueueHandle queue, _In_ XblLocalStorageWriteHandler writeHandler, _In_ XblLocalStorageReadHandler readHandler, _In_ XblLocalStorageClearHandler clearHandler, _In_opt_ void* context ) XBL_NOEXCEPT; #endif } ================================================ FILE: Include/xsapi-c/presence_c.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #if !defined(__cplusplus) #error C++11 required #endif #pragma once #include "real_time_activity_c.h" extern "C" { /// /// Defines values used to indicate the device type associate with an XblSocialManagerPresenceTitleRecord. /// /// /// /// enum class XblPresenceDeviceType : uint32_t { /// /// Unknown device. /// Unknown, /// /// Windows Phone device. /// WindowsPhone, /// /// Windows Phone 7 device. /// WindowsPhone7, /// /// Web device, like Xbox.com. /// Web, /// /// Xbox360 device. /// Xbox360, /// /// PC Games for Windows Live. /// PC, /// /// Xbox Live for Windows device. /// Windows8, /// /// Xbox One device. /// XboxOne, /// /// Windows One Core devices. /// WindowsOneCore, /// /// Windows One Core Mobile devices. /// WindowsOneCoreMobile, /// /// iOS device. /// iOS, /// /// Android device. /// Android, /// /// AppleTV device. /// AppleTV, /// /// Nintendo device. /// Nintendo, /// /// PlayStation device. /// PlayStation, /// /// Win32 based device. /// Win32, /// /// Scarlett device. /// Scarlett }; /// /// Defines values used to indicate the state of the user with regard to the presence service. /// /// enum class XblPresenceUserState : uint32_t { /// /// The state is unknown. /// Unknown, /// /// User is signed in to Xbox Live and active in a title. /// Online, /// /// User is signed-in to Xbox Live, but inactive in all titles. /// Away, /// /// User is not signed in to Xbox Live. /// Offline }; /// /// Defines values used to indicate the states of the screen view of presence information. /// /// enum class XblPresenceTitleViewState : uint32_t { /// /// Unknown view state. /// Unknown, /// /// The title's view is using the full screen. /// FullScreen, /// /// The title's view is using part of the screen with another application snapped. /// Filled, /// /// The title's view is snapped with another application using a part of the screen. /// Snapped, /// /// The title's running in the background and is not visible. /// Background }; /// /// Defines values used to set the level of presence detail return from the service. /// Choosing proper detail level could help the performance of the API. /// /// enum class XblPresenceDetailLevel : uint32_t { /// /// Default detail level. /// Default, /// /// User detail level. User presence info only, no device, title or rich presence info. /// User, /// /// Device detail level. User and device presence info only, no title or rich presence info. /// Device, /// /// Title detail level. User, device and title presence info only, no rich presence info. /// Title, /// /// All detail possible. User, device, title and rich presence info will be provided. /// All }; /// /// Defines values used to indicate the media id types for media presence data. /// enum class XblPresenceMediaIdType : uint32_t { /// /// Unknown media Id. /// Unknown, /// /// Bing media Id. /// Bing, /// /// MediaProvider media Id. /// MediaProvider }; /// /// Defines values used to indicate the title presence state for a user. /// /// enum class XblPresenceTitleState : uint32_t { /// /// Indicates this is a Unknown state. /// Unknown, /// /// Indicates the user started playing the title. /// Started, /// /// Indicates the user ended playing the title. /// Ended }; /// /// Defines values representing the streaming provider. /// /// enum class XblPresenceBroadcastProvider : uint32_t { /// /// Unknown streaming provider. /// Unknown, /// /// Streaming using Twitch. /// Twitch }; /// /// The handle to an xbl presence record. /// typedef struct XblPresenceRecord* XblPresenceRecordHandle; /// /// Defines values representing the xbl presence device record. /// /// typedef struct XblPresenceDeviceRecord { /// /// The device type associated with this record. /// XblPresenceDeviceType deviceType; /// /// The records containing title presence data. /// const struct XblPresenceTitleRecord* titleRecords; /// /// The number of title records in the titleRecords array. /// size_t titleRecordsCount; } XblPresenceDeviceRecord; /// /// Defines values representing the xbl presence title record. /// /// typedef struct XblPresenceTitleRecord { /// /// The title ID. /// uint32_t titleId; /// /// The title name. /// _Field_z_ const char* titleName; /// /// Time when the record was last updated. /// time_t lastModified; /// /// The active state for the title. /// bool titleActive; /// /// The formatted and localized presence string. /// _Field_z_ const char* richPresenceString; /// /// The title view state. /// XblPresenceTitleViewState viewState; /// /// The broadcast information of what the user is broadcasting. /// struct XblPresenceBroadcastRecord* broadcastRecord; } XblPresenceTitleRecord; /// /// The broadcast information of what the user is broadcasting. /// /// typedef struct XblPresenceBroadcastRecord { /// /// Id for this broadcast as defined by the broadcasting service. /// _Field_z_ const char* broadcastId; /// /// The GUID uniquely identifying the broadcasting session. /// char session[XBL_GUID_LENGTH]; /// /// The streaming provider. /// XblPresenceBroadcastProvider provider; /// /// Approximate number of current viewers. /// uint32_t viewerCount; /// /// Time the broadcast was started. /// time_t startTime; } XblPresenceBroadcastRecord; /// /// Ids needed to set Rich Presence. /// /// typedef struct XblPresenceRichPresenceIds { /// /// ID of the service configuration containing the presence strings. /// char scid[XBL_SCID_LENGTH]; /// /// The ID of a presence string that is defined in the service configuration. /// For example, PresenceId = "1" could equal "Playing {0} on {1}" in the service configuration. /// The service configuration might map token 0 to Maps and token 1 to MapId. /// _Field_z_ const char* presenceId; /// /// The IDs of the strings to replace the format string tokens found in the presence string. /// These strings are also defined in the service configuration. /// The ID values in the collection map to the strings associated with the token arguments found in the PresenceId. /// For example let's say this vector view contained the values "4" and "1" and PresenceId = "1" equals "Playing {0} on {1}" in the service configuration. /// The service configuration might map Token 0 = Maps, where MapId = "4" equals "Hometown". /// The service configuration might map Token 1 = Difficulty, where DifficultyId = "1" equals "Casual". /// const char** presenceTokenIds; /// /// The number of Ids in the presenceTokenIds array. /// size_t presenceTokenIdsCount; } XblPresenceRichPresenceIds; /// /// Struct passed to presence APIs to filter the presence records returned. /// /// /// If the filters are not provided, defaults will be used:
/// - Returns records for all possible titles on all devices.
/// - Defaults to XblPresenceDetailLevel::Default which is equivalent to XblPresenceDetailLevel::Title (get basic title level information).
/// - Does not filter out users who are offline or broadcasting.
///
/// /// typedef struct XblPresenceQueryFilters { /// /// Array of device types. If this field is null, defaults to all possible deviceTypes. /// const XblPresenceDeviceType* deviceTypes; /// /// Size of the deviceTypes array. /// size_t deviceTypesCount; /// /// List of titleIds for filtering the result. If the input is an empty vector, defaults to all possible titles. /// const uint32_t* titleIds; /// /// Size of the titleIds array. /// size_t titleIdsCount; /// /// Detail level of the result. Defaults to XblPresenceDetailLevel::Title which get basic title level information. /// To get rich presence info, set to XblPresenceDetailLevel::All /// XblPresenceDetailLevel detailLevel; /// /// If true, API will filter out records for users that are offline. /// bool onlineOnly; /// /// If true, API will filter out records for users that are not broadcasting. /// bool broadcastingOnly; } XblPresenceQueryFilters; /// /// Get the Xuid for the user a presence record is associated with. /// /// Handle for the presence record returned from a GetPresence API. /// Passes back the Xuid the record is associated with. /// HRESULT return code for this API operation. STDAPI XblPresenceRecordGetXuid( _In_ XblPresenceRecordHandle handle, _Out_ uint64_t* xuid ) XBL_NOEXCEPT; /// /// Get the global presence state for a presence record. /// /// Handle for the presence record returned from a GetPresence API. /// A caller allocated struct that passes back the presence state of the record. /// HRESULT return code for this API operation. STDAPI XblPresenceRecordGetUserState( _In_ XblPresenceRecordHandle handle, _Out_ XblPresenceUserState* userState ) XBL_NOEXCEPT; /// /// Get the device presence records associated with a returned presence record. /// /// Handle for the presence record returned from a GetPresence API. /// Passes back a pointer to an array of device presence records. /// The memory for the returned pointer array remains valid for the life of the XblPresenceRecordHandle object until it is closed. /// Passes back the size of the returned array. /// HRESULT return code for this API operation. STDAPI XblPresenceRecordGetDeviceRecords( _In_ XblPresenceRecordHandle handle, _Out_ const XblPresenceDeviceRecord** deviceRecords, _Out_ size_t* deviceRecordsCount ) XBL_NOEXCEPT; /// /// Duplicates a XblPresenceRecordHandle. /// /// The presence record handle. /// Passe back the duplicated handle. /// HRESULT return code for this API operation. STDAPI XblPresenceRecordDuplicateHandle( _In_ XblPresenceRecordHandle handle, _Out_ XblPresenceRecordHandle* duplicatedHandle ) XBL_NOEXCEPT; /// /// Closes a XblPresenceRecordHandle. /// /// The presence record handle. /// /// /// When all outstanding handles have been closed, XblPresenceRecordCloseHandle will free the memory /// associated with the presence record handle. /// STDAPI_(void) XblPresenceRecordCloseHandle( _In_ XblPresenceRecordHandle handle ) XBL_NOEXCEPT; /// /// Sets presence info for the current user context. /// /// Xbox live context for the local user. /// Indicates if the current user context is currently active or inactive in the title. /// The application can choose to set this based on an amount of inactivity. /// Optional pointer to struct which controls the rich presence strings. /// The AsyncBlock for this operation. /// HRESULT return code for this API operation. STDAPI XblPresenceSetPresenceAsync( _In_ XblContextHandle xblContextHandle, _In_ bool isUserActiveInTitle, _In_opt_ XblPresenceRichPresenceIds* richPresenceIds, _In_ XAsyncBlock* async ) XBL_NOEXCEPT; /// /// Gets presence info for a specific Xbox User Id. /// /// Xbox live context for the local user. /// The Xbox User ID of the user to get presence for. /// The AsyncBlock for this operation. /// HRESULT return code for this API operation. /// /// If presence info is needed for multiple users, use the batch API instead: /// STDAPI XblPresenceGetPresenceAsync( _In_ XblContextHandle xblContextHandle, _In_ uint64_t xuid, _In_ XAsyncBlock* async ) XBL_NOEXCEPT; /// /// Get result for an XblPresenceGetPresenceAsync call. /// /// The AsyncBlock for this operation. /// Returned handle to a presence record. /// The associated presence record must be released with /// when it is no longer needed. /// HRESULT return code for this API operation. STDAPI XblPresenceGetPresenceResult( _In_ XAsyncBlock* async, _Out_ XblPresenceRecordHandle* presenceRecordHandle ) XBL_NOEXCEPT; /// /// Gets presence info for multiple users. /// /// Xbox live context for the local user. /// The Xbox User IDs of the users to get presence for. /// Size of the xuids array. /// Optional filters struct to filter results. /// The AsyncBlock for this operation. /// HRESULT return code for this API operation. STDAPI XblPresenceGetPresenceForMultipleUsersAsync( _In_ XblContextHandle xblContextHandle, _In_ uint64_t* xuids, _In_ size_t xuidsCount, _In_opt_ XblPresenceQueryFilters* filters, _In_ XAsyncBlock* async ) XBL_NOEXCEPT; /// /// Get result count for an XblPresenceGetPresenceForMultipleUsersAsync call. /// /// The AsyncBlock for this operation. /// Passes back the number of presence records. /// HRESULT return code for this API operation. STDAPI XblPresenceGetPresenceForMultipleUsersResultCount( _In_ XAsyncBlock* async, _Out_ size_t* resultCount ) XBL_NOEXCEPT; /// /// Get result for an XblPresenceGetPresenceForMultipleUsers call. /// /// The AsyncBlock for this operation. /// A caller allocated array that passes back the record handles result. /// Each handle will need to be released with when they are no longer needed. /// Size of the handles array. /// Use to get the count required. /// HRESULT return code for this API operation. STDAPI XblPresenceGetPresenceForMultipleUsersResult( _In_ XAsyncBlock* async, _Out_writes_(presenceRecordHandlesCount) XblPresenceRecordHandle* presenceRecordHandles, _In_ size_t presenceRecordHandlesCount ) XBL_NOEXCEPT; /// /// Gets presence info for a specific group of users. /// /// Xbox live context for the local user. /// The name of the group of users to get presence for. /// This can be either "Favorites" to retrieve information about favorites, "People" to retrieve information about mutual friends and people a user follows, or "Friends" to retrieve information about mutual friends. /// The user whose group should be targeted. If the input is null, current user will be used. /// Optional filters struct to filter results. /// The AsyncBlock for this operation. /// HRESULT return code for this API operation. /// /// To retrieve the result of calling XblPresenceGetPresenceForSocialGroupAsync, call [XblPresenceGetPresenceForSocialGroupResult](xblpresencegetpresenceforsocialgroupresult.md). /// To retrieve the required buffer size to hold the results of calling XblPresenceGetPresenceForSocialGroupAsync, call [XblPresenceGetPresenceForSocialGroupResultCount](xblpresencegetpresenceforsocialgroupresultcount.md). /// STDAPI XblPresenceGetPresenceForSocialGroupAsync( _In_ XblContextHandle xblContextHandle, _In_z_ const char* socialGroupName, _In_opt_ uint64_t* socialGroupOwnerXuid, _In_opt_ XblPresenceQueryFilters* filters, _In_ XAsyncBlock* async ) XBL_NOEXCEPT; /// /// Get result count for an XblPresenceGetPresenceForSocialGroupAsync call. /// /// The AsyncBlock for this operation. /// Passes back the number of presence records. /// HRESULT return code for this API operation. STDAPI XblPresenceGetPresenceForSocialGroupResultCount( _In_ XAsyncBlock* async, _Out_ size_t* resultCount ) XBL_NOEXCEPT; /// /// Get result for an XblPresenceGetPresenceForSocialGroup call. /// /// The AsyncBlock for this operation. /// A caller allocated array that passes back the record handles result. /// Each handle will need to be released with when they are no longer needed. /// Size of the handles array. /// Use to get the count required. /// HRESULT return code for this API operation. STDAPI XblPresenceGetPresenceForSocialGroupResult( _In_ XAsyncBlock* async, _Out_ XblPresenceRecordHandle* presenceRecordHandles, _In_ size_t presenceRecordHandlesCount ) XBL_NOEXCEPT; /// /// Subscribes to device presence change notifications. /// DEPRECATED. This API continues to work, however it will be removed in a future release. /// Individual RTA subscription will be managed automatically by XSAPI as users are tracked with . /// /// Xbox live context for the local user. /// The Xbox User ID of the person of the subscription. /// Passes back the subscription handle that will be used to unsubscribe. /// HRESULT return code for this API operation. STDAPI_XBL_DEPRECATED XblPresenceSubscribeToDevicePresenceChange( _In_ XblContextHandle xblContextHandle, _In_ uint64_t xuid, _Out_ XblRealTimeActivitySubscriptionHandle* subscriptionHandle ) XBL_NOEXCEPT; /// /// Unsubscribes a previously created device presence change subscription. /// DEPRECATED. This API continues to work, however it will be removed in a future release. /// Individual RTA subscription will be managed automatically by XSAPI as users are untracked with . /// /// Xbox live context for the local user. /// The RTA subscription handle created with . /// This will cause the underlying object to be cleaned up,and will invalidate the subscription handle. /// HRESULT return code for this API operation. STDAPI_XBL_DEPRECATED XblPresenceUnsubscribeFromDevicePresenceChange( _In_ XblContextHandle xblContextHandle, _In_ XblRealTimeActivitySubscriptionHandle subscriptionHandle ) XBL_NOEXCEPT; /// /// Subscribes to title presence change notifications. /// DEPRECATED. This API will be removed in a future release. Individual RTA subscription will be managed automatically by XSAPI as /// titles are tracked with . /// /// Xbox live context for the local user. /// The Xbox User ID of the person of the subscription. /// The title ID. /// Passes back the RTA subscription handle that will be used to unsubscribe. /// HRESULT return code for this API operation. STDAPI_XBL_DEPRECATED XblPresenceSubscribeToTitlePresenceChange( _In_ XblContextHandle xblContextHandle, _In_ uint64_t xuid, _In_ uint32_t titleId, _Out_ XblRealTimeActivitySubscriptionHandle* subscriptionHandle ) XBL_NOEXCEPT; /// /// Unsubscribes a previously created title presence change subscription. /// DEPRECATED. This API will be removed in a future release. Individual RTA subscription will be managed automatically by XSAPI as /// titles are untracked with . /// /// Xbox live context for the local user. /// Handle for the subscription created with . /// This will cause the underlying object to be cleaned up,and will invalidate the subscription handle. /// HRESULT return code for this API operation. STDAPI_XBL_DEPRECATED XblPresenceUnsubscribeFromTitlePresenceChange( _In_ XblContextHandle xblContextHandle, _In_ XblRealTimeActivitySubscriptionHandle subscriptionHandle ) XBL_NOEXCEPT; /// /// Event handler for device presence change notifications. /// /// Caller context that will be passed back to the handler. /// The XboxUserID of the User whose device presence changed. /// The associated device type. /// Boolean for if user is logged on device. /// /// typedef void CALLBACK XblPresenceDevicePresenceChangedHandler( _In_opt_ void* context, _In_ uint64_t xuid, _In_ XblPresenceDeviceType deviceType, _In_ bool isUserLoggedOnDevice ); /// /// Registers an event handler for device presence change notifications. Notifications will /// only be received for the Users configured with . /// /// Xbox live context for the local user. /// The callback function that receives notifications. /// Caller context that will be passed back to the handler. /// An XblFunctionContext object that can be used to unregister the event handler. STDAPI_(XblFunctionContext) XblPresenceAddDevicePresenceChangedHandler( _In_ XblContextHandle xblContextHandle, _In_ XblPresenceDevicePresenceChangedHandler* handler, _In_opt_ void* context ) XBL_NOEXCEPT; /// /// Unregisters an event handler for device presence change notifications. /// /// Xbox live context for the local user. /// The XblFunctionContext object that was returned when the event handler was registered. /// HRESULT return code for this API operation. STDAPI XblPresenceRemoveDevicePresenceChangedHandler( _In_ XblContextHandle xblContextHandle, _In_ XblFunctionContext token ) XBL_NOEXCEPT; /// /// Event handler for title presence change notifications. /// /// Caller context that will be passed back to the handler. /// The XboxUserID of the User whose title presence changed. /// The title ID. /// The title presence state for the user. /// /// typedef void CALLBACK XblPresenceTitlePresenceChangedHandler( _In_opt_ void* context, _In_ uint64_t xuid, _In_ uint32_t titleId, _In_ XblPresenceTitleState titleState ); /// /// Registers an event handler for title presence change notifications. Notifications will /// only be received for the Users and Titles configured with and /// respectively. /// /// Xbox live context for the local user. /// The callback function that receives notifications. /// Caller context that will be passed back to the handler. /// An XblFunctionContext object that can be used to unregister the event handler. STDAPI_(XblFunctionContext) XblPresenceAddTitlePresenceChangedHandler( _In_ XblContextHandle xblContextHandle, _In_ XblPresenceTitlePresenceChangedHandler* handler, _In_opt_ void* context ) XBL_NOEXCEPT; /// /// Unregisters an event handler for title presence change notifications. /// /// Xbox live context for the local user. /// The XblFunctionContext object that was returned when the event handler was registered. /// HRESULT return code for this API operation. STDAPI XblPresenceRemoveTitlePresenceChangedHandler( _In_ XblContextHandle xblContextHandle, _In_ XblFunctionContext token ) XBL_NOEXCEPT; /// /// Configures the list of users for whom real-time device and title presence updates will be tracked. /// /// Xbox live context for the local user. /// Array of XboxUserIDs to append to the existing list of tracked Users. /// Length of xuids array. /// HRESULT return code for this API operation. /// /// Updates will be delivered via XblPresenceDevicePresenceChangedHandlers and XblPresenceTitlePresenceChangedHandlers. /// Note that the set of tracked users can be updated independent from the handlers. /// STDAPI XblPresenceTrackUsers( _In_ XblContextHandle xblContextHandle, _In_ const uint64_t* xuids, _In_ size_t xuidsCount ) XBL_NOEXCEPT; /// /// Configures the list of users for whom real-time device and title presence updates will be tracked. /// Presence updates for the specified Users will no longer be received. /// /// Xbox live context for the local user. /// Array of XboxUserIDs to remove from the list of tracked Users. /// Length of xuids array. /// HRESULT return code for this API operation. STDAPI XblPresenceStopTrackingUsers( _In_ XblContextHandle xblContextHandle, _In_ const uint64_t* xuids, _In_ size_t xuidsCount ) XBL_NOEXCEPT; /// /// Configures the list of titles for which real-time title presence will be tracked. To receive title /// presence updates for titles other than the current title, they must be added using this API. /// /// Xbox live context for the local user. /// Array of title IDs to append to the existing list of tracked titles. Note that /// the current title will be tracked by default. /// Length of the titleIds array. /// HRESULT return code for this API operation. /// /// Updates will be delivered via XblPresenceTitlePresenceChangedHandlers. /// Note that the set of tracked titles can be updated independent from the handlers. /// STDAPI XblPresenceTrackAdditionalTitles( _In_ XblContextHandle xblContextHandle, _In_ const uint32_t* titleIds, _In_ size_t titleIdsCount ) XBL_NOEXCEPT; /// /// Configures the list of titles for which real-time title presence will be tracked. /// Title presence updates for the specified titles will no longer be received. /// /// Xbox live context for the local user. /// Array of title IDs to remove from the list of tracked titles. /// Length of the titleIds array. /// HRESULT return code for this API operation. STDAPI XblPresenceStopTrackingAdditionalTitles( _In_ XblContextHandle xblContextHandle, _In_ const uint32_t* titleIds, _In_ size_t titleIdsCount ) XBL_NOEXCEPT; } ================================================ FILE: Include/xsapi-c/privacy_c.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #if !defined(__cplusplus) #error C++11 required #endif #pragma once extern "C" { /// /// All of the things protected by the Privacy Engine. /// /// enum class XblPrivacySetting : uint32_t { /// /// Unrecognized privacy setting (not one of the below values). /// Unknown = 0, /// /// List of friends. /// ShareFriendList = 1, /// /// Played game history. /// ShareGameHistory = 2, /// /// Messaging and Voice chat. /// CommunicateUsingTextAndVoice = 3, /// /// Online status. /// SharePresence = 4, /// /// User Profile. /// ShareProfile = 5, /// /// Video and Music Status. /// ShareVideoAndMusicStatus = 6, /// /// Video Communication. /// CommunicateUsingVideo = 7, /// /// Voice Data Collection. /// CollectVoiceData = 8, /// /// Share Xbox Music Activity. /// ShareXboxMusicActivity = 9, /// /// Fitness Data. /// ShareExerciseInfo = 11, /// /// Share identity - Real Name, aka MSA Name. /// ShareIdentity = 12, /// /// Real Identity Data for in game use. /// ShareIdentityInGame = 13, /// /// Game DVR. /// ShareRecordedGameSessions = 14, /// /// Allow Microsoft to collect data about Live TV watching. /// CollectLiveTvData = 15, /// /// Allow Microsoft to collect data about Xbox Video watching. /// CollectXboxVideoData = 16, /// /// Allow other users to view real identity via "transitive real name". /// ShareIdentityTransitively = 17, /// /// Allow other users to view the owner's video viewing history. /// ShareVideoHistory = 18, /// /// Allow other users to view the owner's music history. /// ShareMusicHistory = 19, /// /// Allow the user to view user created content of other users. /// This is a parental control on the owner, not a privacy setting that affects other users. /// AllowUserCreatedContentViewing = 20, /// /// Allow the user to view the profiles of other users. /// This is a parental control on the owner, not a privacy setting that affects other users. /// AllowProfileViewing = 21, /// /// Sometimes called the 'Cloaked' bit. /// ShowRealTimeActivity = 22, /// /// Allow full voice data (identifiable to user) to be collected on Xbox One. /// CollectVoiceDataXboxOneFull = 23, /// /// Enforcement setting to prevent a user from sharing identity. /// CanShareIdentity = 24, /// /// Allow other users to share owner's Xbox LIVE content to external social networks. /// ShareContentToExternalNetworks = 25, /// /// Allow voice search data to be collected on Xbox One. /// CollectVoiceSearchData = 26, /// /// Allow other users to see the public clubs that the user has joined. /// ShareClubMembership = 27, /// /// Allow voice data collection from game chats. /// CollectVoiceGameChatData = 28, /// /// Allow other users to view items posted by the owner on their activity feed. /// ShareActivityFeed = 29, /// /// Augments the existing communications settings to allow communications with /// user on platforms other than Xbox Live. /// CommunicateDuringCrossNetworkPlay = 30, }; /// /// Controls user's privileges. /// /// enum class XblPrivilege : uint32_t { /// /// Unrecognized privilege (not one of the below values). /// Unknown = 0, /// /// Controls the ability of the user to view the profile (bio, motto, etc) of other users. /// AllowIngameVoiceCommunications = 205, /// /// Controls the ability of the user to communicate with other users via video. /// AllowVideoCommunications = 235, /// /// Controls the ability of the user to view the profile (bio, motto, etc) of other users. /// AllowProfileViewing = 249, /// /// Controls the ability of the user to communicate with other users. /// AllowCommunications = 252, /// /// Controls the ability of the user to join parties with other users. /// AllowMultiplayer = 254, /// /// Controls the ability of the user to add friends. /// AllowAddFriend = 255 }; /// /// Actions that a client can check permission for. /// /// /// Permission may be restricted by either a missing privilege of the caller or /// a privacy restriction of the target. /// /// /// /// /// enum class XblPermission : uint32_t { /// /// Unrecognized permission (not one of the below values). /// Unknown = 0, /// /// Check whether or not the user can send a message with text content or an invitation to the target user. /// This value does not change if the player has muted the target user. Use CommunicateUsingVoice instead. /// This value will be false if for example you have set your comms to friends only and the target is not a friend. /// This value will be false if for example if the target user has blocked you. /// This value will be false if for example you have set your comms settings to Blocked. /// CommunicateUsingText = 1000, /// /// Check whether or not the user can communicate using video with the target user. /// CommunicateUsingVideo = 1001, /// /// Check whether or not the user can communicate using voice with the target user. /// This will be false if the player has muted the target user. /// CommunicateUsingVoice = 1002, /// /// Check whether or not the user can view the profile of the target user. /// ViewTargetProfile = 1004, /// /// Check whether or not the user can view the game history of the target user. /// ViewTargetGameHistory = 1005, /// /// Check whether or not the user can view the detailed video watching history of the target user. /// ViewTargetVideoHistory = 1006, /// /// Check whether or not the user can view the detailed music listening history of the target user. /// ViewTargetMusicHistory = 1007, /// /// Check whether or not the user can view the exercise info of the target user. /// ViewTargetExerciseInfo = 1009, /// /// Check whether or not the user can view the online status of the target user. /// ViewTargetPresence = 1011, /// /// Check whether or not the user can view the details of the targets video status (extended online presence). /// ViewTargetVideoStatus = 1012, /// /// Check whether or not the user can view the details of the targets music status (extended online presence). /// ViewTargetMusicStatus = 1013, /// /// Check whether or not a user can play multiplayer with the target user. /// PlayMultiplayer = 1014, /// /// Check whether or not the user can view user created content produced by target user. /// ViewTargetUserCreatedContent = 1018, /// /// Check whether or not the user can broadcast sessions on Twitch. /// BroadcastWithTwitch = 1019, /// /// Check whether or not the user can write a comment on an object owned by the target. /// WriteComment = 1022, /// /// Check whether or not the user can share an item owned by the target. /// ShareItem = 1024, /// /// Check whether or not the user can share an item owned by the target to external social networks. /// ShareTargetContentToExternalNetworks = 1025, }; /// /// This describes the various ways that we expose to a requestor why a permission check may fail. /// /// * enum class XblPermissionDenyReason : uint32_t { /// /// Permission was denied, but either no reason was given or the privacy service threw an /// unexpected error. /// Unknown = 0, /// /// The request was processed successfully, but the requestor is not allowed to perform the action. /// No reason is given. /// NotAllowed = 2, /// /// The requestor was missing a privilege necessary for the action. /// MissingPrivilege = 3, /// /// A privilege value for the requestor has a restriction that doesn't allow interaction with the target. /// For instance, a parental control only allows interaction with friends and the target isn't a friend. /// PrivilegeRestrictsTarget = 4, /// /// The requestor has blocked the target user. /// BlockListRestrictsTarget = 5, /// /// The requestor has muted the target user. /// MuteListRestrictsTarget = 7, /// /// A privacy value for the requestor has a restriction that doesn't allow interaction with the target. /// For instance, a parental control only allows interaction with friends and the target isn't a friend. /// PrivacySettingRestrictsTarget = 9, /// /// The target is a cross-network user, but cross-network privacy settings indicated only friends are allowed. /// Cross-network friends are (currently) only managed at the title level, so the title must validate /// that the user are friends. /// CrossNetworkUserMustBeFriend = 12 }; /// /// Represents the different classes of non-Xbox Live users that we can check permissions for. /// /// /// /// enum class XblAnonymousUserType : uint32_t { /// /// Invalid XblAnonymousUserType. Returned if service returns unrecognized XblAnonymousUserType /// Unknown = 0, /// /// A non Xbox Live user. /// CrossNetworkUser, /// /// A non Xbox Live user that a title recognizes as an in-game friend. /// CrossNetworkFriend }; /// /// This struct gives details about why permission is denied. /// /// * typedef struct XblPermissionDenyReasonDetails { /// /// Reason why permission was denied. /// Additional detail maybe found in restrictedPrivilege or /// restrictedPrivacySetting depending on what the reason is. /// XblPermissionDenyReason reason; /// /// Active when the deny reason is either XblPermissionDenyReason::MissingPrivilege or /// XblPermissionDenyReason::PrivilegeRestrictsTarget. /// Unknown otherwise. /// XblPrivilege restrictedPrivilege; /// /// Active when the deny reason is XblPermissionDenyReason::PrivacySettingRestrictsTarget. /// Unknown otherwise. /// XblPrivacySetting restrictedPrivacySetting; } XblPermissionDenyReasonDetails; /// /// Struct describing the result of a permission check request. /// /// /// /// typedef struct XblPermissionCheckResult { /// /// Value indicating whether or not permission to take the requested action is granted. /// bool isAllowed; /// /// Target Xuid for the permission check request. /// Will be 0 if the permission check was for an anonymous user. /// uint64_t targetXuid; /// /// The class of anonymous user the permission check was for. /// Will be XblAnonymousUserType::Unknown if the permission check was for an Xbox Live user. /// XblAnonymousUserType targetUserType; /// /// The permission that was requested. /// XblPermission permissionRequested; /// /// Array of reasons why permission was denied. /// Null when isAllowed is true. /// XblPermissionDenyReasonDetails* reasons; /// /// Number of entries in the reasons array. /// size_t reasonsCount; } XblPermissionCheckResult; /// /// Get the list of Xuids the calling user should avoid during multiplayer matchmaking. /// /// Xbox live context for the local user. /// The AsyncBlock for this operation. /// HRESULT return code for this API operation. /// /// Call and /// upon completion to get the result. /// STDAPI XblPrivacyGetAvoidListAsync( _In_ XblContextHandle xblContextHandle, _In_ XAsyncBlock* async ) XBL_NOEXCEPT; /// /// Get result count for an XblPrivacyGetAvoidListAsync call. /// /// The AsyncBlock for this operation. /// Passes back the number of Xuids in the avoid list. /// HRESULT return code for this API operation. STDAPI XblPrivacyGetAvoidListResultCount( _In_ XAsyncBlock* async, _Out_ size_t* xuidCount ) XBL_NOEXCEPT; /// /// Get result for an XblPrivacyGetAvoidListAsync call. /// /// The AsyncBlock for this operation. /// Size of the xuids array. /// Use to get the count required. /// A caller allocated array that passes back the avoid list xuids result. /// HRESULT return code for this API operation. STDAPI XblPrivacyGetAvoidListResult( _In_ XAsyncBlock* async, _In_ size_t xuidCount, _Out_writes_(xuidCount) uint64_t* xuids ) XBL_NOEXCEPT; /// /// Check a single permission with a single target user. /// /// Xbox live context for the local user. /// The permission to check. /// The target user's Xuid for validation. /// The AsyncBlock for this operation. /// HRESULT return code for this API operation. /// /// If multiple permissions and/or target users are needed, use the batch API instead: /// Call and /// upon completion to get the result. /// STDAPI XblPrivacyCheckPermissionAsync( _In_ XblContextHandle xblContextHandle, _In_ XblPermission permissionToCheck, _In_ uint64_t targetXuid, _In_ XAsyncBlock* async ) XBL_NOEXCEPT; /// /// Get the result size for an XblPrivacyCheckPermissionAsync call. /// /// The AsyncBlock for this operation. /// Passes bakc the size in bytes required to store the permission check result. /// HRESULT return code for this API operation. STDAPI XblPrivacyCheckPermissionResultSize( _In_ XAsyncBlock* async, _Out_ size_t* resultSizeInBytes ) XBL_NOEXCEPT; /// /// Get the result for an XblPrivacyCheckPermissionAsync call. /// /// The AsyncBlock for this operation. /// The size of the provided buffer. /// Use to get the size required. /// A caller allocated byte buffer that passes back the permission results. /// Passes back a strongly typed pointer that points into buffer. /// Do not free this as its lifecycle is tied to buffer. /// Passes back the number of bytes written to the buffer. /// HRESULT return code for this API operation. STDAPI XblPrivacyCheckPermissionResult( _In_ XAsyncBlock* async, _In_ size_t bufferSize, _Out_writes_bytes_to_(bufferSize, *bufferUsed) void* buffer, _Outptr_ XblPermissionCheckResult** ptrToBuffer, _Out_opt_ size_t* bufferUsed ) XBL_NOEXCEPT; /// /// Check a single permission for class of anonymous users. /// /// Xbox live context for the local user. /// The permission to check. /// The class of anonymous user to check permission for. /// The AsyncBlock for this operation. /// HRESULT return code for this API operation. /// /// Call and /// upon completion to get the result. /// STDAPI XblPrivacyCheckPermissionForAnonymousUserAsync( _In_ XblContextHandle xblContextHandle, _In_ XblPermission permissionToCheck, _In_ XblAnonymousUserType userType, _In_ XAsyncBlock* async ) XBL_NOEXCEPT; /// /// Get the result size for an XblPrivacyCheckPermissionForAnonymousUserAsync call. /// /// The AsyncBlock for this operation. /// Passes back the size in bytes required to store the permission check result. /// HRESULT return code for this API operation. STDAPI XblPrivacyCheckPermissionForAnonymousUserResultSize( _In_ XAsyncBlock* async, _Out_ size_t* resultSizeInBytes ) XBL_NOEXCEPT; /// /// Get the result for an XblPrivacyCheckPermissionForAnonymousUserAsync call. /// /// The AsyncBlock for this operation. /// The size of the provided buffer. /// Use to get the size required. /// A caller allocated byte buffer that passes back the permission results. /// Passes back a strongly typed pointer that points into buffer. /// Do not free this as its lifecycle is tied to buffer. /// Passes back the number of bytes written to the buffer. /// HRESULT return code for this API operation. STDAPI XblPrivacyCheckPermissionForAnonymousUserResult( _In_ XAsyncBlock* async, _In_ size_t bufferSize, _Out_writes_bytes_to_(bufferSize, *bufferUsed) void* buffer, _Outptr_ XblPermissionCheckResult** ptrToBuffer, _Out_opt_ size_t* bufferUsed ) XBL_NOEXCEPT; /// /// Check multiple permissions with multiple target users. /// Each permission will be checked against each target user. /// /// Xbox live context for the local user. /// Array of permissions to check. /// Number of entries in the permissions array. /// Array of target Xuids to check permissions against. /// Number of entries in the xuids array. /// Array of anonymous user types to check permissions against. /// Number of entries in the user types array. /// The AsyncBlock for this operation. /// HRESULT return code for this API operation. /// /// Call and /// upon completion to get result. /// STDAPI XblPrivacyBatchCheckPermissionAsync( _In_ XblContextHandle xblContextHandle, _In_reads_(permissionsCount) XblPermission* permissionsToCheck, _In_ size_t permissionsCount, _In_reads_(xuidsCount) uint64_t* targetXuids, _In_ size_t xuidsCount, _In_reads_(targetAnonymousUserTypesCount) XblAnonymousUserType* targetAnonymousUserTypes, _In_ size_t targetAnonymousUserTypesCount, _In_ XAsyncBlock* async ) XBL_NOEXCEPT; /// /// Get the result size for an XblPrivacyBatchCheckPermissionAsync call. /// /// The AsyncBlock for this operation. /// Passes back the size in bytes required to store the permission check results. /// HRESULT return code for this API operation. STDAPI XblPrivacyBatchCheckPermissionResultSize( _In_ XAsyncBlock* async, _Out_ size_t* resultSizeInBytes ) XBL_NOEXCEPT; /// /// Get the results for an XblPrivacyBatchCheckPermissionAsync call. /// /// The AsyncBlock for this operation. /// The size of the provided buffer. /// Use to get the size required. /// A caller allocated byte buffer that passes back the permission result. /// Passes back a strongly typed array of XblPermissionCheckResult that points into buffer. /// Do not free this as its lifecycle is tied to buffer. /// Passes back the number of entries in the ptrToBufferResults array. /// Passes back the number of bytes written to the buffer. /// HRESULT return code for this API operation. STDAPI XblPrivacyBatchCheckPermissionResult( _In_ XAsyncBlock* async, _In_ size_t bufferSize, _Out_writes_bytes_to_(bufferSize, *bufferUsed) void* buffer, _Outptr_ XblPermissionCheckResult** ptrToBufferResults, _Out_ size_t* ptrToBufferCount, _Out_opt_ size_t* bufferUsed ) XBL_NOEXCEPT; /// /// Get the list of Xuids that the calling user should not hear (mute) during multiplayer matchmaking. /// /// Xbox live context for the local user. /// The AsyncBlock for this operation. /// HRESULT return code for this API operation. /// /// Call and /// upon completion to get result. /// STDAPI XblPrivacyGetMuteListAsync( _In_ XblContextHandle xblContextHandle, _In_ XAsyncBlock* async ) XBL_NOEXCEPT; /// /// Get result count for an XblPrivacyGetMuteListAsync call. /// /// The AsyncBlock for this operation. /// Passes back the number of Xuids in the mute list. /// HRESULT return code for this API operation. STDAPI XblPrivacyGetMuteListResultCount( _In_ XAsyncBlock* async, _Out_ size_t* xuidCount ) XBL_NOEXCEPT; /// /// Get result for an XblPrivacyGetMuteListAsync call. /// /// The AsyncBlock for this operation. /// Size of the xuids array. /// Use to get the count required. /// A caller allocated array that passes back the mute list xuids result. /// HRESULT return code for this API operation. STDAPI XblPrivacyGetMuteListResult( _In_ XAsyncBlock* async, _In_ size_t xuidCount, _Out_writes_(xuidCount) uint64_t* xuids ) XBL_NOEXCEPT; } ================================================ FILE: Include/xsapi-c/profile_c.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #if !defined(__cplusplus) #error C++11 required #endif #pragma once extern "C" { /// /// Represents a user's Xbox Live profile. /// /// /// /// typedef struct XblUserProfile { /// /// The user's Xbox user ID. /// uint64_t xboxUserId; /// /// The UTF-8 encoded user's display name to be used in application UI. This will always be the user's gamertag and identical to the gameDisplayName field. /// char appDisplayName[XBL_DISPLAY_NAME_CHAR_SIZE]; /// /// UTF-8 encoded Uri for the user's gamer pic. This will always be identical to the gameDisplayPictureResizeUri field. /// The Uri is a resizable Uri. It can be used to specify one of the following sizes and formats by appending '&format={format}&w={width}&h={height}:
/// Format: png
/// Width Height
/// 64 64
/// 208 208
/// 424 424
///
char appDisplayPictureResizeUri[XBL_DISPLAY_PIC_URL_RAW_CHAR_SIZE]; /// /// The UTF-8 encoded user's display name to be used in application UI. This will always be the user's gamertag and identical to the appDisplayName field. /// char gameDisplayName[XBL_DISPLAY_NAME_CHAR_SIZE]; /// /// UTF-8 encoded Uri for the user's gamer pic. This will always be identical to the appDisplayPictureResizeUri field. /// The Uri is a resizable Uri. It can be used to specify one of the following sizes and formats by appending '&format={format}&w={width}&h={height}:
/// Format: png
/// Width Height
/// 64 64
/// 208 208
/// 424 424
///
char gameDisplayPictureResizeUri[XBL_DISPLAY_PIC_URL_RAW_CHAR_SIZE]; /// /// The UTF-8 encoded user's Gamerscore. /// char gamerscore[XBL_GAMERSCORE_CHAR_SIZE]; /// /// The UTF-8 encoded user's classic gamertag. /// This field only uses ASCII characters and does not include a suffix. /// char gamertag[XBL_GAMERTAG_CHAR_SIZE]; /// /// The UTF-8 encoded modern gamertag for the user. /// This field uses specific ranges of UTF-8 characters and does not include a suffix. /// Not guaranteed to be unique. /// char modernGamertag[XBL_MODERN_GAMERTAG_CHAR_SIZE]; /// /// The UTF-8 encoded numeric suffix appended to modern gamertag to ensure uniqueness. /// May be empty in some cases. /// char modernGamertagSuffix[XBL_MODERN_GAMERTAG_SUFFIX_CHAR_SIZE]; /// /// The UTF-8 encoded unique modern gamertag and numeric suffix. /// Format will be "modernGamertag#suffix". /// Guaranteed to be no more than 16 rendered characters. /// char uniqueModernGamertag[XBL_UNIQUE_MODERN_GAMERTAG_CHAR_SIZE]; } XblUserProfile; /// /// Gets a user profile for a specific Xbox user. /// /// An xbox live context handle created with XblContextCreateHandle. /// The Xbox User ID of the user to get the profile for. /// Caller allocated AsyncBlock. /// HRESULT return code for this API operation. /// /// If profiles are needed for multiple users, use the batch API instead: /// To get the result, call /// inside the AsyncBlock callback or after the AsyncBlock is complete. /// /// Calls V2 GET /users/batch/profile/settings STDAPI XblProfileGetUserProfileAsync( _In_ XblContextHandle xboxLiveContext, _In_ uint64_t xboxUserId, _In_ XAsyncBlock* async ) XBL_NOEXCEPT; /// /// Get the result for a completed XblProfileGetUserProfileAsync operation. /// /// The same AsyncBlock that passed to XblProfileGetUserProfileAsync. /// A caller allocated profile object to write result to. /// HRESULT return code for this API operation. STDAPI XblProfileGetUserProfileResult( _In_ XAsyncBlock* async, _Out_ XblUserProfile* profile ) XBL_NOEXCEPT; /// /// Gets one or more user profiles for a collection of specified Xbox users. /// /// An xbox live context handle created with XblContextCreateHandle. /// C-style array of Xbox User IDs of the users to get profiles for. /// The number of Xbox User IDs in the array. /// Caller allocated AsyncBlock. /// HRESULT return code for this API operation. /// /// To get the result, first call to /// get the count of returned profiles and then call /// inside the AsyncBlock callback or after the AsyncBlock is complete. /// /// Calls V2 GET /users/batch/profile/settings STDAPI XblProfileGetUserProfilesAsync( _In_ XblContextHandle xboxLiveContext, _In_ uint64_t* xboxUserIds, _In_ size_t xboxUserIdsCount, _In_ XAsyncBlock* async ) XBL_NOEXCEPT; /// /// Get the number of profiles from a completed get XblProfileGetUserProfilesAsync operation. /// /// The same AsyncBlock that passed to XblProfileGetUserProfilesAsync. /// Passes back the number of profiles. /// HRESULT return code for this API operation. STDAPI XblProfileGetUserProfilesResultCount( _In_ XAsyncBlock* async, _Out_ size_t* profileCount ) XBL_NOEXCEPT; /// /// Get the result for a completed XblProfileGetUserProfilesAsync operation. /// /// The same AsyncBlock that passed to XblProfileGetUserProfilesAsync. /// The size of the caller allocated profiles array. /// Use to get the count required. /// A caller allocated array that passes back the user profile results. /// HRESULT return code for this API operation. STDAPI XblProfileGetUserProfilesResult( _In_ XAsyncBlock* async, _In_ size_t profilesCount, _Out_writes_(profilesCount) XblUserProfile* profiles ) XBL_NOEXCEPT; /// /// Gets profiles for users in a specified social group. /// /// An xbox live context handle created with XblContextCreateHandle. /// The UTF-8 encoded name of the social group of users to search. Options are "Favorites" and "People". /// Caller allocated AsyncBlock. /// HRESULT return code for this API operation. /// /// To get the result, first call to /// get the count of returned profiles and then call /// inside the AsyncBlock callback or after the AsyncBlock is complete. /// /// Calls V2 GET /users/{userId}/profile/settings/people/{socialGroup} STDAPI XblProfileGetUserProfilesForSocialGroupAsync( _In_ XblContextHandle xboxLiveContext, _In_z_ const char* socialGroup, _In_ XAsyncBlock* async ) XBL_NOEXCEPT; /// /// Get the number of profiles from a completed XblProfileGetUserProfilesForSocialGroupAsync operation. /// /// The same AsyncBlock that passed to XblProfileGetUserProfilesForSocialGroupAsync. /// Passes back the number of profiles. /// HRESULT return code for this API operation. STDAPI XblProfileGetUserProfilesForSocialGroupResultCount( _In_ XAsyncBlock* async, _Out_ size_t* profileCount ) XBL_NOEXCEPT; /// /// Get the result for a completed XblProfileGetUserProfilesForSocialGroupAsync operation. /// /// The same AsyncBlock that passed to XblProfileGetUserProfilesForSocialGroupAsync. /// The size of the caller allocated profiles array. /// Use to get the count required. /// A caller allocated array that passes back the social group user profile results. /// HRESULT return code for this API operation. STDAPI XblProfileGetUserProfilesForSocialGroupResult( _In_ XAsyncBlock* async, _In_ size_t profilesCount, _Out_writes_(profilesCount) XblUserProfile* profiles ) XBL_NOEXCEPT; } ================================================ FILE: Include/xsapi-c/real_time_activity_c.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #if !defined(__cplusplus) #error C++11 required #endif #pragma once extern "C" { /// /// Enumeration for the possible states of a statistic subscription request /// to the real-time activity service. /// enum class XblRealTimeActivitySubscriptionState : uint32_t { /// /// The subscription state is unknown. /// Unknown, /// /// Waiting for the server to respond to the subscription request. /// PendingSubscribe, /// /// Subscription confirmed. /// Subscribed, /// /// Waiting for the server to respond to the unsubscribe request. /// PendingUnsubscribe, /// /// Unsubscribe confirmed. /// Closed }; /// /// Enumeration for the possible connection states of the connection /// to the real-time activity service. /// enum class XblRealTimeActivityConnectionState : uint32_t { /// /// Currently connected to the real-time activity service. /// Connected, /// /// Currently connecting to the real-time activity service. /// Connecting, /// /// Currently disconnected from the real-time activity service. /// Disconnected }; /// /// Subscription handle. /// typedef struct XblRealTimeActivitySubscription* XblRealTimeActivitySubscriptionHandle; /// /// Get the state of the subscription. /// DEPRECATED. The state of RTA subscriptions is no longer exposed publicly. XblRealTimeActivitySubscriptionState::Unknown /// will always be returned. /// /// Subscription handle returned from a subscribe API. /// Passes back the current state of the subscription. /// HRESULT return code for this API operation. STDAPI_XBL_DEPRECATED XblRealTimeActivitySubscriptionGetState( _In_ XblRealTimeActivitySubscriptionHandle subscriptionHandle, _Out_ XblRealTimeActivitySubscriptionState* state ) XBL_NOEXCEPT; /// /// Get the unique ID for the subscription. /// DEPRECATED. The state of RTA subscriptions is no longer exposed publicly. This API will return a unique /// client side ID, but it is in no way related to the ID assigned by the RTA service. /// /// Subscription handle returned from a subscribe API. /// Passes back the ID for the subscription. /// HRESULT return code for this API operation. STDAPI_XBL_DEPRECATED XblRealTimeActivitySubscriptionGetId( _In_ XblRealTimeActivitySubscriptionHandle subscriptionHandle, _Out_ uint32_t* id ) XBL_NOEXCEPT; /// /// Forces XSAPI to open a WebSocket connection to the Xbox Live real-time activity service. /// DEPRECATED. Calling this API is no longer required. The WebSocket connection will be made automatically by /// XSAPI as necessary. This API will be removed in a future release. /// /// Xbox Live context handle. /// HRESULT return code for this API operation. STDAPI_XBL_DEPRECATED XblRealTimeActivityActivate( _In_ XblContextHandle xboxLiveContext ) XBL_NOEXCEPT; /// /// Indicates that a client real-time activity session is complete. If there are no remaining real-time service /// activations, the WebSocket connection will be cleaned up along with remaining subscriptions. /// DEPRECATED. Calling this API is no longer required. The WebSocket connection will be cleaned up automatically /// by XSAPI when it is no longer needed. This API will be removed in a future release. /// /// Xbox Live context handle. /// HRESULT return code for this API operation. STDAPI_XBL_DEPRECATED XblRealTimeActivityDeactivate( _In_ XblContextHandle xboxLiveContext ) XBL_NOEXCEPT; /// /// Handler function for when the client service loses or gains connectivity to the real time activity service. /// /// Caller context that will be passed back to the handler function. /// State of the connection. /// typedef void CALLBACK XblRealTimeActivityConnectionStateChangeHandler( _In_opt_ void* context, _In_ XblRealTimeActivityConnectionState connectionState ); /// /// Registers a handler function to receive a notification that is sent when the client service /// loses or gains connectivity to the real time activity service. /// /// Xbox Live context handle. /// The callback function that receives notifications. /// Caller context that will be passed back to the handler function. /// An XblFunctionContext object that can be used to unregister the event handler. STDAPI_(XblFunctionContext) XblRealTimeActivityAddConnectionStateChangeHandler( _In_ XblContextHandle xboxLiveContext, _In_ XblRealTimeActivityConnectionStateChangeHandler* handler, _In_opt_ void* context ) XBL_NOEXCEPT; /// /// Unregisters an event handler for real time activity connectivity state changes. /// /// Xbox Live context handle. /// The XblFunctionContext object that was returned when the event handler was registered. /// HRESULT return code for this API operation. STDAPI XblRealTimeActivityRemoveConnectionStateChangeHandler( _In_ XblContextHandle xboxLiveContext, _In_ XblFunctionContext token ) XBL_NOEXCEPT; /// /// Handler function for when there is an error in the real time activity service. /// /// Caller context that will be passed back to the handler function. /// Subscription handle. /// The HRESULT error code. /// typedef void CALLBACK XblRealTimeActivitySubscriptionErrorHandler( _In_opt_ void* context, _In_ XblRealTimeActivitySubscriptionHandle subscription, _In_ HRESULT subscriptionError ); /// /// Registers a handler function to receive a notification that is sent when there is an /// error in the real time activity service. /// DEPRECATED. RTA service errors will now be handled by XSAPI internally and callback will no longer be invoked. /// /// Xbox Live context handle. /// The callback function that receives notifications. /// Caller context that will be passed back to the handler function. /// A XblFunctionContext object that can be used to unregister the event handler. STDAPI_XBL_DEPRECATED_(XblFunctionContext) XblRealTimeActivityAddSubscriptionErrorHandler( _In_ XblContextHandle xboxLiveContext, _In_ XblRealTimeActivitySubscriptionErrorHandler* handler, _In_opt_ void* context ) XBL_NOEXCEPT; /// /// Unregisters an event handler for real time activity error notifications. /// DEPRECATED. RTA service errors will now be handled by XSAPI internally and callback will no longer be invoked. /// /// Xbox Live context handle. /// The XblFunctionContext object that was returned when the event handler was registered. /// HRESULT return code for this API operation. STDAPI_XBL_DEPRECATED XblRealTimeActivityRemoveSubscriptionErrorHandler( _In_ XblContextHandle xboxLiveContext, _In_ XblFunctionContext token ) XBL_NOEXCEPT; /// /// Handler function for when there is a resync message from the real time activity service. /// /// Caller context that will be passed back to the handler function. /// typedef void CALLBACK XblRealTimeActivityResyncHandler( _In_opt_ void* context ); /// /// Registers a handler function to receive a notification that is sent when there is a /// resync message from the real time activity service. /// /// Xbox Live context handle. /// The callback function that receives notifications. /// Caller context that will be passed back to the handler function. /// A XblFunctionContext object that can be used to unregister the event handler. /// /// This message indicates that data may have been lost and to resync all data by calling /// corresponding REST API's. Wherever possible, XSAPI will automatically resync the subscription and /// invoke the corresponding handler. For multiplayer session changed subscriptions, titles must resync /// their own sessions. /// STDAPI_(XblFunctionContext) XblRealTimeActivityAddResyncHandler( _In_ XblContextHandle xboxLiveContext, _In_ XblRealTimeActivityResyncHandler* handler, _In_opt_ void* context ) XBL_NOEXCEPT; /// /// Unregisters an event handler for real time activity resync notifications. /// /// Xbox Live context handle. /// The XblFunctionContext object that was returned when the event handler was registered. /// HRESULT return code for this API operation. STDAPI XblRealTimeActivityRemoveResyncHandler( _In_ XblContextHandle xboxLiveContext, _In_ XblFunctionContext token ) XBL_NOEXCEPT; } ================================================ FILE: Include/xsapi-c/services_c.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #if !defined(__cplusplus) #error C++11 required #endif #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef XSAPI_NOTIFICATION_SERVICE #include #endif ================================================ FILE: Include/xsapi-c/social_c.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #if !defined(__cplusplus) #error C++11 required #endif #pragma once #include "real_time_activity_c.h" #include "multiplayer_c.h" extern "C" { /// /// Defines the relationship filters available for social groups. /// /// enum class XblSocialRelationshipFilter : uint32_t { /// /// All the people on the user's people list. /// All, /// /// Filters to only the people on the user's people list that have the attribute "Favorite" associated with them. /// Favorite, /// /// Filters to only the people on the user's people list that are also legacy Xbox Live friends. /// LegacyXboxLiveFriends }; /// /// Defines values used to identify the type of reputation feedback. /// /// /// enum class XblReputationFeedbackType : uint32_t { /// /// Titles that are able to automatically determine that a user kills a teammate /// may send this feedback without user intervention. /// FairPlayKillsTeammates, /// /// Titles that are able to automatically determine that a user is cheating /// may send this feedback without user intervention. /// FairPlayCheater, /// /// Titles that are able to automatically determine that a user has tampered /// with on-disk content may send this feedback without user intervention. /// FairPlayTampering, /// /// Titles that are able to automatically determine that a user quit a game early /// may send this feedback without user intervention. /// FairPlayQuitter, /// /// When a user is voted out of a game (kicked), titles /// may send this feedback without user intervention. /// FairPlayKicked, /// /// Titles that allow users to report inappropriate video communications /// may send this feedback. /// CommunicationsInappropriateVideo, /// /// Titles that allow users to report inappropriate voice communications /// may send this feedback. /// CommunicationsAbusiveVoice, /// /// Titles that allow users to report inappropriate user generated content /// may send this feedback. /// InappropriateUserGeneratedContent, /// /// Titles that allow users to vote on a most valuable player at the end of a multiplayer session /// may send this feedback. /// PositiveSkilledPlayer, /// /// Titles that allow users to submit positive feedback on helpful fellow players /// may send this feedback. /// PositiveHelpfulPlayer, /// /// Titles that allow users to submit positive feedback on shared user generated content /// may send this feedback. /// PositiveHighQualityUserGeneratedContent, /// /// Titles that allow users to report phishing message may send this feedback. /// CommsPhishing, /// /// Titles that allow users to report communication based on a picture /// may send this feedback. /// CommsPictureMessage, /// /// Titles that allow users to report spam messages may send this feedback. /// CommsSpam, /// /// Titles that allow users to report text messages may send this feedback. /// CommsTextMessage, /// /// Titles that allow users to report voice messages may send this feedback. /// CommsVoiceMessage, /// /// Titles that allow users to report voice messages may send this feedback. /// FairPlayConsoleBanRequest, /// /// Titles that allow users to report if a user stands idle on purpose in a game, /// usually round after round, may send this feedback. /// FairPlayIdler, /// /// Titles that report a recommendation to ban a user from Xbox Live may send this feedback. /// FairPlayUserBanRequest, /// /// Titles that allow users to report inappropriate gamer picture may send this feedback. /// UserContentGamerpic, /// /// Titles that allow users to report inappropriate biography and other personal information /// may send this feedback. /// UserContentPersonalInfo, /// /// Titles that allow users to report unsporting behavior may send this feedback. /// FairPlayUnsporting, /// /// Titles that allow users to report leaderboard cheating may send this feedback. /// FairPlayLeaderboardCheater }; /// /// Defines values used to identify the type of social notification. /// /// enum class XblSocialNotificationType : uint32_t { /// /// Unknown. /// Unknown, /// /// User(s) were added. /// Added, /// /// User(s) data changed. /// Changed, /// /// User(s) were removed. /// Removed, /// /// The number of pending incoming friend requests has changed. /// IncomingFriendRequestCountChanged }; /// /// Represents the relationship between the user and another Xbox user. /// /// typedef struct XblSocialRelationship { /// /// The person's Xbox user identifier. /// uint64_t xboxUserId; /// /// Indicates whether the person is one that the user cares about more. /// Since users can have a very large number of people in their people list, favorite people /// should be prioritized first in experiences and shown before others that are not favorites. /// bool isFavorite; /// /// Indicates whether there exists a mutual follower/following relation between a user and another Xbox User /// bool isFriend; /// /// Does not reflect a follower/following relationship, is currently kept for backwards compatibility purposes. /// The value of this field is determined by the value of 'isFriend' within a XblSocialRelationship /// bool isFollowingCaller; /// /// A UTF-8 encoded collection of strings indicating which social networks /// this person has a relationship with. /// _Field_z_ const char** socialNetworks; /// /// The count of social networks strings in the socialNetworks array. /// size_t socialNetworksCount; } XblSocialRelationship; /// /// Event arguments for a social relationship change. /// typedef struct XblSocialRelationshipChangeEventArgs { /// /// The Xbox user ID for the user who's social graph changes are being listed for. /// uint64_t callerXboxUserId; /// /// The type of notification change. /// XblSocialNotificationType socialNotification; /// /// The Xbox user ids who the event is for. /// uint64_t* xboxUserIds; /// /// The number of strings in the xboxUserIds array. /// size_t xboxUserIdsCount; } XblSocialRelationshipChangeEventArgs; typedef struct XblSocialFriendRequestCountChangedEventArgs { /// /// The Xbox user ID for the user who's social graph changes are being listed for. /// uint64_t callerXboxUserId; /// /// Current number of pending incoming friend requests. /// size_t incomingFriendRequestCount; } XblSocialFriendRequestCountChangedEventArgs; /// /// A handle to a social relationship result. /// /// /// This handle is used by other APIs to get the social relationship objects and to get /// the next page of results from the service if there is one. /// The handle must be closed using when the result is no longer needed. /// typedef struct XblSocialRelationshipResult* XblSocialRelationshipResultHandle; /// /// Gets a list of people that the caller is socially connected to. /// /// An xbox live context handle created with XblContextCreateHandle. /// The Xbox User Id to get the social relationships for. /// Controls how the list is filtered. /// Controls the starting index of the results list. /// The maximum number of items that the results list can contain. /// Pass 0 to attempt to retrieve all items. /// Caller allocated . /// HRESULT return code for this API operation. /// /// Call to get the result. /// /// V1 GET /users/{ownerId}/people?view={view}&startIndex={startIndex}&maxItems={maxItems} STDAPI XblSocialGetSocialRelationshipsAsync( _In_ XblContextHandle xboxLiveContext, _In_ uint64_t xboxUserId, _In_ XblSocialRelationshipFilter socialRelationshipFilter, _In_ size_t startIndex, _In_ size_t maxItems, _In_ XAsyncBlock* async ) XBL_NOEXCEPT; /// /// Get the result handle from an XblSocialGetSocialRelationshipsAsync API call. /// /// The same AsyncBlock that was passed to . /// Passes back an XblSocialRelationshipResultHandle. /// HRESULT return code for this API operation. /// /// When no longer using the XblSocialRelationshipResultHandle, make sure to call . /// When all outstanding handles have been closed, the memory associated with the social relationship's results list will be freed. /// After calling this API, make sure to call to get the results list to iterate over. /// Then call to check if there are additional pages of results. /// STDAPI XblSocialGetSocialRelationshipsResult( _In_ XAsyncBlock* async, _Out_ XblSocialRelationshipResultHandle* handle ) XBL_NOEXCEPT; /// /// Get a list of the XblSocialRelationship objects from an XblSocialRelationshipResultHandle. /// /// Social relationship result handle. /// Passes back a pointer to an array of XblSocialRelationship objects. /// The memory for the returned pointer remains valid for the life of the XblSocialRelationshipResultHandle object until it is closed. /// Passes back the number of items in the relationships array. /// HRESULT return code for this API operation. /// /// When no longer using the XblSocialRelationshipResultHandle, make sure to call . /// When all outstanding handles have been closed, the memory associated with the social relationship's results list will be freed. /// STDAPI XblSocialRelationshipResultGetRelationships( _In_ XblSocialRelationshipResultHandle resultHandle, _Out_ const XblSocialRelationship** relationships, _Out_ size_t* relationshipsCount ) XBL_NOEXCEPT; /// /// Checks if there are more pages of social relationships to retrieve from the service. /// /// The XblSocialRelationshipResultHandle from XblSocialGetSocialRelationshipsResult. /// Passes back true if there are more results to retrieve, false otherwise. /// HRESULT return code for this API operation. /// /// If the result has a next page, then call to retrieve the next page of items. /// STDAPI XblSocialRelationshipResultHasNext( _In_ XblSocialRelationshipResultHandle resultHandle, _Out_ bool* hasNext ) XBL_NOEXCEPT; /// /// Gets the total number of results for an XblSocialRelationshipResultHandle. /// /// The XblSocialRelationshipResultHandle from . /// Passes back the total number of results for the query. /// HRESULT return code for this API operation. /// /// Note that this is only the total number of results requested by /// and may be different from the maximum number of result items. /// STDAPI XblSocialRelationshipResultGetTotalCount( _In_ XblSocialRelationshipResultHandle resultHandle, _Out_ size_t* totalCount ) XBL_NOEXCEPT; /// /// Gets the next page of a list of people that the caller is socially connected to. /// /// An xbox live context handle created with XblContextCreateHandle. /// Social relationship result handle from a previous call to XblSocialGetSocialRelationshipsAsync. /// Controls the number of XblSocialRelationship objects to get. 0 will return as many as possible. /// Caller allocated AsyncBlock. /// HRESULT return code for this API operation. /// /// Call this API after finding more results from . /// After calling this API, make sure to call to get the result. /// STDAPI XblSocialRelationshipResultGetNextAsync( _In_ XblContextHandle xboxLiveContext, _In_ XblSocialRelationshipResultHandle resultHandle, _In_ size_t maxItems, _In_ XAsyncBlock* async ) XBL_NOEXCEPT; /// /// Get the result from an XblSocialRelationshipResultGetNextAsync API call. /// /// The same AsyncBlock that passed to . /// Passes back a XblSocialRelationshipResultHandle. /// HRESULT return code for this API operation. /// /// When no longer using the XblSocialRelationshipResultHandle, make sure to call . /// When all outstanding handles have been closed, the memory associated with the social relationship's results list will be freed. /// After calling this API, make sure to call to get the results list to iterate over. /// Then call to see if there are more results. /// STDAPI XblSocialRelationshipResultGetNextResult( _In_ XAsyncBlock* async, _Out_ XblSocialRelationshipResultHandle* handle ) XBL_NOEXCEPT; /// /// Duplicates the XblSocialRelationshipResultHandle. /// /// The XblSocialRelationshipResultHandle to duplicate. /// Passes back the duplicated handle. /// HRESULT return code for this API operation. /// /// When no longer using the XblSocialRelationshipResultHandle, make sure to call . /// STDAPI XblSocialRelationshipResultDuplicateHandle( _In_ XblSocialRelationshipResultHandle handle, _Out_ XblSocialRelationshipResultHandle* duplicatedHandle ) XBL_NOEXCEPT; /// /// Closes the XblSocialRelationshipResultHandle. /// /// The XblSocialRelationshipResultHandle to close. /// /// /// When all outstanding handles have been closed, the memory associated with the social relationship's results list will be freed. /// STDAPI_(void) XblSocialRelationshipResultCloseHandle( _In_ XblSocialRelationshipResultHandle handle ) XBL_NOEXCEPT; /// /// Subscribes to the social service for people changed events. /// DEPRECATED. Calling this API is no longer required and it will be removed in a future release. RTA subscription will be managed /// automatically by XSAPI as `XblSocialRelationshipChangedHandler` are added and removed. /// /// An xbox live context handle created with XblContextCreateHandle. /// The Xbox User ID of the player requesting the subscription. /// Passes back a handle to the subscription which is used to un-subscribe. /// HRESULT return code for this API operation. /// /// Call to un-subscribe. /// STDAPI_XBL_DEPRECATED XblSocialSubscribeToSocialRelationshipChange( _In_ XblContextHandle xboxLiveContext, _In_ uint64_t xboxUserId, _Out_ XblRealTimeActivitySubscriptionHandle* subscriptionHandle ) XBL_NOEXCEPT; /// /// Un-subscribes a previously created social relationship change subscription. /// DEPRECATED. Calling this API is no longer required and it will be removed in a future release. RTA subscription will be managed /// automatically by XSAPI as `XblSocialRelationshipChangedHandler` are added and removed. /// /// An xbox live context handle created with XblContextCreateHandle. /// The subscription handle to unsubscribe. /// HRESULT return code for this API operation. /// /// Call this API only if was used to subscribe to social relationship changes. /// STDAPI_XBL_DEPRECATED XblSocialUnsubscribeFromSocialRelationshipChange( _In_ XblContextHandle xboxLiveContext, _In_ XblRealTimeActivitySubscriptionHandle subscriptionHandle ) XBL_NOEXCEPT; /// /// A callback invoked when a social relationship changes. /// /// The arguments associated with the relationship change. /// The fields of the struct are only valid during the callback. /// Context provided by when the handler is added. /// /// /// For the callback to work properly, you must be subscribed to social relationship changes for at least one user. /// typedef void (STDAPIVCALLTYPE* XblSocialRelationshipChangedHandler)( _In_ const XblSocialRelationshipChangeEventArgs* eventArgs, _In_opt_ void* context ); /// /// Registers an event handler for notifications of social relationship changes caused by the registering user. /// /// An xbox live context handle created with XblContextCreateHandle. /// The callback function that receives notifications. /// Client context pointer to be passed back to the handler. /// A XblFunctionContext used to remove the handler. /// /// This handler triggers only if the user changes the relationship with another user. /// This handler does not trigger if another user changes the relationship with the user. /// /// Call to un-register event handler. /// STDAPI_(XblFunctionContext) XblSocialAddSocialRelationshipChangedHandler( _In_ XblContextHandle xboxLiveContext, _In_ XblSocialRelationshipChangedHandler handler, _In_opt_ void* handlerContext ) XBL_NOEXCEPT; /// /// Removes a social relationship change handler. /// /// An xbox live context handle created with XblContextCreateHandle. /// Context for the handler to remove. /// /// /// /// Call this API only if was used to register an event handler. /// STDAPI XblSocialRemoveSocialRelationshipChangedHandler( _In_ XblContextHandle xboxLiveContext, _In_ XblFunctionContext handlerFunctionContext ) XBL_NOEXCEPT; /// /// A callback invoked the number of incoming friend requests for a user changes. /// /// The arguments associated with the change. /// The fields of the struct are only valid during the callback. /// Context provided by when the handler is added. /// typedef void (STDAPIVCALLTYPE* XblSocialIncomingFriendRequestCountChangedHandler)( _In_ const XblSocialFriendRequestCountChangedEventArgs* eventArgs, _In_opt_ void* context ); /// /// Registers an event handler for notifications of incoming friend requests for the registering user. /// This callback is only useful for developers trying to build social experiences on non-Windows platforms. /// /// An xbox live context handle created with XblContextCreateHandle. /// The callback function that receives notifications. /// Client context pointer to be passed back to the handler. /// Token to be populated and later used to remove the handler. /// /// STDAPI XblSocialAddFriendRequestCountChangedHandler( _In_ XblContextHandle xboxLiveContext, _In_ XblSocialIncomingFriendRequestCountChangedHandler handler, _In_opt_ void* handlerContext, _Out_ XblFunctionContext* handlerFunctionContext ) XBL_NOEXCEPT; /// /// Removes a friend request count change handler. /// /// An xbox live context handle created with XblContextCreateHandle. /// Context for the handler to remove. /// /// /// /// Call this API only if was used to register an event handler. /// STDAPI XblSocialRemoveFriendRequestCountChangedHandler( _In_ XblContextHandle xboxLiveContext, _In_ XblFunctionContext handlerFunctionContext ) XBL_NOEXCEPT; /// /// Represents the parameters for submitting reputation feedback on a user. /// /// typedef struct XblReputationFeedbackItem { /// /// The Xbox User ID of the user that reputation feedback is being submitted on. /// uint64_t xboxUserId; /// /// The reputation feedback type being submitted. /// XblReputationFeedbackType feedbackType; /// /// Multiplayer session reference describing the MPSD session this feedback relates to. (Optional) /// XblMultiplayerSessionReference* sessionReference; /// /// UTF-8 encoded user supplied text added to explain the reason for the feedback. /// _Field_z_ const char* reasonMessage; /// /// The UTF-8 encoded id of a resource that can be used as evidence for the feedback. /// Example: the Id of a video file. /// _Field_z_ const char* evidenceResourceId; } XblReputationFeedbackItem; /// /// Submits reputation feedback on the specified user. /// /// An xbox live context handle created with XblContextCreateHandle. /// The Xbox User ID of the user that reputation feedback is being submitted on. /// The reputation feedback type being submitted. /// Multiplayer session reference describing the MPSD session this feedback relates to. (Optional) /// User supplied text in UTF-8 encoded added to explain the reason for the feedback. (Optional) /// The UTF-8 encoded id of a resource that can be used as evidence for the feedback. /// Example: the Id of a video file. (Optional) /// Caller allocated AsyncBlock. /// HRESULT return code for this API operation. /// /// Make sure to call to get the result. /// /// V100 POST /users/xuid({xuid})/feedback STDAPI XblSocialSubmitReputationFeedbackAsync( _In_ XblContextHandle xboxLiveContext, _In_ uint64_t xboxUserId, _In_ XblReputationFeedbackType reputationFeedbackType, _In_opt_ const XblMultiplayerSessionReference* sessionReference, _In_z_ const char* reasonMessage, _In_opt_z_ const char* evidenceResourceId, _In_ XAsyncBlock* async ) XBL_NOEXCEPT; /// /// Submits reputation feedback on the specified user. /// /// An xbox live context handle created with XblContextCreateHandle. /// An array of XblReputationFeedbackItem objects to submit reputation feedback on. /// The count of items in the feedbackItems array. /// Caller allocated AsyncBlock. /// HRESULT return code for this API operation. /// /// Make sure to call to get the result. /// /// V101 POST /users/batchfeedback STDAPI XblSocialSubmitBatchReputationFeedbackAsync( _In_ XblContextHandle xboxLiveContext, _In_ const XblReputationFeedbackItem* feedbackItems, _In_ size_t feedbackItemsCount, _In_ XAsyncBlock* async ) XBL_NOEXCEPT; } ================================================ FILE: Include/xsapi-c/social_manager_c.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #if !defined(__cplusplus) #error C++11 required #endif #pragma once #include extern "C" { #define XBL_SOCIAL_MANAGER_MAX_USERS_FROM_LIST 100 /// /// Controls how much information is exposed in each xbox_live_social_graph_user. /// Detail level can only be set on construction of social_manager. /// /// enum class XblSocialManagerExtraDetailLevel : uint32_t { /// /// Only get default PeopleHub information (presence, profile). /// NoExtraDetail, /// /// Add extra detail for the title history for the users. /// TitleHistoryLevel = 0x1, /// /// Add extra detail for the preferred color for the users. /// PreferredColorLevel = 0x2, /// /// Add all extra detail. /// All = 0x3, }; DEFINE_ENUM_FLAG_OPERATORS(XblSocialManagerExtraDetailLevel); /// /// The filter level of information. /// Title will only show users associated with a particular title. /// /// /// enum class XblPresenceFilter : uint32_t { /// /// Unknown. /// Unknown, /// /// Is currently playing current title and is online. /// TitleOnline, /// /// Has played this title and is offline. /// /// /// This filter option requires ::TitleHistoryLevel to be set in /// TitleOffline, /// /// Has played this title, is online but not currently playing this title. /// /// /// This filter option requires ::TitleHistoryLevel to be set in /// TitleOnlineOutsideTitle, /// /// Everyone currently online. /// AllOnline, /// /// Everyone currently offline. /// AllOffline, /// /// Everyone who has played or is playing the title. /// /// /// This filter option requires ::TitleHistoryLevel to be set in /// AllTitle, /// /// Everyone. /// All }; /// /// The types of possible social manager events. /// /// enum class XblSocialManagerEventType : uint32_t { /// /// Fired when one or more users are added to social graph. Users are added to the graph if they are tracked /// by a list group or if they are followed by the local user. /// UsersAddedToSocialGraph, /// /// Fired when one or more users are removed from social graph. User's will be removed from the social graph if /// they aren't followed by the local user, nor are they tracked by any list groups. /// UsersRemovedFromSocialGraph, /// /// Users presence record has changed. This event implies that the set of users tracked by filter groups /// may have changed (i.e. if the group was created with an XblPresenceFilter). /// PresenceChanged, /// /// Users profile information has changed. /// ProfilesChanged, /// /// Relationship to users has changed. This event implies that the set of users tracked by filter groups /// may have changed (i.e. if the group was created with an XblRelationshipFilter). /// SocialRelationshipsChanged, /// /// Fired when the initial social graph has been loaded for a local user. /// LocalUserAdded, /// /// Fired when all users tracked by a social group are in social graph. /// SocialUserGroupLoaded, /// /// After updating a list based user group with , this event /// is fired to indicate all of the new users are in the social graph. If they are not already part of the graph, /// the new users will be added. Note that this event doesn't apply for filter based user groups. /// SocialUserGroupUpdated, /// /// Unknown. /// UnknownEvent }; /// /// Possible relationship types to filter by. /// /// /// enum class XblRelationshipFilter : uint32_t { /// /// Unknown. /// Unknown, /// /// Friends of the user (user is following). /// Friends, /// /// Favorites of the user. /// Favorite }; /// /// Identifies type of social user group created. /// /// enum class XblSocialUserGroupType : uint32_t { /// /// Social user group based off of filters. /// FilterType, /// /// Social user group based off of list of users. /// UserListType }; /// /// Data about whether the user has played the title. /// /// typedef struct XblTitleHistory { /// /// Whether the user has played this title. /// bool hasUserPlayed; /// /// The last time the user had played. /// /// /// Do not use both this and lastTimeUserPlayedText. /// For playtime within the past 14 days, this will be accurate for the date and fuzzily accurate for the time. /// For playtime older than 14 days, this will only be accurate to the year and month, up to a year ago. The date will be returned as the 1st of the month, /// but the play time could have occurred anywhere within that month. /// time_t lastTimeUserPlayed; /// /// The last time the user had played in a standardized plaintext format (e.g. "a few minutes ago" or "x hours/days/months ago" or "this/last month"). /// /// /// Do not use both this and lastTimeUserPlayed. /// char lastTimeUserPlayedText[XBL_LAST_TIME_PLAYED_CHAR_SIZE]; } XblTitleHistory; /// /// Preferred color for the user. Set via the shell. /// /// typedef struct XblPreferredColor { /// /// UTF-8 encoded user's primary color. /// char primaryColor[XBL_COLOR_CHAR_SIZE]; /// /// UTF-8 encoded user's secondary color. /// char secondaryColor[XBL_COLOR_CHAR_SIZE]; /// /// UTF-8 encoded user's tertiary color. /// char tertiaryColor[XBL_COLOR_CHAR_SIZE]; } XblPreferredColor; /// /// Social manager version of the presence title record. /// Gives information about different titles presence information. /// /// typedef struct XblSocialManagerPresenceTitleRecord { /// /// The title ID. /// uint32_t titleId; /// /// The title name. /// char titleName[XBL_TITLE_NAME_CHAR_SIZE]; /// /// The active state for the title. /// bool isTitleActive; /// /// The UTF-8 encoded formatted and localized presence string. /// char presenceText[XBL_RICH_PRESENCE_CHAR_SIZE]; /// /// The active state for the title. /// bool isBroadcasting; /// /// Device type. /// XblPresenceDeviceType deviceType; /// /// Whether or not this is the primary presence record. /// bool isPrimary; } XblSocialManagerPresenceTitleRecord; #define XBL_NUM_PRESENCE_RECORDS 6 /// /// Social manager presence record. Shows information on users current presence status and stores title records. /// /// /// typedef struct XblSocialManagerPresenceRecord { /// /// The user's presence state. /// XblPresenceUserState userState; /// /// Collection of presence title record objects returned by a request. /// XblSocialManagerPresenceTitleRecord presenceTitleRecords[XBL_NUM_PRESENCE_RECORDS]; /// /// Number of valid presence records in presenceTitleRecords array. /// uint32_t presenceTitleRecordCount; } XblSocialManagerPresenceRecord; /// /// Xbox Social User that contains profile, presence, preferred color, and title history data. /// /// typedef struct XblSocialManagerUser { /// /// The xbox user id. /// uint64_t xboxUserId; /// /// Whether they are a favorite. /// bool isFavorite; /// /// Indicates whether there exists a mutual follower/following relation between a user and another user /// bool isFriend; /// /// No longer indicates whether a calling user is following a given user. Kept for backwards compatibility purposes. /// The value of this field is determined by the value of 'isFriend' /// bool isFollowingUser; /// /// No longer indicates whether a calling user is followed by given user. Kept for backwards compatibility purposes. /// The value of this field is determined by the value of 'isFriend' /// bool isFollowedByCaller; /// /// The UTF-8 encoded display name. /// Many batch operations performed by XblSocialManager will not return a displayName for players. Use gamerTag or modernGamerTag instead /// char displayName[XBL_DISPLAY_NAME_CHAR_SIZE]; /// /// The UTF-8 encoded real name. /// char realName[XBL_REAL_NAME_CHAR_SIZE]; /// /// The UTF-8 encoded display picture uri. /// char displayPicUrlRaw[XBL_DISPLAY_PIC_URL_RAW_CHAR_SIZE]; /// /// Whether to use the players avatar. /// bool useAvatar; /// /// UTF-8 encoded player's gamerscore. /// char gamerscore[XBL_GAMERSCORE_CHAR_SIZE]; /// /// UTF-8 encoded player's gamertag. /// char gamertag[XBL_GAMERTAG_CHAR_SIZE]; /// /// Modern gamertag for the player. Not guaranteed to be unique. /// char modernGamertag[XBL_MODERN_GAMERTAG_CHAR_SIZE]; /// /// Suffix appended to modern gamertag to ensure uniqueness. May be empty in some cases. /// char modernGamertagSuffix[XBL_MODERN_GAMERTAG_SUFFIX_CHAR_SIZE]; /// /// Combined modern gamertag and suffix. Format will be "MGT#suffix". /// Guaranteed to be no more than 16 rendered characters. /// char uniqueModernGamertag[XBL_UNIQUE_MODERN_GAMERTAG_CHAR_SIZE]; /// /// Users presence record. /// XblSocialManagerPresenceRecord presenceRecord; /// /// Title history for the user. /// XblTitleHistory titleHistory; /// /// Preferred color for the user. /// XblPreferredColor preferredColor; } XblSocialManagerUser; /// /// A handle to a social manager user group. /// typedef struct XblSocialManagerUserGroup* XblSocialManagerUserGroupHandle; #define XBL_SOCIAL_MANAGER_MAX_AFFECTED_USERS_PER_EVENT 10 /// /// An event that something in the social graph has changed. /// /// typedef struct XblSocialManagerEvent { /// /// The user whose graph got changed. /// XblUserHandle user; /// /// The type of event this is. /// XblSocialManagerEventType eventType; /// /// Error that occurred, or S_OK. /// HRESULT hr; /// /// The user group that was loaded for XblSocialManagerEventType::SocialUserGroupLoaded or updated. /// For XblSocialManagerEventType::SocialUserGroupUpdated. Will be null for other types of events. /// XblSocialManagerUserGroupHandle groupAffected; /// /// The users affected. Returned pointers valid until the next XblSocialManagerDoWork call. /// XblSocialManagerUser* usersAffected[XBL_SOCIAL_MANAGER_MAX_AFFECTED_USERS_PER_EVENT]; } XblSocialManagerEvent; /// /// Query whether the user associated with the provided presence record is playing a given title id. /// /// A presence record returned from another social manager API. /// Title ID to query about. /// True if the user is playing the title and false otherwise. STDAPI_(bool) XblSocialManagerPresenceRecordIsUserPlayingTitle( _In_ const XblSocialManagerPresenceRecord* presenceRecord, _In_ uint32_t titleId ) XBL_NOEXCEPT; /// /// Gets the type of a Social Manager user group. /// /// The group handle. /// Passes back the group type. /// HRESULT return code for this API operation. /// /// Call this API after calling either /// or to create an XblSocialManagerUserGroup. /// The XblSocialManagerUserGroupHandle is returned by the ::SocialUserGroupLoaded /// event in . /// STDAPI XblSocialManagerUserGroupGetType( _In_ XblSocialManagerUserGroupHandle group, _Out_ XblSocialUserGroupType* type ) XBL_NOEXCEPT; /// /// Gets local user the group is associated with. /// /// The group handle. /// Passes back the local user handle. /// This user handle does not need to be closed. /// It remains valid for as long as the user is added to the social manager. /// HRESULT return code for this API operation. /// /// Call this API after calling either /// or to create an XblSocialManagerUserGroup. /// The XblSocialManagerUserGroupHandle is returned by the ::SocialUserGroupLoaded event /// in . /// STDAPI XblSocialManagerUserGroupGetLocalUser( _In_ XblSocialManagerUserGroupHandle group, _Out_ XblUserHandle* localUser ) XBL_NOEXCEPT; /// /// Gets the filters for a filter group. /// /// The group handle. /// Passes back the presence filter. /// Passes back the relationship filter. /// HRESULT return code for this API operation. /// /// If the group is not a filter group, E_UNEXPECTED will be returned. /// Call this API after either /// or to create an XblSocialManagerUserGroup. /// The XblSocialManagerUserGroupHandle is returned by the ::SocialUserGroupLoaded /// event in .
///
STDAPI XblSocialManagerUserGroupGetFilters( _In_ XblSocialManagerUserGroupHandle group, _Out_opt_ XblPresenceFilter* presenceFilter, _Out_opt_ XblRelationshipFilter* relationshipFilter ) XBL_NOEXCEPT; /// /// An array of pointers to internally owned XblSocialManagerUsers. /// /// /// This will be returned from and the array may change /// with each call to . /// typedef const XblSocialManagerUser* const* XblSocialManagerUserPtrArray; /// /// Gets an XblSocialManagerUserPtrArray of the users tracked by the user group. /// /// The group handle from which to get users. /// Passes back a pointer to an array of XblSocialManagerUser objects. /// The memory for the returned pointer remains valid until the next time is called. /// Passes back the size of the users array. /// HRESULT return code for this API operation. /// /// The XblSocialManagerUserPtrArray is only guaranteed to be valid until the next call to . /// If the user objects are needed beyond the scope of the next call, /// they are statically sized and trivially copyable. /// Call this API after calling either /// or to create an XblSocialManagerUserGroup. /// Wait for ::SocialUserGroupLoaded event in . Prior to this the group will be empty. /// STDAPI XblSocialManagerUserGroupGetUsers( _In_ XblSocialManagerUserGroupHandle group, _Outptr_result_maybenull_ XblSocialManagerUserPtrArray* users, _Out_ size_t* usersCount ) XBL_NOEXCEPT; /// /// Gets a pointer to an XSAPI owned array of xuids tracked by the group. /// /// The group from which to get users. /// Passes back a pointer to tracked xuids array. /// The memory for the returned pointer remains valid until the next time is called. /// Passes back the size of the tracked users array. /// HRESULT return code for this API operation. /// /// For list-based groups, the set of tracked users is static (i.e. the list of xuids provided when the group is created), /// but for filter-based groups, the set tracked users changes as the local user's relationships change. /// Call this API after calling either /// or to create an XblSocialManagerUserGroup. /// Wait for ::SocialUserGroupLoaded event in . Prior to this the group will be empty. /// STDAPI XblSocialManagerUserGroupGetUsersTrackedByGroup( _In_ XblSocialManagerUserGroupHandle group, _Outptr_result_maybenull_ const uint64_t** trackedUsers, _Out_ size_t* trackedUsersCount ) XBL_NOEXCEPT; /// /// Create a social graph for the specified local user. /// /// Xbox Live User to create a graph for. /// The level of verbosity that should be in the service calls for this user. /// Queue to be used for background operation for this user (Optional). /// Note: Using XTaskQueueDispatchMode::Immediate for this queue may cause poor performance. /// HRESULT return code for this API operation. /// /// The result of a local user being added will be triggered through /// the ::LocalUserAdded event in . /// To remove the social graph for the specified local user, call . /// STDAPI XblSocialManagerAddLocalUser( _In_ XblUserHandle user, _In_ XblSocialManagerExtraDetailLevel extraLevelDetail, _In_opt_ XTaskQueueHandle queue ) XBL_NOEXCEPT; /// /// Immediately removes a social graph for the specified local user. /// /// Xbox Live User to remove. /// HRESULT return code for this API operation. /// /// When a user is removed, their social graph and user groups will be destroyed as well. /// There will be no future events associated with the user after they are removed. /// Call this API only if was used to create a social graph for a local user. /// STDAPI XblSocialManagerRemoveLocalUser( _In_ XblUserHandle user ) XBL_NOEXCEPT; /// /// Called whenever the title wants to update the social graph and get list of change events. /// /// Passes back a pointer to the array of social events that have occurred since the last call to XblSocialManagerDoWork. /// This array is only valid until the next call to XblSocialManagerDoWork. /// The internal array will automatically be cleaned up when XblCleanup is called. /// Passes back the number of events in the social events array. /// HRESULT return code for this API operation. /// /// Must be called every frame for data to be up to date. /// The array of social events that is sent back is only valid until the next call to . /// Make sure to check if there were social events sent back. /// If the social events array is null, no results. /// If the social events count is 0, no results. /// If there were social events sent back then handle each /// by their respective . /// STDAPI XblSocialManagerDoWork( _Outptr_result_maybenull_ const XblSocialManagerEvent** socialEvents, _Out_ size_t* socialEventsCount ) XBL_NOEXCEPT; /// /// Constructs a XblSocialManagerUserGroup, which is a collection of users with social information. /// /// Xbox Live User the group is associated with. /// The restriction of users based on their presence and title activity. /// The restriction of users based on their relationship to the calling user. /// Passes back a handle to the created group. /// This group can be later be cleaned up with XblSocialManagerDestroySocialUserGroup. The group will also be cleaned up /// (invalidating the returned handle) if the associated user is removed from Social Manager. /// HRESULT return code for this API operation. /// /// Wait for ::SocialUserGroupLoaded event in . Prior to this the group will be empty. /// STDAPI XblSocialManagerCreateSocialUserGroupFromFilters( _In_ XblUserHandle user, _In_ XblPresenceFilter presenceFilter, _In_ XblRelationshipFilter relationshipFilter, _Outptr_result_maybenull_ XblSocialManagerUserGroupHandle* group ) XBL_NOEXCEPT; /// /// Constructs a social XblSocialManagerUserGroup, which is a collection of users with social information. /// /// Xbox Live User. /// List of users to populate the Xbox Social User Group with. /// The list cannot exceed XBL_SOCIAL_MANAGER_MAX_USERS_FROM_LIST. /// The number of items in the xboxUserIdList. /// Passes back a handle to the created group. /// This group may later be cleaned up with XblSocialManagerDestroySocialUserGroup. The group will also be cleaned up /// (invalidating the returned handle) if the associated user is removed from Social Manager. /// HRESULT return code for this API operation. /// /// Wait for ::SocialUserGroupLoaded event in . Prior to this the group will be empty. /// STDAPI XblSocialManagerCreateSocialUserGroupFromList( _In_ XblUserHandle user, _In_ uint64_t* xboxUserIdList, _In_ size_t xboxUserIdListCount, _Outptr_result_maybenull_ XblSocialManagerUserGroupHandle* group ) XBL_NOEXCEPT; /// /// Destroys a created Social User Group. /// /// The Social User Group to destroy and stop tracking. /// HRESULT return code for this API operation. /// /// This will stop updaing the Xbox Social User Group and remove tracking for any users the XblSocialManagerUserGroup holds. /// Call this API after calling either /// or to create an XblSocialManagerUserGroup. /// The XblSocialManagerUserGroupHandle is returned by the ::SocialUserGroupLoaded event /// in . /// STDAPI XblSocialManagerDestroySocialUserGroup( _In_ XblSocialManagerUserGroupHandle group ) XBL_NOEXCEPT; /// /// Get the number of local users currently tracked by SocialManager. /// /// The number of local users currently tracked by SocialManager. /// /// To add local users to be tracked, call . /// To remove local users from being tracked, call . /// This function must be called before calling . /// STDAPI_(size_t) XblSocialManagerGetLocalUserCount() XBL_NOEXCEPT; /// /// Returns user handles for all users tracked by SocialManager. /// /// The size of the user handle array. /// Passes back a pointer to an array to populate with local users. /// This array is only valid until the next call to XblSocialManagerDoWork. /// The user handles returned are not duplicated copies so do not call close on them. /// HRESULT return code for this API operation. /// /// Duplicate handle is not called before returning the handles, so if the user /// is needed after removing it from SocialManager, duplicate handle must be called. /// Make sure to have created a social graph for at least one local user by calling . /// Also, call to get the number of local users currently tracked. /// STDAPI XblSocialManagerGetLocalUsers( _In_ size_t usersCount, _Out_writes_(usersCount) XblUserHandle* users ) XBL_NOEXCEPT; /// /// Updates specified social user group to new group of users. /// /// The xbox social user group to update. /// New list of users to track. Note that this replaces the existing list of tracked users. /// The list cannot exceed XBL_SOCIAL_MANAGER_MAX_USERS_FROM_LIST. /// Number of items in the users array. /// HRESULT return code for this API operation. /// /// Does a diff to see which users have been added or removed. /// The result of a user group being updated will be triggered through the /// ::SocialUserGroupUpdated event in . /// Call this API after calling either /// or to create an XblSocialManagerUserGroup. /// The XblSocialManagerUserGroupHandle is returned by the ::SocialUserGroupLoaded /// event in . /// STDAPI XblSocialManagerUpdateSocialUserGroup( _In_ XblSocialManagerUserGroupHandle group, _In_ uint64_t* users, _In_ size_t usersCount ) XBL_NOEXCEPT; /// /// Whether to enable social manager to poll every 30 seconds from the presence service. /// /// Xbox Live User to enable polling for. /// Whether or not polling should enabled. /// HRESULT return code for this API operation. STDAPI XblSocialManagerSetRichPresencePollingStatus( _In_ XblUserHandle user, _In_ bool shouldEnablePolling ) XBL_NOEXCEPT; } ================================================ FILE: Include/xsapi-c/string_verify_c.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #if !defined(__cplusplus) #error C++11 required #endif #pragma once extern "C" { /// /// Enumeration values that indicate the result code from string verification. /// /// /// These values are defined on the service side and should not be modified. /// /// enum class XblVerifyStringResultCode { /// /// No issues were found with the string. /// Success, /// /// The string contains offensive content. /// Offensive, /// /// The string is too long to verify. /// TooLong, /// /// An unknown error was encountered during string verification. /// UnknownError }; /// /// Contains information about the results of a string verification. /// /// /// typedef struct XblVerifyStringResult { /// /// The result code for the string verification. /// XblVerifyStringResultCode resultCode; /// /// Contains the first offending substring if the /// resultCode is XblVerifyStringResultCode::Offensive. /// char* firstOffendingSubstring; } XblVerifyStringResult; /// /// Verifies if a string contains acceptable text for use with Xbox Live. /// /// Xbox live context for the local user. /// The string to verify. /// The AsyncBlock for this operation. /// HRESULT return code for this API operation. /// /// Call to retrieve size of buffer needed for result. /// Call to retrieve the size of the results. /// STDAPI XblStringVerifyStringAsync( _In_ XblContextHandle xboxLiveContext, _In_ const char* stringToVerify, _In_ XAsyncBlock* asyncBlock ) XBL_NOEXCEPT; /// /// Get the result size for an XblStringVerifyStringAsync call. /// /// The AsyncBlock for this operation. /// Passes back the size in bytes required to store the XblVerifyStringResult result. /// HRESULT return code for this API operation. STDAPI XblStringVerifyStringResultSize( _In_ XAsyncBlock* asyncBlock, _Out_ size_t* resultSizeInBytes ) XBL_NOEXCEPT; /// /// Get the result for an XblStringVerifyStringAsync call. /// /// The AsyncBlock for this operation. /// The size of the provided buffer. /// Use to get the size required. /// A caller allocated byte buffer that passes back the verify string result. /// Passes back a strongly typed pointer that points into buffer. /// Do not free this as its lifecycle is tied to buffer. /// Passes back the number of bytes written to the buffer. /// HRESULT return code for this API operation. STDAPI XblStringVerifyStringResult( _In_ XAsyncBlock* asyncBlock, _In_ size_t bufferSize, _Out_writes_bytes_to_(bufferSize, *bufferUsed) void* buffer, _Outptr_ XblVerifyStringResult** ptrToBuffer, _Out_opt_ size_t* bufferUsed ) XBL_NOEXCEPT; /// /// Verifies a collection of strings to see if each string contains acceptable text for use with Xbox Live. /// /// Xbox live context for the local user. /// The string to verify. /// The number of strings being verified. /// The AsyncBlock for this operation. /// HRESULT return code for this API operation. /// /// Call to retrieve size of buffer needed for result. /// Call to retrieve the size of the results. /// STDAPI XblStringVerifyStringsAsync( _In_ XblContextHandle xboxLiveContext, _In_ const char** stringsToVerify, _In_ const uint64_t stringsCount, _In_ XAsyncBlock* asyncBlock ) XBL_NOEXCEPT; /// /// Get the result size for an XblStringVerifyStringsAsync call. /// /// The AsyncBlock for this operation. /// Passes back the size in bytes required to store the XblVerifyStringResult results. /// HRESULT return code for this API operation. STDAPI XblStringVerifyStringsResultSize( _In_ XAsyncBlock* asyncBlock, _Out_ size_t* resultSizeInBytes ) XBL_NOEXCEPT; /// /// Get the result for an XblStringVerifyStringsAsync call. /// /// The AsyncBlock for this operation. /// The size of the provided buffer. /// Call to get the size required. /// A caller allocated byte buffer that passes back the verify strings result. /// Passes back a strongly typed array of XblVerifyStringResult that points into buffer. /// Do not free this as its lifecycle is tied to buffer. /// Passes back the number of XblVerifyStringResult results. /// Passes back the number of bytes written to the buffer. /// HRESULT return code for this API operation. STDAPI XblStringVerifyStringsResult( _In_ XAsyncBlock* asyncBlock, _In_ size_t bufferSize, _Out_writes_bytes_to_(bufferSize, *bufferUsed) void* buffer, _Outptr_ XblVerifyStringResult** ptrToBufferStrings, _Out_ size_t* stringsCount, _Out_opt_ size_t* bufferUsed ) XBL_NOEXCEPT; } // end extern C ================================================ FILE: Include/xsapi-c/title_managed_statistics_c.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #if !defined(__cplusplus) #error C++11 required #endif #pragma once extern "C" { /// /// Enumeration values that indicate the Title Managed Stat type. /// /// enum class XblTitleManagedStatType : uint32_t { /// /// Sets the type of Title Managed Stat to a Number. /// Number, /// /// Sets the type of Title Managed Stat to a String. /// String }; /// /// Contains information about a Title Managed statistic. /// /// typedef struct XblTitleManagedStatistic { /// /// The name of the statistic (case insensitive). /// _Field_z_ const char* statisticName; /// /// The type of the statistic. /// XblTitleManagedStatType statisticType; /// /// The value of the double statistic. Backed by a JSON number value, which can /// lead to precision issues when storing 64-bit fixed point values. /// double numberValue; /// /// The value of the string statistic. /// _Field_z_ const char* stringValue; } XblTitleManagedStatistic; /// /// Completely update the calling user's stats. /// This call wipes out all existing stats (any stats not referenced in the provided array will be removed). /// /// Xbox live context for the local user. /// User whose stats are being updated (can only be the local user). /// A list of XblTitleManagedStatistic to submit. /// Number of items in the statistics array. /// Used for async call. /// HRESULT return code for this API operation. /// /// To get the result, call inside /// the AsyncBlock callback or after the AsyncBlock is complete. /// If the call fails for any reason, it is the responsibility of the game to re-submit the request. /// STDAPI XblTitleManagedStatsWriteAsync( _In_ XblContextHandle xblContextHandle, _In_ uint64_t xboxUserId, _In_ const XblTitleManagedStatistic* statistics, _In_ size_t statisticsCount, _In_ XAsyncBlock* async ) XBL_NOEXCEPT; /// /// Partially update the calling user's stats. /// Stats will only be overwritten if they already exist (any stats not referenced will remain unchanged). /// /// Xbox live context for the local user. /// A list of XblTitleManagedStatistic to submit. /// Number of items in the statistics array. /// Used for async call. /// HRESULT return code for this API operation. /// /// Note that stat names are case insensitive. /// To get the result, call inside /// the AsyncBlock callback or after the AsyncBlock is complete. /// If the call fails for any reason, it is the responsibility of the game to re-submit the request. /// STDAPI XblTitleManagedStatsUpdateStatsAsync( _In_ XblContextHandle xblContextHandle, _In_ const XblTitleManagedStatistic* statistics, _In_ size_t statisticsCount, _In_ XAsyncBlock* async ) XBL_NOEXCEPT; /// /// Delete stats for the calling user (any stats not referenced will remain unchanged). /// /// Xbox live context for the local user. /// A list of names of statistics to delete. /// If there isn't an existing statistic matching a provided name, no changes will be made for that statistic. /// Number of items in the statisticNames array. /// Used for async call. /// HRESULT return code for this API operation. /// /// Note that stat names are case insensitive. /// To get the result, call inside /// the AsyncBlock callback or after the AsyncBlock is complete. /// If the call fails for any reason, it is the responsibility of the game to re-submit the request. /// STDAPI XblTitleManagedStatsDeleteStatsAsync( _In_ XblContextHandle xblContextHandle, _In_ const char** statisticNames, _In_ size_t statisticNamesCount, _In_ XAsyncBlock* async ) XBL_NOEXCEPT; } ================================================ FILE: Include/xsapi-c/title_storage_c.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #if !defined(__cplusplus) #error C++11 required #endif #pragma once extern "C" { #define XBL_TITLE_STORAGE_MIN_UPLOAD_BLOCK_SIZE 1024 #define XBL_TITLE_STORAGE_MAX_UPLOAD_BLOCK_SIZE (4 * 1024 * 1024) #define XBL_TITLE_STORAGE_DEFAULT_UPLOAD_BLOCK_SIZE (256 * 1024) #define XBL_TITLE_STORAGE_MIN_DOWNLOAD_BLOCK_SIZE 1024 #define XBL_TITLE_STORAGE_DEFAULT_DOWNLOAD_BLOCK_SIZE (1024 * 1024) #define XBL_TITLE_STORAGE_BLOB_PATH_MAX_LENGTH (257 * 3) #define XBL_TITLE_STORAGE_BLOB_DISPLAY_NAME_MAX_LENGTH (129 * 3) #define XBL_TITLE_STORAGE_BLOB_ETAG_MAX_LENGTH (18 * 3) /// /// Defines values used to indicate title storage type. /// enum class XblTitleStorageType : uint32_t { /// /// Per-user data storage such as game state or game settings that can be only be accessed by Xbox consoles. /// User restrictions can be configured to public or owner only in the service configuration. /// TrustedPlatformStorage, /// /// Global data storage. This storage type is only writable via title configuration sites or Xbox Live developer tools. /// Any platform may read from this storage type. Data could be rosters, maps, challenges, art resources, etc. /// GlobalStorage, /// /// Per-user data storage such as game state or game settings that can be accessed by Xbox consoles, Windows 10, and mobile devices. /// User restrictions can be configured to public or owner only in the service configuration. /// Universal }; /// /// Defines values used to indicate title storage blob type. /// enum class XblTitleStorageBlobType : uint32_t { /// /// Unknown blob type. /// Unknown, /// /// Binary blob type. /// Binary, /// /// JSON blob type. /// Json, /// /// Config blob type. /// Config }; /// /// Defines values used to indicate the ETag match condition used when downloading, uploading or deleting title storage data. /// enum class XblTitleStorageETagMatchCondition : uint32_t { /// /// There is no match condition. /// NotUsed, /// /// Perform the request if the Etag value specified matches the service value. /// IfMatch, /// /// Perform the request if the Etag value specified does not match the service value. /// IfNotMatch }; /// /// Metadata about a blob. /// typedef struct XblTitleStorageBlobMetadata { /// /// Blob path is a unique string that conforms to a SubPath\file format (example: "foo\bar\blob.txt"). /// _Null_terminated_ char blobPath[XBL_TITLE_STORAGE_BLOB_PATH_MAX_LENGTH]; /// /// Type of blob data. Possible values are: Binary, Json, and Config. /// XblTitleStorageBlobType blobType; /// /// Type of storage. /// XblTitleStorageType storageType; /// /// [optional] Friendly display name to show in app UI. /// _Null_terminated_ char displayName[XBL_TITLE_STORAGE_BLOB_DISPLAY_NAME_MAX_LENGTH]; /// /// ETag for the file used in read and write requests. /// _Null_terminated_ char eTag[XBL_TITLE_STORAGE_BLOB_ETAG_MAX_LENGTH]; /// /// [optional] Timestamp assigned by the client. /// time_t clientTimestamp; /// /// Gets the number of bytes of the blob data. /// size_t length; /// /// The service configuration ID of the title /// _Null_terminated_ char serviceConfigurationId[XBL_SCID_LENGTH]; /// /// The Xbox User ID of the player this file belongs to. /// This value will be null for Global and Session files. /// uint64_t xboxUserId; } XblTitleStorageBlobMetadata; /// /// A handle that represents metadata about blob data returned from the cloud. /// typedef struct XblTitleStorageBlobMetadataResult* XblTitleStorageBlobMetadataResultHandle; /// /// Get a list of XblTitleStorageBlobMetadata objects. /// /// Title storage blob metadata result handle. /// Passes back a pointer to an array of XblTitleStorageBlobMetadata objects. /// The memory for the returned pointer remains valid for the life of /// the XblTitleStorageBlobMetadataResultHandle object until it is closed. /// Passes back the number of objects in the items array. /// HRESULT return code for this API operation. /// /// This memory of the list is freed when the XblTitleStorageBlobMetadataResultHandle is closed /// with . /// STDAPI XblTitleStorageBlobMetadataResultGetItems( _In_ XblTitleStorageBlobMetadataResultHandle resultHandle, _Out_ const XblTitleStorageBlobMetadata** items, _Out_ size_t* itemsCount ) XBL_NOEXCEPT; /// /// Checks if there are more pages of XblTitleStorageBlobMetadata to retrieve from the service. /// /// Title storage blob metadata result handle. /// Passes back true if there are more results to retrieve, false otherwise. /// HRESULT return code for this API operation. /// /// To retrieve the next page of items, call . /// STDAPI XblTitleStorageBlobMetadataResultHasNext( _In_ XblTitleStorageBlobMetadataResultHandle resultHandle, _Out_ bool* hasNext ) XBL_NOEXCEPT; /// /// Retrieves the next page of XblTitleStorageBlobMetadata objects. /// /// Title storage blob metadata result handle. /// The maximum number of items the result can contain. Pass 0 to attempt to retrieve all items. /// Caller allocated AsyncBlock. /// HRESULT return code for this API operation. /// /// To get the result, call inside /// the AsyncBlock callback or after the AsyncBlock is complete. /// STDAPI XblTitleStorageBlobMetadataResultGetNextAsync( _In_ XblTitleStorageBlobMetadataResultHandle resultHandle, _In_ uint32_t maxItems, _In_ XAsyncBlock* async ) XBL_NOEXCEPT; /// /// Get the result for a completed XblTitleStorageBlobMetadataResultGetNextAsync operation. /// /// The same AsyncBlock that passed to XblTitleStorageBlobMetadataResultGetNextAsync. /// Passes back the next XblTitleStorageBlobMetadataResultHandle. /// Note that this is a separate handle than the one passed to the XblTitleStorageBlobMetadataResultGetNextAsync API. /// Each result handle must be closed separately. /// HRESULT return code for this API operation. STDAPI XblTitleStorageBlobMetadataResultGetNextResult( _In_ XAsyncBlock* async, _Out_ XblTitleStorageBlobMetadataResultHandle* result ) XBL_NOEXCEPT; /// /// Duplicates a XblTitleStorageBlobMetadataResultHandle. /// /// The XblTitleStorageBlobMetadataResultHandle to duplicate. /// Passes back the duplicated handle. /// HRESULT return code for this API operation. STDAPI XblTitleStorageBlobMetadataResultDuplicateHandle( _In_ XblTitleStorageBlobMetadataResultHandle handle, _Out_ XblTitleStorageBlobMetadataResultHandle* duplicatedHandle ) XBL_NOEXCEPT; /// /// Closes the XblTitleStorageBlobMetadataResultHandle. /// /// The XblTitleStorageBlobMetadataResultHandle to close. /// /// /// When all outstanding handles have been closed, the memory associated with the title storage blob metadata result will be freed. /// STDAPI_(void) XblTitleStorageBlobMetadataResultCloseHandle( _In_ XblTitleStorageBlobMetadataResultHandle handle ) XBL_NOEXCEPT; /// /// Gets title storage quota information for the specified service configuration and storage type. /// /// An xbox live context handle created with XblContextCreateHandle. /// The Service Configuration ID (SCID) for the title. The SCID is considered case sensitive so paste it directly from the Partner Center. /// The storage type to get quota information for. /// Caller allocated AsyncBlock. /// HRESULT return code for this API operation. /// /// For TrustedPlatform storage types, the request will be made for the calling user's Xbox user Id. /// To get the result, call XblTitleStorageGetQuotaResult inside the AsyncBlock callback /// or after the AsyncBlock is complete. /// /// V1 GET trustedplatform/users/xuid({xuid})/scids/{scid} /// V1 GET trustedplatform/users/xuid({xuid})/scids/{scid} /// V1 GET json/users/xuid({xuid})/scids/{scid} /// V1 GET global/scids/{scid} STDAPI XblTitleStorageGetQuotaAsync( _In_ XblContextHandle xboxLiveContext, _In_z_ const char* serviceConfigurationId, _In_ XblTitleStorageType storageType, _In_ XAsyncBlock* async ) XBL_NOEXCEPT; /// /// Get the result for a completed XblTitleStorageGetQuotaAsync operation. /// /// The same AsyncBlock that passed to XblTitleStorageGetQuotaAsync. /// Passes back the number of bytes used in title storage of type StorageType. /// Passes back the maximum number of bytes that can be used in title storage of type StorageType. /// Note that this is a soft limit and the used bytes may actually exceed this value. /// HRESULT return code for this API operation. STDAPI XblTitleStorageGetQuotaResult( _In_ XAsyncBlock* async, _Out_ size_t* usedBytes, _Out_ size_t* quotaBytes ) XBL_NOEXCEPT; /// /// Gets a list of blob metadata objects under a given path for the specified service configuration, storage type and storage ID. /// /// An xbox live context handle created with XblContextCreateHandle. /// The Service Configuration ID (SCID) for the title. The SCID is considered case sensitive so paste it directly from the Partner Center. /// The storage type to get blob metadata objects for. /// The root path to enumerate. Results will be for blobs contained in this path and all subpaths. (Optional) /// The Xbox User ID of the title storage to enumerate. Ignored when enumerating GlobalStorage, so passing 0 is acceptable. (Optional) /// The number of items to skip before returning results. (Optional) /// The maximum number of items to return. Pass 0 to attempt to retrieve all items. (Optional) /// Caller allocated AsyncBlock. /// HRESULT return code for this API operation. /// /// To get the result, call XblTitleStorageGetBlobMetadataResult inside the AsyncBlock callback /// or after the AsyncBlock is complete. /// /// V1 GET trustedplatform/users/xuid({xuid})/scids/{scid}/data/{path}?maxItems={maxItems}[skipItems={skipItems}] /// V1 GET json/users/xuid({xuid})/scids/{scid}/data/{path}?maxItems={maxItems}[skipItems={skipItems}] /// V1 GET global/scids/{scid}/data/{path}?maxItems={maxItems}[skipItems={skipItems}] STDAPI XblTitleStorageGetBlobMetadataAsync( _In_ XblContextHandle xboxLiveContext, _In_z_ const char* serviceConfigurationId, _In_ XblTitleStorageType storageType, _In_z_ const char* blobPath, _In_ uint64_t xboxUserId, _In_ uint32_t skipItems, _In_ uint32_t maxItems, _In_ XAsyncBlock* async ) XBL_NOEXCEPT; /// /// Get the result for a completed XblTitleStorageGetBlobMetadataAsync operation. /// /// The same AsyncBlock that passed to XblTitleStorageGetBlobMetadataAsync. /// Passes back the title storage blob metadata result handle. /// HRESULT return code for this API operation. STDAPI XblTitleStorageGetBlobMetadataResult( _In_ XAsyncBlock* async, _Out_ XblTitleStorageBlobMetadataResultHandle* result ) XBL_NOEXCEPT; /// /// Deletes a blob from title storage. /// /// An xbox live context handle created with XblContextCreateHandle. /// The blob metadata for the title storage blob to delete. /// Specifies whether or not to have the delete operation check that the ETag matches before deleting the blob. /// Caller allocated AsyncBlock. /// HRESULT return code for this API operation. /// /// The result of the asynchronous operation can be obtained by calling /// inside the AsyncBlock callback or after the AsyncBlock is complete. /// /// V1 DELETE trustedplatform/users/xuid({xuid})/scids/{scid}/data/{path},{type} /// V1 DELETE json/users/xuid({xuid})/scids/{scid}/data/{path},{type} /// V1 DELETE sessions/{sessionId}/scids/{scid}/data/{path},{type} STDAPI XblTitleStorageDeleteBlobAsync( _In_ XblContextHandle xboxLiveContext, _In_ XblTitleStorageBlobMetadata blobMetadata, _In_ bool deleteOnlyIfEtagMatches, _In_ XAsyncBlock* async ) XBL_NOEXCEPT; /// /// Downloads blob data from title storage. /// /// An xbox live context handle created with XblContextCreateHandle. /// The blob metadata for the title storage blob to download. /// A caller allocated buffer that passes back the downloaded blob data. /// This buffer needs to be large enough to store the blob being downloaded. /// If necessary, the length required for the buffer can be retrieved by getting the blob metadata. /// The length of blobBuffer. /// The ETag match condition used to determine if the blob should be downloaded. /// ConfigStorage filter string or JSONStorage json property name string to filter. (Optional) /// The preferred download block size in bytes for binary blobs. Pass 0 to use the default size. (Optional) /// Caller allocated AsyncBlock. /// HRESULT return code for this API operation. /// /// To get the result, call inside the AsyncBlock callback /// or after the AsyncBlock is complete. /// This method will return E_NOT_SUFFICIENT_BUFFER (0x8007007A) if the blobBuffer doesn't have enough capacity to hold the blob data. /// /// V1 GET trustedplatform/users/xuid({xuid})/scids/{scid}/data/{path},{type} /// V1 GET json/users/xuid({xuid})/scids/{scid}/data/{path},{type} /// V1 GET global/scids/{scid}/data/{path},{type} /// V1 GET sessions/{sessionId}/scids/{scid}/data/{path},{type} STDAPI XblTitleStorageDownloadBlobAsync( _In_ XblContextHandle xboxLiveContext, _In_ XblTitleStorageBlobMetadata blobMetadata, _Out_writes_(blobBufferCount) uint8_t* blobBuffer, _In_ size_t blobBufferCount, _In_ XblTitleStorageETagMatchCondition etagMatchCondition, _In_opt_z_ const char* selectQuery, _In_ size_t preferredDownloadBlockSize, _In_ XAsyncBlock* async ) XBL_NOEXCEPT; /// /// Get the result for a completed XblTitleStorageDownloadBlobAsync operation. /// /// The same AsyncBlock that passed to XblTitleStorageDownloadBlobAsync. /// A caller allocated XblTitleStorageBlobMetadata that passes back the downloaded blob metadata. /// HRESULT return code for this API operation. STDAPI XblTitleStorageDownloadBlobResult( _In_ XAsyncBlock* async, _Out_ XblTitleStorageBlobMetadata* blobMetadata ) XBL_NOEXCEPT; /// /// Uploads blob data to title storage. /// /// An xbox live context handle created with XblContextCreateHandle. /// Contains properties required to upload the buffer to title storage. /// Uploads require a service configuration Id, blob path, blob type and storage type at a minimum. /// The buffer containing the blob data to upload. /// This buffer must be available for the duration of the async operation. /// Clients should not modify the buffer while an upload is in progress. /// The length of blobBuffer. /// The ETag match condition used to determine if the blob data should be uploaded. /// The preferred upload block size in bytes for binary blobs. /// Binary blobs will be uploaded in multiple chunks of this size if they exceed it. /// Larger sizes are preferred by the service. /// If timeouts occur, the app should retry with a smaller size. /// Block size must be within the 1K to 4MB range. /// This method will use a default size if this parameter is not within the acceptable range. /// The current minimum size is 1024 bytes, maximum size is 4194304 bytes and the default size is 262144 bytes. (Optional) /// Caller allocated AsyncBlock. /// HRESULT return code for this API operation. /// V1 PUT json/users/xuid({xuid})/scids/{scid}/data/{path},{type} /// V1 PUT global/scids/{scid}/data/{path},{type} /// V1 PUT sessions/{sessionId}/scids/{scid}/data/{path},{type} STDAPI XblTitleStorageUploadBlobAsync( _In_ XblContextHandle xboxLiveContext, _In_ XblTitleStorageBlobMetadata blobMetadata, _In_ const uint8_t* blobBuffer, _In_ size_t blobBufferCount, _In_ XblTitleStorageETagMatchCondition etagMatchCondition, _In_ size_t preferredUploadBlockSize, _In_ XAsyncBlock* async ) XBL_NOEXCEPT; /// /// Get the result for a completed XblTitleStorageUploadBlobAsync operation. /// /// The same AsyncBlock that passed to XblTitleStorageUploadBlobAsync. /// A caller allocated XblTitleStorageBlobMetadata that passes back the uploaded blob metadata. /// HRESULT return code for this API operation. STDAPI XblTitleStorageUploadBlobResult( _In_ XAsyncBlock* async, _Out_ XblTitleStorageBlobMetadata* blobMetadata ) XBL_NOEXCEPT; } ================================================ FILE: Include/xsapi-c/types_c.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #if !defined(__cplusplus) #error C++11 required #endif #include #include "pal.h" #include "Xal/xal_types.h" extern "C" { // Xbox live error codes. // FACILITY_XBOX + 0x5200 + offset #define E_XBL_RUNTIME_ERROR _HRESULT_TYPEDEF_(0x89235200) #define E_XBL_RTA_GENERIC_ERROR _HRESULT_TYPEDEF_(0x89235201) #define E_XBL_RTA_SUBSCRIPTION_LIMIT_REACHED _HRESULT_TYPEDEF_(0x89235202) #define E_XBL_RTA_ACCESS_DENIED _HRESULT_TYPEDEF_(0x89235203) #define E_XBL_AUTH_UNKNOWN_ERROR _HRESULT_TYPEDEF_(0x89235204) #define E_XBL_AUTH_RUNTIME_ERROR _HRESULT_TYPEDEF_(0x89235205) #define E_XBL_AUTH_NO_TOKEN _HRESULT_TYPEDEF_(0x89235206) #define E_XBL_ALREADY_INITIALIZED _HRESULT_TYPEDEF_(0x89235207) #define E_XBL_NOT_INITIALIZED _HRESULT_TYPEDEF_(0x89235208) #define E_XBL_RTA_NOT_ACTIVATED _HRESULT_TYPEDEF_(0x89235209) // Xbox live size constants #define XBL_COLOR_CHAR_SIZE (7 * 3) #define XBL_DISPLAY_NAME_CHAR_SIZE (30 * 3) #define XBL_DISPLAY_PIC_URL_RAW_CHAR_SIZE (225 * 3) #define XBL_GAMERSCORE_CHAR_SIZE (16 * 3) #define XBL_GAMERTAG_CHAR_SIZE (16 * 3) #define XBL_MODERN_GAMERTAG_CHAR_SIZE (((12 + 12) * 4) + 1) // 12 characters + 12 diacritic, 4 bytes each, plus 1 byte null terminator #define XBL_MODERN_GAMERTAG_SUFFIX_CHAR_SIZE (14 + 1) // 14 alphanumeric characters + null terminator #define XBL_UNIQUE_MODERN_GAMERTAG_CHAR_SIZE (XBL_MODERN_GAMERTAG_CHAR_SIZE + 1 + 3 ) // modern gamertag + '#' + max suffix size for cases when MGT display length is 12. Null terminator already accoutned for in MGT #define XBL_REAL_NAME_CHAR_SIZE (255 * 3) #define XBL_RICH_PRESENCE_CHAR_SIZE (100 * 3) #define XBL_TITLE_NAME_CHAR_SIZE (100 * 3) #define XBL_XBOX_USER_ID_CHAR_SIZE (21 * 3) #define XBL_LAST_TIME_PLAYED_CHAR_SIZE (25 * 3) #define XBL_GUID_LENGTH 40 #define XBL_SCID_LENGTH XBL_GUID_LENGTH /// /// Handle to the underlying user used to create an Xbox Live context. /// #if HC_PLATFORM == HC_PLATFORM_GDK typedef XUserHandle XblUserHandle; #else typedef XalUserHandle XblUserHandle; #endif /// /// A context token returned when registering a callback/handler to identify the registration. /// This context token is later used to unregister the callback/handler. /// A value of 0 indicates and invalid handler token. /// typedef int32_t XblFunctionContext; /// /// Handle to an Xbox live context. /// Needed to interact with Xbox live services. /// typedef struct XblContext* XblContextHandle; /// /// Generic Guid struct. /// Used by various Xbox live services. /// typedef struct XblGuid { /// /// The stored Guid. /// _Null_terminated_ char value[XBL_GUID_LENGTH]; } XblGuid; } ================================================ FILE: Include/xsapi-c/user_statistics_c.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #if !defined(__cplusplus) #error C++11 required #endif #include "real_time_activity_c.h" #pragma once extern "C" { /// /// Contains information about a user statistic. /// /// /// typedef struct XblStatistic { /// /// The name of the statistic. /// _Field_z_ const char* statisticName; /// /// The type of the statistic. /// _Field_z_ const char* statisticType; /// /// The value of the statistic. /// _Field_z_ const char* value; } XblStatistic; /// /// Contains statistical information from a service configuration. /// /// typedef struct XblServiceConfigurationStatistic { /// /// The service configuration ID (SCID) associated with the leaderboard. /// _Null_terminated_ char serviceConfigurationId[XBL_SCID_LENGTH]; /// /// A collection of statistics used in leaderboards. /// XblStatistic* statistics; /// /// The size of **statistics**. /// uint32_t statisticsCount; } XblServiceConfigurationStatistic; /// /// Represents the results of a user statistic query. /// /// /// /// /// typedef struct XblUserStatisticsResult { /// /// The Xbox User ID for the user in a statistic. /// uint64_t xboxUserId; /// /// A collection of statistics from a service configuration. /// XblServiceConfigurationStatistic* serviceConfigStatistics; /// /// The size of **serviceConfigStatistics**. /// uint32_t serviceConfigStatisticsCount; } XblUserStatisticsResult; /// /// Represents the results of a user statistic query. /// /// typedef struct XblRequestedStatistics { /// /// The service configuration ID in use. /// _Null_terminated_ char serviceConfigurationId[XBL_SCID_LENGTH]; /// /// A collection of statistics. /// _Field_z_ const char** statistics; /// /// The size of **statistics**. /// uint32_t statisticsCount; } XblRequestedStatistics; /// /// Contains information about a change to a subscribed statistic. /// /// typedef struct XblStatisticChangeEventArgs { /// /// The Xbox user ID used to create the subscription. /// uint64_t xboxUserId; /// /// The service configuration ID used to create the subscription. /// _Null_terminated_ char serviceConfigurationId[XBL_SCID_LENGTH]; /// /// The statistic with an updated value. /// XblStatistic latestStatistic; } XblStatisticChangeEventArgs; /// /// Get a specified statistic for a specified user. /// /// Xbox live context for the local user. /// The Xbox User ID of the player to get statistics for. /// The Service Configuration ID (SCID) for the title. The SCID is considered case sensitive so paste it directly from the Partner Center. /// The name of the statistic to return. /// The AsyncBlock for this operation. /// HRESULT return code for this API operation. /// /// If multiple statistics are required for a single user, use this batch API instead: /// If statistics are needed for multiple users, use this batch API: /// Call and /// upon completion to get the result. /// /// V1 GET /users/xuid({xuid})/scids/{scid}/stats/{statname1} STDAPI XblUserStatisticsGetSingleUserStatisticAsync( _In_ XblContextHandle xblContextHandle, _In_ uint64_t xboxUserId, _In_z_ const char* serviceConfigurationId, _In_z_ const char* statisticName, _In_ XAsyncBlock* async ) XBL_NOEXCEPT; /// /// Get the result size for an XblUserStatisticsGetSingleUserStatisticsAsync call. /// /// The AsyncBlock for this operation. /// Passes bakc the size in bytes required to store the user statistics result. /// HRESULT return code for this API operation. STDAPI XblUserStatisticsGetSingleUserStatisticResultSize( _In_ XAsyncBlock* async, _Out_ size_t* resultSizeInBytes ) XBL_NOEXCEPT; /// /// Get the result for an XblUserStatisticsGetSingleUserStatisticAsync call. /// /// The AsyncBlock for this operation. /// The size of the provided buffer. /// Use to get the size required. /// A caller allocated byte buffer that passes back the single user statistic. /// Passes back a strongly typed pointer that points into buffer. /// Do not free this as its lifecycle is tied to buffer. /// Passes back the number of bytes written to the buffer. /// HRESULT return code for this API operation. STDAPI XblUserStatisticsGetSingleUserStatisticResult( _In_ XAsyncBlock* async, _In_ size_t bufferSize, _Out_writes_bytes_to_(bufferSize, *bufferUsed) void* buffer, _Outptr_ XblUserStatisticsResult** ptrToBuffer, _Out_opt_ size_t* bufferUsed ) XBL_NOEXCEPT; /// /// Get specified statistics for a single user. /// /// Xbox live context for the local user. /// The Xbox User ID of the player to get statistics for. /// The Service Configuration ID (SCID) for the title. The SCID is considered case sensitive so paste it directly from the Partner Center. /// A collection of statistic names to lookup. /// The number of statistic names. /// The AsyncBlock for this operation. /// HRESULT return code for this API operation. /// /// Call and /// upon completion to get the result. Only statistics with values are returned. /// For example, if you ask for 3 statistic names and only 2 have values, /// only 2 statistics are returned by the service. /// /// V1 GET /users/xuid({xuid})/scids/{scid}/stats/{statname1},...,{statnameN} STDAPI XblUserStatisticsGetSingleUserStatisticsAsync( _In_ XblContextHandle xblContextHandle, _In_ uint64_t xboxUserId, _In_z_ const char* serviceConfigurationId, _In_ const char** statisticNames, _In_ size_t statisticNamesCount, _In_ XAsyncBlock* async ) XBL_NOEXCEPT; /// /// Get the result size for an XblUserStatisticsGetSingleUserStatisticsAsync call. /// /// The AsyncBlock for this operation. /// Passes back the size in bytes required to store the user statistics result. /// HRESULT return code for this API operation. STDAPI XblUserStatisticsGetSingleUserStatisticsResultSize( _In_ XAsyncBlock* async, _Out_ size_t* resultSizeInBytes ) XBL_NOEXCEPT; /// /// Get the result for an XblUserStatisticsGetSingleUserStatisticsAsync call. /// /// The AsyncBlock for this operation. /// The size of the provided buffer. /// Use to get the size required. /// A caller allocated byte buffer that passes back the single user statistics. /// Passes back a strongly typed pointer that points into buffer. /// Do not free this as its lifecycle is tied to buffer. /// Passes back the number of bytes written to the buffer. /// HRESULT return code for this API operation. STDAPI XblUserStatisticsGetSingleUserStatisticsResult( _In_ XAsyncBlock* async, _In_ size_t bufferSize, _Out_writes_bytes_to_(bufferSize, *bufferUsed) void* buffer, _Outptr_ XblUserStatisticsResult** ptrToBuffer, _Out_opt_ size_t* bufferUsed ) XBL_NOEXCEPT; /// /// Get statistics for multiple users. /// /// Xbox live context for the local user. /// A list of the user Xbox user IDs to get stats for. /// The number of Xbox user IDs. /// The Service Configuration ID (SCID) for the title. The SCID is considered case sensitive so paste it directly from the Partner Center. /// A collection of statistic names to lookup. /// The number of statistic names. /// The AsyncBlock for this operation. /// HRESULT return code for this API operation. /// /// Call and /// upon completion to get the result. Only statistics with values are returned. /// For example, if you ask for 3 statistic names and only 2 have values, /// only 2 statistics are returned by the service. /// /// V1 POST /batch STDAPI XblUserStatisticsGetMultipleUserStatisticsAsync( _In_ XblContextHandle xblContextHandle, _In_ uint64_t* xboxUserIds, _In_ size_t xboxUserIdsCount, _In_z_ const char* serviceConfigurationId, _In_ const char** statisticNames, _In_ size_t statisticNamesCount, _In_ XAsyncBlock* async ) XBL_NOEXCEPT; /// /// Get the result size for an XblUserStatisticsGetMultipleUserStatisticsAsync call. /// /// The AsyncBlock for this operation. /// Passes back the size in bytes required to store the user statistics result. /// HRESULT return code for this API operation. /// STDAPI XblUserStatisticsGetMultipleUserStatisticsResultSize( _In_ XAsyncBlock* async, _Out_ size_t* resultSizeInBytes ) XBL_NOEXCEPT; /// /// Get the result for an XblUserStatisticsGetMultipleUserStatisticsAsync call. /// /// The AsyncBlock for this operation. /// The size of the provided buffer. /// Use to get the size required. /// A caller allocated byte buffer that passes back the multiple user statistics. /// Passes back a strongly typed pointer that points into buffer. /// Do not free this as its lifecycle is tied to buffer. /// Passes back the number of user statistics results. /// Passes back the number of bytes written to the buffer. /// HRESULT return code for this API operation. /// STDAPI XblUserStatisticsGetMultipleUserStatisticsResult( _In_ XAsyncBlock* async, _In_ size_t bufferSize, _Out_writes_bytes_to_(bufferSize, *bufferUsed) void* buffer, _Outptr_ XblUserStatisticsResult** ptrToBuffer, _Out_ size_t* resultsCount, _Out_opt_ size_t* bufferUsed ) XBL_NOEXCEPT; /// /// Get statistics for users across different Service configurations. /// /// Xbox live context for the local user. /// A list of the user Xbox user ID to get stats for. /// A list of the user Xbox user ID to get stats for. /// A list of the service config IDs and its associated array of statistics. /// The number of the service config IDs and its associated array of statistics. /// The AsyncBlock for this operation. /// HRESULT return code for this API operation. /// /// Call /// and /// upon completion to get the result. Only statistics with values are returned. /// For example, if you ask for 3 statistic names and only 2 have values, /// only 2 statistics are returned by the service. /// /// V1 POST /batch STDAPI XblUserStatisticsGetMultipleUserStatisticsForMultipleServiceConfigurationsAsync( _In_ XblContextHandle xblContextHandle, _In_ uint64_t* xboxUserIds, _In_ uint32_t xboxUserIdsCount, _In_ const XblRequestedStatistics* requestedServiceConfigurationStatisticsCollection, _In_ uint32_t requestedServiceConfigurationStatisticsCollectionCount, _In_ XAsyncBlock* async ) XBL_NOEXCEPT; /// /// Get the result size for an XblUserStatisticsGetMultipleUserStatisticsAsync call. /// /// The AsyncBlock for this operation. /// Passes back the size in bytes required to store the user statistics result. /// HRESULT return code for this API operation. STDAPI XblUserStatisticsGetMultipleUserStatisticsForMultipleServiceConfigurationsResultSize( _In_ XAsyncBlock* async, _Out_ size_t* resultSizeInBytes ) XBL_NOEXCEPT; /// /// Get the result for an XblUserStatisticsGetMultipleUserStatisticsAsync call. /// /// The AsyncBlock for this operation. /// The size of the provided buffer. /// Use to get the size required. /// A caller allocated byte buffer that passes back the multiple user statistics. /// Passes back a strongly typed array of XblUserStatisticsResult that points into buffer. /// Do not free this as its lifecycle is tied to buffer. /// Passes back the number of results. /// Passes back the number of bytes written to the buffer. /// HRESULT return code for this API operation. STDAPI XblUserStatisticsGetMultipleUserStatisticsForMultipleServiceConfigurationsResult( _In_ XAsyncBlock* async, _In_ size_t bufferSize, _Out_writes_bytes_to_(bufferSize, *bufferUsed) void* buffer, _Outptr_ XblUserStatisticsResult** ptrToBufferResults, _Out_ size_t* resultsCount, _Out_opt_ size_t* bufferUsed ) XBL_NOEXCEPT; /// /// Subscribes to statistic update notifications via the StatisticChanged event. /// DEPRECATED. This continues to work, however it will be removed in a future release. /// Individual RTA subscription will be managed automatically by XSAPI as statistics are tracked with . /// /// Xbox live context for the local user. /// The Xbox User ID of the player requesting the subscription. /// The Service Configuration ID (SCID) for the title. The SCID is considered case sensitive so paste it directly from the Partner Center. /// The name of the statistic to subscribe to. /// Passes back an XblRealTimeActivitySubscriptionHandle object that contains the state of the subscription. /// You can register an event handler for statistic changes by calling XblUserStatisticsAddStatisticChangedHandler(). /// HRESULT return code for this API operation. STDAPI_XBL_DEPRECATED XblUserStatisticsSubscribeToStatisticChange( _In_ XblContextHandle xblContextHandle, _In_ uint64_t xboxUserId, _In_z_ const char* serviceConfigurationId, _In_z_ const char* statisticName, _Out_ XblRealTimeActivitySubscriptionHandle* subscriptionHandle ) XBL_NOEXCEPT; /// /// Unsubscribes a previously created statistic change subscription. /// DEPRECATED. This continues to work, however it will be removed in a future release. /// Individual RTA subscription will be managed automatically by XSAPI as statistics are untracked with /// or /// /// Xbox live context for the local user. /// The subscription object to unsubscribe. /// HRESULT return code for this API operation. STDAPI_XBL_DEPRECATED XblUserStatisticsUnsubscribeFromStatisticChange( _In_ XblContextHandle xblContextHandle, _In_ XblRealTimeActivitySubscriptionHandle subscriptionHandle ) XBL_NOEXCEPT; /// /// Event handler for statistic change notifications. /// /// Contains information about a change to a subscribed statistic. /// Caller context that will be passed back to the handler. /// /// typedef void CALLBACK XblStatisticChangedHandler( _In_ XblStatisticChangeEventArgs statisticChangeEventArgs, _In_ void* context ); /// /// Registers an event handler for statistic change notifications. /// Event handlers receive a XblStatisticChangeEventArgs object. /// /// Xbox live context for the local user. /// The callback function that receives notifications. /// Client context pointer to be passed back to the handler. /// A XblFunctionContext object that can be used to unregister the event handler. STDAPI_(XblFunctionContext) XblUserStatisticsAddStatisticChangedHandler( _In_ XblContextHandle xblContextHandle, _In_ XblStatisticChangedHandler handler, _In_opt_ void* handlerContext ) XBL_NOEXCEPT; /// /// Unregisters an event handler for statistic change notifications. /// /// Xbox live context for the local user. /// The function_context object that was returned when the event handler was registered. /// STDAPI_(void) XblUserStatisticsRemoveStatisticChangedHandler( _In_ XblContextHandle xblContextHandle, _In_ XblFunctionContext context ) XBL_NOEXCEPT; /// /// Configures the set of stats that will be tracked real-time. This call will have no affect on stats /// that were already being tracked. /// /// Xbox live context for the local user. /// Array of XboxUserIDs for whom to track the provided stats. /// Length of xboxUserIds array. /// The Service Configuration ID (SCID) for the title. The SCID is considered case sensitive so paste it directly from the Partner Center. /// Array of statistic names for which real-time updates will be received. /// Length of statisticNames array. /// HRESULT return code for this API operation. /// /// Updates will be delivered via XblStatisticChangedHandlers. /// Note that the set of tracked stats can be updated independent from the handlers. /// STDAPI XblUserStatisticsTrackStatistics( _In_ XblContextHandle xblContextHandle, _In_ const uint64_t* xboxUserIds, _In_ size_t xboxUserIdsCount, _In_z_ const char* serviceConfigurationId, _In_ const char** statisticNames, _In_ size_t statisticNamesCount ) XBL_NOEXCEPT; /// /// Configures the set of stats that will be tracked real-time. Updates will no longer be received /// for the provided stats and users. /// /// Xbox live context for the local user. /// Array of XboxUserIDs for whom to stop tracking the provided stats. /// Length of xboxUserIds array. /// The Service Configuration ID (SCID) for the title. The SCID is considered case sensitive so paste it directly from the Partner Center. /// Array of statistic names for which real-time updates are no longer needed. /// Length of statisticNames array. /// HRESULT return code for this API operation. STDAPI XblUserStatisticsStopTrackingStatistics( _In_ XblContextHandle xblContextHandle, _In_ const uint64_t* xboxUserIds, _In_ size_t xboxUserIdsCount, _In_z_ const char* serviceConfigurationId, _In_ const char** statisticNames, _In_ size_t statisticNamesCount ) XBL_NOEXCEPT; /// /// Configures the set of stats that will be tracked real-time. The API will cancel all real-time /// stat updates for the provided users. /// /// Xbox live context for the local user. /// Array of XboxUserIDs for whom to stop tracking the all stats. /// Length of xboxUserIds array. /// HRESULT return code for this API operation. STDAPI XblUserStatisticsStopTrackingUsers( _In_ XblContextHandle xblContextHandle, _In_ const uint64_t* xboxUserIds, _In_ size_t xboxUserIdsCount ) XBL_NOEXCEPT; } // end extern c ================================================ FILE: Include/xsapi-c/xbox_live_context_c.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #if !defined(__cplusplus) #error C++11 required #endif #pragma once extern "C" { /// /// Creates an XblContextHandle used to access Xbox Live services. /// /// The user associated with this Xbox Live context. /// Passes back the Xbox Live context handle. /// HRESULT return code for this API operation. STDAPI XblContextCreateHandle( _In_ XblUserHandle user, _Out_ XblContextHandle* context ) XBL_NOEXCEPT; /// /// Duplicates the XblContextHandle. /// /// The Xbox Live context handle. /// Passes back the duplicated handle. /// HRESULT return code for this API operation. /// /// Use this method rather than creating a new context with the same user /// if the XblContextHandle is needed by multiple components with independent lifespans. /// STDAPI XblContextDuplicateHandle( _In_ XblContextHandle xboxLiveContextHandle, _Out_ XblContextHandle* duplicatedHandle ) XBL_NOEXCEPT; /// /// Closes the XblContextHandle. /// /// The Xbox Live context handle. /// /// /// When all outstanding handles have been closed, XblContextCloseHandle() /// will free the memory associated with the handle. /// STDAPI_(void) XblContextCloseHandle( _In_ XblContextHandle xboxLiveContextHandle ) XBL_NOEXCEPT; /// /// Get the XblContextHandle associated with this context. /// /// The Xbox Live context handle. /// Passes back the Xbox Live user handle. /// XSAPI will call XalUserDuplicateHandle before returning the handle, /// so it is the responsibility of the caller to later call XalUserCloseHandle. /// HRESULT return code for this API operation. STDAPI XblContextGetUser( _In_ XblContextHandle context, _Out_ XblUserHandle* user ) XBL_NOEXCEPT; /// /// Get the Xbox user ID of the user associated with the context. /// /// The Xbox Live context handle. /// Passes back the xbox user ID. /// HRESULT return code for this API operation. STDAPI XblContextGetXboxUserId( _In_ XblContextHandle context, _Out_ uint64_t* xboxUserId ) XBL_NOEXCEPT; } ================================================ FILE: Include/xsapi-c/xbox_live_context_settings_c.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #if !defined(__cplusplus) #error C++11 required #endif #pragma once extern "C" { /// /// Gets the connect, send, and receive timeouts for HTTP socket operations of long calls such as Title Storage calls. /// /// Xbox live context that the settings are associated with. /// Passes back the timeout for long HTTP calls in seconds. /// Default is 5 minutes (300 seconds). /// HRESULT return code for this API operation. /// /// Calls that take longer than the given timeout are aborted. /// STDAPI XblContextSettingsGetLongHttpTimeout( _In_ XblContextHandle context, _Out_ uint32_t* timeoutInSeconds ) XBL_NOEXCEPT; /// /// Sets the connect, send, and receive timeouts for HTTP socket operations of long calls such as Title Storage calls. /// /// Xbox live context that the settings are associated with. /// The timeout for long HTTP calls in seconds. /// Default is 5 minutes (300 seconds). /// HRESULT return code for this API operation. /// /// Calls that take longer than the given timeout are aborted. /// Take care when setting this to smaller values as some calls like Title Storage may take a few minutes to complete. /// STDAPI XblContextSettingsSetLongHttpTimeout( _In_ XblContextHandle context, _In_ uint32_t timeoutInSeconds ) XBL_NOEXCEPT; /// /// Gets the HTTP retry delay in seconds. /// /// Xbox live context that the settings are associated with. /// Passes back the retry delay in seconds. /// Retries are delayed using an exponential back off. /// By default, it will delay 2 seconds then the next retry will delay 4 seconds, then 8 seconds, /// and so on up to a max of 1 min until either the call succeeds or the http_timeout_window /// is reached, at which point the call will fail. /// The delay is also jittered between the current and next delay to spread out service load. /// The default for http_timeout_window is 20 seconds and can be changed using XblContextSettingsSetHttpTimeoutWindow. /// HRESULT return code for this API operation. /// /// If the service returns an HTTP error with a "Retry-After" header, then all future calls to that API will /// immediately fail with the original error without contacting the service until the "Retry-After" time has been reached. ///
/// Idempotent service calls are retried when a network error occurs or the server responds with one of these HTTP status codes:
/// 408 (Request Timeout)
/// 429 (Too Many Requests)
/// 500 (Internal Server Error)
/// 502 (Bad Gateway)
/// 503 (Service Unavailable)
/// 504 (Gateway Timeout)
///
STDAPI XblContextSettingsGetHttpRetryDelay( _In_ XblContextHandle context, _Out_ uint32_t* delayInSeconds ) XBL_NOEXCEPT; /// /// Sets the HTTP retry delay in seconds. /// /// Xbox live context that the settings are associated with. /// The retry delay in seconds. /// Retries are delayed using an exponential back off. /// By default, it will delay 2 seconds then the next retry will delay 4 seconds, then 8 seconds, /// and so on up to a max of 1 min until either the call succeeds or the http_timeout_window /// is reached, at which point the call will fail. /// The delay is also jittered between the current and next delay to spread out service load. /// The default for http_timeout_window is 20 seconds and can be changed using XblContextSettingsSetHttpTimeoutWindow. /// HRESULT return code for this API operation. /// /// If the service returns an HTTP error with a "Retry-After" header, then all future calls to that API will /// immediately fail with the original error without contacting the service until the "Retry-After" time has been reached. ///
/// Idempotent service calls are retried when a network error occurs or the server responds with one of these HTTP status codes:
/// 408 (Request Timeout)
/// 429 (Too Many Requests)
/// 500 (Internal Server Error)
/// 502 (Bad Gateway)
/// 503 (Service Unavailable)
/// 504 (Gateway Timeout)
///
STDAPI XblContextSettingsSetHttpRetryDelay( _In_ XblContextHandle context, _In_ uint32_t delayInSeconds ) XBL_NOEXCEPT; /// /// Gets the HTTP timeout window in seconds. /// /// Xbox live context that the settings are associated with. /// Passes back the timeout window in seconds. /// The default is 20 seconds. /// HRESULT return code for this API operation. /// /// This controls how long to spend attempting to retry idempotent service calls before failing.
///
/// Idempotent service calls are retried when a network error occurs or the server responds with one of these HTTP status codes:
/// 408 (Request Timeout)
/// 429 (Too Many Requests)
/// 500 (Internal Server Error)
/// 502 (Bad Gateway)
/// 503 (Service Unavailable)
/// 504 (Gateway Timeout)
///
STDAPI XblContextSettingsGetHttpTimeoutWindow( _In_ XblContextHandle context, _Out_ uint32_t* timeoutWindowInSeconds ) XBL_NOEXCEPT; /// /// Sets the HTTP timeout window in seconds. /// /// Xbox live context that the settings are associated with. /// The timeout window in seconds. /// The default is 20 seconds. /// Set to 0 to turn off retry. /// HRESULT return code for this API operation. /// /// This controls how long to spend attempting to retry idempotent service calls before failing.
///
/// Idempotent service calls are retried when a network error occurs or the server responds with one of these HTTP status codes:
/// 408 (Request Timeout)
/// 429 (Too Many Requests)
/// 500 (Internal Server Error)
/// 502 (Bad Gateway)
/// 503 (Service Unavailable)
/// 504 (Gateway Timeout)
///
STDAPI XblContextSettingsSetHttpTimeoutWindow( _In_ XblContextHandle context, _In_ uint32_t timeoutWindowInSeconds ) XBL_NOEXCEPT; /// /// Gets the web socket timeout window in seconds. /// /// Xbox live context that the settings are associated with. /// Passes back the timeout window in seconds. /// HRESULT return code for this API operation. STDAPI XblContextSettingsGetWebsocketTimeoutWindow( _In_ XblContextHandle context, _Out_ uint32_t* timeoutWindowInSeconds ) XBL_NOEXCEPT; /// /// Sets the web socket timeout window in seconds. /// /// Xbox live context that the settings are associated with. /// The timeout window in seconds. /// Default is 300 seconds. /// Set to 0 to turn off retry. /// HRESULT return code for this API operation. /// /// Controls how long to spend attempting to retry establishing a web socket connection before failing. /// STDAPI XblContextSettingsSetWebsocketTimeoutWindow( _In_ XblContextHandle context, _In_ uint32_t timeoutWindowInSeconds ) XBL_NOEXCEPT; // TODO these probably should be moved somewhere else, they aren't really related to context settings /// /// Gets whether to use the xplatqos server for QoS calls. /// /// Xbox live context that the settings are associated with. /// Passes back true if the cross platform QoS servers should be used, false otherwise. /// HRESULT return code for this API operation. STDAPI XblContextSettingsGetUseCrossPlatformQosServers( _In_ XblContextHandle context, _Out_ bool* value ) XBL_NOEXCEPT; /// /// Controls whether we use cross platform qos endpoints or not. /// In some case if you are shipping with TV_API enabled, you want to be able to choose. /// /// Xbox live context that the settings are associated with. /// True if the cross platform QoS servers should be used, false otherwise. /// HRESULT return code for this API operation. STDAPI XblContextSettingsSetUseCrossPlatformQosServers( _In_ XblContextHandle context, _In_ bool value ) XBL_NOEXCEPT; } ================================================ FILE: Include/xsapi-c/xbox_live_global_c.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #if !defined(__cplusplus) #error C++11 required #endif #pragma once #if HC_PLATFORM != HC_PLATFORM_ANDROID #pragma warning(disable: 4265) #pragma warning(disable: 4266) #pragma warning(disable: 4062) #endif #if (!defined(HC_LINK_STATIC) || HC_LINK_STATIC == 0) && HC_PLATFORM_IS_APPLE #include #else #include #endif extern "C" { ///////////////////////////////////////////////////////////////////////////////////////// // Memory APIs // /// /// A callback invoked every time a new memory buffer must be dynamically allocated by the library. /// This callback is optionally installed by calling XblMemSetFunctions(). /// /// The size of the allocation to be made. /// This value will never be zero. /// An opaque identifier representing the /// internal category of memory being allocated. /// A pointer to an allocated block of memory of the specified size, /// or a null pointer if allocation failed. /// /// The callback must allocate and return a pointer to a contiguous block of memory of the /// specified size that will remain valid until the app's corresponding XblMemFreeFunction /// callback is invoked to release it. /// Every non-null pointer returned by this method will be subsequently passed to the corresponding /// XblMemFreeFunction callback once the memory is no longer needed. /// typedef _Ret_maybenull_ _Post_writable_byte_size_(size) void* (STDAPIVCALLTYPE* XblMemAllocFunction)( _In_ size_t size, _In_ HCMemoryType memoryType ); /// /// A callback invoked every time a previously allocated memory buffer is no longer needed by /// the library and can be freed. /// This callback is optionally installed by calling XblMemSetFunctions(). /// /// The pointer to the memory buffer previously allocated. /// This value will never be a null pointer. /// An opaque identifier representing the internal category of /// memory being allocated. /// /// /// The callback is invoked whenever the library has finished using a memory buffer previously /// returned by the app's corresponding XblMemAllocFunction such that the application can free the /// memory buffer. /// typedef void (STDAPIVCALLTYPE* XblMemFreeFunction)( _In_ _Post_invalid_ void* pointer, _In_ HCMemoryType memoryType ); /// /// Optionally sets the memory hook functions to allow callers to control route memory /// allocations to their own memory manager. /// /// A pointer to the custom allocation callback to use, or a null /// pointer to restore the default. /// A pointer to the custom freeing callback to use, or a null /// pointer to restore the default. /// HRESULT return code for this API operation. /// /// This must be called before XblInitialize() and can not be called again until XblCleanup(). /// This method allows the application to install custom memory allocation routines in order /// to service all requests for new memory buffers instead of using default allocation routines. /// The and parameters can be null /// pointers to restore the default routines. /// Both callback pointers must be null or both must be non-null. /// Mixing custom and default routines is not permitted. /// STDAPI XblMemSetFunctions( _In_opt_ XblMemAllocFunction memAllocFunc, _In_opt_ XblMemFreeFunction memFreeFunc ) XBL_NOEXCEPT; /// /// Gets the memory hook functions to allow callers to control route memory allocations to their /// own memory manager. /// /// Set to the current allocation callback. /// Returns the default routine if not previously set. /// Set to the current memory free callback. /// Returns the default routine if not previously set. /// HRESULT return code for this API operation. /// /// This method allows the application get the default memory allocation routines. /// This can be used along with XblMemSetFunctions() to monitor all memory allocations. /// STDAPI XblMemGetFunctions( _Out_ XblMemAllocFunction* memAllocFunc, _Out_ XblMemFreeFunction* memFreeFunc ) XBL_NOEXCEPT; ///////////////////////////////////////////////////////////////////////////////////////// // Global APIs // /// /// Defines values representing the Xbox Live initialization arguments. /// typedef struct XblInitArgs { /// /// Queue used for XSAPI internal asynchronous work (telemetry, rta, etc.). /// This field if optional - if not provided, a threadpool based queue will be used. /// XTaskQueueHandle queue; #if !(HC_PLATFORM == HC_PLATFORM_XDK || HC_PLATFORM == HC_PLATFORM_UWP) /// /// The Service Configuration ID (SCID) for the app. /// You can find it on Partner Center in the Game Setup page under Identity details. /// This string is considered case sensitive so paste it directly from the Partner Center /// _Field_z_ const char* scid; #endif #if HC_PLATFORM == HC_PLATFORM_ANDROID /// /// A required reference to the application's Java VM. /// JavaVM* javaVM; /// /// A required reference to an instance of the application's context /// provided by JNI. /// jobject applicationContext; #endif #if HC_PLATFORM == HC_PLATFORM_IOS /// /// An optional reference to the iOS APNS environment. /// This field is required if the app is integrating with notifications. /// _Field_z_ const char* apnsEnvironment; #endif #if HC_PLATFORM == HC_PLATFORM_WIN32 /// /// Local storage location for XSAPI. /// Used to cache data platform events in the event that they cannot be uploaded due to connectivity problems. /// This field is required and if custom local storage hooks are not set with /// XblLocalStorageSetHandlers, otherwise it will be ignored. /// _Field_z_ const char* localStoragePath; #endif #if HC_PLATFORM_IS_EXTERNAL /// /// The id of the app. /// _Field_z_ char const* appId; /// /// App version. /// _Field_z_ char const* appVer; /// /// The os the app is running on. /// _Field_z_ char const* osName; /// /// The version of the os. /// _Field_z_ char const* osVersion; /// /// The locale the os is using. /// _Field_z_ char const* osLocale; /// /// The device type the app is running on. /// _Field_z_ char const* deviceClass; /// /// The id of the device. /// _Field_z_ char const* deviceId; #endif } XblInitArgs; /// /// Initializes the library instance. /// /// Platform-specific args for XblInitialize. /// HRESULT return code for this API operation. /// /// This must be called before any other Xbl* method, except for XblMemSetFunctions() and XblMemGetFunctions(). /// Should have a corresponding call to XblCleanup(). /// STDAPI XblInitialize(_In_ const XblInitArgs* args) XBL_NOEXCEPT; /// /// Immediately reclaims all resources associated with the library. /// /// The AsyncBlock for this operation. /// HRESULT return code for this API operation. /// /// If you called XblMemSetFunctions(), call this before shutting down your app's memory manager. /// It is the responsibility of the game to wait for any outstanding Async calls to complete before calling XblCleanup. /// If there are background async tasks started by XSAPI pending, this API will wait for them to complete. /// STDAPI XblCleanupAsync( XAsyncBlock* async ) XBL_NOEXCEPT; /// /// Gets the async queue that is used for XSAPI's internal asynchronous operations. /// /// Returns the async queue being used. /// /// Note that this queue will be derived from the queue passed in during XblInitialize, not the exact same one. /// Xsapi will call XTaskQueueDuplicateHandle before returning the queue, so XTaskQueueCloseHandle must be called /// later by callers. /// STDAPI_(XTaskQueueHandle) XblGetAsyncQueue() XBL_NOEXCEPT; /// /// Get the service configuration Id for the application. /// This is set during XblInitialize. /// /// The service configuration Id for the app. /// HRESULT return code for this API operation. /// /// This string will be valid until XblCleanup is called. /// STDAPI XblGetScid( _Out_ const char** scid ) XBL_NOEXCEPT; /// /// Defines the config settings value that is passed to the below API's. /// enum class XblConfigSetting : uint32_t { /// /// Only passed to the below API's to warn code reviewers that there's an outstanding Xbox Live calling /// pattern issue that needs to be addressed. /// ThisCodeNeedsToBeChanged }; /// /// Disables asserts for Xbox Live throttling in dev sandboxes. /// /// The config settings value to be passed down. /// HRESULT return code for this API operation. /// /// The asserts will not fire in RETAIL sandbox, and this setting has no affect in RETAIL sandboxes. /// It is best practice to not call this API, and instead adjust the calling pattern but this is provided /// as a temporary way to get unblocked while in early stages of game development. /// STDAPI_(void) XblDisableAssertsForXboxLiveThrottlingInDevSandboxes( _In_ XblConfigSetting setting ) XBL_NOEXCEPT; /// /// For advanced scenarios where a common Service Configuration ID (SCID) and title Id are needed for cross platform experiences. /// /// Override Service Configuration ID (SCID) to be used by multiplayer manager. This SCID is considered case sensitive so paste it directly from the Partner Center /// Override title Id to be used by multiplayer manager. /// HRESULT return code for this API operation. /// /// Currently only used by multiplayer manager to enable cross platform multiplayer scenarios. /// STDAPI XblSetOverrideConfiguration( _In_ const char* overrideScid, _In_ uint32_t overrideTitleId ) XBL_NOEXCEPT; /// /// To override the locale used across XSAPI. If not set, the default is to use the OS locale /// /// Override locale to be used /// HRESULT return code for this API operation. STDAPI XblSetOverrideLocale( _In_ char const* overrideLocale ) XBL_NOEXCEPT; /// /// Contains information about a service call. /// typedef struct XblServiceCallRoutedArgs { /// /// Handle for the service call. /// HCCallHandle call; /// /// The number of responses in this session. /// uint64_t responseCount; /// /// Returns the a full response log formatted message of all the properties in XblServiceCallRoutedArgs. /// const char* fullResponseFormatted; } XblServiceCallRoutedArgs; /// /// A callback that will be synchronously invoked each time an HTTP call fails but will be automatically be retried. /// /// Contains information about the HTTP call that failed. /// The fields are only valid until the callback returns. /// Client context pass when the handler was added. /// /// /// Can be used to track intermittent failures similar to fiddler. /// typedef void (STDAPIVCALLTYPE* XblCallRoutedHandler)( _In_ XblServiceCallRoutedArgs args, _In_opt_ void* context ); /// /// Registers for all service call notifications. /// /// The event handler function to call. /// Caller context to be passed back to the handler. /// A XblFunctionContext that can be used to unregister the event handler. STDAPI_(XblFunctionContext) XblAddServiceCallRoutedHandler( _In_ XblCallRoutedHandler handler, _In_opt_ void* context ) XBL_NOEXCEPT; /// /// Unregisters from all service call notifications. /// /// The XblFunctionContext object that was returned when the event handler was registered. /// STDAPI_(void) XblRemoveServiceCallRoutedHandler( _In_ XblFunctionContext token ) XBL_NOEXCEPT; /// /// Internal use only. /// enum class XblApiType { /// /// Using C API. /// XblCApi, /// /// Using C++ API. /// XblCPPApi }; /// /// Internal method. /// /// The internal API type. /// void XblSetApiType( _In_ XblApiType apiType ) XBL_NOEXCEPT; } ================================================ FILE: Include/xsapi-cpp/achievements.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "xbox_live_app_config.h" #include "xsapi-cpp/system.h" #include "xsapi-c/achievements_c.h" namespace xbox { namespace services { class xbox_live_context; /// /// Contains classes and enumerations that let you retrieve /// information about player achievements from Xbox Live. /// namespace achievements { class achievement_service_internal; /// Enumeration values that indicate the achievement type. enum class achievement_type { /// The achievement type is unknown. unknown, /// Used as a request input parameter. /// All means to get all achievements regardless of type. all, /// A persistent achievement that may be unlocked at any time. /// Persistent achievements can give Gamerscore as a reward. persistent, /// A challenge achievement that may only be unlocked within a certain time period. /// Challenge achievements can't give Gamerscore as a reward. challenge }; /// Enumeration values that indicate the achievement sort order. enum class achievement_order_by { /// Default order does not guarantee sort order. default_order, /// Sort by title id. title_id, /// Sort by achievement unlock time. unlock_time }; /// Enumeration values that indicate the state of a player's progress towards unlocking an achievement. enum class achievement_progress_state { /// Achievement progress is unknown. unknown, /// Achievement has been earned. achieved, /// Achievement progress has not been started. not_started, /// Achievement progress has started. in_progress }; /// /// Enumeration values that indicate the media asset type associated with /// the achievement. /// enum class achievement_media_asset_type { /// The media asset type is unknown. unknown, /// An icon media asset. icon, /// An art media asset. art }; /// Enumeration values that indicate the participation type for an achievement. enum class achievement_participation_type { /// The participation type is unknown. unknown, /// An achievement that can be earned as an individual participant. individual, /// An achievement that can be earned as a group participant. group }; /// Enumeration values that indicate the reward type for an achievement. enum class achievement_reward_type { /// The reward type is unknown. unknown, /// A Gamerscore reward. gamerscore, /// An in-app reward, defined and delivered by the title. in_app, /// A digital art reward. art }; /// /// Represents the association between a title and achievements. /// class achievement_title_association { public: /// /// The localized name of the title. /// inline const string_t& name() const; /// /// The title ID. /// inline uint32_t title_id() const; inline achievement_title_association(const XblAchievementTitleAssociation& association); private: string_t m_name; uint32_t m_titleId; }; /// /// Represents requirements for unlocking the achievement. /// class achievement_requirement { public: /// /// The achievement requirement ID. /// inline const string_t& id() const; /// /// A value that indicates the current progress of the player towards meeting /// the requirement. /// inline const string_t& current_progress_value() const; /// /// The target progress value that the player must reach in order to meet /// the requirement. /// inline const string_t& target_progress_value() const; inline achievement_requirement(const XblAchievementRequirement& requirement); private: string_t m_id; string_t m_currentProgressValue; string_t m_targetProgressValue; }; /// /// Represents progress details about the achievement, including requirements. /// class achievement_progression { public: /// /// The actions and conditions that are required to unlock the achievement. /// inline const std::vector& requirements() const; /// /// The timestamp when the achievement was first unlocked. /// inline const utility::datetime& time_unlocked() const; inline achievement_progression(const XblAchievementProgression& progression); private: std::vector m_requirements; utility::datetime m_timeUnlocked; }; /// /// Represents an interval of time during which an achievement can be unlocked. /// This class is only used when the achievement_type enumeration is set to challenge. /// class achievement_time_window { public: /// /// The start date and time of the achievement time window. /// inline const utility::datetime& start_date() const; /// /// The end date and time of the achievement time window. /// inline const utility::datetime& end_date() const; inline achievement_time_window(const XblAchievementTimeWindow& timeWindow); private: utility::datetime m_startDate; utility::datetime m_endDate; }; /// /// Represents a media asset for an achievement. /// class achievement_media_asset { public: /// /// The name of the media asset, such as "tile01". /// inline const string_t& name() const; /// /// The type of media asset. /// inline achievement_media_asset_type media_asset_type() const; /// /// The URL of the media asset. /// inline const web::uri& url() const; inline achievement_media_asset(const XblAchievementMediaAsset* mediaAsset); private: string_t m_name; achievement_media_asset_type m_type{ achievement_media_asset_type::unknown }; web::uri m_url; }; #if !XSAPI_NO_PPL /// /// Represents a reward that is associated with the achievement. /// class achievement_reward { public: /// /// The localized reward name. /// inline const string_t& name() const; /// /// The description of the reward. /// inline const string_t& description() const; /// /// The title-defined reward value (data type and content varies by reward type). /// inline const string_t& value() const; /// /// The reward type. /// inline achievement_reward_type reward_type() const; /// /// The property type of the reward value string. /// inline const string_t& value_type() const; /// /// The media asset associated with the reward. /// If the reward type is gamerscore, this will be nullptr. /// If the reward type is in_app, this will be a media asset. /// If the reward type is art, this may be a media asset or nullptr. /// inline const achievement_media_asset& media_asset() const; inline achievement_reward(const XblAchievementReward& reward); private: string_t m_name; string_t m_description; string_t m_value; achievement_reward_type m_rewardType; string_t m_valueType; achievement_media_asset m_mediaAsset; }; /// /// Represents an achievement, a system-wide mechanism for directing and /// rewarding users' in-game actions consistently across all games. /// class achievement { public: /// /// The achievement ID. Can be a uint or a guid. /// inline string_t id() const; /// /// The ID of the service configuration set associated with the achievement. /// inline string_t service_configuration_id() const; /// /// The localized achievement name. /// inline string_t name() const; /// /// The game/app titles associated with the achievement. /// inline std::vector title_associations() const; /// /// The state of a user's progress towards the earning of the achievement. /// inline achievement_progress_state progress_state() const; /// /// The progression object containing progress details about the achievement, /// including requirements. /// inline achievement_progression progression() const; /// /// The media assets associated with the achievement, such as image IDs. /// inline std::vector media_assets() const; /// /// The collection of platforms that the achievement is available on. /// inline std::vector platforms_available_on() const; /// /// Whether or not the achievement is secret. /// inline bool is_secret() const; /// /// The description of the unlocked achievement. /// inline string_t unlocked_description() const; /// /// The description of the locked achievement. /// inline string_t locked_description() const; /// /// The product_id the achievement was released with. This is a globally unique identifier that /// may correspond to an application, downloadable content, etc. /// inline string_t product_id() const; /// /// The type of achievement, such as a challenge achievement. /// inline achievement_type type() const; /// /// The participation type for the achievement, such as group or individual. /// inline achievement_participation_type participation_type() const; /// /// The time window during which the achievement is available. Applies to Challenges. /// inline achievement_time_window available() const; /// /// The collection of rewards that the player earns when the achievement is unlocked. /// inline std::vector rewards() const; /// /// The estimated time that the achievement takes to be earned. /// inline std::chrono::seconds estimated_unlock_time() const; /// /// A deeplink for clients that enables the title to launch at a desired starting point /// for the achievement. /// inline string_t deep_link() const; /// /// A value that indicates whether or not the achievement is revoked by enforcement. /// inline bool is_revoked() const; achievement() = default; inline achievement(XblAchievementsResultHandle handle, const XblAchievement* achievement); inline achievement(const achievement& other); inline achievement& operator=(achievement other); inline ~achievement(); private: XblAchievementsResultHandle m_handle{ nullptr }; const XblAchievement* m_achievement{ nullptr }; }; /// /// Represents a collection of Achievement class objects returned by a request. /// class achievements_result { public: /// /// The collection of achievement objects returned by a request. /// inline std::vector items() const; /// /// Returns a boolean value that indicates if there are more pages of achievements to retrieve. /// /// True if there are more pages, otherwise false. inline bool has_next() const; /// /// Returns an achievements_result object that contains the next page of achievements. /// /// The maximum number of items that the result can contain. Pass 0 to attempt /// to retrieve all items. /// An achievements_result object that contains a list of achievement objects. /// /// Returns a concurrency::task<T> object that represents the state of the asynchronous operation. /// /// This method calls V2 GET /users/xuid({xuid})/achievements. /// inline pplx::task> get_next( _In_ uint32_t maxItems ); achievements_result() = default; inline achievements_result(XblAchievementsResultHandle handle); inline achievements_result(const achievements_result& other); inline achievements_result& operator=(achievements_result other); inline ~achievements_result(); private: XblAchievementsResultHandle m_handle{ nullptr }; }; /// /// Represents an endpoint that you can use to access the Achievement service. /// class achievement_service { public: /// /// Allow achievement progress to be updated and achievements to be unlocked. /// This API will work even when offline. On PC and Xbox One, updates will be /// posted by the system when connection is re-established even if the title isn't running. /// /// The Xbox User ID of the player. /// The achievement ID as defined by XDP or Dev Center. /// The completion percentage of the achievement to indicate progress. /// Valid values are from 1 to 100. Set to 100 to unlock the achievement. /// Progress will be set by the server to the highest value sent /// /// Returns a task<T> object that represents the state of the asynchronous operation. /// /// This method calls V2 POST /users/xuid({xuid})/achievements/{scid}/update /// inline pplx::task> update_achievement( _In_ const string_t& xboxUserId, _In_ const string_t& achievementId, _In_ uint32_t percentComplete ); /// /// Allow achievement progress to be updated and achievements to be unlocked. /// This API will work even when offline. On PC and Xbox One, updates will be /// posted by the system when connection is re-established even if the title isn't running. /// /// The Xbox User ID of the player. /// The title ID. /// The service configuration ID (SCID) for the title. /// The achievement ID as defined by XDP or Dev Center. /// The completion percentage of the achievement to indicate progress. /// Valid values are from 1 to 100. Set to 100 to unlock the achievement. /// Progress will be set by the server to the highest value sent /// /// Returns a task<T> object that represents the state of the asynchronous operation. /// /// This method calls V2 POST /users/xuid({xuid})/achievements/{scid}/update /// inline pplx::task> update_achievement( _In_ const string_t& xboxUserId, _In_ uint32_t titleId, _In_ const string_t& serviceConfigurationId, _In_ const string_t& achievementId, _In_ uint32_t percentComplete ); /// /// Returns an achievements_result object containing the first page of achievements /// for a player of the specified title. /// /// The Xbox User ID of the player. /// The title ID. /// The achievement type to retrieve. /// Indicates whether to return unlocked achievements only. /// Controls how the list of achievements is ordered. /// The number of achievements to skip. /// The maximum number of achievements the result can contain. Pass 0 to attempt /// to retrieve all items. /// An AchievementsResult object that contains a list of Achievement objects. /// /// Returns a task<T> object that represents the state of the asynchronous operation. /// /// See achievements_result:get_next to page in the next set of results. /// /// This method calls V2 GET /users/xuid({xuid})/achievements /// inline pplx::task> get_achievements_for_title_id( _In_ const string_t& xboxUserId, _In_ uint32_t titleId, _In_ achievement_type type, _In_ bool unlockedOnly, _In_ achievement_order_by orderBy, _In_ uint32_t skipItems, _In_ uint32_t maxItems ); /// /// Returns a specific achievement object for a specified player. /// /// The Xbox User ID of the player. /// The service configuration ID (SCID) for the title. /// The unique identifier of the Achievement as defined by XDP or Dev Center. /// The requested achievement object if it exists. /// If the achievement does not exist, the method returns xbox_live_error_code::runtime_error . /// /// Returns a task<T> object that represents the state of the asynchronous operation. /// /// This method calls V2 GET /users/xuid({xuid})/achievements/{scid}/{achievementId}. /// inline pplx::task> get_achievement( _In_ const string_t& xboxUserId, _In_ const string_t& serviceConfigurationId, _In_ const string_t& achievementId ); inline achievement_service(const achievement_service& other); inline achievement_service& operator=(achievement_service other); inline ~achievement_service(); private: inline achievement_service(XblContextHandle xblContextHandle); XblContextHandle m_xblContextHandle; friend xbox_live_context; }; #endif // !XSAPI_NO_PPL }}} #if !XSAPI_NO_PPL #include "impl/achievements.hpp" #endif ================================================ FILE: Include/xsapi-cpp/errors.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include #ifndef _NOEXCEPT #define _NOEXCEPT noexcept #endif namespace xbox { /// /// Contains classes, enumerations, and namespaces used to indicate error conditions for Xbox Live service components. /// namespace services { /// /// Enumeration values that define the Xbox Live API error conditions. /// /// /// A best practice is to test the returned std::error_code against these error conditions. /// For more detail about std::error_code vs std::error_condition, see /// http://en.cppreference.com/w/cpp/error/error_condition /// /// /// For example: /// /// if( result.err() == xbox::services::xbox_live_error_condition::auth ) /// { /// // ... /// } /// /// or /// /// switch (result.err().default_error_condition().value()) /// { /// case xbox::services::xbox_live_error_condition::auth: /// // ... /// break; /// } /// /// enum class xbox_live_error_condition { /// /// No error. /// no_error = 0, /// /// A generic error condition. /// generic_error, /// /// An error condition related to an object being out of range. /// generic_out_of_range, /// /// An error condition related to attempting to authenticate. /// auth, /// /// An error condition related to network connectivity. /// network, /// /// An error condition related to an HTTP method call. /// http, /// /// The requested resource was not found. /// http_404_not_found, /// /// The precondition given in one or more of the request-header fields evaluated /// to false when it was tested on the server. /// http_412_precondition_failed, /// /// Client is sending too many requests /// http_429_too_many_requests, /// /// The service timed out while attempting to process the request. /// http_service_timeout, /// /// An error related to real time activity. /// rta }; /// /// These are XSAPI specific error codes. /// Only the HTTP codes and errors that are commonly seen when calling Xbox LIVE are called out with guidance about the cause. /// The best practice to test for and react to using the xbox_live_error_condition enum instead of these error codes. /// enum class xbox_live_error_code { /// /// 0 /// No error /// no_error = 0, ////////////////////////////////////////////////////////////////////////// // HTTP errors ////////////////////////////////////////////////////////////////////////// /// /// The 204 response indicates that the content data was not found. /// This code is returned when you are trying to access or write to a session that has been deleted. /// http_status_204_resource_data_not_found = 204, /// /// 0x8019012C /// The 300 response indicates there are multiple choices. Not returned by Xbox Live services /// http_status_300_multiple_choices = 300, /// /// 0x8019012D /// The 301 response indicates that the request should be directed at a new URI /// http_status_301_moved_permanently = 301, /// /// 0x8019012E /// The 302 response indicates found. /// http_status_302_found = 302, /// /// 0x8019012F /// The 303 response indicates see other. Not returned by Xbox Live services /// http_status_303_see_other = 303, /// /// 0x80190130 /// The 304 response indicates resource has not been modified. /// http_status_304_not_modified = 304, /// /// 0x80190131 /// The 305 response indicates you must a use proxy. Not typically returned by Xbox Live services /// http_status_305_use_proxy = 305, /// /// 0x80190133 /// The 307 response indicates a temporary redirect. /// http_status_307_temporary_redirect = 307, /// /// 0x80190190 /// The request could not be understood by the server due to malformed syntax. /// The client should not repeat this request without modification. /// /// This code is returned by the following methods: /// Social services /// GetUserProfileAsync: /// The list of XUIDs returned from a leaderboard read operation and passed to this method has failed /// due to one or more invalid XUID values. /// /// Data services /// GetSingleUserStatisticsAsync: /// Corresponds to a general HTTP error code: HTTP 400 Bad Request. /// /// Server Platform services /// AllocateClusterAsync: /// Matchmaking request failure, typically due to QoS (where the requirement is all-or-nothing). /// /// Marketplace services /// GetCatalogItemDetailsAsync: /// The Details service can only support 10 CatalogItems per request (MAX_DETAILS_ITEMS). /// BrowseCatalogAsync: Typically an error of setting maxItems too large. The catalog service only supports a max of /// 25 items per call. /// http_status_400_bad_request = 400, /// /// 0x80190191 /// The 401 response typically indicates that authorization has been refused for provided credentials. /// This code is returned when the user(s) or the device is not authorized to access the requested data /// or perform the requested action. /// http_status_401_unauthorized = 401, /// /// 0x80190192 /// 402 Payment Required /// http_status_402_payment_required = 402, /// /// 0x80190193 /// Corresponds to general HTTP error code HTTP 403 - Forbidden. /// The server understood the request but is refusing to fulfill. Authorization will not resolve the issue /// and the request should not be repeated. /// /// General /// This is typically a service configuration issue or a malformed HTTP request. To resolve, make sure that /// your service configuration is correct, that your console is in the right sandbox, and that you are using /// the correct title id and SCID. Using fiddler to examine the failing HTTP request will often help root cause the issue. /// /// Multiplayer services /// The user doesn't have multiplayer privileges, or the session is private/reserved and the user isn't a member. /// http_status_403_forbidden = 403, /// /// 0x80190194 /// This typically indicates that the server has not found anything matching the provided URI. /// The root cause of the 404 will depend on the API you are using. /// For example many of the "Get Leaderboard" /// APIs return a 404 error if you request a stat that does not exist, or if the leader board is empty, /// CreateSessionAsync returns a 404 error if you call it with an invalid Session Template name, and /// GetPartyViewAsync fails with a 404 if the multiplayer session times out. Using fiddler to examine the failing /// HTTP request will often help root cause the issue. /// http_status_404_not_found = 404, /// /// 0x80190195 /// The method specified in the Request-Line is not allowed for the resource identified by the Request-URI. /// For example, the request is a POST where a PUT is needed. /// http_status_405_method_not_allowed = 405, /// /// 0x80190196 /// The resource identified by the request is only capable for generating response entities which have /// content characteristics not acceptable according to the accept headers sent in the Request. /// http_status_406_not_acceptable = 406, /// /// 0x80190197 /// This code is similar to HTTP 401 (Unauthorized), but indicates that the client must first /// authenticate itself with the proxy. /// http_status_407_proxy_authentication_required = 407, /// /// 0x80190198 /// The client did not produce a request within the time the server was prepared to wait. The client MAY /// repeat the request without modifications at a later time. /// http_status_408_request_timeout = 408, /// /// 0x80190199 /// This typically indicates that the request could not be completed due to a conflict with the current /// state of the resource. This code is only allowed in situations where it is expected that a user might /// be able to resolve the conflict and re-submit the request. /// /// Conflicts are most likely to occur in response to a PUT request. For example, if versioning were being /// used and the entity being PUT included changes to a resource which conflict with those made by an earlier /// (third-party) request, the server might use the 409 response to indicate that it can't complete the request. /// In this case, the response entity would likely contain a list of the differences between the two versions in a /// format defined by the Response-ContentType. /// /// Multiplayer services: /// Incorrect session or matchmaking query syntax or re-setting properties that are already pre-defined in the session template. /// The session couldn't be updated because the request is incompatible with the session. For example: /// Constants in the request conflict with constants in the session or session template. /// Members other than the caller are added to, or removed, from a large session. /// http_status_409_conflict = 409, /// /// 0x8019019A /// This typically indicates that the requested resource is no longer available at the server and no forwarding address /// is known. The expectation is that this condition is considered to be permanent. /// http_status_410_gone = 410, /// /// 0x8019019B /// The server refuses to accept the request without a defined Content-Length. The client MAY repeat the request if it /// adds a valid Content-Length header field containing the length of the message-body in the request. /// http_status_411_length_required = 411, /// /// 0x8019019C /// The precondition given in one or more of the request-header fields evaluated to false when it was tested on the /// server. This response code allows the client to place preconditions on the current resource meta information /// (header field data) to prevent the requested method from being applied to a resource other than the one intended. /// /// Multiplayer services /// The If-Match header, or (other than on a GET) the If-None-Match header couldn't be satisfied. /// The If-Match header couldn't be satisfied on a PUT or DELETE to an existing session. The current state of the session /// is returned along with the current ETag. /// http_status_412_precondition_failed = 412, /// /// 0x8019019D /// The server is refusing to process a request because the request entity is larger than the server is willing, or able, /// to process. The server MAY close the connection to prevent the client from continuing with the request. /// http_status_413_request_entity_too_large = 413, /// /// 0x8019019E /// The server is refusing to service the request because the Request-URI is longer than the server is willing to interpret. /// This rare condition is only likely to occur when a client has improperly converted a POST Request to a GET Request with long query information. /// http_status_414_request_uri_too_long = 414, /// /// 0x8019019F /// The server is refusing to service the request because the entity of the request is in a format not supported by the /// requested resource for the requested method. /// http_status_415_unsupported_media_type = 415, /// /// 0x801901A0 /// A server SHOULD return a response with this status code if a requested included in a Range request-header field, and none of /// the range-specifier values in this field overlap the current extent of the selected resource, and the request did not /// include an If-Range request-header field. /// http_status_416_requested_range_not_satisfiable = 416, /// /// 0x801901A1 /// Expect-request header failure /// http_status_417_expectation_failed = 417, /// /// 0x801901A5 /// The request was misdirected /// http_status_421_misdirected_request = 421, /// /// 0x801901A6 /// The request was not processable /// http_status_422_unprocessable_entity = 422, /// /// 0x801901A7 /// The resource was locked /// http_status_423_locked = 423, /// /// 0x801901A8 /// The request failed due to dependency /// http_status_424_failed_dependency = 424, /// /// 0x801901AA /// The client should upgrade to later protocol /// http_status_426_upgrade_required = 426, /// /// 0x801901AC /// The server requires a precondition /// http_status_428_precondition_required = 428, /// /// 0x801901AD /// Client is sending too many requests /// http_status_429_too_many_requests = 429, /// /// 0x801901AF /// The request headers are too large /// http_status_431_request_header_fields_too_large = 431, /// /// 0x801901C1 /// The request should be retried after doing the appropriate action /// http_status_449_retry_with = 449, /// /// 0x801901C3 /// The request was unavailable for legal reasons /// http_status_451_unavailable_for_legal_reasons = 451, /// /// 0x801901F4 /// Corresponds to HTTP 500 Internal Server Error.This error can occur when invalid parameters are provided to the web service /// via a RESTful API call, or if the web service encounters a crash or other unexpected error state. Using fiddler to examine /// the failing HTTP request will often help root cause the issue. /// http_status_500_internal_server_error = 500, /// /// 0x801901F5 /// The requested service is not implemented. /// http_status_501_not_implemented = 501, /// /// 0x801901F6 /// The request got an invalid response to the gateway /// http_status_502_bad_gateway = 502, /// /// 0x801901F7 /// The requested service is not available. /// http_status_503_service_unavailable = 503, /// /// 0x801901F8 /// The HTTP gateway has timed out. /// http_status_504_gateway_timeout = 504, /// /// 0x801901F9 /// This version of HTTP is not supported by the endpoint. /// http_status_505_http_version_not_supported = 505, /// /// 0x801901FA /// Internal configuration error /// http_status_506_variant_also_negotiates = 506, /// /// 0x801901FB /// The service was unable complete the request due to storage /// http_status_507_insufficient_storage = 507, /// /// 0x801901FC /// The service detected an infinite loop while processing /// http_status_508_loop_detected = 508, /// /// 0x801901FE /// The request requires more extensions to complete /// http_status_510_not_extended = 510, /// /// 0x801901FF /// The client needs to authenticate to gain network access. Not used by Xbox Live services. /// http_status_511_network_authentication_required = 511, ////////////////////////////////////////////////////////////////////////// // Errors from exception enabled components such as Casablanca ////////////////////////////////////////////////////////////////////////// /// /// 0x8007000e /// xbox_live_error_code 1000 /// Bad alloc /// bad_alloc = 1000, /// /// 0x80004002 /// xbox_live_error_code 1001 /// Bad cast /// bad_cast, /// /// 0x80070057 /// xbox_live_error_code 1002 /// Invalid argument /// invalid_argument, /// /// 0x8000000b /// xbox_live_error_code 1003 /// Out of range /// out_of_range, /// /// 0x80070018 /// xbox_live_error_code 1004 /// Length error /// length_error, /// /// 0x8000000b /// xbox_live_error_code 1005 /// Range error /// range_error, /// /// 0x8000ffff /// xbox_live_error_code 1006 /// Logic error /// logic_error, /// /// 0x89235200 /// xbox_live_error_code 1007 /// Runtime error /// runtime_error, /// /// 0x83750007 /// xbox_live_error_code 1008 /// JSON error /// json_error, /// /// 0x83750005 /// xbox_live_error_code 1009 /// Websocket error /// websocket_error, /// /// 0x83750005 /// xbox_live_error_code 1010 /// URI error /// uri_error, /// /// 0x80004005 /// xbox_live_error_code 1011 /// Generic error /// generic_error, ////////////////////////////////////////////////////////////////////////// // RTA errors ////////////////////////////////////////////////////////////////////////// /// /// 0x89235201 /// xbox_live_error_code 1500 /// RTA generic error /// rta_generic_error = 1500, /// /// 0x89235202 /// xbox_live_error_code 1501 /// RTA subscription limit reached /// rta_subscription_limit_reached, /// /// 0x89235203 /// xbox_live_error_code 1502 /// RTA access denied /// rta_access_denied, /// /// 0x89235209 /// xbox_live_error_code 1503 /// RTA not activated /// rta_not_activated, ////////////////////////////////////////////////////////////////////////// // Auth errors ////////////////////////////////////////////////////////////////////////// /// /// 0x89235204 /// xbox_live_error_code 2000 /// Unknown auth error /// auth_unknown_error = 2000, /// /// 0x8086000c /// xbox_live_error_code 2001 /// User interaction required /// auth_user_interaction_required, /// /// 0x80070525 /// xbox_live_error_code 2002 /// User interaction required /// auth_user_switched, /// /// 0x80070525 /// xbox_live_error_code 2003 /// User cancelled /// auth_user_cancel, /// /// 0x80070525 /// xbox_live_error_code 2004 /// User not signed in /// auth_user_not_signed_in, /// /// 0x89235205 /// xbox_live_error_code 2005 /// Auth runtime error /// auth_runtime_error, /// /// 0x89235206 /// xbox_live_error_code 2006 /// Auth no token error /// auth_no_token_error, ////////////////////////////////////////////////////////////////////////// // Xbox Live SDK errors ////////////////////////////////////////////////////////////////////////// /// /// 0x8007064a /// xbox_live_error_code 3000 /// Could not read the xboxservices.config. Ensure it is deployed in the package at /// Windows::ApplicationModel::Package::Current->InstalledLocation + "\xboxservices.config" /// invalid_config = 3000, /// /// 0x80004001 /// xbox_live_error_code 3001 /// API is not supported /// unsupported = 3001, ////////////////////////////////////////////////////////////////////////// // xbox live auth errors ////////////////////////////////////////////////////////////////////////// /// /// 0x80072EE2 /// This error is typically returned by Xbox::Services APIs, GetTokenAndSignatureAsync, /// and CheckLicense. It indicates that the Internet connection or a server response was /// interrupted; possibly the result of a incorrectly configured NSAL. Pending requests /// should be retried when this error is encountered. /// HR_ERROR_INTERNET_TIMEOUT = (int)0x80072EE2, /// /// 0x87DD0003 /// XASD returned an unexpected response. /// AM_E_XASD_UNEXPECTED = (int)0x87DD0003, /// /// 0x87DD0004 /// XASU returned an unexpected response. /// AM_E_XASU_UNEXPECTED = (int)0x87DD0004, /// /// 0x87DD0005 /// XAST returned an unexpected response. /// Cause: Your NSAL configuration is set up correctly for your service, however the /// Relying Party certificate is not yet trusted by Xbox Live. /// /// Resolution : Relying Party configuration on XDP and provide the Relying Party /// certificate in the XSTS token certificate for your endpoint. /// If you are using the NSAL.json file, double check that the Relying Party name is /// correct in the 'target' field of your NSAL.json file on the console and ensure that /// the certificate has been specified in XDP. /// /// If you are developing a UWP title and performing service configuration in XDP, then /// you may have not completed the Application ID binding step correctly. For more /// information, please see the "Troubleshooting Sign-in" article in the Xbox Live documentation. /// AM_E_XAST_UNEXPECTED = (int)0x87DD0005, /// /// 0x87DD0006 /// Cause : Your NSAL configuration is set up correctly for your service, however the /// Relying Party certificate is not yet trusted by Xbox Live. /// /// Resolution : Relying Party configuration on XDP and provide the Relying Party /// certificate in the XSTS token certificate for your endpoint. /// If you are using the NSAL.json file, double check that the Relying Party name is /// correct in the 'target' field of your NSAL.json file on the console and ensure /// that the certificate has been specified in XDP. /// AM_E_XSTS_UNEXPECTED = (int)0x87DD0006, /// /// 0x87DD0007 /// XDevice returned an unexpected response. /// AM_E_XDEVICE_UNEXPECTED = (int)0x87DD0007, /// /// 0x87DD0008 /// The console is not authorized to enable development mode. /// AM_E_DEVMODE_NOT_AUTHORIZED = (int)0x87DD0008, /// /// 0x87DD0009 /// The operation was not authorized. /// Cause: The user is not authorized to get an XSTS token with the current TitleID or /// SandboxID setup on the console for the provided URL. /// /// Resolution: /// Ensure that your console is in the proper SandboxID for the TitleID you are using. /// See this forum thread for instructions on changing your SandboxID and which SandboxID /// to use for samples. /// /// Ensure that the test account you are using is partitioned to a group in XDP that has /// access to the TitleID and SandboxID for your title(Note that all accounts are able to /// access the samples while in the sample SandboxID). /// AM_E_NOT_AUTHORIZED = (int)0x87DD0009, /// /// 0x87DD000A /// The operation was forbidden. The server responded with a 403, and a more detailed error is not available. /// AM_E_FORBIDDEN = (int)0x87DD000A, /// /// 0x87DD000B /// The URL specified does not match a known target. /// /// Cause: /// The URL you are attempting to get a token for was not found within your NSAL configuration. /// /// Resolution: /// Ensure that the Relying Party and server name are properly set up as an endpoint /// on your console and in your NSAL. /// /// Try calling a known Xbox Service such as https ://social.xboxlive.com/users/xuid(user's xuid)/people /// to ensure that you can get tokens for the Xbox services. If you are using the NSAL.json file, make sure /// that the 'target' field in the file has your Relying Party name ending with '/' and /// that the server name is set up correctly. /// AM_E_UNKNOWN_TARGET = (int)0x87DD000B, /// /// 0x87DD000C /// There were problems with the JSON data downloaded from the server. /// AM_E_INVALID_NSAL_DATA = (int)0x87DD000C, /// /// 0x87DD000D /// The title has not yet been successfully authenticated. /// Cause: This will be returned when a title token is required, but has not been cached. For example, /// attempting to retrieve an X token with title claims will fail with this error if AuthenticateTitle /// hasn't been called successfully. /// /// Resolution: Check your TitleID, SOCID, and SandboxID, or check your title's configuration on XDP. /// AM_E_TITLE_NOT_AUTHENTICATED = (int)0x87DD000D, /// /// 0x87DD000E /// XAST returned 401 when attempting to retrieve the T token. Double-check that your device is set to the /// proper development sandbox and that the user has access to the sandbox. /// /// For more information, please see the "Troubleshooting Sign-in" article in the Xbox Live documentation. /// AM_E_TITLE_NOT_AUTHORIZED = (int)0x87DD000E, /// /// 0x87DD0011 /// The user hash value for the specified user hasn't been recorded, so a valid token can't be generated. /// AM_E_USER_HASH_MISSING = (int)0x87DD0011, /// /// 0x87DD0013 /// The Authentication Manager can't find the user for which an authentication token is being retrieved. /// One possible scenario is that the User signed out, but the title still has a reference to the User object. /// AM_E_USER_NOT_FOUND = (int)0x87DD0013, /// /// 0x87DD0015 /// The environment configured or specified is not valid. /// AM_E_INVALID_ENVIRONMENT = (int)0x87DD0015, /// /// 0x87DD0016 /// The XASD authentication server has timed out. /// AM_E_XASD_TIMEOUT = (int)0x87DD0016, /// /// 0x87DD0017 /// The XASU authentication server has timed out. /// AM_E_XASU_TIMEOUT = (int)0x87DD0017, /// /// 0x87DD0018 /// The XAST authentication server has timed out. /// AM_E_XAST_TIMEOUT = (int)0x87DD0018, /// /// 0x87DD0019 /// The XSTS authentication server has timed out. /// AM_E_XSTS_TIMEOUT = (int)0x87DD0019, /// /// 0x87DD001A /// Title authentication failed because a connection to Xbox Live is required, by policy, and none is present. /// AM_E_LIVE_CONNECTION_REQUIRED = (int)0x87DD001A, /// /// 0x87dd001e /// There is no network connection. /// AM_E_NO_NETWORK = (int)0x87dd001e, /// /// 0x87dd0020 /// The Network Security Authorization List(NSAL) returned an unexpected response. /// AM_E_XTITLE_UNEXPECTED = (int)0x87dd0020, /// /// 0x87dd0021 /// The endpoint does not require an authorization token, but the application is attempting to /// retrieve a token via GetTokenAndSignatureAsync. /// AM_E_NO_TOKEN_REQUIRED = (int)0x87dd0021, /// /// 0x87dd0022 /// Timeouts were received from the various authorization servers. /// AM_E_XTITLE_TIMEOUT = (int)0x87dd0022, /// /// 0x8015DC00 /// Developer mode is not authorized for the client device. /// XO_E_DEVMODE_NOT_AUTHORIZED = (int)0x8015DC00, /// /// 0x8015DC01 /// A system update is required before this action can be performed. /// XO_E_SYSTEM_UPDATE_REQUIRED = (int)0x8015DC01, /// /// 0x8015DC02 /// A content update is required before this action can be performed. /// XO_E_CONTENT_UPDATE_REQUIRED = (int)0x8015DC02, /// /// 0x8015DC03 /// The device or user was banned. /// XO_E_ENFORCEMENT_BAN = (int)0x8015DC03, /// /// 0x8015DC04 /// The device or user was banned. /// XO_E_THIRD_PARTY_BAN = (int)0x8015DC04, /// /// 0x8015DC05 /// Access to this resource has been parentally restricted. /// XO_E_ACCOUNT_PARENTALLY_RESTRICTED = (int)0x8015DC05, /// /// 0x8015DC08 /// Access to this resource requires that the account billing information /// is updated. /// XO_E_ACCOUNT_BILLING_MAINTENANCE_REQUIRED = (int)0x8015DC08, /// /// 0x8015DC0A /// The user has not accepted the terms of use for this resource. /// XO_E_ACCOUNT_TERMS_OF_USE_NOT_ACCEPTED = (int)0x8015DC0A, /// /// 0x8015DC0B /// This resource is not available in the country associated with the user. /// XO_E_ACCOUNT_COUNTRY_NOT_AUTHORIZED = (int)0x8015DC0B, /// /// 0x8015DC0C /// Access to this resource requires age verification. /// XO_E_ACCOUNT_AGE_VERIFICATION_REQUIRED = (int)0x8015DC0C, /// /// 0x8015DC0D /// XO_E_ACCOUNT_CURFEW = (int)0x8015DC0D, /// /// 0x8015DC0E /// XO_E_ACCOUNT_CHILD_NOT_IN_FAMILY = (int)0x8015DC0E, /// /// 0x8015DC0F /// XO_E_ACCOUNT_CSV_TRANSITION_REQUIRED = (int)0x8015DC0F, /// /// 0x8015DC09 /// XO_E_ACCOUNT_CREATION_REQUIRED = (int)0x8015DC09, /// /// 0x8015DC10 /// XO_E_ACCOUNT_MAINTENANCE_REQUIRED = (int)0x8015DC10, /// /// 0x8015DC11 /// The call was blocked because there was a conflict with the sandbox, console, application, or /// your account.Verify your account, console and title settings in XDP, and check the current /// Sandbox on the device. /// XO_E_ACCOUNT_TYPE_NOT_ALLOWED = (int)0x8015DC11, /// /// 0x8015DC12 /// Your device does not have access to the Sandbox it is set to, or the account you are signed /// in with does not have access to the Sandbox.Check that you are using the correct Sandbox. /// /// Note: All XDK samples use XDKS.1 SandboxID, which allow all user accounts to access and run /// the samples.SandboxID's are case sensitive- Not matching the case of your SandboxID exactly may /// result in errors. If you are still having issues running the sample, please work with your /// Developer Account Manager and provide a fiddler trace to help with troubleshooting. /// /// For more information on handling this error, please see the "Troubleshooting Sign-in" article /// in the Xbox Live documentation /// XO_E_CONTENT_ISOLATION = (int)0x8015DC12, /// /// 0x8015DC13 /// XO_E_ACCOUNT_NAME_CHANGE_REQUIRED = (int)0x8015DC13, /// /// 0x8015DC14 /// XO_E_DEVICE_CHALLENGE_REQUIRED = (int)0x8015DC14, /// /// 0x8015DC16 /// The account was signed in on another device. /// XO_E_SIGNIN_COUNT_BY_DEVICE_TYPE_EXCEEDED = (int)0x8015DC16, /// /// 0x8015DC17 /// XO_E_PIN_CHALLENGE_REQUIRED = (int)0x8015DC17, /// /// 0x8015DC18 /// XO_E_RETAIL_ACCOUNT_NOT_ALLOWED = (int)0x8015DC18, /// /// 0x8015DC19 /// The current sandbox is not allowed to access the SCID. Please ensure that your current /// sandbox is set to your development sandbox. If you are running on a Windows 10 PC, then /// you can change your current sandbox using the SwitchSandbox.cmd script in the Xbox Live SDK /// tools directory. If you are using an Xbox One, you can switch the sandbox using Xbox One /// Manager. /// /// For more information on handling this error, please see the "Troubleshooting Sign-in" article /// in the Xbox Live documentation. /// XO_E_SANDBOX_NOT_ALLOWED = (int)0x8015DC19, /// /// 0x8015DC1A /// XO_E_ACCOUNT_SERVICE_UNAVAILABLE_UNKNOWN_USER = (int)0x8015DC1A, /// /// 0x8015DC1B /// XO_E_GREEN_SIGNED_CONTENT_NOT_AUTHORIZED = (int)0x8015DC1B, /// /// 0x8015DC1C /// XO_E_CONTENT_NOT_AUTHORIZED = (int)0x8015DC1C, ////////////////////////////////////////////////////////////////////////// // Generic errors ////////////////////////////////////////////////////////////////////////// /// /// 0x800C0002 /// The URL is invalid. /// HR_INET_E_INVALID_URL = (int)0x800C0002, /// /// 0x800C0003 /// No session. /// HR_INET_E_NO_SESSION = (int)0x800C0003, /// /// 0x800C0004 /// WinINet cannot connect to the requested resource. /// HR_INET_E_CANNOT_CONNECT = (int)0x800C0004, /// /// 0x800C0005 /// The requested resource was not found. /// HR_INET_E_RESOURCE_NOT_FOUND = (int)0x800C0005, /// /// 0x800C0006 /// The requested resource was not found. /// HR_INET_E_OBJECT_NOT_FOUND = (int)0x800C0006, /// /// 0x800C0007 /// The requested resource was not found. /// HR_INET_E_DATA_NOT_AVAILABLE = (int)0x800C0007, /// /// 0x800C0008 /// Operation restricted by the current inability to discover or join the multiplayer session, or /// the player does not have the multiplayer privilege. /// HR_INET_E_DOWNLOAD_FAILURE = (int)0x800C0008, /// /// 0x800C0009 /// Authentication is required to access this resource. /// HR_INET_E_AUTHENTICATION_REQUIRED = (int)0x800C0009, /// /// 0x800C000A /// No valid media /// HR_INET_E_NO_VALID_MEDIA = (int)0x800C000A, /// /// 0x800C000B /// The connection has timed out. /// HR_INET_E_CONNECTION_TIMEOUT = (int)0x800C000B, /// /// 0x800C000C /// Invalid request /// HR_INET_E_INVALID_REQUEST = (int)0x800C000C, /// /// 0x800C000D /// The requested protocol is unknown. /// HR_INET_E_UNKNOWN_PROTOCOL = (int)0x800C000D, /// /// 0x800C000E /// Security problem /// HR_INET_E_SECURITY_PROBLEM = (int)0x800C000E, /// /// 0x800C000F /// Cannot load data /// HR_INET_E_CANNOT_LOAD_DATA = (int)0x800C000F, /// /// 0x800C0010 /// Cannot instantiate object /// HR_INET_E_CANNOT_INSTANTIATE_OBJECT = (int)0x800C0010, /// /// 0x800C0019 /// The certificate presented for the request is invalid. /// HR_INET_E_INVALID_CERTIFICATE = (int)0x800C0019, /// /// 0x800C0014 /// The redirect to a different endpoint has failed. /// HR_INET_E_REDIRECT_FAILED = (int)0x800C0014, /// /// 0x800C0015 /// A resource request has been directed to a directory rather than an individual resource. /// HR_INET_E_REDIRECT_TO_DIR = (int)0x800C0015, /// /// 0x800704cf /// The network location cannot be reached. /// HR_ERROR_NETWORK_UNREACHABLE = (int)0x800704cf, /// /// 0x89235007 /// The network has not been initialized. /// HR_HC_NETWORK_NOT_INTIALIZED = (int)0x89235007 }; /// /// Category error type for XSAPI error codes. /// class xbox_services_error_code_category_impl : public std::error_category { public: /// /// Gets the error category name. /// /// A string identifying the category. inline const char* name() const _NOEXCEPT override { return "XBL"; } /// /// Converts an error value into a string that describes the error. /// /// A string that describes the error. inline std::string message(_In_ int errorCode) const _NOEXCEPT override; }; /// /// Category error type for XSAPI error conditions. /// class xbox_services_error_condition_category_impl : public std::error_category { public: /// /// Gets the error category name. /// /// A string identifying the category. inline const char* name() const _NOEXCEPT override { return "XBL CONDITION"; } /// /// Converts an error value into a string that describes the error. /// /// A string that describes the error. inline std::string message(_In_ int errorCode) const _NOEXCEPT override; /// /// Used to establish equivalence between arbitrary error_codes in /// the current category with arbitrary error_conditions. /// /// An error code. /// An error condition. /// True if the error code and the error condition are related; otherwise false. inline bool equivalent( _In_ const std::error_code& arbitraryErrorCode, _In_ int xboxLiveErrorCondition) const _NOEXCEPT override; }; /// /// Gets the one global instance of the error code category. /// /// An error category instance. const xbox_services_error_code_category_impl& xbox_services_error_code_category(); /// /// Gets the one global instance of the error condition category. /// /// An error category instance. const xbox_services_error_condition_category_impl& xbox_services_error_condition_category(); } }; #if !HC_PLATFORM_IS_MICROSOFT NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN inline std::error_code make_error_code(xbox::services::xbox_live_error_code e) { return std::error_code(static_cast(e), xbox::services::xbox_services_error_code_category()); } inline std::error_condition make_error_condition(xbox::services::xbox_live_error_condition e) { return std::error_condition(static_cast(e), xbox::services::xbox_services_error_condition_category()); } NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END #endif namespace std { inline std::error_code make_error_code(xbox::services::xbox_live_error_code e) { return std::error_code(static_cast(e), xbox::services::xbox_services_error_code_category()); } inline std::error_condition make_error_condition(xbox::services::xbox_live_error_condition e) { return std::error_condition(static_cast(e), xbox::services::xbox_services_error_condition_category()); } template <> struct is_error_code_enum : public true_type{}; template <> struct is_error_condition_enum : public true_type{}; } NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN template class xbox_live_result { public: xbox_live_result(); xbox_live_result(_In_ T payload); xbox_live_result(_In_ std::error_code errorCode, _In_ std::string errorMessage = std::string()); xbox_live_result(_In_ T payload, _In_ std::error_code errorCode, _In_ std::string errorMessage = std::string()); xbox_live_result(_In_ const xbox_live_result&); xbox_live_result& operator=(_In_ const xbox_live_result&); xbox_live_result(_In_ xbox_live_result&& other) noexcept; xbox_live_result& operator=(_In_ xbox_live_result&& other) noexcept; /// /// The payload of the result. It may be empty if there was an error /// T& payload(); /// /// The payload of the result. It may be empty if there was an error /// const T& payload() const; /// /// Sets the payload. /// void set_payload(T payload); /// /// The error returned by the operation. /// To test for and potentially react to in your code, use the values in xbox_error_condition /// To see the specific error value returned by the operation, use err_code().value() but testing for these values is discouraged. /// For more detail about std::error_code vs std::error_condition, see /// http://en.cppreference.com/w/cpp/error/error_condition /// const std::error_code& err() const; /// /// Sets the error. /// To test for and potentially react to in your code, use the values in xbox_error_condition /// To see the specific error value returned by the operation, use err_code().value() but testing for these values is discouraged. /// For more detail about std::error_code vs std::error_condition, see /// http://en.cppreference.com/w/cpp/error/error_condition /// void _Set_err(std::error_code errc); /// /// Returns call specific debug information. /// It is not localized, so only use for debugging purposes. /// const std::string& err_message() const; /// /// Sets error code debug information. /// It is not localized, so only use for debugging purposes. /// void _Set_err_message(std::string errMessage); private: T m_payload{}; std::error_code m_errorCode{}; std::string m_errorMessage; }; template<> class xbox_live_result { public: xbox_live_result() : m_errorMessage(std::string()) { m_errorCode = std::make_error_code(xbox_live_error_code::no_error); } xbox_live_result(_In_ std::error_code errorCode, _In_ std::string errorMessage = std::string()) : m_errorCode(std::move(errorCode)), m_errorMessage(std::move(errorMessage)) { } xbox_live_result(_In_ const xbox_live_result& other) { *this = other; } xbox_live_result& operator=(_In_ const xbox_live_result& other) { m_errorCode = other.m_errorCode; m_errorMessage = other.m_errorMessage; return *this; } xbox_live_result(_In_ xbox_live_result&& other) noexcept { *this = std::move(other); } xbox_live_result& operator=(_In_ xbox_live_result&& other) noexcept { if (this != &other) { m_errorCode = std::move(other.m_errorCode); m_errorMessage = std::move(other.m_errorMessage); } return *this; } /// /// The error returned by the operation. /// To test for and potentially react to in your code, use the values in xbox_error_condition /// To see the specific error value returned by the operation, use err_code().value() but testing for these values is discouraged. /// For more detail about std::error_code vs std::error_condition, see /// http://en.cppreference.com/w/cpp/error/error_condition /// const std::error_code& err() const { return m_errorCode; } /// /// Returns call specific debug information. /// It is not localized, so only use for debugging purposes. /// const std::string& err_message() const { return m_errorMessage; } /// /// Returns call specific debug information. /// It is not localized, so only use for debugging purposes. /// void _Set_err(std::error_code errCode) { m_errorCode = std::move(errCode); } /// /// Sets error code debug information. /// It is not localized, so only use for debugging purposes. /// void _Set_err_message(std::string errMessage) { m_errorMessage = std::move(errMessage); } private: std::error_code m_errorCode; std::string m_errorMessage; }; template xbox_live_result::xbox_live_result() : m_errorMessage(std::string()) { m_errorCode = std::make_error_code(xbox_live_error_code::no_error); } template xbox_live_result::xbox_live_result(_In_ T payload) : m_payload(std::move(payload)), m_errorMessage(std::string()) { m_errorCode = std::make_error_code(xbox_live_error_code::no_error); } template xbox_live_result::xbox_live_result( _In_ std::error_code errorCode, _In_ std::string errorMessage ) : m_errorCode(std::move(errorCode)), m_errorMessage(std::move(errorMessage)) { } template xbox_live_result::xbox_live_result( _In_ T payload, _In_ std::error_code errorCode, _In_ std::string errorMessage ) : m_payload(std::move(payload)), m_errorCode(std::move(errorCode)), m_errorMessage(std::move(errorMessage)) { } template T& xbox_live_result::payload() { return m_payload; } template const T& xbox_live_result::payload() const { return m_payload; } template void xbox_live_result::set_payload(T payload) { m_payload = std::move(payload); } template const std::error_code& xbox_live_result::err() const { return m_errorCode; } template void xbox_live_result::_Set_err(std::error_code errCode) { m_errorCode = std::move(errCode); } template const std::string& xbox_live_result::err_message() const { return m_errorMessage; } template void xbox_live_result::_Set_err_message(std::string errorMsg) { m_errorMessage = std::move(errorMsg); } template xbox_live_result::xbox_live_result( _In_ const xbox_live_result& other ) : m_payload{ other.m_payload }, m_errorCode{ other.m_errorCode }, m_errorMessage{ other.m_errorMessage } { } template xbox_live_result& xbox_live_result::operator=(_In_ const xbox_live_result& other) { m_payload = other.m_payload; m_errorCode = other.m_errorCode; m_errorMessage = other.m_errorMessage; return *this; } template xbox_live_result::xbox_live_result( _In_ xbox_live_result&& other ) noexcept : m_payload{ std::move(other.m_payload) }, m_errorCode{ other.m_errorCode }, m_errorMessage{ std::move(other.m_errorMessage) } { } template xbox_live_result& xbox_live_result::operator=(_In_ xbox_live_result&& other) noexcept { if (this != &other) { m_payload = std::move(other.m_payload); m_errorCode = std::move(other.m_errorCode); m_errorMessage = std::move(other.m_errorMessage); } return *this; } NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END #include "impl/errors.hpp" ================================================ FILE: Include/xsapi-cpp/events.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #ifdef XSAPI_EVENTS_SERVICE NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN class xbox_live_context; namespace events { /// /// Represents a service class that provides APIs that you can use to write in-game events. /// class events_service : public std::enable_shared_from_this { public: /// /// Write a simple in-game event without providing any data fields. /// /// Event name /// /// The name of the event must match the event name declared in the title's service configuration. /// The names are case insensitive. /// If the API writes an event with a name that does not match a name in the service configuration, the /// service drops the event with no notification. /// inline xbox_live_result write_in_game_event(_In_ const string_t& eventName); /// /// Write an in-game event that includes "dimensions" and "measurement" data fields. /// /// Dimensions include event fields with a finite number of defined numeric or string values. /// Examples of dimensions: map id, difficulty level, character or weapon class, game mode, boolean settings, etc. /// /// Measurements include event fields that represent scalar numeric metrics. /// Examples of measurements: score, time, counters, position, etc. /// /// Example: for an in-game event that tracks the highest match score for a particular difficulty level: /// The difficulty level should be included in dimensions, and the score should be included in measurements. /// /// Event name /// Dimensions data fields /// Measurement data fields /// /// The name of the event, and the names of the event fields (both dimensions and measurements), must match /// the names declared in the title's service configuration. The names are case insensitive. /// If the API writes an event with a name that does not match a name in the service configuration, the /// service drops the event with no notification. /// inline xbox_live_result write_in_game_event( _In_ const string_t& eventName, _In_ const web::json::value& dimensions, _In_ const web::json::value& measurement ); inline events_service(const events_service& other); inline events_service& operator=(events_service other); inline ~events_service(); private: inline events_service(_In_ XblContextHandle contextHandle); XblContextHandle m_xblContext; friend xbox_live_context; }; } NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END #include "impl/events.hpp" #endif ================================================ FILE: Include/xsapi-cpp/http_call.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "xsapi-c/http_call_c.h" #if HC_PLATFORM == HC_PLATFORM_ANDROID || HC_PLATFORM_IS_APPLE #pragma clang diagnostic ignored "-Woverloaded-virtual" #endif #if HC_PLATFORM == HC_PLATFORM_XDK NAMESPACE_MICROSOFT_XBOX_SERVICES_SYSTEM_BEGIN ref class XboxLiveUser; NAMESPACE_MICROSOFT_XBOX_SERVICES_SYSTEM_END #else NAMESPACE_MICROSOFT_XBOX_SERVICES_SYSTEM_CPP_BEGIN class xbox_live_user; NAMESPACE_MICROSOFT_XBOX_SERVICES_SYSTEM_CPP_END #endif NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN class xbox_live_context_settings; /// /// Enumerates the type of structured data contained in the http response body. /// enum class http_call_response_body_type { /// /// The response body consists of a string. /// string_body, /// /// The response body consists of a vector of bytes. /// vector_body, /// /// The response body consists of a JavaScript Object Notation (JSON) object. /// json_body }; // Forward declare enum class xbox_live_api; /// /// Represents an http response from the Xbox Live service. /// class http_call_response { public: #ifndef DEFAULT_MOVE_ENABLED inline http_call_response(http_call_response&& other); inline http_call_response& operator=(http_call_response&& other); #endif inline http_call_response( _In_ XblHttpCallHandle call, _In_ XblHttpCallResponseBodyType type ); /// /// Gets the body type of the response. /// inline http_call_response_body_type body_type() const; /// /// Gets the response body of the response as a string. /// inline string_t response_body_string(); /// /// Gets the response body of the response as a JSON value. /// inline web::json::value response_body_json(); /// /// Gets the response body of the response as a byte vector. /// inline std::vector response_body_vector(); /// /// Gets the http headers of the response. /// inline web::http::http_headers response_headers(); /// /// Gets the http status of the response. /// inline uint32_t http_status(); /// /// Gets the error code of the response. /// inline std::error_code err_code(); /// /// Gets the error message of the response. /// inline std::string err_message(); /// /// Gets the eTag of the response. /// inline string_t e_tag() const; /// /// Gets the response date of the response. /// inline string_t response_date() const; /// /// Gets the "retry after" value found in the response. /// inline std::chrono::seconds retry_after() const; inline http_call_response(const http_call_response&); inline http_call_response& operator=(http_call_response other); inline ~http_call_response(); private: XblHttpCallHandle m_handle; http_call_response_body_type m_bodyType; string_t m_responseBodyString; web::json::value m_responseBodyJson; std::vector m_responseBodyVector; web::http::http_headers m_responseHeaders; uint32_t m_httpStatus; std::error_code m_errorCode; std::string m_errMessage; inline http_call_response() = delete; }; class http_call { public: inline http_call( _In_ XblHttpCallHandle callHandle, _In_ string_t httpMethod, _In_ string_t serverName, _In_ web::uri pathQueryFragment ); #if !XSAPI_NO_PPL /// /// Attach the Xbox Live token, sign the request, send the request to the service, and return the response. /// inline pplx::task> get_response_with_auth( _In_ http_call_response_body_type httpCallResponseBodyType = http_call_response_body_type::json_body ); inline pplx::task> get_response_with_auth( _In_ XalUserHandle user, _In_ http_call_response_body_type httpCallResponseBodyType = http_call_response_body_type::json_body, _In_ bool allUsersAuthRequired = false ); /// /// Send the request without authentication and get the response. /// inline pplx::task< std::shared_ptr > get_response( _In_ http_call_response_body_type httpCallResponseBodyType ); #endif // !XSAPI_NO_PPL /// /// Sets the request body using a string. /// inline void set_request_body(_In_ const string_t& value); /// /// Sets the request body using a JSON value. /// inline void set_request_body(_In_ const web::json::value& value); /// /// Sets the request body using a byte array value. /// inline void set_request_body(_In_ const std::vector& value); /// /// Sets a custom header. /// inline void set_custom_header( _In_ const string_t& headerName, _In_ const string_t& headerValue); /// /// Sets if this is a long http call, and should use the long_http_timeout setting. /// inline void set_long_http_call(_In_ bool value); /// /// Gets if this is a long http call, and should use the long_http_timeout setting. /// inline bool long_http_call() const; /// /// Sets if retry is allowed during this call. /// inline void set_retry_allowed(_In_ bool value); /// /// Get if retry is allowed during this call. /// inline bool retry_allowed() const; /// /// Sets the content type header value for this call. /// inline void set_content_type_header_value(_In_ const string_t& value); /// /// Gets the content type header value for this call. /// inline string_t content_type_header_value() const; /// /// Sets the Xbox Live contract version header value for this call. /// inline void set_xbox_contract_version_header_value(_In_ const string_t& value); /// /// Gets the Xbox Live contract version header value for this call. /// inline string_t xbox_contract_version_header_value() const; /// /// Gets the server name for this call. /// inline string_t server_name() const; /// /// Gets the path for this call. /// inline const web::uri& path_query_fragment() const; /// /// Gets the http method for this call. /// inline string_t http_method() const; /// /// Sets a flag indicating if default headers should be added or not. /// inline void set_add_default_headers(_In_ bool value); /// /// Internal function /// inline bool add_default_headers() const; inline ~http_call(); private: XblHttpCallHandle m_callHandle; string_t m_httpMethod; string_t m_serverName; web::uri m_pathQueryFragment; bool m_longHttpCall; bool m_retryAllowed; }; inline std::shared_ptr create_xbox_live_http_call( _In_ const std::shared_ptr& xboxLiveContextSettings, _In_ const string_t& httpMethod, _In_ const string_t& serverName, _In_ const web::uri& pathQueryFragment ); NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END #include "impl/http_call.hpp" ================================================ FILE: Include/xsapi-cpp/http_call_request_message.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN /// /// Enumerates the type of data contained in the http request body. /// enum http_request_message_type { /// /// No message. /// empty_message, /// /// The message is of type string. /// string_message, /// /// The message is of type vector, and acts as a memory buffer. /// vector_message }; /// /// Represents an http request message. /// class http_call_request_message { public: /// /// Internal function /// inline http_call_request_message(); /// /// The http request message if it is a string type. /// inline const string_t& request_message_string() const; /// /// The http request message if it is a buffer. /// inline const std::vector& request_message_vector() const; /// /// The type of message. /// inline http_request_message_type get_http_request_message_type() const; private: std::vector m_requestMessageVector; string_t m_requestMessageString; http_request_message_type m_httpRequestMessageType; }; NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END #include "impl/http_call_request_message.hpp" ================================================ FILE: Include/xsapi-cpp/impl/achievements.hpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. NAMESPACE_MICROSOFT_XBOX_SERVICES_ACHIEVEMENTS_CPP_BEGIN achievement_title_association::achievement_title_association( const XblAchievementTitleAssociation& association ) : m_titleId{ association.titleId } { m_name = Utils::StringTFromUtf8(association.name); } const string_t& achievement_title_association::name() const { return m_name; } uint32_t achievement_title_association::title_id() const { return m_titleId; } achievement_requirement::achievement_requirement( const XblAchievementRequirement& requirement ) { m_id = Utils::StringTFromUtf8(requirement.id); m_currentProgressValue = Utils::StringTFromUtf8(requirement.currentProgressValue); m_targetProgressValue = Utils::StringTFromUtf8(requirement.targetProgressValue); } const string_t& achievement_requirement::id() const { return m_id; } const string_t& achievement_requirement::current_progress_value() const { return m_currentProgressValue; } const string_t& achievement_requirement::target_progress_value() const { return m_targetProgressValue; } achievement_progression::achievement_progression( const XblAchievementProgression& progression ) { for (size_t i = 0; i < progression.requirementsCount; ++i) { m_requirements.push_back(achievement_requirement{ progression.requirements[i] }); } m_timeUnlocked = Utils::DatetimeFromTimeT(progression.timeUnlocked); } const std::vector& achievement_progression::requirements() const { return m_requirements; } const utility::datetime& achievement_progression::time_unlocked() const { return m_timeUnlocked; } achievement_time_window::achievement_time_window( const XblAchievementTimeWindow& timeWindow ) : m_startDate{ Utils::DatetimeFromTimeT(timeWindow.startDate) }, m_endDate{ Utils::DatetimeFromTimeT(timeWindow.endDate) } { } const utility::datetime& achievement_time_window::start_date() const { return m_startDate; } const utility::datetime& achievement_time_window::end_date() const { return m_endDate; } achievement_media_asset::achievement_media_asset( const XblAchievementMediaAsset* mediaAsset ) { if (mediaAsset) { m_type = static_cast(mediaAsset->mediaAssetType); if (mediaAsset->name) { m_name = Utils::StringTFromUtf8(mediaAsset->name); } if (mediaAsset->url) { m_url = Utils::StringTFromUtf8(mediaAsset->url); } } } const string_t& achievement_media_asset::name() const { return m_name; } achievement_media_asset_type achievement_media_asset::media_asset_type() const { return m_type; } const web::uri& achievement_media_asset::url() const { return m_url; } achievement_reward::achievement_reward( const XblAchievementReward& reward ) : m_name{ Utils::StringTFromUtf8(reward.name) }, m_description{ Utils::StringTFromUtf8(reward.description) }, m_value{ Utils::StringTFromUtf8(reward.value) }, m_rewardType{ static_cast(reward.rewardType) }, m_valueType{ Utils::StringTFromUtf8(reward.valueType) }, m_mediaAsset{ reward.mediaAsset } { } const string_t& achievement_reward::name() const { return m_name; } const string_t& achievement_reward::description() const { return m_description; } const string_t& achievement_reward::value() const { return m_value; } achievement_reward_type achievement_reward::reward_type() const { return m_rewardType; } const string_t& achievement_reward::value_type() const { return m_valueType; } const achievement_media_asset& achievement_reward::media_asset() const { return m_mediaAsset; } achievement::achievement( XblAchievementsResultHandle handle, const XblAchievement* achievement ) : m_achievement{ achievement } { XblAchievementsResultDuplicateHandle(handle, &m_handle); } achievement::achievement(const achievement& other) : m_achievement{ other.m_achievement } { XblAchievementsResultDuplicateHandle(other.m_handle, &m_handle); } achievement& achievement::operator=(achievement other) { std::swap(m_handle, other.m_handle); m_achievement = other.m_achievement; return *this; } achievement::~achievement() { XblAchievementsResultCloseHandle(m_handle); } string_t achievement::id() const { return Utils::StringTFromUtf8(m_achievement->id); } string_t achievement::service_configuration_id() const { return Utils::StringTFromUtf8(m_achievement->serviceConfigurationId); } string_t achievement::name() const { return Utils::StringTFromUtf8(m_achievement->name); } std::vector achievement::title_associations() const { std::vector titleAssociations; for (size_t i = 0; i < m_achievement->titleAssociationsCount; ++i) { titleAssociations.push_back(achievement_title_association{ m_achievement->titleAssociations[i] }); } return titleAssociations; } achievement_progress_state achievement::progress_state() const { return static_cast(m_achievement->progressState); } achievement_progression achievement::progression() const { return achievement_progression{ m_achievement->progression }; } std::vector achievement::media_assets() const { std::vector mediaAssets; for (size_t i = 0; i < m_achievement->mediaAssetsCount; ++i) { mediaAssets.push_back(achievement_media_asset{ &m_achievement->mediaAssets[i] }); } return mediaAssets; } std::vector achievement::platforms_available_on() const { return Utils::StringTVectorFromCStringArray(m_achievement->platformsAvailableOn, m_achievement->platformsAvailableOnCount); } bool achievement::is_secret() const { return m_achievement->isSecret; } string_t achievement::unlocked_description() const { return Utils::StringTFromUtf8(m_achievement->unlockedDescription); } string_t achievement::locked_description() const { return Utils::StringTFromUtf8(m_achievement->lockedDescription); } string_t achievement::product_id() const { return Utils::StringTFromUtf8(m_achievement->productId); } achievement_type achievement::type() const { return static_cast(m_achievement->type); } achievement_participation_type achievement::participation_type() const { return static_cast(m_achievement->participationType); } achievement_time_window achievement::available() const { return achievement_time_window{ m_achievement->available }; } std::vector achievement::rewards() const { std::vector rewards; for (size_t i = 0; i < m_achievement->rewardsCount; ++i) { rewards.push_back(achievement_reward{ m_achievement->rewards[i] }); } return rewards; } std::chrono::seconds achievement::estimated_unlock_time() const { return std::chrono::seconds{ m_achievement->estimatedUnlockTime }; } string_t achievement::deep_link() const { return Utils::StringTFromUtf8(m_achievement->deepLink); } bool achievement::is_revoked() const { return m_achievement->isRevoked; } achievements_result::achievements_result(XblAchievementsResultHandle handle) { if (handle != nullptr) { XblAchievementsResultDuplicateHandle(handle, &m_handle); } } achievements_result::achievements_result(const achievements_result& other) { if (other.m_handle != nullptr) { XblAchievementsResultDuplicateHandle(other.m_handle, &m_handle); } } achievements_result& achievements_result::operator=(achievements_result other) { std::swap(m_handle, other.m_handle); return *this; } achievements_result::~achievements_result() { XblAchievementsResultCloseHandle(m_handle); } std::vector achievements_result::items() const { std::vector achievementsVector; const XblAchievement* achievements; size_t achievementsCount; HRESULT hr = XblAchievementsResultGetAchievements(m_handle, &achievements, &achievementsCount); if (SUCCEEDED(hr)) { for (size_t i = 0; i < achievementsCount; ++i) { achievementsVector.push_back(achievement{ m_handle, achievements + i }); } } return achievementsVector; } bool achievements_result::has_next() const { bool hasNext{ false }; XblAchievementsResultHasNext(m_handle, &hasNext); return hasNext; } pplx::task> achievements_result::get_next( _In_ uint32_t maxItems ) { auto asyncWrapper = new AsyncWrapper( [](XAsyncBlock* async, achievements_result& result) { XblAchievementsResultHandle resultHandle; auto hr = XblAchievementsResultGetNextResult(async, &resultHandle); if (SUCCEEDED(hr)) { result = achievements_result{ resultHandle }; XblAchievementsResultCloseHandle(resultHandle); } return hr; }); auto hr = XblAchievementsResultGetNextAsync(m_handle, maxItems, &asyncWrapper->async); return asyncWrapper->Task(hr); } achievement_service::achievement_service(XblContextHandle xblContextHandle) { XblContextDuplicateHandle(xblContextHandle, &m_xblContextHandle); } achievement_service::achievement_service(const achievement_service& other) { XblContextDuplicateHandle(other.m_xblContextHandle, &m_xblContextHandle); } achievement_service& achievement_service::operator=(achievement_service other) { std::swap(m_xblContextHandle, other.m_xblContextHandle); return *this; } achievement_service::~achievement_service() { XblContextCloseHandle(m_xblContextHandle); } pplx::task> achievement_service::update_achievement( _In_ const string_t& xboxUserId, _In_ const string_t& achievementId, _In_ uint32_t percentComplete ) { auto xblContext = m_xblContextHandle; auto asyncWrapper = new AsyncWrapper(); HRESULT hr = XblAchievementsUpdateAchievementAsync( xblContext, Utils::Uint64FromStringT(xboxUserId), Utils::StringFromStringT(achievementId).c_str(), percentComplete, &asyncWrapper->async ); return asyncWrapper->Task(hr); } pplx::task> achievement_service::update_achievement( _In_ const string_t& xboxUserId, _In_ uint32_t titleId, _In_ const string_t& serviceConfigurationId, _In_ const string_t& achievementId, _In_ uint32_t percentComplete ) { auto xblContext = m_xblContextHandle; auto asyncWrapper = new AsyncWrapper(); HRESULT hr = XblAchievementsUpdateAchievementForTitleIdAsync( xblContext, Utils::Uint64FromStringT(xboxUserId), titleId, Utils::StringFromStringT(serviceConfigurationId).c_str(), Utils::StringFromStringT(achievementId).c_str(), percentComplete, &asyncWrapper->async ); return asyncWrapper->Task(hr); } pplx::task> achievement_service::get_achievements_for_title_id( _In_ const string_t& xboxUserId, _In_ uint32_t titleId, _In_ achievement_type type, _In_ bool unlockedOnly, _In_ achievement_order_by orderBy, _In_ uint32_t skipItems, _In_ uint32_t maxItems ) { auto xblContext = m_xblContextHandle; auto asyncWrapper = new AsyncWrapper( [](XAsyncBlock* async, achievements_result& result) { XblAchievementsResultHandle resultHandle; HRESULT hr = XblAchievementsGetAchievementsForTitleIdResult(async, &resultHandle); if (SUCCEEDED(hr)) { result = achievements_result(resultHandle); } return hr; }); HRESULT hr = XblAchievementsGetAchievementsForTitleIdAsync( xblContext, Utils::Uint64FromStringT(xboxUserId), titleId, static_cast(type), unlockedOnly, static_cast(orderBy), skipItems, maxItems, &asyncWrapper->async ); return asyncWrapper->Task(hr); } pplx::task> achievement_service::get_achievement( _In_ const string_t& xboxUserId, _In_ const string_t& serviceConfigurationId, _In_ const string_t& achievementId ) { auto xblContext = m_xblContextHandle; auto asyncWrapper = new AsyncWrapper( [](XAsyncBlock* async, achievement result) { XblAchievementsResultHandle resultHandle; HRESULT hr = XblAchievementsGetAchievementResult(async, &resultHandle); if (SUCCEEDED(hr)) { const XblAchievement* achievements = nullptr; size_t achievementsCount = 0; hr = XblAchievementsResultGetAchievements(resultHandle, &achievements, &achievementsCount); if (SUCCEEDED(hr)) { result = achievement(resultHandle, achievements); } } return hr; }); HRESULT hr = XblAchievementsGetAchievementAsync( xblContext, Utils::Uint64FromStringT(xboxUserId), Utils::StringFromStringT(serviceConfigurationId).c_str(), Utils::StringFromStringT(achievementId).c_str(), &asyncWrapper->async ); return asyncWrapper->Task(hr); } NAMESPACE_MICROSOFT_XBOX_SERVICES_ACHIEVEMENTS_CPP_END ================================================ FILE: Include/xsapi-cpp/impl/errors.hpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN static xbox_services_error_code_category_impl s_error_code_category_instance; inline const xbox_services_error_code_category_impl& xbox_services_error_code_category() { return s_error_code_category_instance; } static xbox_services_error_condition_category_impl s_error_condition_category_instance; inline const xbox_services_error_condition_category_impl& xbox_services_error_condition_category() { return s_error_condition_category_instance; } inline std::string xbox_services_error_code_category_impl::message(_In_ int errorCode) const _NOEXCEPT { xbox_live_error_code code = static_cast(errorCode); switch (code) { case xbox_live_error_code::no_error: return "No error"; case xbox_live_error_code::bad_alloc: return "bad_alloc"; case xbox_live_error_code::bad_cast: return "bad_cast"; case xbox_live_error_code::invalid_argument: return "invalid_argument"; case xbox_live_error_code::out_of_range: return "out_of_range"; case xbox_live_error_code::length_error: return "length_error"; case xbox_live_error_code::range_error: return "range_error"; case xbox_live_error_code::logic_error: return "logic_error"; case xbox_live_error_code::runtime_error: return "runtime_error"; case xbox_live_error_code::json_error: return "json_error"; case xbox_live_error_code::websocket_error: return "websocket_error"; case xbox_live_error_code::uri_error: return "uri_error"; case xbox_live_error_code::generic_error: return "generic_error"; case xbox_live_error_code::rta_generic_error: return "rta_generic_error"; case xbox_live_error_code::rta_access_denied: return "rta_access_denied"; case xbox_live_error_code::rta_subscription_limit_reached: return "rta_subscription_limit_reached"; case xbox_live_error_code::auth_user_interaction_required: return "auth_user_interaction_required"; case xbox_live_error_code::auth_user_switched: return "auth_user_switched"; case xbox_live_error_code::auth_user_cancel: return "auth_user_cancel"; case xbox_live_error_code::auth_unknown_error: return "auth_unknown_error"; case xbox_live_error_code::auth_user_not_signed_in: return "auth_user_not_signed_in"; case xbox_live_error_code::auth_runtime_error: return "auth_runtime_error"; case xbox_live_error_code::auth_no_token_error: return "auth_no_token_error"; case xbox_live_error_code::invalid_config: return "invalid_config"; case xbox_live_error_code::unsupported: return "unsupported"; case xbox_live_error_code::HR_ERROR_INTERNET_TIMEOUT: return "ERROR_INTERNET_TIMEOUT"; case xbox_live_error_code::AM_E_XASD_UNEXPECTED: return "AM_E_XASD_UNEXPECTED"; case xbox_live_error_code::AM_E_XASU_UNEXPECTED: return "AM_E_XASU_UNEXPECTED"; case xbox_live_error_code::AM_E_XAST_UNEXPECTED: return "AM_E_XAST_UNEXPECTED"; case xbox_live_error_code::AM_E_XSTS_UNEXPECTED: return "AM_E_XSTS_UNEXPECTED"; case xbox_live_error_code::AM_E_XDEVICE_UNEXPECTED: return "AM_E_XDEVICE_UNEXPECTED"; case xbox_live_error_code::AM_E_DEVMODE_NOT_AUTHORIZED: return "AM_E_DEVMODE_NOT_AUTHORIZED"; case xbox_live_error_code::AM_E_NOT_AUTHORIZED: return "AM_E_NOT_AUTHORIZED"; case xbox_live_error_code::AM_E_FORBIDDEN: return "AM_E_FORBIDDEN"; case xbox_live_error_code::AM_E_UNKNOWN_TARGET: return "AM_E_UNKNOWN_TARGET"; case xbox_live_error_code::AM_E_INVALID_NSAL_DATA: return "AM_E_INVALID_NSAL_DATA"; case xbox_live_error_code::AM_E_TITLE_NOT_AUTHENTICATED: return "AM_E_TITLE_NOT_AUTHENTICATED"; case xbox_live_error_code::AM_E_TITLE_NOT_AUTHORIZED: return "AM_E_TITLE_NOT_AUTHORIZED"; case xbox_live_error_code::AM_E_USER_HASH_MISSING: return "AM_E_USER_HASH_MISSING"; case xbox_live_error_code::AM_E_USER_NOT_FOUND: return "AM_E_USER_NOT_FOUND"; case xbox_live_error_code::AM_E_INVALID_ENVIRONMENT: return "AM_E_INVALID_ENVIRONMENT"; case xbox_live_error_code::AM_E_XASD_TIMEOUT: return "AM_E_XASD_TIMEOUT"; case xbox_live_error_code::AM_E_XASU_TIMEOUT: return "AM_E_XASU_TIMEOUT"; case xbox_live_error_code::AM_E_XAST_TIMEOUT: return "AM_E_XAST_TIMEOUT"; case xbox_live_error_code::AM_E_XSTS_TIMEOUT: return "AM_E_XSTS_TIMEOUT"; case xbox_live_error_code::AM_E_LIVE_CONNECTION_REQUIRED: return "AM_E_LIVE_CONNECTION_REQUIRED"; case xbox_live_error_code::AM_E_NO_NETWORK: return "AM_E_NO_NETWORK"; case xbox_live_error_code::AM_E_XTITLE_UNEXPECTED: return "AM_E_XTITLE_UNEXPECTED"; case xbox_live_error_code::AM_E_NO_TOKEN_REQUIRED: return "AM_E_NO_TOKEN_REQUIRED"; case xbox_live_error_code::AM_E_XTITLE_TIMEOUT: return "AM_E_XTITLE_TIMEOUT"; case xbox_live_error_code::XO_E_DEVMODE_NOT_AUTHORIZED: return "XO_E_DEVMODE_NOT_AUTHORIZED"; case xbox_live_error_code::XO_E_SYSTEM_UPDATE_REQUIRED: return "XO_E_SYSTEM_UPDATE_REQUIRED"; case xbox_live_error_code::XO_E_CONTENT_UPDATE_REQUIRED: return "XO_E_CONTENT_UPDATE_REQUIRED"; case xbox_live_error_code::XO_E_ENFORCEMENT_BAN: return "XO_E_ENFORCEMENT_BAN"; case xbox_live_error_code::XO_E_THIRD_PARTY_BAN: return "XO_E_THIRD_PARTY_BAN"; case xbox_live_error_code::XO_E_ACCOUNT_PARENTALLY_RESTRICTED: return "XO_E_ACCOUNT_PARENTALLY_RESTRICTED"; case xbox_live_error_code::XO_E_ACCOUNT_BILLING_MAINTENANCE_REQUIRED: return "XO_E_ACCOUNT_BILLING_MAINTENANCE_REQUIRED"; case xbox_live_error_code::XO_E_ACCOUNT_TERMS_OF_USE_NOT_ACCEPTED: return "XO_E_ACCOUNT_TERMS_OF_USE_NOT_ACCEPTED"; case xbox_live_error_code::XO_E_ACCOUNT_COUNTRY_NOT_AUTHORIZED: return "XO_E_ACCOUNT_COUNTRY_NOT_AUTHORIZED"; case xbox_live_error_code::XO_E_ACCOUNT_AGE_VERIFICATION_REQUIRED: return "XO_E_ACCOUNT_AGE_VERIFICATION_REQUIRED"; case xbox_live_error_code::XO_E_ACCOUNT_CURFEW: return "XO_E_ACCOUNT_CURFEW"; case xbox_live_error_code::XO_E_ACCOUNT_CHILD_NOT_IN_FAMILY: return "XO_E_ACCOUNT_CHILD_NOT_IN_FAMILY"; case xbox_live_error_code::XO_E_ACCOUNT_CSV_TRANSITION_REQUIRED: return "XO_E_ACCOUNT_CSV_TRANSITION_REQUIRED"; case xbox_live_error_code::XO_E_ACCOUNT_MAINTENANCE_REQUIRED: return "XO_E_ACCOUNT_MAINTENANCE_REQUIRED"; case xbox_live_error_code::XO_E_ACCOUNT_TYPE_NOT_ALLOWED: return "XO_E_ACCOUNT_TYPE_NOT_ALLOWED"; case xbox_live_error_code::XO_E_CONTENT_ISOLATION: return "XO_E_CONTENT_ISOLATION"; case xbox_live_error_code::XO_E_ACCOUNT_NAME_CHANGE_REQUIRED: return "XO_E_ACCOUNT_NAME_CHANGE_REQUIRED"; case xbox_live_error_code::XO_E_DEVICE_CHALLENGE_REQUIRED: return "XO_E_DEVICE_CHALLENGE_REQUIRED"; case xbox_live_error_code::XO_E_SIGNIN_COUNT_BY_DEVICE_TYPE_EXCEEDED: return "XO_E_SIGNIN_COUNT_BY_DEVICE_TYPE_EXCEEDED"; case xbox_live_error_code::XO_E_PIN_CHALLENGE_REQUIRED: return "XO_E_PIN_CHALLENGE_REQUIRED"; case xbox_live_error_code::XO_E_RETAIL_ACCOUNT_NOT_ALLOWED: return "XO_E_RETAIL_ACCOUNT_NOT_ALLOWED"; case xbox_live_error_code::XO_E_SANDBOX_NOT_ALLOWED: return "XO_E_SANDBOX_NOT_ALLOWED"; case xbox_live_error_code::XO_E_ACCOUNT_SERVICE_UNAVAILABLE_UNKNOWN_USER: return "XO_E_ACCOUNT_SERVICE_UNAVAILABLE_UNKNOWN_USER"; case xbox_live_error_code::XO_E_GREEN_SIGNED_CONTENT_NOT_AUTHORIZED: return "XO_E_GREEN_SIGNED_CONTENT_NOT_AUTHORIZED"; case xbox_live_error_code::XO_E_CONTENT_NOT_AUTHORIZED: return "XO_E_CONTENT_NOT_AUTHORIZED"; case xbox_live_error_code::http_status_204_resource_data_not_found: return "204 - ResourceDataNotFound"; case xbox_live_error_code::http_status_400_bad_request: return "400 - BadRequest"; case xbox_live_error_code::http_status_401_unauthorized: return "401 - Unauthorized"; case xbox_live_error_code::http_status_403_forbidden: return "403 - Forbidden"; case xbox_live_error_code::http_status_404_not_found: return "404 - NotFound"; case xbox_live_error_code::http_status_405_method_not_allowed: return "405 - MethodNotAllowed"; case xbox_live_error_code::http_status_406_not_acceptable: return "406 - NotAcceptable"; case xbox_live_error_code::http_status_407_proxy_authentication_required: return "407 - ProxyAuthenticationRequired"; case xbox_live_error_code::http_status_408_request_timeout: return "408 - RequestTimeout"; case xbox_live_error_code::http_status_409_conflict: return "409 - Conflict"; case xbox_live_error_code::http_status_410_gone: return "410 - Gone"; case xbox_live_error_code::http_status_411_length_required: return "411 - LengthRequired"; case xbox_live_error_code::http_status_412_precondition_failed: return "412 - PreconditionFailed"; case xbox_live_error_code::http_status_413_request_entity_too_large: return "413 - RequestEntityTooLarge"; case xbox_live_error_code::http_status_414_request_uri_too_long: return "414 - RequestUriTooLong"; case xbox_live_error_code::http_status_415_unsupported_media_type: return "415 - UnsupportedMediaType"; case xbox_live_error_code::http_status_416_requested_range_not_satisfiable: return "416 - RequestedRangeNotSatisfiable"; case xbox_live_error_code::http_status_417_expectation_failed: return "417 - Expectation Failed"; case xbox_live_error_code::http_status_421_misdirected_request: return "421 - Misdirected Request"; case xbox_live_error_code::http_status_422_unprocessable_entity: return "422 - Unprocessable Entity"; case xbox_live_error_code::http_status_423_locked: return "423 - Locked"; case xbox_live_error_code::http_status_424_failed_dependency: return "424 - Failed Dependency"; case xbox_live_error_code::http_status_426_upgrade_required: return "426 - Upgrade Required"; case xbox_live_error_code::http_status_428_precondition_required: return "428 - Precondition Required"; case xbox_live_error_code::http_status_429_too_many_requests: return "429 - TooManyRequests"; case xbox_live_error_code::http_status_431_request_header_fields_too_large: return "431 - Request Header Fields Too Large"; case xbox_live_error_code::http_status_451_unavailable_for_legal_reasons: return "451 - Unavailable For Legal Reasons"; case xbox_live_error_code::http_status_500_internal_server_error: return "500 - InternalServerError"; case xbox_live_error_code::http_status_501_not_implemented: return "501 - Not Implemented"; case xbox_live_error_code::http_status_502_bad_gateway: return "502 - Bad Gateway"; case xbox_live_error_code::http_status_503_service_unavailable: return "503 - ServiceUnavailable"; case xbox_live_error_code::http_status_504_gateway_timeout: return "504 - GatewayTimeout"; case xbox_live_error_code::http_status_505_http_version_not_supported: return "505 - HttpVersionNotSupported"; case xbox_live_error_code::http_status_506_variant_also_negotiates: return "506 - Variant Also Negotiates"; case xbox_live_error_code::http_status_507_insufficient_storage: return "507 - Insufficient Storage"; case xbox_live_error_code::http_status_508_loop_detected: return "508 - Loop Detected"; case xbox_live_error_code::http_status_510_not_extended: return "510 - Not Extended"; case xbox_live_error_code::http_status_511_network_authentication_required: return "511 - Network Authentication Required"; case xbox_live_error_code::HR_INET_E_INVALID_URL: return "INET_E_INVALID_URL"; case xbox_live_error_code::HR_INET_E_NO_SESSION: return "INET_E_NO_SESSION"; case xbox_live_error_code::HR_INET_E_CANNOT_CONNECT: return "INET_E_CANNOT_CONNECT"; case xbox_live_error_code::HR_INET_E_RESOURCE_NOT_FOUND: return "INET_E_RESOURCE_NOT_FOUND"; case xbox_live_error_code::HR_INET_E_OBJECT_NOT_FOUND: return "INET_E_OBJECT_NOT_FOUND"; case xbox_live_error_code::HR_INET_E_DATA_NOT_AVAILABLE: return "INET_E_DATA_NOT_AVAILABLE"; case xbox_live_error_code::HR_INET_E_DOWNLOAD_FAILURE: return "INET_E_DOWNLOAD_FAILURE"; case xbox_live_error_code::HR_INET_E_AUTHENTICATION_REQUIRED: return "INET_E_AUTHENTICATION_REQUIRED"; case xbox_live_error_code::HR_INET_E_NO_VALID_MEDIA: return "INET_E_NO_VALID_MEDIA"; case xbox_live_error_code::HR_INET_E_CONNECTION_TIMEOUT: return "INET_E_CONNECTION_TIMEOUT"; case xbox_live_error_code::HR_INET_E_INVALID_REQUEST: return "INET_E_INVALID_REQUEST"; case xbox_live_error_code::HR_INET_E_UNKNOWN_PROTOCOL: return "INET_E_UNKNOWN_PROTOCOL"; case xbox_live_error_code::HR_INET_E_SECURITY_PROBLEM: return "INET_E_SECURITY_PROBLEM"; case xbox_live_error_code::HR_INET_E_CANNOT_LOAD_DATA: return "INET_E_CANNOT_LOAD_DATA"; case xbox_live_error_code::HR_INET_E_CANNOT_INSTANTIATE_OBJECT: return "INET_E_CANNOT_INSTANTIATE_OBJECT"; case xbox_live_error_code::HR_INET_E_INVALID_CERTIFICATE: return "INET_E_INVALID_CERTIFICATE"; case xbox_live_error_code::HR_INET_E_REDIRECT_FAILED: return "INET_E_REDIRECT_FAILED"; case xbox_live_error_code::HR_INET_E_REDIRECT_TO_DIR: return "INET_E_REDIRECT_TO_DIR"; case xbox_live_error_code::HR_ERROR_NETWORK_UNREACHABLE: return "ERROR_NETWORK_UNREACHABLE"; default: { std::stringstream msg; msg << "Unknown error: 0x" << std::hex << errorCode; return msg.str(); } } } inline std::string xbox_services_error_condition_category_impl::message(_In_ int errorCode) const _NOEXCEPT { xbox_live_error_condition code = static_cast(errorCode); switch (code) { case xbox_live_error_condition::no_error: return "No error"; case xbox_live_error_condition::generic_error: return "Generic Error"; case xbox_live_error_condition::generic_out_of_range: return "Out of Range"; case xbox_live_error_condition::auth: return "Authorization Error"; case xbox_live_error_condition::http: return "HTTP"; case xbox_live_error_condition::http_404_not_found: return "404 - Not Found"; case xbox_live_error_condition::http_412_precondition_failed: return "412 - PreconditionFailed"; case xbox_live_error_condition::http_429_too_many_requests: return "429- Too Many Requests"; case xbox_live_error_condition::http_service_timeout: return "Service Timeout"; case xbox_live_error_condition::network: return "Network Error"; case xbox_live_error_condition::rta: return "Real Time Activity"; default: { std::stringstream msg; msg << "Unknown error: 0x" << std::hex << errorCode; return msg.str(); } } } inline bool xbox_services_error_condition_category_impl::equivalent( _In_ const std::error_code& arbitraryErrorCode, _In_ int xboxLiveErrorCondition) const _NOEXCEPT { if (arbitraryErrorCode.category() != xbox_services_error_code_category()) { return false; } xbox_live_error_code code = static_cast(arbitraryErrorCode.value()); xbox_live_error_condition condition = static_cast(xboxLiveErrorCondition); // range 0x80860000 - 0x8086FFFF is reserved for other OnlineId error code // put is under auth_msa error condition if ((arbitraryErrorCode.value() & 0x80860000) == 0x80860000) { return condition == xbox_live_error_condition::auth; } switch (code) { case xbox_live_error_code::no_error: return (condition == xbox_live_error_condition::no_error); case xbox_live_error_code::http_status_404_not_found: return (condition == xbox_live_error_condition::http_404_not_found || condition == xbox_live_error_condition::http); case xbox_live_error_code::http_status_412_precondition_failed: return (condition == xbox_live_error_condition::http_412_precondition_failed || condition == xbox_live_error_condition::http); case xbox_live_error_code::http_status_408_request_timeout: case xbox_live_error_code::http_status_503_service_unavailable: case xbox_live_error_code::http_status_504_gateway_timeout: return (condition == xbox_live_error_condition::http_service_timeout || condition == xbox_live_error_condition::http); case xbox_live_error_code::http_status_429_too_many_requests: return (condition == xbox_live_error_condition::http_429_too_many_requests || condition == xbox_live_error_condition::http); case xbox_live_error_code::http_status_500_internal_server_error: return (condition == xbox_live_error_condition::http); case xbox_live_error_code::http_status_204_resource_data_not_found: case xbox_live_error_code::http_status_400_bad_request: case xbox_live_error_code::http_status_401_unauthorized: case xbox_live_error_code::http_status_402_payment_required: case xbox_live_error_code::http_status_403_forbidden: case xbox_live_error_code::http_status_405_method_not_allowed: case xbox_live_error_code::http_status_406_not_acceptable: case xbox_live_error_code::http_status_407_proxy_authentication_required: case xbox_live_error_code::http_status_409_conflict: case xbox_live_error_code::http_status_410_gone: case xbox_live_error_code::http_status_411_length_required: case xbox_live_error_code::http_status_413_request_entity_too_large: case xbox_live_error_code::http_status_414_request_uri_too_long: case xbox_live_error_code::http_status_415_unsupported_media_type: case xbox_live_error_code::http_status_416_requested_range_not_satisfiable: case xbox_live_error_code::http_status_417_expectation_failed: case xbox_live_error_code::http_status_421_misdirected_request: case xbox_live_error_code::http_status_422_unprocessable_entity: case xbox_live_error_code::http_status_423_locked: case xbox_live_error_code::http_status_424_failed_dependency: case xbox_live_error_code::http_status_426_upgrade_required: case xbox_live_error_code::http_status_428_precondition_required: case xbox_live_error_code::http_status_431_request_header_fields_too_large: case xbox_live_error_code::http_status_449_retry_with: case xbox_live_error_code::http_status_451_unavailable_for_legal_reasons: case xbox_live_error_code::http_status_501_not_implemented: case xbox_live_error_code::http_status_502_bad_gateway: case xbox_live_error_code::http_status_505_http_version_not_supported: case xbox_live_error_code::http_status_506_variant_also_negotiates: case xbox_live_error_code::http_status_507_insufficient_storage: case xbox_live_error_code::http_status_508_loop_detected: case xbox_live_error_code::http_status_510_not_extended: case xbox_live_error_code::http_status_511_network_authentication_required: return (condition == xbox_live_error_condition::http); case xbox_live_error_code::auth_user_interaction_required: case xbox_live_error_code::auth_user_switched: case xbox_live_error_code::auth_unknown_error: case xbox_live_error_code::auth_user_cancel: case xbox_live_error_code::auth_user_not_signed_in: case xbox_live_error_code::auth_runtime_error: case xbox_live_error_code::auth_no_token_error: return condition == xbox_live_error_condition::auth; case xbox_live_error_code::invalid_config: case xbox_live_error_code::unsupported: return condition == xbox_live_error_condition::generic_error; case xbox_live_error_code::AM_E_XASD_UNEXPECTED: case xbox_live_error_code::AM_E_XASU_UNEXPECTED: case xbox_live_error_code::AM_E_XAST_UNEXPECTED: case xbox_live_error_code::AM_E_XSTS_UNEXPECTED: case xbox_live_error_code::AM_E_XDEVICE_UNEXPECTED: case xbox_live_error_code::AM_E_DEVMODE_NOT_AUTHORIZED: case xbox_live_error_code::AM_E_NOT_AUTHORIZED: case xbox_live_error_code::AM_E_FORBIDDEN: case xbox_live_error_code::AM_E_UNKNOWN_TARGET: case xbox_live_error_code::AM_E_INVALID_NSAL_DATA: case xbox_live_error_code::AM_E_TITLE_NOT_AUTHENTICATED: case xbox_live_error_code::AM_E_TITLE_NOT_AUTHORIZED: case xbox_live_error_code::AM_E_USER_HASH_MISSING: case xbox_live_error_code::AM_E_USER_NOT_FOUND: case xbox_live_error_code::AM_E_INVALID_ENVIRONMENT: case xbox_live_error_code::AM_E_XASD_TIMEOUT: case xbox_live_error_code::AM_E_XASU_TIMEOUT: case xbox_live_error_code::AM_E_XAST_TIMEOUT: case xbox_live_error_code::AM_E_XSTS_TIMEOUT: case xbox_live_error_code::AM_E_LIVE_CONNECTION_REQUIRED: case xbox_live_error_code::AM_E_NO_NETWORK: case xbox_live_error_code::AM_E_XTITLE_UNEXPECTED: case xbox_live_error_code::AM_E_NO_TOKEN_REQUIRED: case xbox_live_error_code::AM_E_XTITLE_TIMEOUT: case xbox_live_error_code::XO_E_DEVMODE_NOT_AUTHORIZED: case xbox_live_error_code::XO_E_SYSTEM_UPDATE_REQUIRED: case xbox_live_error_code::XO_E_CONTENT_UPDATE_REQUIRED: case xbox_live_error_code::XO_E_ENFORCEMENT_BAN: case xbox_live_error_code::XO_E_THIRD_PARTY_BAN: case xbox_live_error_code::XO_E_ACCOUNT_PARENTALLY_RESTRICTED: case xbox_live_error_code::XO_E_ACCOUNT_BILLING_MAINTENANCE_REQUIRED: case xbox_live_error_code::XO_E_ACCOUNT_TERMS_OF_USE_NOT_ACCEPTED: case xbox_live_error_code::XO_E_ACCOUNT_COUNTRY_NOT_AUTHORIZED: case xbox_live_error_code::XO_E_ACCOUNT_AGE_VERIFICATION_REQUIRED: case xbox_live_error_code::XO_E_ACCOUNT_CURFEW: case xbox_live_error_code::XO_E_ACCOUNT_CHILD_NOT_IN_FAMILY: case xbox_live_error_code::XO_E_ACCOUNT_CSV_TRANSITION_REQUIRED: case xbox_live_error_code::XO_E_ACCOUNT_MAINTENANCE_REQUIRED: case xbox_live_error_code::XO_E_ACCOUNT_TYPE_NOT_ALLOWED: case xbox_live_error_code::XO_E_CONTENT_ISOLATION: case xbox_live_error_code::XO_E_ACCOUNT_NAME_CHANGE_REQUIRED: case xbox_live_error_code::XO_E_DEVICE_CHALLENGE_REQUIRED: case xbox_live_error_code::XO_E_SIGNIN_COUNT_BY_DEVICE_TYPE_EXCEEDED: case xbox_live_error_code::XO_E_PIN_CHALLENGE_REQUIRED: case xbox_live_error_code::XO_E_RETAIL_ACCOUNT_NOT_ALLOWED: case xbox_live_error_code::XO_E_SANDBOX_NOT_ALLOWED: case xbox_live_error_code::XO_E_ACCOUNT_SERVICE_UNAVAILABLE_UNKNOWN_USER: case xbox_live_error_code::XO_E_GREEN_SIGNED_CONTENT_NOT_AUTHORIZED: case xbox_live_error_code::XO_E_CONTENT_NOT_AUTHORIZED: case xbox_live_error_code::HR_ERROR_INTERNET_TIMEOUT: return (condition == xbox_live_error_condition::auth); case xbox_live_error_code::HR_INET_E_INVALID_URL: case xbox_live_error_code::HR_INET_E_NO_SESSION: case xbox_live_error_code::HR_INET_E_CANNOT_CONNECT: case xbox_live_error_code::HR_INET_E_RESOURCE_NOT_FOUND: case xbox_live_error_code::HR_INET_E_OBJECT_NOT_FOUND: case xbox_live_error_code::HR_INET_E_DATA_NOT_AVAILABLE: case xbox_live_error_code::HR_INET_E_DOWNLOAD_FAILURE: case xbox_live_error_code::HR_INET_E_AUTHENTICATION_REQUIRED: case xbox_live_error_code::HR_INET_E_NO_VALID_MEDIA: case xbox_live_error_code::HR_INET_E_CONNECTION_TIMEOUT: case xbox_live_error_code::HR_INET_E_INVALID_REQUEST: case xbox_live_error_code::HR_INET_E_UNKNOWN_PROTOCOL: case xbox_live_error_code::HR_INET_E_SECURITY_PROBLEM: case xbox_live_error_code::HR_INET_E_CANNOT_LOAD_DATA: case xbox_live_error_code::HR_INET_E_CANNOT_INSTANTIATE_OBJECT: case xbox_live_error_code::HR_INET_E_INVALID_CERTIFICATE: case xbox_live_error_code::HR_INET_E_REDIRECT_FAILED: case xbox_live_error_code::HR_INET_E_REDIRECT_TO_DIR: case xbox_live_error_code::HR_ERROR_NETWORK_UNREACHABLE: return (condition == xbox_live_error_condition::network); case xbox_live_error_code::out_of_range: return (condition == xbox_live_error_condition::generic_out_of_range || condition == xbox_live_error_condition::generic_error); case xbox_live_error_code::bad_alloc: case xbox_live_error_code::bad_cast: case xbox_live_error_code::invalid_argument: case xbox_live_error_code::json_error: case xbox_live_error_code::length_error: case xbox_live_error_code::range_error: case xbox_live_error_code::logic_error: case xbox_live_error_code::runtime_error: case xbox_live_error_code::uri_error: case xbox_live_error_code::rta_generic_error: case xbox_live_error_code::rta_access_denied: case xbox_live_error_code::rta_subscription_limit_reached: case xbox_live_error_code::generic_error: default: return (condition == xbox_live_error_condition::generic_error); } } NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END ================================================ FILE: Include/xsapi-cpp/impl/events.hpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "public_utils.h" #include "xsapi-c/events_c.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_EVENTS_CPP_BEGIN events_service::events_service(_In_ XblContextHandle contextHandle) { XblContextDuplicateHandle(contextHandle, &m_xblContext); } events_service::events_service(const events_service& other) { XblContextDuplicateHandle(other.m_xblContext, &m_xblContext); } events_service& events_service::operator=(events_service other) { std::swap(m_xblContext, other.m_xblContext); return *this; } events_service::~events_service() { XblContextCloseHandle(m_xblContext); } xbox_live_result events_service::write_in_game_event( _In_ const string_t& eventName ) { return xbox_live_result(Utils::ConvertHr(XblEventsWriteInGameEvent(m_xblContext, Utils::StringFromStringT(eventName).data(), nullptr, nullptr))); } xbox_live_result events_service::write_in_game_event( _In_ const string_t& eventName, _In_ const web::json::value& dimensionsJson, _In_ const web::json::value& measurementJson ) { return xbox_live_result(Utils::ConvertHr(XblEventsWriteInGameEvent( m_xblContext, Utils::StringFromStringT(eventName).data(), Utils::StringFromStringT(dimensionsJson.serialize()).data(), Utils::StringFromStringT(measurementJson.serialize()).data() ))); } NAMESPACE_MICROSOFT_XBOX_SERVICES_EVENTS_CPP_END ================================================ FILE: Include/xsapi-cpp/impl/http_call.hpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "public_utils.h" #include "../xbox_live_context_settings.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN http_call_response::http_call_response( _In_ XblHttpCallHandle call, _In_ XblHttpCallResponseBodyType type ) { XblHttpCallDuplicateHandle(call, &m_handle); m_bodyType = static_cast(type); } http_call_response_body_type http_call_response::body_type() const { return m_bodyType; } string_t http_call_response::response_body_string() { if (m_responseBodyString != string_t()) { return m_responseBodyString; } const char* responseString; HRESULT hr = XblHttpCallGetResponseString(m_handle, &responseString); if (FAILED(hr)) { return string_t(); } m_responseBodyString = Utils::StringTFromUtf8(responseString); return m_responseBodyString; } web::json::value http_call_response::response_body_json() { auto str = response_body_string(); m_responseBodyJson = web::json::value::parse(str); return m_responseBodyJson; } std::vector http_call_response::response_body_vector() { if (!m_responseBodyVector.empty()) { return m_responseBodyVector; } size_t bufferSize; HRESULT hr = XblHttpCallGetResponseBodyBytesSize(m_handle, &bufferSize); if (FAILED(hr)) { return {}; } uint8_t* buffer = new uint8_t[bufferSize]; size_t bufferUsed; hr = XblHttpCallGetResponseBodyBytes(m_handle, bufferSize, buffer, &bufferUsed); if (FAILED(hr)) { return {}; } auto bufferInput = std::vector(buffer, buffer + bufferUsed); m_responseBodyVector = std::vector(bufferUsed); std::transform(bufferInput.begin(), bufferInput.end(), m_responseBodyVector.begin(), [](uint8_t c) { return static_cast(c); }); return m_responseBodyVector; } web::http::http_headers http_call_response::response_headers() { if (!m_responseHeaders.empty()) { return m_responseHeaders; } uint32_t numHeader; HRESULT hr = XblHttpCallGetNumHeaders(m_handle, &numHeader); if (FAILED(hr)) { return web::http::http_headers(); } const char* headerName; const char* headerValue; auto headers = web::http::http_headers(); for (auto i = 0u; i < numHeader; i++) { hr = XblHttpCallGetHeaderAtIndex(m_handle, i, &headerName, &headerValue); if (SUCCEEDED(hr)) { headers.add(Utils::StringTFromUtf8(headerName), Utils::StringTFromUtf8(headerValue)); } } m_responseHeaders = headers; return headers; } uint32_t http_call_response::http_status() { uint32_t statusCode; HRESULT hr = XblHttpCallGetStatusCode(m_handle, &statusCode); if (FAILED(hr)) { return 0; } m_httpStatus = statusCode; return m_httpStatus; } std::error_code http_call_response::err_code() { if (m_errorCode == std::error_code()) { return m_errorCode; } HRESULT networkErrorCode; uint32_t platformNetworkErrorCode; HRESULT hr = XblHttpCallGetNetworkErrorCode(m_handle, &networkErrorCode, &platformNetworkErrorCode); if (FAILED(hr)) { return std::make_error_code(Utils::ConvertHrToXblErrorCode(hr)); } m_errorCode = std::make_error_code(Utils::ConvertHrToXblErrorCode(networkErrorCode)); return m_errorCode; } std::string http_call_response::err_message() { if (m_errMessage != std::string()) { return m_errMessage; } const char* errorMessage; HRESULT hr = XblHttpCallGetPlatformNetworkErrorMessage(m_handle, &errorMessage); if (FAILED(hr)) { return std::string(); } m_errMessage = std::string(errorMessage); return m_errMessage; } string_t http_call_response::e_tag() const { // return default value because there is no matching method return string_t(); } string_t http_call_response::response_date() const { // return default value because there is no matching method return string_t(); } std::chrono::seconds http_call_response::retry_after() const { // return default value because there is no matching method return std::chrono::seconds(); } http_call_response::http_call_response(const http_call_response& other) : m_bodyType(other.m_bodyType), m_responseBodyString(other.m_responseBodyString), m_responseBodyJson(other.m_responseBodyJson), m_responseBodyVector(other.m_responseBodyVector), m_responseHeaders(other.m_responseHeaders), m_httpStatus(other.m_httpStatus), m_errorCode(other.m_errorCode), m_errMessage(other.m_errMessage) { XblHttpCallDuplicateHandle(other.m_handle, &m_handle); } http_call_response& http_call_response::operator=(http_call_response other) { std::swap(m_handle, other.m_handle); m_responseBodyString = other.m_responseBodyString; m_responseBodyJson = other.m_responseBodyJson; m_responseBodyVector = other.m_responseBodyVector; m_httpStatus = other.m_httpStatus; m_responseHeaders = other.m_responseHeaders; m_errorCode = other.m_errorCode; m_errMessage = other.m_errMessage; return *this; } http_call_response::~http_call_response() { XblHttpCallCloseHandle(m_handle); } http_call::http_call( _In_ XblHttpCallHandle callHandle, _In_ string_t httpMethod, _In_ string_t serverName, _In_ web::uri pathQueryFragment ) : m_callHandle(std::move(callHandle)), m_httpMethod(std::move(httpMethod)), m_serverName(std::move(serverName)), m_pathQueryFragment(std::move(pathQueryFragment)) { } #if !XSAPI_NO_PPL pplx::task> http_call::get_response_with_auth( _In_ http_call_response_body_type httpCallResponseBodyType ) { XblHttpCallHandle xblHttpCall = m_callHandle; auto asyncWrapper = new AsyncWrapper>( [xblHttpCall, httpCallResponseBodyType]( _In_ XAsyncBlock* async, _In_ std::shared_ptr& result) { HRESULT hr = XAsyncGetStatus(async, false); if (SUCCEEDED(hr)) { result = std::make_shared(xblHttpCall, static_cast(httpCallResponseBodyType)); } return hr; }); auto hr = XblHttpCallPerformAsync( m_callHandle, static_cast(httpCallResponseBodyType), &asyncWrapper->async ); pplx::task_completion_event> taskCompletionEvent; auto response = asyncWrapper->Task(hr).then([taskCompletionEvent](xbox_live_result> result) { taskCompletionEvent.set(result.payload()); }); return pplx::task>(taskCompletionEvent); } pplx::task> http_call::get_response_with_auth( _In_ XalUserHandle user, _In_ http_call_response_body_type httpCallResponseBodyType, _In_ bool allUsersAuthRequired ) { UNREFERENCED_PARAMETER(user); UNREFERENCED_PARAMETER(allUsersAuthRequired); auto xblHttpCall = m_callHandle; auto asyncWrapper = new AsyncWrapper>( [xblHttpCall, httpCallResponseBodyType](XAsyncBlock* async, std::shared_ptr& response) { HRESULT hr = XAsyncGetStatus(async, false); if (SUCCEEDED(hr)) { response = std::make_shared(xblHttpCall, static_cast(httpCallResponseBodyType)); } return hr; }); auto hr = XblHttpCallPerformAsync( m_callHandle, static_cast(httpCallResponseBodyType), &asyncWrapper->async ); pplx::task_completion_event> taskCompletionEvent; auto response = asyncWrapper->Task(hr).then([taskCompletionEvent](xbox_live_result> result) { taskCompletionEvent.set(result.payload()); }); return pplx::task>(taskCompletionEvent); } pplx::task> http_call::get_response( _In_ http_call_response_body_type httpCallResponseBodyType ) { // no matching function UNREFERENCED_PARAMETER(httpCallResponseBodyType); return pplx::task>(); } #endif // !XSAPI_NO_PPL void http_call::set_request_body( _In_ const string_t& value ) { auto convertedValue = Utils::StringFromStringT(value); XblHttpCallRequestSetRequestBodyString(m_callHandle, convertedValue.c_str()); } void http_call::set_request_body( _In_ const web::json::value& value ) { auto convertedValue = Utils::StringFromStringT(value.serialize()); XblHttpCallRequestSetRequestBodyString(m_callHandle, convertedValue.c_str()); } void http_call::set_request_body( _In_ const std::vector& value ) { uint8_t* buffer{ nullptr }; auto neededSize = value.size(); std::copy(value.begin(), value.end(), buffer); XblHttpCallRequestSetRequestBodyBytes(m_callHandle, buffer, static_cast(neededSize)); } void http_call::set_custom_header( _In_ const string_t& Name, _In_ const string_t& Value ) { const auto headerName = Utils::StringFromStringT(Name); const auto headerValue = Utils::StringFromStringT(Value); XblHttpCallRequestSetHeader( m_callHandle, headerName.c_str(), headerValue.c_str(), false ); } void http_call::set_long_http_call( _In_ bool value ) { XblHttpCallRequestSetLongHttpCall(m_callHandle, value); m_longHttpCall = value; } bool http_call::long_http_call() const { return m_longHttpCall; } void http_call::set_retry_allowed( _In_ bool value ) { XblHttpCallRequestSetLongHttpCall(m_callHandle, value); m_retryAllowed = value; } bool http_call::retry_allowed() const { return m_retryAllowed; } void http_call::set_content_type_header_value( _In_ const string_t& value ) { XblHttpCallRequestSetHeader(m_callHandle, "Content-Type", Utils::StringFromStringT(value).c_str(), true); } string_t http_call::content_type_header_value() const { //no matching method return string_t(); } void http_call::set_xbox_contract_version_header_value( _In_ const string_t& value ) { XblHttpCallRequestSetHeader(m_callHandle, "x-xbl-contract-version", Utils::StringFromStringT(value).c_str(), true); } string_t http_call::xbox_contract_version_header_value() const { //no matching method return string_t(); } string_t http_call::server_name() const { return m_serverName; } const web::uri& http_call::path_query_fragment() const { return m_pathQueryFragment; } string_t http_call::http_method() const { return m_httpMethod; } void http_call::set_add_default_headers( _In_ bool value ) { UNREFERENCED_PARAMETER(value); //no matching method } bool http_call::add_default_headers() const { //no matching method return false; } http_call::~http_call() { XblHttpCallCloseHandle(m_callHandle); } std::shared_ptr create_xbox_live_http_call( _In_ const std::shared_ptr& xboxLiveContextSettings, _In_ const string_t& httpMethod, _In_ const string_t& serverName, _In_ const web::uri& pathQueryFragment ) { // access context handle via friend function auto xblContext = xboxLiveContextSettings->m_xblContextHandle; auto httpMethod_c = Utils::StringFromStringT(httpMethod); auto fullUrl = serverName; if (!pathQueryFragment.is_empty()) { fullUrl += pathQueryFragment.to_string(); } auto fullUrl_c = Utils::StringFromStringT(fullUrl); XblHttpCallHandle callHandle; HRESULT hr = XblHttpCallCreate(xblContext, httpMethod_c.c_str(), fullUrl_c.c_str(), &callHandle); if (FAILED(hr)) { return nullptr; } return std::make_shared(callHandle, httpMethod, serverName, pathQueryFragment); } NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END ================================================ FILE: Include/xsapi-cpp/impl/http_call_request_message.hpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN http_call_request_message::http_call_request_message() : m_httpRequestMessageType(http_request_message_type::empty_message) { } const string_t& http_call_request_message::request_message_string() const { return m_requestMessageString; } const std::vector& http_call_request_message::request_message_vector() const { return m_requestMessageVector; } http_request_message_type http_call_request_message::get_http_request_message_type() const { return m_httpRequestMessageType; } NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END ================================================ FILE: Include/xsapi-cpp/impl/leaderboard.hpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "xsapi-cpp/social.h" #include "public_utils.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_LEADERBOARD_CPP_BEGIN leaderboard_column::leaderboard_column( std::shared_ptr buffer, const XblLeaderboardColumn& leaderboardColumn ) : m_buffer(buffer), m_leaderboardColumn(leaderboardColumn) { } string_t leaderboard_column::stat_name() const { return Utils::StringTFromUtf8(m_leaderboardColumn.statName); } leaderboard_stat_type leaderboard_column::stat_type() const { return static_cast(m_leaderboardColumn.statType); } leaderboard_row::leaderboard_row( std::shared_ptr buffer, const XblLeaderboardRow& leaderboardRow ) : m_buffer(buffer), m_leaderboardRow(leaderboardRow) { } string_t leaderboard_row::gamertag() const { return Utils::StringTFromUtf8(m_leaderboardRow.gamertag); } string_t leaderboard_row::xbox_user_id() const { return Utils::StringTFromUint64(m_leaderboardRow.xboxUserId); } double leaderboard_row::percentile() const { return m_leaderboardRow.percentile; } uint32_t leaderboard_row::rank() const { return m_leaderboardRow.rank; } std::vector leaderboard_row::column_values() const { return Utils::StringTVectorFromCStringArray(m_leaderboardRow.columnValues, m_leaderboardRow.columnValuesCount); } leaderboard_result::leaderboard_result( std::shared_ptr buffer, const XblContextHandle& xblContext ) : m_buffer(buffer), m_leaderboardResult(*reinterpret_cast(m_buffer.get())), m_xblContext(xblContext) { for (uint32_t i = 0; i < m_leaderboardResult.columnsCount; i++) { m_columns.push_back(leaderboard_column(m_buffer, m_leaderboardResult.columns[i])); } for (uint32_t i = 0; i < m_leaderboardResult.rowsCount; i++) { m_rows.push_back(leaderboard_row(m_buffer, m_leaderboardResult.rows[i])); } } uint32_t leaderboard_result::total_row_count() const { return m_leaderboardResult.totalRowCount; } const std::vector& leaderboard_result::columns() const { return m_columns; } const std::vector& leaderboard_result::rows() const { return m_rows; } bool leaderboard_result::has_next() const { return m_leaderboardResult.hasNext; } #if !defined(XBOX_LIVE_CREATORS_SDK) pplx::task> leaderboard_result::get_next(_In_ uint32_t maxItems) { auto xblContext = m_xblContext; auto asyncWrapper = new AsyncWrapper( [xblContext](XAsyncBlock* async, leaderboard_result& result) { size_t bufferSize; auto hr = XblLeaderboardResultGetNextResultSize(async, &bufferSize); if (SUCCEEDED(hr)) { std::shared_ptr buffer(new char[bufferSize], std::default_delete()); XblLeaderboardResult* resultPtr; hr = XblLeaderboardResultGetNextResult(async, bufferSize, buffer.get(), &resultPtr, nullptr); if (SUCCEEDED(hr)) { result = leaderboard_result(buffer, xblContext); } } return hr; }); auto hr = XblLeaderboardResultGetNextAsync( m_xblContext, &m_leaderboardResult, maxItems, &asyncWrapper->async ); return asyncWrapper->Task(hr); } leaderboard_service::leaderboard_service(_In_ XblContextHandle contextHandle) { XblContextDuplicateHandle(contextHandle, &m_xblContext); } leaderboard_service::leaderboard_service(const leaderboard_service& other) { XblContextDuplicateHandle(other.m_xblContext, &m_xblContext); } leaderboard_service& leaderboard_service::operator=(leaderboard_service other) { std::swap(m_xblContext, other.m_xblContext); return *this; } leaderboard_service::~leaderboard_service() { XblContextCloseHandle(m_xblContext); } XblSocialGroupType leaderboard_service::XblSocialGroupTypeFromString(string_t socialGroup) { XblSocialGroupType group = XblSocialGroupType::None; if (Utils::Stricmp(socialGroup, xbox::services::social::social_group_constants::people()) == 0 || Utils::Stricmp(socialGroup, _T("all")) == 0) { group = XblSocialGroupType::People; } else if (Utils::Stricmp(socialGroup, xbox::services::social::social_group_constants::favorite()) == 0) { group = XblSocialGroupType::Favorites; } return group; } XblLeaderboardSortOrder leaderboard_service::XblLeaderboardSortOrderFromString(string_t sortOrder) { XblLeaderboardSortOrder order = XblLeaderboardSortOrder::Descending; if (Utils::Stricmp(sortOrder, _T("ascending")) == 0) { order = XblLeaderboardSortOrder::Ascending; } return order; } pplx::task> leaderboard_service::get_leaderboard( _In_ const string_t& scid, _In_ const string_t& name, _In_ const std::vector& additionalColumnNames ) { auto leaderboardName = Utils::StringFromStringT(name); UTF8StringArrayRef additionalColumnleaderboardNames{ additionalColumnNames }; XblLeaderboardQuery query = {}; Utils::Utf8FromCharT(scid.c_str(), query.scid, XBL_SCID_LENGTH); query.leaderboardName = leaderboardName.c_str(); query.additionalColumnleaderboardNames = additionalColumnleaderboardNames.Data(); query.additionalColumnleaderboardNamesCount = additionalColumnleaderboardNames.Size(); auto xblContext = m_xblContext; auto asyncWrapper = new AsyncWrapper( [xblContext](XAsyncBlock* async, leaderboard_result& result) { size_t bufferSize; auto hr = XblLeaderboardGetLeaderboardResultSize(async, &bufferSize); if (SUCCEEDED(hr)) { std::shared_ptr buffer(new char[bufferSize], std::default_delete()); XblLeaderboardResult* resultPtr; hr = XblLeaderboardGetLeaderboardResult(async, bufferSize, buffer.get(), &resultPtr, nullptr); if (SUCCEEDED(hr)) { result = leaderboard_result(buffer, xblContext); } } return hr; }); auto hr = XblLeaderboardGetLeaderboardAsync( m_xblContext, query, &asyncWrapper->async ); return asyncWrapper->Task(hr); } pplx::task> leaderboard_service::get_leaderboard( _In_ const string_t& scid, _In_ const string_t& name, _In_ const string_t& xuid, _In_ const string_t& socialGroup, _In_ uint32_t maxItems, _In_ const std::vector& additionalColumnNames ) { auto leaderboardName = Utils::StringFromStringT(name); UTF8StringArrayRef additionalColumnleaderboardNames{ additionalColumnNames }; XblLeaderboardQuery query = {}; Utils::Utf8FromCharT(scid.c_str(), query.scid, XBL_SCID_LENGTH); query.leaderboardName = leaderboardName.c_str(); query.xboxUserId = Utils::Uint64FromStringT(xuid); query.socialGroup = XblSocialGroupTypeFromString(socialGroup); query.maxItems = maxItems; query.additionalColumnleaderboardNames = additionalColumnleaderboardNames.Data(); query.additionalColumnleaderboardNamesCount = (uint32_t)additionalColumnleaderboardNames.Size(); auto xblContext = m_xblContext; auto asyncWrapper = new AsyncWrapper( [xblContext](XAsyncBlock* async, leaderboard_result& result) { size_t bufferSize; auto hr = XblLeaderboardGetLeaderboardResultSize(async, &bufferSize); if (SUCCEEDED(hr)) { std::shared_ptr buffer(new char[bufferSize], std::default_delete()); XblLeaderboardResult* resultPtr; hr = XblLeaderboardGetLeaderboardResult(async, bufferSize, buffer.get(), &resultPtr, nullptr); if (SUCCEEDED(hr)) { result = leaderboard_result(buffer, xblContext); } } return hr; }); auto hr = XblLeaderboardGetLeaderboardAsync( m_xblContext, query, &asyncWrapper->async ); return asyncWrapper->Task(hr); } pplx::task> leaderboard_service::get_leaderboard( _In_ const string_t& scid, _In_ const string_t& name, _In_ uint32_t skipToRank, _In_ uint32_t maxItems, _In_ const std::vector& additionalColumnNames ) { auto leaderboardName = Utils::StringFromStringT(name); UTF8StringArrayRef additionalColumnleaderboardNames{ additionalColumnNames }; XblLeaderboardQuery query = {}; Utils::Utf8FromCharT(scid.c_str(), query.scid, XBL_SCID_LENGTH); query.leaderboardName = leaderboardName.c_str(); query.skipResultToRank = skipToRank; query.maxItems = maxItems; query.additionalColumnleaderboardNames = additionalColumnleaderboardNames.Data(); query.additionalColumnleaderboardNamesCount = additionalColumnleaderboardNames.Size(); auto xblContext = m_xblContext; auto asyncWrapper = new AsyncWrapper( [xblContext](XAsyncBlock* async, leaderboard_result& result) { size_t bufferSize; auto hr = XblLeaderboardGetLeaderboardResultSize(async, &bufferSize); if (SUCCEEDED(hr)) { std::shared_ptr buffer(new char[bufferSize], std::default_delete()); XblLeaderboardResult* resultPtr; hr = XblLeaderboardGetLeaderboardResult(async, bufferSize, buffer.get(), &resultPtr, nullptr); if (SUCCEEDED(hr)) { result = leaderboard_result(buffer, xblContext); } } return hr; }); auto hr = XblLeaderboardGetLeaderboardAsync( m_xblContext, query, &asyncWrapper->async ); return asyncWrapper->Task(hr); } pplx::task> leaderboard_service::get_leaderboard( _In_ const string_t& scid, _In_ const string_t& name, _In_ uint32_t skipToRank, _In_ const string_t& xuid, _In_ const string_t& socialGroup, _In_ uint32_t maxItems, _In_ const std::vector& additionalColumnNames ) { auto leaderboardName = Utils::StringFromStringT(name); UTF8StringArrayRef additionalColumnleaderboardNames{ additionalColumnNames }; XblLeaderboardQuery query = {}; Utils::Utf8FromCharT(scid.c_str(), query.scid, XBL_SCID_LENGTH); query.leaderboardName = leaderboardName.c_str(); query.skipResultToRank = skipToRank; query.xboxUserId = Utils::Uint64FromStringT(xuid); query.socialGroup = XblSocialGroupTypeFromString(socialGroup); query.maxItems = maxItems; query.additionalColumnleaderboardNames = additionalColumnleaderboardNames.Data(); query.additionalColumnleaderboardNamesCount = (uint32_t)additionalColumnleaderboardNames.Size(); auto xblContext = m_xblContext; auto asyncWrapper = new AsyncWrapper( [xblContext](XAsyncBlock* async, leaderboard_result& result) { size_t bufferSize; auto hr = XblLeaderboardGetLeaderboardResultSize(async, &bufferSize); if (SUCCEEDED(hr)) { std::shared_ptr buffer(new char[bufferSize], std::default_delete()); XblLeaderboardResult* resultPtr; hr = XblLeaderboardGetLeaderboardResult(async, bufferSize, buffer.get(), &resultPtr, nullptr); if (SUCCEEDED(hr)) { result = leaderboard_result(buffer, xblContext); } } return hr; }); auto hr = XblLeaderboardGetLeaderboardAsync( m_xblContext, query, &asyncWrapper->async ); return asyncWrapper->Task(hr); } pplx::task> leaderboard_service::get_leaderboard_skip_to_xuid( _In_ const string_t& scid, _In_ const string_t& name, _In_ const string_t& skipToXuid, _In_ uint32_t maxItems, _In_ const std::vector& additionalColumnNames ) { auto leaderboardName = Utils::StringFromStringT(name); UTF8StringArrayRef additionalColumnleaderboardNames{ additionalColumnNames }; XblLeaderboardQuery query = {}; Utils::Utf8FromCharT(scid.c_str(), query.scid, XBL_SCID_LENGTH); query.leaderboardName = leaderboardName.c_str(); query.skipToXboxUserId = Utils::Uint64FromStringT(skipToXuid); query.maxItems = maxItems; query.additionalColumnleaderboardNames = additionalColumnleaderboardNames.Data(); query.additionalColumnleaderboardNamesCount = additionalColumnleaderboardNames.Size(); auto xblContext = m_xblContext; auto asyncWrapper = new AsyncWrapper( [xblContext](XAsyncBlock* async, leaderboard_result& result) { size_t bufferSize; auto hr = XblLeaderboardGetLeaderboardResultSize(async, &bufferSize); if (SUCCEEDED(hr)) { std::shared_ptr buffer(new char[bufferSize], std::default_delete()); XblLeaderboardResult* resultPtr; hr = XblLeaderboardGetLeaderboardResult(async, bufferSize, buffer.get(), &resultPtr, nullptr); if (SUCCEEDED(hr)) { result = leaderboard_result(buffer, xblContext); } } return hr; }); auto hr = XblLeaderboardGetLeaderboardAsync( m_xblContext, query, &asyncWrapper->async ); return asyncWrapper->Task(hr); } pplx::task> leaderboard_service::get_leaderboard_skip_to_xuid( _In_ const string_t& scid, _In_ const string_t& name, _In_ const string_t& xuid, _In_ const string_t& socialGroup, _In_ const string_t& skipToXuid, _In_ uint32_t maxItems, _In_ const std::vector& additionalColumnNames ) { auto leaderboardName = Utils::StringFromStringT(name); UTF8StringArrayRef additionalColumnleaderboardNames{ additionalColumnNames }; XblLeaderboardQuery query = {}; Utils::Utf8FromCharT(scid.c_str(), query.scid, XBL_SCID_LENGTH); query.leaderboardName = leaderboardName.c_str(); query.xboxUserId = Utils::Uint64FromStringT(xuid); query.socialGroup = XblSocialGroupTypeFromString(socialGroup); query.skipToXboxUserId = Utils::Uint64FromStringT(skipToXuid); query.maxItems = maxItems; query.additionalColumnleaderboardNames = additionalColumnleaderboardNames.Data(); query.additionalColumnleaderboardNamesCount = (uint32_t)additionalColumnleaderboardNames.Size(); auto xblContext = m_xblContext; auto asyncWrapper = new AsyncWrapper( [xblContext](XAsyncBlock* async, leaderboard_result& result) { size_t bufferSize; auto hr = XblLeaderboardGetLeaderboardResultSize(async, &bufferSize); if (SUCCEEDED(hr)) { std::shared_ptr buffer(new char[bufferSize], std::default_delete()); XblLeaderboardResult* resultPtr; hr = XblLeaderboardGetLeaderboardResult(async, bufferSize, buffer.get(), &resultPtr, nullptr); if (SUCCEEDED(hr)) { result = leaderboard_result(buffer, xblContext); } } return hr; }); auto hr = XblLeaderboardGetLeaderboardAsync( m_xblContext, query, &asyncWrapper->async ); return asyncWrapper->Task(hr); } pplx::task> leaderboard_service::get_leaderboard_for_social_group( _In_ const string_t& xuid, _In_ const string_t& scid, _In_ const string_t& statName, _In_ const string_t& socialGroup, _In_ uint32_t maxItems ) { auto statNameCpp = Utils::StringFromStringT(statName); XblLeaderboardQuery query = {}; query.xboxUserId = Utils::Uint64FromStringT(xuid); Utils::Utf8FromCharT(scid.c_str(), query.scid, XBL_SCID_LENGTH); query.statName = statNameCpp.c_str(); query.socialGroup = XblSocialGroupTypeFromString(socialGroup); query.maxItems = maxItems; auto xblContext = m_xblContext; auto asyncWrapper = new AsyncWrapper( [xblContext](XAsyncBlock* async, leaderboard_result& result) { size_t bufferSize; auto hr = XblLeaderboardGetLeaderboardResultSize(async, &bufferSize); if (SUCCEEDED(hr)) { std::shared_ptr buffer(new char[bufferSize], std::default_delete()); XblLeaderboardResult* resultPtr; hr = XblLeaderboardGetLeaderboardResult(async, bufferSize, buffer.get(), &resultPtr, nullptr); if (SUCCEEDED(hr)) { result = leaderboard_result(buffer, xblContext); } } return hr; }); auto hr = XblLeaderboardGetLeaderboardAsync( m_xblContext, query, &asyncWrapper->async ); return asyncWrapper->Task(hr); } pplx::task> leaderboard_service::get_leaderboard_for_social_group( _In_ const string_t& xuid, _In_ const string_t& scid, _In_ const string_t& statName, _In_ const string_t& socialGroup, _In_ const string_t& sortOrder, _In_ uint32_t maxItems ) { auto statNameCpp = Utils::StringFromStringT(statName); XblLeaderboardQuery query = {}; query.xboxUserId = Utils::Uint64FromStringT(xuid); Utils::Utf8FromCharT(scid.c_str(), query.scid, XBL_SCID_LENGTH); query.statName = statNameCpp.c_str(); query.socialGroup = XblSocialGroupTypeFromString(socialGroup); query.order = XblLeaderboardSortOrderFromString(sortOrder); query.maxItems = maxItems; auto xblContext = m_xblContext; auto asyncWrapper = new AsyncWrapper( [xblContext](XAsyncBlock* async, leaderboard_result& result) { size_t bufferSize; auto hr = XblLeaderboardGetLeaderboardResultSize(async, &bufferSize); if (SUCCEEDED(hr)) { std::shared_ptr buffer(new char[bufferSize], std::default_delete()); XblLeaderboardResult* resultPtr; hr = XblLeaderboardGetLeaderboardResult(async, bufferSize, buffer.get(), &resultPtr, nullptr); if (SUCCEEDED(hr)) { result = leaderboard_result(buffer, xblContext); } } return hr; }); auto hr = XblLeaderboardGetLeaderboardAsync( m_xblContext, query, &asyncWrapper->async ); return asyncWrapper->Task(hr); } pplx::task> leaderboard_service::get_leaderboard_for_social_group_skip_to_rank( _In_ const string_t& xuid, _In_ const string_t& scid, _In_ const string_t& statName, _In_ const string_t& socialGroup, _In_ uint32_t skipToRank, _In_ const string_t& sortOrder, _In_ uint32_t maxItems ) { auto statNameCpp = Utils::StringFromStringT(statName); XblLeaderboardQuery query = {}; query.xboxUserId = Utils::Uint64FromStringT(xuid); Utils::Utf8FromCharT(scid.c_str(), query.scid, XBL_SCID_LENGTH); query.statName = statNameCpp.c_str(); query.socialGroup = XblSocialGroupTypeFromString(socialGroup); query.skipResultToRank = skipToRank; query.order = XblLeaderboardSortOrderFromString(sortOrder); query.maxItems = maxItems; auto xblContext = m_xblContext; auto asyncWrapper = new AsyncWrapper( [xblContext](XAsyncBlock* async, leaderboard_result& result) { size_t bufferSize; auto hr = XblLeaderboardGetLeaderboardResultSize(async, &bufferSize); if (SUCCEEDED(hr)) { std::shared_ptr buffer(new char[bufferSize], std::default_delete()); XblLeaderboardResult* resultPtr; hr = XblLeaderboardGetLeaderboardResult(async, bufferSize, buffer.get(), &resultPtr, nullptr); if (SUCCEEDED(hr)) { result = leaderboard_result(buffer, xblContext); } } return hr; }); auto hr = XblLeaderboardGetLeaderboardAsync( m_xblContext, query, &asyncWrapper->async ); return asyncWrapper->Task(hr); } pplx::task> leaderboard_service::get_leaderboard_for_social_group_skip_to_xuid( _In_ const string_t& xuid, _In_ const string_t& scid, _In_ const string_t& statName, _In_ const string_t& socialGroup, _In_ const string_t& skipToXuid, _In_ const string_t& sortOrder, _In_ uint32_t maxItems ) { auto statNameCpp = Utils::StringFromStringT(statName); XblLeaderboardQuery query = {}; query.xboxUserId = Utils::Uint64FromStringT(xuid); Utils::Utf8FromCharT(scid.c_str(), query.scid, XBL_SCID_LENGTH); query.statName = statNameCpp.c_str(); query.socialGroup = XblSocialGroupTypeFromString(socialGroup); query.skipToXboxUserId = Utils::Uint64FromStringT(skipToXuid); query.order = XblLeaderboardSortOrderFromString(sortOrder); query.maxItems = maxItems; auto xblContext = m_xblContext; auto asyncWrapper = new AsyncWrapper( [xblContext](XAsyncBlock* async, leaderboard_result& result) { size_t bufferSize; auto hr = XblLeaderboardGetLeaderboardResultSize(async, &bufferSize); if (SUCCEEDED(hr)) { std::shared_ptr buffer(new char[bufferSize], std::default_delete()); XblLeaderboardResult* resultPtr; hr = XblLeaderboardGetLeaderboardResult(async, bufferSize, buffer.get(), &resultPtr, nullptr); if (SUCCEEDED(hr)) { result = leaderboard_result(buffer, xblContext); } } return hr; }); auto hr = XblLeaderboardGetLeaderboardAsync( m_xblContext, query, &asyncWrapper->async ); return asyncWrapper->Task(hr); } #endif // !defined(XBOX_LIVE_CREATORS_SDK) NAMESPACE_MICROSOFT_XBOX_SERVICES_LEADERBOARD_CPP_END ================================================ FILE: Include/xsapi-cpp/impl/matchmaking.hpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "public_utils.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_MATCHMAKING_CPP_BEGIN match_ticket_details_response::match_ticket_details_response() : m_buffer(), m_matchTicketDetailsResponse() { } match_ticket_details_response::match_ticket_details_response(std::shared_ptr buffer) : m_buffer(buffer), m_matchTicketDetailsResponse(*reinterpret_cast(m_buffer.get())) { } ticket_status match_ticket_details_response::match_status() const { return static_cast(m_matchTicketDetailsResponse.matchStatus); } std::chrono::seconds match_ticket_details_response::estimated_wait_time() const { return std::chrono::seconds(m_matchTicketDetailsResponse.estimatedWaitTime); } preserve_session_mode match_ticket_details_response::preserve_session() const { return static_cast(m_matchTicketDetailsResponse.preserveSession); } xbox::services::multiplayer::multiplayer_session_reference match_ticket_details_response::ticket_session() const { return xbox::services::multiplayer::multiplayer_session_reference( Utils::StringTFromUtf8(m_matchTicketDetailsResponse.ticketSession.Scid), Utils::StringTFromUtf8(m_matchTicketDetailsResponse.ticketSession.SessionTemplateName), Utils::StringTFromUtf8(m_matchTicketDetailsResponse.ticketSession.SessionName)); } xbox::services::multiplayer::multiplayer_session_reference match_ticket_details_response::target_session() const { return xbox::services::multiplayer::multiplayer_session_reference ( Utils::StringTFromUtf8(m_matchTicketDetailsResponse.targetSession.Scid), Utils::StringTFromUtf8(m_matchTicketDetailsResponse.targetSession.SessionTemplateName), Utils::StringTFromUtf8(m_matchTicketDetailsResponse.targetSession.SessionName)); } web::json::value match_ticket_details_response::ticket_attributes() const { return web::json::value(Utils::StringTFromUtf8(m_matchTicketDetailsResponse.ticketAttributes)); } ticket_status match_ticket_details_response::convert_string_to_ticket_status( _In_ const string_t& value ) { ticket_status ticketStatus = ticket_status::unknown; if (!value.empty()) { if (Utils::Stricmp(value, _T("expired")) == 0) { ticketStatus = ticket_status::expired; } else if (Utils::Stricmp(value, _T("searching")) == 0) { ticketStatus = ticket_status::searching; } else if (Utils::Stricmp(value, _T("found")) == 0) { ticketStatus = ticket_status::found; } else if (Utils::Stricmp(value, _T("canceled")) == 0) { ticketStatus = ticket_status::canceled; } } return ticketStatus; } preserve_session_mode match_ticket_details_response::convert_string_to_preserve_session_mode( _In_ const string_t& value ) { preserve_session_mode preserve_session_mode = preserve_session_mode::unknown; if (!value.empty()) { if (Utils::Stricmp(value, _T("always")) == 0) { preserve_session_mode = preserve_session_mode::always; } else if (Utils::Stricmp(value, _T("never")) == 0) { preserve_session_mode = preserve_session_mode::never; } } return preserve_session_mode; } hopper_statistics_response::hopper_statistics_response() : m_buffer(), m_hopperStatisticsResponse() { } hopper_statistics_response::hopper_statistics_response(std::shared_ptr buffer) : m_buffer(buffer), m_hopperStatisticsResponse(*reinterpret_cast(m_buffer.get())) { } /// /// Name of the hopper in which a match was requested. /// string_t hopper_statistics_response::hopper_name() const { return Utils::StringTFromUtf8(m_hopperStatisticsResponse.hopperName); } /// /// Estimated wait time for a match request to be matched with other players. /// std::chrono::seconds hopper_statistics_response::estimated_wait_time() const { return std::chrono::seconds(m_hopperStatisticsResponse.estimatedWaitTime); } /// /// The number of players in the hopper waiting to be matched. /// uint32_t hopper_statistics_response::players_waiting_to_match() const { return m_hopperStatisticsResponse.playersWaitingToMatch; } create_match_ticket_response::create_match_ticket_response(): m_createMatchTicketResponse{ } { } create_match_ticket_response::create_match_ticket_response( XblCreateMatchTicketResponse response ) : m_createMatchTicketResponse(response) { } string_t create_match_ticket_response::match_ticket_id() const { return Utils::StringTFromUtf8(m_createMatchTicketResponse.matchTicketId); } std::chrono::seconds create_match_ticket_response::estimated_wait_time() const { return std::chrono::seconds(m_createMatchTicketResponse.estimatedWaitTime); } matchmaking_service::matchmaking_service(_In_ XblContextHandle contextHandle) { XblContextDuplicateHandle(contextHandle, &m_xblContext); } matchmaking_service::matchmaking_service(const matchmaking_service& other) { XblContextDuplicateHandle(other.m_xblContext, &m_xblContext); } matchmaking_service& matchmaking_service::operator=(matchmaking_service other) { std::swap(m_xblContext, other.m_xblContext); return *this; } matchmaking_service::~matchmaking_service() { XblContextCloseHandle(m_xblContext); } pplx::task> matchmaking_service::create_match_ticket( _In_ const xbox::services::multiplayer::multiplayer_session_reference& ticketSessionReference, _In_ const string_t& matchmakingServiceConfigurationId, _In_ const string_t& hopperName, _In_ const std::chrono::seconds& ticketTimeout, _In_ preserve_session_mode preserveSession, _In_ const web::json::value& ticketAttributesJson ) { auto xblContext = m_xblContext; auto asyncWrapper = new AsyncWrapper( [](XAsyncBlock* async, create_match_ticket_response& result) { XblCreateMatchTicketResponse resultResponse; auto hr = XblMatchmakingCreateMatchTicketResult(async, &resultResponse); if (SUCCEEDED(hr)) { result = create_match_ticket_response(resultResponse); } return hr; }); XblMultiplayerSessionReference _ticketSessionReference; Utils::Utf8FromCharT(ticketSessionReference.service_configuration_id().data(), _ticketSessionReference.Scid, sizeof(_ticketSessionReference.Scid)); Utils::Utf8FromCharT(ticketSessionReference.session_template_name().data(), _ticketSessionReference.SessionTemplateName, sizeof(_ticketSessionReference.SessionTemplateName)); Utils::Utf8FromCharT(ticketSessionReference.session_name().data(), _ticketSessionReference.SessionName, sizeof(_ticketSessionReference.SessionName)); auto hr = XblMatchmakingCreateMatchTicketAsync( xblContext, _ticketSessionReference, Utils::StringFromStringT(matchmakingServiceConfigurationId).c_str(), Utils::StringFromStringT(hopperName).c_str(), ticketTimeout.count(), static_cast(preserveSession), Utils::StringFromStringT(ticketAttributesJson.serialize()).c_str(), &asyncWrapper->async ); return asyncWrapper->Task(hr); } pplx::task> matchmaking_service::delete_match_ticket( _In_ const string_t& serviceConfigurationId, _In_ const string_t& hopperName, _In_ const string_t& ticketId ) { auto xblContext = m_xblContext; auto asyncWrapper = new AsyncWrapper(); auto hr = XblMatchmakingDeleteMatchTicketAsync( xblContext, Utils::StringFromStringT(serviceConfigurationId).c_str(), Utils::StringFromStringT(hopperName).c_str(), Utils::StringFromStringT(ticketId).c_str(), &asyncWrapper->async); return asyncWrapper->Task(hr); } pplx::task> matchmaking_service::get_match_ticket_details( _In_ const string_t& serviceConfigurationId, _In_ const string_t& hopperName, _In_ const string_t& ticketId ) { auto xblContext = m_xblContext; auto asyncWrapper = new AsyncWrapper( [](XAsyncBlock* async, match_ticket_details_response& result) { size_t bufferSize; auto hr = XblMatchmakingGetMatchTicketDetailsResultSize(async, &bufferSize); if (SUCCEEDED(hr)) { std::shared_ptr buffer(new char[bufferSize], std::default_delete()); XblMatchTicketDetailsResponse* resultPtr; hr = XblMatchmakingGetMatchTicketDetailsResult(async, bufferSize, buffer.get(), &resultPtr, nullptr); if (SUCCEEDED(hr)) { result = match_ticket_details_response(buffer); } } return hr; }); auto hr = XblMatchmakingGetMatchTicketDetailsAsync( xblContext, Utils::StringFromStringT(serviceConfigurationId).c_str(), Utils::StringFromStringT(hopperName).c_str(), Utils::StringFromStringT(ticketId).c_str(), &asyncWrapper->async); return asyncWrapper->Task(hr); } pplx::task> matchmaking_service::get_hopper_statistics( _In_ const string_t& serviceConfigurationId, _In_ const string_t& hopperName ) { auto xblContext = m_xblContext; auto asyncWrapper = new AsyncWrapper( [](XAsyncBlock* async, hopper_statistics_response& result) { size_t bufferSize; auto hr = XblMatchmakingGetHopperStatisticsResultSize(async, &bufferSize); if (SUCCEEDED(hr)) { std::shared_ptr buffer(new char[bufferSize], std::default_delete()); XblHopperStatisticsResponse* resultPtr; hr = XblMatchmakingGetHopperStatisticsResult(async, bufferSize, buffer.get(), &resultPtr, nullptr); if (SUCCEEDED(hr)) { result = hopper_statistics_response(buffer); } } return hr; }); auto hr = XblMatchmakingGetHopperStatisticsAsync( xblContext, Utils::StringFromStringT(serviceConfigurationId).c_str(), Utils::StringFromStringT(hopperName).c_str(), &asyncWrapper->async); return asyncWrapper->Task(hr); } NAMESPACE_MICROSOFT_XBOX_SERVICES_MATCHMAKING_CPP_END ================================================ FILE: Include/xsapi-cpp/impl/mem.hpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "xsapi-c/xbox_live_global_c.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_SYSTEM_CPP_BEGIN _Ret_maybenull_ _Post_writable_byte_size_(dwSize) void* xsapi_memory::mem_alloc( _In_ size_t dwSize ) { XblMemAllocFunction allocHook{ nullptr }; XblMemFreeFunction freeHook{ nullptr }; HRESULT hr = XblMemGetFunctions(&allocHook, &freeHook); assert(SUCCEEDED(hr)); UNREFERENCED_PARAMETER(hr); try { return allocHook(dwSize, 0); } catch (...) { return nullptr; } } void xsapi_memory::mem_free( _In_opt_ void* pAddress ) { XblMemAllocFunction allocHook{ nullptr }; XblMemFreeFunction freeHook{ nullptr }; HRESULT hr = XblMemGetFunctions(&allocHook, &freeHook); assert(SUCCEEDED(hr)); UNREFERENCED_PARAMETER(hr); try { freeHook(pAddress, 0); } catch (...) { } } NAMESPACE_MICROSOFT_XBOX_SERVICES_SYSTEM_CPP_END ================================================ FILE: Include/xsapi-cpp/impl/multiplayer.hpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "public_utils.h" #include "xsapi-c/multiplayer_manager_c.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_CPP_BEGIN multiplayer_peer_to_host_requirements::multiplayer_peer_to_host_requirements( _In_ const XblMultiplayerPeerToHostRequirements& requirements ) : m_requirements(requirements) { } std::chrono::milliseconds multiplayer_peer_to_host_requirements::latency_maximum() const { return std::chrono::milliseconds(m_requirements.LatencyMaximum); } uint64_t multiplayer_peer_to_host_requirements::bandwidth_down_minimum_in_kilobits_per_second() const { return m_requirements.BandwidthDownMinimumInKbps; } uint64_t multiplayer_peer_to_host_requirements::bandwidth_up_minimum_in_kilobits_per_second() const { return m_requirements.BandwidthUpMinimumInKbps; } multiplay_metrics multiplayer_peer_to_host_requirements::host_selection_metric() const { return static_cast(m_requirements.HostSelectionMetric); } multiplayer_peer_to_peer_requirements::multiplayer_peer_to_peer_requirements( _In_ const XblMultiplayerPeerToPeerRequirements& requirements ) : m_requirements(requirements) { } uint64_t multiplayer_peer_to_peer_requirements::bandwidth_minimum_in_kilobits_per_second() const { return m_requirements.BandwidthMinimumInKbps; } std::chrono::milliseconds multiplayer_peer_to_peer_requirements::latency_maximum() const { return std::chrono::milliseconds(m_requirements.LatencyMaximum); } multiplayer_member_initialization::multiplayer_member_initialization( _In_opt_ const XblMultiplayerMemberInitialization* memberInitialization ) : m_initializationSet(memberInitialization != nullptr) { if (m_initializationSet) { m_memberInitialization = *memberInitialization; } } bool multiplayer_member_initialization::member_initialization_set() const { return m_initializationSet; } std::chrono::milliseconds multiplayer_member_initialization::join_timeout() const { return std::chrono::milliseconds(m_memberInitialization.JoinTimeout); } std::chrono::milliseconds multiplayer_member_initialization::measurement_timeout() const { return std::chrono::milliseconds(m_memberInitialization.MeasurementTimeout); } std::chrono::milliseconds multiplayer_member_initialization::evaluation_timeout() const { return std::chrono::milliseconds(m_memberInitialization.EvaluationTimeout); } bool multiplayer_member_initialization::external_evaluation() const { return m_memberInitialization.ExternalEvaluation; } uint32_t multiplayer_member_initialization::members_need_to_start() const { return m_memberInitialization.MembersNeededToStart; } multiplayer_session_capabilities::multiplayer_session_capabilities() { m_capabilities = XblMultiplayerSessionCapabilities{}; } bool multiplayer_session_capabilities::connectivity() const { return m_capabilities.Connectivity; } void multiplayer_session_capabilities::set_connectivity(_In_ bool connectivity) { m_capabilities.Connectivity = connectivity; } bool multiplayer_session_capabilities::suppress_presence_activity_check() const { return m_capabilities.SuppressPresenceActivityCheck; } void multiplayer_session_capabilities::set_suppress_presence_activity_check(_In_ bool suppressPresenceActivityCheck) { m_capabilities.SuppressPresenceActivityCheck = suppressPresenceActivityCheck; } bool multiplayer_session_capabilities::gameplay() const { return m_capabilities.Gameplay; } void multiplayer_session_capabilities::set_gameplay(_In_ bool gameplay) { m_capabilities.Gameplay = gameplay; } bool multiplayer_session_capabilities::large() const { return m_capabilities.Large; } void multiplayer_session_capabilities::set_large(_In_ bool large) { m_capabilities.Large = large; } bool multiplayer_session_capabilities::connection_required_for_active_members() const { return m_capabilities.ConnectionRequiredForActiveMembers; } void multiplayer_session_capabilities::set_connection_required_for_active_members(_In_ bool connectionRequired) { m_capabilities.ConnectionRequiredForActiveMembers = connectionRequired; } bool multiplayer_session_capabilities::user_authorization_style() const { return m_capabilities.UserAuthorizationStyle; } void multiplayer_session_capabilities::set_user_authorization_style(_In_ bool userAuthorizationStyle) { m_capabilities.UserAuthorizationStyle = userAuthorizationStyle; } bool multiplayer_session_capabilities::crossplay() const { return m_capabilities.Crossplay; } void multiplayer_session_capabilities::set_crossplay(_In_ bool crossplay) { m_capabilities.Crossplay = crossplay; } bool multiplayer_session_capabilities::searchable() const { return m_capabilities.Searchable; } void multiplayer_session_capabilities::set_searchable(_In_ bool searchable) { m_capabilities.Searchable = searchable; } bool multiplayer_session_capabilities::has_owners() const { return m_capabilities.HasOwners; } void multiplayer_session_capabilities::set_has_owners(_In_ bool hasOwners) { m_capabilities.HasOwners = hasOwners; } multiplayer_quality_of_service_measurements::multiplayer_quality_of_service_measurements( _In_ const string_t& memberDeviceToken, _In_ std::chrono::milliseconds latency, _In_ uint64_t bandwidthDownInKilobitsPerSecond, _In_ uint64_t bandwidthUpInKilobitsPerSecond, _In_ const string_t& customJson ) : m_memberDeviceToken(memberDeviceToken), m_latency(latency), m_bandwidthDown(bandwidthDownInKilobitsPerSecond), m_bandwidthUp(bandwidthUpInKilobitsPerSecond) { try { m_customJson = web::json::value::parse(customJson); m_measurementsJson[_T("latency")] = static_cast(m_latency.count()); m_measurementsJson[_T("bandwidthDown")] = m_bandwidthDown; m_measurementsJson[_T("bandwidthUp")] = m_bandwidthUp; m_measurementsJson[_T("custom")] = m_customJson; } catch (web::json::json_exception) {} } multiplayer_quality_of_service_measurements::multiplayer_quality_of_service_measurements( _In_ const string_t& memberDeviceToken, _In_ const web::json::value& measurementsJson ) : m_memberDeviceToken(memberDeviceToken) , m_measurementsJson(measurementsJson) { m_latency = std::chrono::milliseconds(Utils::ExtractJsonUint64(m_measurementsJson, _T("latency"))); m_bandwidthDown = Utils::ExtractJsonUint64(m_measurementsJson, _T("bandwidthDown")); m_bandwidthUp = Utils::ExtractJsonUint64(m_measurementsJson, _T("bandwidthUp")); m_customJson = Utils::ExtractJsonField(m_measurementsJson, _T("custom"), false); } const string_t& multiplayer_quality_of_service_measurements::member_device_token() const { return m_memberDeviceToken; } const std::chrono::milliseconds& multiplayer_quality_of_service_measurements::latency() const { return m_latency; } uint64_t multiplayer_quality_of_service_measurements::bandwidth_down_in_kilobits_per_second() const { return m_bandwidthDown; } uint64_t multiplayer_quality_of_service_measurements::bandwidth_up_in_kilobits_per_second() const { return m_bandwidthUp; } const web::json::value& multiplayer_quality_of_service_measurements::custom_json() const { return m_customJson; } multiplayer_session_constants::multiplayer_session_constants(_In_ XblMultiplayerSessionHandle sessionHandle) { XblMultiplayerSessionDuplicateHandle(sessionHandle, &m_sessionHandle); m_constants = XblMultiplayerSessionSessionConstants(m_sessionHandle); } multiplayer_session_constants::multiplayer_session_constants(bool isLobbySession) : m_sessionHandle(nullptr) { if (isLobbySession) { m_constants = XblMultiplayerManagerLobbySessionConstants(); } else // MPM game session { m_constants = XblMultiplayerManagerGameSessionConstants(); } } multiplayer_session_constants::~multiplayer_session_constants() { if (m_sessionHandle != nullptr) { XblMultiplayerSessionCloseHandle(m_sessionHandle); } } uint32_t multiplayer_session_constants::max_members_in_session() const { return m_constants->MaxMembersInSession; } void multiplayer_session_constants::set_max_members_in_session(_In_ uint32_t maxMembersInSession) { if (m_sessionHandle != nullptr) { XblMultiplayerSessionConstantsSetMaxMembersInSession(m_sessionHandle, maxMembersInSession); } } multiplayer_session_visibility multiplayer_session_constants::visibility() const { return static_cast(m_constants->Visibility); } void multiplayer_session_constants::set_visibility(_In_ multiplayer_session_visibility visibility) { if (m_sessionHandle != nullptr) { XblMultiplayerSessionConstantsSetVisibility(m_sessionHandle, static_cast(visibility)); } } std::vector multiplayer_session_constants::initiator_xbox_user_ids() const { return Utils::XuidStringVectorFromXuidArray(m_constants->InitiatorXuids, m_constants->InitiatorXuidsCount); } web::json::value multiplayer_session_constants::session_custom_constants_json() const { return Utils::ParseJson(m_constants->CustomJson); } web::json::value multiplayer_session_constants::session_cloud_compute_package_constants_json() const { return Utils::ParseJson(m_constants->SessionCloudComputePackageConstantsJson); } std::chrono::milliseconds multiplayer_session_constants::member_reserved_time_out() const { return std::chrono::milliseconds(m_constants->MemberReservedTimeout); } std::chrono::milliseconds multiplayer_session_constants::member_inactive_timeout() const { return std::chrono::milliseconds(m_constants->MemberInactiveTimeout); } std::chrono::milliseconds multiplayer_session_constants::member_ready_timeout() const { return std::chrono::milliseconds(m_constants->MemberReadyTimeout); } std::chrono::milliseconds multiplayer_session_constants::session_empty_timeout() const { return std::chrono::milliseconds(m_constants->SessionEmptyTimeout); } bool multiplayer_session_constants::enable_metrics_latency() const { return m_constants->EnableMetricsLatency; } bool multiplayer_session_constants::enable_metrics_bandwidth_down() const { return m_constants->EnableMetricsBandwidthDown; } bool multiplayer_session_constants::enable_metrics_bandwidth_up() const { return m_constants->EnableMetricsBandwidthUp; } bool multiplayer_session_constants::enable_metrics_custom() const { return m_constants->EnableMetricsCustom; } multiplayer_member_initialization multiplayer_session_constants::member_initialization() const { return multiplayer_member_initialization(m_constants->MemberInitialization); } multiplayer_peer_to_peer_requirements multiplayer_session_constants::peer_to_peer_requirements() const { return multiplayer_peer_to_peer_requirements(m_constants->PeerToPeerRequirements); } multiplayer_peer_to_host_requirements multiplayer_session_constants::peer_to_host_requirements() const { return multiplayer_peer_to_host_requirements(m_constants->PeerToHostRequirements); } web::json::value multiplayer_session_constants::measurement_server_addresses_json() const { return Utils::ParseJson(m_constants->MeasurementServerAddressesJson); } bool multiplayer_session_constants::client_matchmaking_capable() const { return m_constants->ClientMatchmakingCapable; } bool multiplayer_session_constants::capabilities_connectivity() const { return m_constants->SessionCapabilities.Connectivity; } bool multiplayer_session_constants::capabilities_suppress_presence_activity_check() const { return m_constants->SessionCapabilities.SuppressPresenceActivityCheck; } bool multiplayer_session_constants::capabilities_gameplay() const { return m_constants->SessionCapabilities.Gameplay; } bool multiplayer_session_constants::capabilities_large() const { return m_constants->SessionCapabilities.Large; } bool multiplayer_session_constants::capabilities_connection_required_for_active_member() const { return m_constants->SessionCapabilities.ConnectionRequiredForActiveMembers; } bool multiplayer_session_constants::capabilities_crossplay() const { return m_constants->SessionCapabilities.Crossplay; } bool multiplayer_session_constants::capabilities_user_authorization_style() const { return m_constants->SessionCapabilities.UserAuthorizationStyle; } bool multiplayer_session_constants::capabilities_searchable() const { return m_constants->SessionCapabilities.Searchable; } multiplayer_session_reference::multiplayer_session_reference() : m_reference{} { } multiplayer_session_reference::multiplayer_session_reference( _In_ const string_t& serviceConfigurationId, _In_ const string_t& sessionTemplateName, _In_ const string_t& sessionName ) { Utils::Utf8FromCharT(serviceConfigurationId.data(), m_reference.Scid, sizeof(m_reference.Scid)); Utils::Utf8FromCharT(sessionTemplateName.data(), m_reference.SessionTemplateName, sizeof(m_reference.SessionTemplateName)); Utils::Utf8FromCharT(sessionName.data(), m_reference.SessionName, sizeof(m_reference.SessionName)); } multiplayer_session_reference::multiplayer_session_reference( _In_ const XblMultiplayerSessionReference& reference ) : m_reference(reference) { } string_t multiplayer_session_reference::service_configuration_id() const { return Utils::StringTFromUtf8(m_reference.Scid); } string_t multiplayer_session_reference::session_template_name() const { return Utils::StringTFromUtf8(m_reference.SessionTemplateName); } string_t multiplayer_session_reference::session_name() const { return Utils::StringTFromUtf8(m_reference.SessionName); } bool multiplayer_session_reference::is_null() const { return m_reference.Scid[0] == 0 || m_reference.SessionName[0] == 0 || m_reference.SessionTemplateName[0] == 0; } string_t multiplayer_session_reference::to_uri_path() const { XblMultiplayerSessionReferenceUri uri{}; XblMultiplayerSessionReferenceToUriPath(&m_reference, &uri); return Utils::StringTFromUtf8(uri.value); } multiplayer_session_reference multiplayer_session_reference::parse_from_uri_path(_In_ const string_t& path) { XblMultiplayerSessionReference reference; XblMultiplayerSessionReferenceParseFromUriPath(Utils::StringFromStringT(path).data(), &reference); return multiplayer_session_reference(reference); } multiplayer_session_matchmaking_server::multiplayer_session_matchmaking_server( _In_ XblMultiplayerSessionHandle sessionHandle ) { XblMultiplayerSessionDuplicateHandle(sessionHandle, &m_sessionHandle); m_server = XblMultiplayerSessionMatchmakingServer(m_sessionHandle); } multiplayer_session_matchmaking_server::multiplayer_session_matchmaking_server( const multiplayer_session_matchmaking_server& other ) : m_server(other.m_server) { XblMultiplayerSessionDuplicateHandle(other.m_sessionHandle, &m_sessionHandle); m_server = other.m_server; } multiplayer_session_matchmaking_server& multiplayer_session_matchmaking_server::operator=( multiplayer_session_matchmaking_server other ) { std::swap(m_sessionHandle, other.m_sessionHandle); m_server = other.m_server; return *this; } multiplayer_session_matchmaking_server::~multiplayer_session_matchmaking_server() { XblMultiplayerSessionCloseHandle(m_sessionHandle); } matchmaking_status multiplayer_session_matchmaking_server::status() const { return m_server != nullptr ? static_cast(m_server->Status) : matchmaking_status::unknown; } string_t multiplayer_session_matchmaking_server::status_details() const { return m_server != nullptr ? Utils::StringTFromUtf8(m_server->StatusDetails) : string_t(); } std::chrono::seconds multiplayer_session_matchmaking_server::typical_wait() const { return std::chrono::seconds(m_server != nullptr ? m_server->TypicalWaitInSeconds : 0); } multiplayer_session_reference multiplayer_session_matchmaking_server::target_session_ref() const { return m_server != nullptr ? multiplayer_session_reference(m_server->TargetSessionRef) : multiplayer_session_reference(); } bool multiplayer_session_matchmaking_server::is_null() const { return m_server == nullptr; } multiplayer_role_info::multiplayer_role_info() { } multiplayer_role_info::multiplayer_role_info( const XblMultiplayerRole* roleInfo ) : m_maxMembersCount(roleInfo->MaxMemberCount), m_membersCount(roleInfo->MemberCount), m_targetCount(roleInfo->TargetCount) { m_memberXuids = Utils::XuidStringVectorFromXuidArray(roleInfo->MemberXuids, roleInfo->MemberCount); } const std::vector& multiplayer_role_info::member_xbox_user_ids() const { return m_memberXuids; } uint32_t multiplayer_role_info::members_count() const { return m_membersCount; } uint32_t multiplayer_role_info::target_count() const { return m_targetCount; } uint32_t multiplayer_role_info::max_members_count() const { return m_maxMembersCount; } void multiplayer_role_info::set_max_members_count(_In_ uint32_t maxCount) { m_maxMembersCount = maxCount; } void multiplayer_role_info::set_target_count(_In_ uint32_t targetCount) { m_targetCount = targetCount; } multiplayer_role_type::multiplayer_role_type() { } multiplayer_role_type::multiplayer_role_type( const XblMultiplayerRoleType* roleType ) : m_ownerManaged(roleType->OwnerManaged) { if ((roleType->MutableRoleSettings & XblMutableRoleSettings::Max) == XblMutableRoleSettings::Max) { m_mutableRoleSettings.push_back(mutable_role_setting::max); } if ((roleType->MutableRoleSettings & XblMutableRoleSettings::Target) == XblMutableRoleSettings::Target) { m_mutableRoleSettings.push_back(mutable_role_setting::target); } for (uint32_t i = 0; i < roleType->RoleCount; ++i) { m_roles.insert(std::make_pair(Utils::StringTFromUtf8(roleType->Roles[i].Name), multiplayer_role_info(roleType->Roles + i))); } } bool multiplayer_role_type::owner_managed() const { return m_ownerManaged; } const std::vector& multiplayer_role_type::mutable_role_settings() const { return m_mutableRoleSettings; } const std::unordered_map& multiplayer_role_type::roles() const { return m_roles; } void multiplayer_role_type::set_roles(_In_ const std::unordered_map& roles) { m_roles = roles; } multiplayer_session_role_types::multiplayer_session_role_types( const XblMultiplayerRoleType* roleTypes, size_t roleTypesCount ) { for (size_t i = 0; i < roleTypesCount; ++i) { m_roleTypes.insert(std::make_pair(Utils::StringTFromUtf8(roleTypes[i].Name), multiplayer_role_type(roleTypes + i))); } } const std::unordered_map& multiplayer_session_role_types::role_types() const { return m_roleTypes; } multiplayer_activity_details::multiplayer_activity_details(const XblMultiplayerActivityDetails& activityDetails) : m_activityDetails(activityDetails) { if (activityDetails.CustomSessionPropertiesJson != nullptr) { auto len = strlen(activityDetails.CustomSessionPropertiesJson) + 1; auto copy = new char[len]{}; Utils::CopyUtf8(copy, len, activityDetails.CustomSessionPropertiesJson); m_activityDetails.CustomSessionPropertiesJson = copy; } } multiplayer_activity_details::multiplayer_activity_details(const multiplayer_activity_details& other) : multiplayer_activity_details(other.m_activityDetails) { } multiplayer_activity_details& multiplayer_activity_details::operator=(multiplayer_activity_details other) { m_activityDetails = other.m_activityDetails; if (m_activityDetails.CustomSessionPropertiesJson != nullptr) { auto len = strlen(m_activityDetails.CustomSessionPropertiesJson) + 1; auto copy = new char[len] {}; Utils::CopyUtf8(copy, len, m_activityDetails.CustomSessionPropertiesJson); m_activityDetails.CustomSessionPropertiesJson = copy; } return *this; } multiplayer_activity_details::~multiplayer_activity_details() { if (m_activityDetails.CustomSessionPropertiesJson != nullptr) { delete[] m_activityDetails.CustomSessionPropertiesJson; } } multiplayer_session_reference multiplayer_activity_details::session_reference() const { return multiplayer_session_reference(m_activityDetails.SessionReference); } string_t multiplayer_activity_details::handle_id() const { return Utils::StringTFromUtf8(m_activityDetails.HandleId); } uint32_t multiplayer_activity_details::title_id() const { return m_activityDetails.TitleId; } multiplayer_session_visibility multiplayer_activity_details::visibility() const { return static_cast(m_activityDetails.Visibility); } multiplayer_session_restriction multiplayer_activity_details::join_restriction() const { return static_cast(m_activityDetails.JoinRestriction); } bool multiplayer_activity_details::closed() const { return m_activityDetails.Closed; } string_t multiplayer_activity_details::owner_xbox_user_id() const { return Utils::StringTFromUint64(m_activityDetails.OwnerXuid); } uint32_t multiplayer_activity_details::max_members_count() const { return m_activityDetails.MaxMembersCount; } uint32_t multiplayer_activity_details::members_count() const { return m_activityDetails.MembersCount; } web::json::value multiplayer_activity_details::custom_session_properties_json() const { return Utils::ParseJson(m_activityDetails.CustomSessionPropertiesJson); } multiplayer_session_reference multiplayer_search_handle_details::session_reference() const { XblMultiplayerSessionReference sessionRef{}; XblMultiplayerSearchHandleGetSessionReference(m_handle, &sessionRef); return multiplayer_session_reference(sessionRef); } string_t multiplayer_search_handle_details::handle_id() const { const char* id{ nullptr }; XblMultiplayerSearchHandleGetId(m_handle, &id); return Utils::StringTFromUtf8(id); } std::vector multiplayer_search_handle_details::session_owner_xbox_user_ids() const { const uint64_t* xuids{ nullptr }; size_t xuidsCount{ 0 }; XblMultiplayerSearchHandleGetSessionOwnerXuids(m_handle, &xuids, &xuidsCount); return Utils::XuidStringVectorFromXuidArray(xuids, xuidsCount); } std::vector multiplayer_search_handle_details::tags() const { const XblMultiplayerSessionTag* tags{ nullptr }; size_t tagsCount{ 0 }; XblMultiplayerSearchHandleGetTags(m_handle, &tags, &tagsCount); return Utils::Transform(tags, tagsCount, [](XblMultiplayerSessionTag tag) { return Utils::StringTFromUtf8(tag.value); }); } std::unordered_map multiplayer_search_handle_details::numbers_metadata() const { const XblMultiplayerSessionNumberAttribute* attributes{ nullptr }; size_t attributesCount{ 0 }; XblMultiplayerSearchHandleGetNumberAttributes(m_handle, &attributes, &attributesCount); std::unordered_map out; for (auto i = 0u; i < attributesCount; ++i) { out[Utils::StringTFromUtf8(attributes[i].name)] = attributes[i].value; } return out; } std::unordered_map multiplayer_search_handle_details::strings_metadata() const { const XblMultiplayerSessionStringAttribute* attributes{ nullptr }; size_t attributesCount{ 0 }; XblMultiplayerSearchHandleGetStringAttributes(m_handle, &attributes, &attributesCount); std::unordered_map out; for (auto i = 0u; i < attributesCount; ++i) { out[Utils::StringTFromUtf8(attributes[i].name)] = Utils::StringTFromUtf8(attributes[i].value); } return out; } std::unordered_map multiplayer_search_handle_details::role_types() const { return std::unordered_map(); } multiplayer_session_visibility multiplayer_search_handle_details::visibility() const { XblMultiplayerSessionVisibility visibility{ XblMultiplayerSessionVisibility::Unknown }; XblMultiplayerSearchHandleGetVisibility(m_handle, &visibility); return static_cast(visibility); } multiplayer_session_restriction multiplayer_search_handle_details::join_restriction() const { XblMultiplayerSessionRestriction joinRestriction{ XblMultiplayerSessionRestriction::Unknown }; XblMultiplayerSearchHandleGetJoinRestriction(m_handle, &joinRestriction); return static_cast(joinRestriction); } bool multiplayer_search_handle_details::closed() const { bool closed{ false }; XblMultiplayerSearchHandleGetSessionClosed(m_handle, &closed); return closed; } uint32_t multiplayer_search_handle_details::max_members_count() const { size_t maxCount{ 0 }; size_t currentCount{ 0 }; XblMultiplayerSearchHandleGetMemberCounts(m_handle, &maxCount, ¤tCount); return static_cast(maxCount); } uint32_t multiplayer_search_handle_details::members_count() const { size_t maxCount{ 0 }; size_t currentCount{ 0 }; XblMultiplayerSearchHandleGetMemberCounts(m_handle, &maxCount, ¤tCount); return static_cast(currentCount); } web::json::value multiplayer_search_handle_details::custom_session_properties_json() const { const char* customPropertiesJson{ nullptr }; XblMultiplayerSearchHandleGetCustomSessionPropertiesJson(m_handle, &customPropertiesJson); return Utils::ParseJson(customPropertiesJson); } utility::datetime multiplayer_search_handle_details::handle_creation_time() const { time_t creationTime{ 0 }; XblMultiplayerSearchHandleGetCreationTime(m_handle, &creationTime); return Utils::DatetimeFromTimeT(creationTime); } multiplayer_search_handle_details::multiplayer_search_handle_details( XblMultiplayerSearchHandle handle ) { XblMultiplayerSearchHandleDuplicateHandle(handle, &m_handle); } multiplayer_search_handle_details::multiplayer_search_handle_details( const multiplayer_search_handle_details& other ) { XblMultiplayerSearchHandleDuplicateHandle(other.m_handle, &m_handle); } multiplayer_search_handle_details& multiplayer_search_handle_details::operator=( multiplayer_search_handle_details other ) { std::swap(m_handle, other.m_handle); return *this; } multiplayer_search_handle_details::~multiplayer_search_handle_details() { XblMultiplayerSearchHandleCloseHandle(m_handle); } multiplayer_session_states::multiplayer_session_states( _In_ const XblMultiplayerSessionQueryResult& state ) : m_state(state) { } utility::datetime multiplayer_session_states::start_time() const { return Utils::DatetimeFromTimeT(m_state.StartTime); } multiplayer_session_reference multiplayer_session_states::session_reference() const { return multiplayer_session_reference(m_state.SessionReference); } multiplayer_session_status multiplayer_session_states::status() const { return static_cast(m_state.Status); } multiplayer_session_visibility multiplayer_session_states::visibility() const { return static_cast(m_state.Visibility); } bool multiplayer_session_states::is_my_turn() const { return m_state.IsMyTurn; } string_t multiplayer_session_states::xbox_user_id() const { return Utils::StringTFromUint64(m_state.Xuid); } uint32_t multiplayer_session_states::accepted_member_count() const { return m_state.AcceptedMemberCount; } multiplayer_session_restriction multiplayer_session_states::join_restriction() const { return static_cast(m_state.JoinRestriction); } std::vector multiplayer_session_states::keywords() const { return std::vector(); } multiplayer_session_member::multiplayer_session_member( _In_ XblMultiplayerSessionHandle session, _In_ const XblMultiplayerSessionMember* member ) : m_member(member) { XblMultiplayerSessionDuplicateHandle(session, &m_session); } multiplayer_session_member::~multiplayer_session_member() { XblMultiplayerSessionCloseHandle(m_session); } uint32_t multiplayer_session_member::member_id() const { return m_member->MemberId; } string_t multiplayer_session_member::initial_team() const { return Utils::StringTFromUtf8(m_member->InitialTeam); } string_t multiplayer_session_member::xbox_user_id() const { return Utils::StringTFromUint64(m_member->Xuid); } web::json::value multiplayer_session_member::member_custom_constants_json() const { return Utils::ParseJson(m_member->CustomConstantsJson); } string_t multiplayer_session_member::secure_device_base_address64() const { return Utils::StringTFromUtf8(m_member->SecureDeviceBaseAddress64); } std::unordered_map multiplayer_session_member::roles() const { std::unordered_map out; for (uint32_t i = 0; i < m_member->RolesCount; ++i) { out[Utils::StringTFromUtf8(m_member->Roles[i].roleTypeName)] = Utils::StringTFromUtf8(m_member->Roles[i].roleName); } return out; } web::json::value multiplayer_session_member::member_custom_properties_json() const { return Utils::ParseJson(m_member->CustomPropertiesJson); } string_t multiplayer_session_member::gamertag() const { return Utils::StringTFromUtf8(m_member->Gamertag); } multiplayer_session_member_status multiplayer_session_member::status() const { return static_cast(m_member->Status); } bool multiplayer_session_member::is_turn_available() const { return m_member->IsTurnAvailable; } bool multiplayer_session_member::is_current_user() const { return m_member->IsCurrentUser; } bool multiplayer_session_member::initialize_requested() const { return m_member->InitializeRequested; } web::json::value multiplayer_session_member::matchmaking_result_server_measurements_json() const { return Utils::ParseJson(m_member->MatchmakingResultServerMeasurementsJson); } web::json::value multiplayer_session_member::member_server_measurements_json() const { return Utils::ParseJson(m_member->ServerMeasurementsJson); } std::vector> multiplayer_session_member::members_in_group() const { return Utils::Transform>(m_member->MembersInGroupIds, m_member->MembersInGroupIds + m_member->MembersInGroupCount, [this](uint32_t id) { auto member = XblMultiplayerSessionGetMember(m_session, id); if (member != nullptr) { return std::make_shared(m_session, member); } return std::shared_ptr(); }); } std::error_code multiplayer_session_member::set_members_list(_In_ const std::vector>& members) { UNREFERENCED_PARAMETER(members); auto ids = Utils::Transform(members, [](std::shared_ptr member) { return member->member_id(); }); return Utils::ConvertHr(XblMultiplayerSessionCurrentUserSetMembersInGroup(m_session, ids.data(), static_cast(ids.size()))); } std::shared_ptr> multiplayer_session_member::member_measurements() const { auto out = std::make_shared>(); try { if (m_member->QosMeasurementsJson != nullptr) { auto measurementsJson = Utils::ParseJson(m_member->QosMeasurementsJson); for (const auto& pair : measurementsJson.as_object()) { out->push_back(multiplayer_quality_of_service_measurements(pair.first, pair.second)); } } } catch (web::json::json_exception) {} return out; } string_t multiplayer_session_member::device_token() const { return Utils::StringTFromUtf8(m_member->DeviceToken.Value); } network_address_translation_setting multiplayer_session_member::nat() const { return static_cast(m_member->Nat); } uint32_t multiplayer_session_member::active_title_id() const { return m_member->ActiveTitleId; } uint32_t multiplayer_session_member::initialization_episode() const { return m_member->InitializationEpisode; } utility::datetime multiplayer_session_member::join_time() const { return Utils::DatetimeFromTimeT(m_member->JoinTime); } multiplayer_measurement_failure multiplayer_session_member::initialization_failure_cause() const { return static_cast(m_member->InitializationFailureCause); } std::vector multiplayer_session_member::groups() const { return Utils::StringTVectorFromCStringArray(m_member->Groups, m_member->GroupsCount); } void multiplayer_session_member::set_groups(_In_ const std::vector& groups) { UTF8StringArrayRef groupsUtf8{ groups }; XblMultiplayerSessionCurrentUserSetGroups(m_session, groupsUtf8.Data(), static_cast(groupsUtf8.Size())); } std::vector multiplayer_session_member::encounters() const { return Utils::StringTVectorFromCStringArray(m_member->Encounters, m_member->EncountersCount); } void multiplayer_session_member::set_encounters(_In_ const std::vector& encounters) { UTF8StringArrayRef encountersUtf8{ encounters }; XblMultiplayerSessionCurrentUserSetEncounters(m_session, encountersUtf8.Data(), static_cast(encountersUtf8.Size())); } multiplayer_session_properties::multiplayer_session_properties(_In_ XblMultiplayerSessionHandle session) { XblMultiplayerSessionDuplicateHandle(session, &m_session); m_properties = XblMultiplayerSessionSessionProperties(m_session); } multiplayer_session_properties::~multiplayer_session_properties() { XblMultiplayerSessionCloseHandle(m_session); } std::vector multiplayer_session_properties::keywords() const { return Utils::StringTVectorFromCStringArray(m_properties->Keywords, m_properties->KeywordCount); } void multiplayer_session_properties::set_keywords(_In_ const std::vector& keywords) { UTF8StringArrayRef utf8Keywords{ keywords }; XblMultiplayerSessionPropertiesSetKeywords(m_session, utf8Keywords.Data(), utf8Keywords.Size()); } multiplayer_session_restriction multiplayer_session_properties::join_restriction() const { return static_cast(m_properties->JoinRestriction); } std::error_code multiplayer_session_properties::set_join_restriction(_In_ multiplayer_session_restriction joinRestriction) { XblMultiplayerSessionPropertiesSetJoinRestriction(m_session, static_cast(joinRestriction)); return std::error_code(); } multiplayer_session_restriction multiplayer_session_properties::read_restriction() const { return static_cast(m_properties->ReadRestriction); } std::error_code multiplayer_session_properties::set_read_restriction(_In_ multiplayer_session_restriction readRestriction) { XblMultiplayerSessionPropertiesSetReadRestriction(m_session, static_cast(readRestriction)); return std::error_code(); } std::vector> multiplayer_session_properties::turn_collection() const { std::vector> out; for (size_t i = 0; i < m_properties->TurnCollectionCount; ++i) { out.push_back(std::make_shared( m_session, XblMultiplayerSessionGetMember(m_session, m_properties->TurnCollection[i]) )); } return out; } std::error_code multiplayer_session_properties::set_turn_collection( _In_ const std::vector>& turnCollection ) { std::vector memberIds; for (auto& member : turnCollection) { memberIds.push_back(member->member_id()); } auto hr = XblMultiplayerSessionPropertiesSetTurnCollection(m_session, memberIds.data(), static_cast(memberIds.size())); return std::make_error_code(xbox::services::xbox_live_error_code(hr)); } web::json::value multiplayer_session_properties::matchmaking_target_session_constants_json() const { return Utils::ParseJson(m_properties->MatchmakingTargetSessionConstantsJson); } web::json::value multiplayer_session_properties::session_custom_properties_json() const { return Utils::ParseJson(m_properties->SessionCustomPropertiesJson); } string_t multiplayer_session_properties::matchmaking_server_connection_string() const { return Utils::StringTFromUtf8(m_properties->MatchmakingServerConnectionString); } std::vector multiplayer_session_properties::server_connection_string_candidates() const { return Utils::StringTVectorFromCStringArray(m_properties->ServerConnectionStringCandidates, m_properties->ServerConnectionStringCandidatesCount); } std::vector multiplayer_session_properties::session_owner_indices() const { return std::vector(m_properties->SessionOwnerMemberIds, m_properties->SessionOwnerMemberIds + m_properties->SessionOwnerMemberIdsCount); } string_t multiplayer_session_properties::host_device_token() const { return Utils::StringTFromUtf8(m_properties->HostDeviceToken.Value); } bool multiplayer_session_properties::closed() const { return m_properties->Closed; } bool multiplayer_session_properties::locked() const { return m_properties->Locked; } bool multiplayer_session_properties::allocate_cloud_compute() const { return m_properties->AllocateCloudCompute; } multiplayer_session::multiplayer_session( _In_ const string_t& xboxUserId ) { m_handle = XblMultiplayerSessionCreateHandle(Utils::Uint64FromStringT(xboxUserId), nullptr, nullptr); m_sessionInfo = XblMultiplayerSessionGetInfo(m_handle); } multiplayer_session::multiplayer_session( _In_ const string_t& xboxUserId, _In_ multiplayer_session_reference sessionReference ) { m_handle = XblMultiplayerSessionCreateHandle( Utils::Uint64FromStringT(xboxUserId), &sessionReference.m_reference, nullptr ); m_sessionInfo = XblMultiplayerSessionGetInfo(m_handle); } multiplayer_session::multiplayer_session( _In_ const string_t& xboxUserId, _In_ multiplayer_session_reference multiplayerSessionReference, _In_ uint32_t maxMembersInSession, _In_ multiplayer_session_visibility multiplayerSessionVisibility, _In_ std::vector initiatorXboxUserIds, _In_ const web::json::value& sessionCustomConstantsJson ) { auto initiatorXuids = Utils::Transform(initiatorXboxUserIds.begin(), initiatorXboxUserIds.end(), Utils::Uint64FromStringT); std::string sessionCustomConstantsString = Utils::StringFromStringT(sessionCustomConstantsJson.serialize()); XblMultiplayerSessionInitArgs args{}; args.MaxMembersInSession = maxMembersInSession; args.Visibility = static_cast(multiplayerSessionVisibility); if (!initiatorXuids.empty()) { args.InitiatorXuidsCount = static_cast(initiatorXuids.size()); args.InitiatorXuids = initiatorXuids.data(); } if (!sessionCustomConstantsJson.is_null()) { args.CustomJson = sessionCustomConstantsString.data(); } m_handle = XblMultiplayerSessionCreateHandle( Utils::Uint64FromStringT(xboxUserId), &multiplayerSessionReference.m_reference, &args ); m_sessionInfo = XblMultiplayerSessionGetInfo(m_handle); } multiplayer_session::multiplayer_session(XblMultiplayerSessionHandle handle) { XblMultiplayerSessionDuplicateHandle(handle, &m_handle); m_sessionInfo = XblMultiplayerSessionGetInfo(m_handle); } multiplayer_session::~multiplayer_session() { XblMultiplayerSessionCloseHandle(m_handle); } string_t multiplayer_session::multiplayer_correlation_id() const { return Utils::StringTFromUtf8(m_sessionInfo->CorrelationId); } string_t multiplayer_session::search_handle_id() const { return Utils::StringTFromUtf8(m_sessionInfo->SearchHandleId); } utility::datetime multiplayer_session::start_time() const { return Utils::DatetimeFromTimeT(m_sessionInfo->StartTime); } utility::datetime multiplayer_session::date_of_next_timer() const { return Utils::DatetimeFromTimeT(m_sessionInfo->NextTimer); } utility::datetime multiplayer_session::date_of_session() const { return Utils::DatetimeFromTimeT(XblMultiplayerSessionTimeOfSession(m_handle)); } multiplayer_initialization_stage multiplayer_session::initialization_stage() const { auto initInfo = XblMultiplayerSessionGetInitializationInfo(m_handle); if (initInfo != nullptr) { return static_cast(initInfo->Stage); } return multiplayer_initialization_stage::none; } utility::datetime multiplayer_session::initializing_stage_start_time() const { auto initInfo = XblMultiplayerSessionGetInitializationInfo(m_handle); if (initInfo != nullptr) { return Utils::DatetimeFromTimeT(initInfo->StageStartTime); } return utility::datetime(); } uint32_t multiplayer_session::intializing_episode() const { auto initInfo = XblMultiplayerSessionGetInitializationInfo(m_handle); if (initInfo != nullptr) { return initInfo->Episode; } return 0; } multiplayer_session_change_types multiplayer_session::subscribed_change_types() const { return static_cast(XblMultiplayerSessionSubscribedChangeTypes(m_handle)); } std::vector multiplayer_session::host_candidates() const { const XblDeviceToken* tokens{ nullptr }; size_t tokensCount; XblMultiplayerSessionHostCandidates(m_handle, &tokens, &tokensCount); return Utils::Transform(tokens, tokens + tokensCount, [](const XblDeviceToken& token) { return Utils::StringTFromUtf8(token.Value); }); } multiplayer_session_reference multiplayer_session::session_reference() const { return multiplayer_session_reference(*XblMultiplayerSessionSessionReference(m_handle)); } std::shared_ptr multiplayer_session::session_constants() const { if (m_sessionConstants == nullptr) { m_sessionConstants = std::make_shared(m_handle); } return m_sessionConstants; } std::shared_ptr multiplayer_session::session_properties() const { if (m_sessionProperties == nullptr) { m_sessionProperties = std::make_shared(m_handle); } return m_sessionProperties; } std::shared_ptr multiplayer_session::session_role_types() const { const XblMultiplayerRoleType* roleTypes{ nullptr }; size_t roleTypesCount; XblMultiplayerSessionRoleTypes(m_handle, &roleTypes, &roleTypesCount); return std::shared_ptr(new multiplayer_session_role_types(roleTypes, roleTypesCount)); } std::vector> multiplayer_session::members() const { const XblMultiplayerSessionMember* members{ nullptr }; size_t membersCount; XblMultiplayerSessionMembers(m_handle, &members, &membersCount); return Utils::Transform>(members, members + membersCount, [this](const XblMultiplayerSessionMember& member) { return std::make_shared(m_handle, &member); }); } multiplayer_session_matchmaking_server multiplayer_session::matchmaking_server() const { return multiplayer_session_matchmaking_server(m_handle); } uint32_t multiplayer_session::members_accepted() const { return XblMultiplayerSessionMembersAccepted(m_handle); } web::json::value multiplayer_session::servers_json() const { return Utils::ParseJson(XblMultiplayerSessionRawServersJson(m_handle)); } void multiplayer_session::set_servers_json(_In_ const web::json::value& serversJson) { std::string serversJsonString = Utils::StringFromStringT(serversJson.serialize()); XblMultiplayerSessionSetRawServersJson(m_handle, serversJsonString.data()); } string_t multiplayer_session::e_tag() const { return Utils::StringTFromUtf8(XblMultiplayerSessionEtag(m_handle)); } std::shared_ptr multiplayer_session::current_user() const { auto currentUser = XblMultiplayerSessionCurrentUser(m_handle); if (currentUser != nullptr) { return std::make_shared(m_handle, currentUser); } return nullptr; } string_t multiplayer_session::branch() const { return Utils::StringTFromUtf8(m_sessionInfo->Branch); } uint64_t multiplayer_session::change_number() const { return m_sessionInfo->ChangeNumber; } write_session_status multiplayer_session::write_status() const { return static_cast(XblMultiplayerSessionWriteStatus(m_handle)); } std::error_code multiplayer_session::add_member_reservation( _In_ const string_t& xboxUserId, _In_ const web::json::value& memberCustomConstantsJson ) { return add_member_reservation(xboxUserId, memberCustomConstantsJson, false); } std::error_code multiplayer_session::add_member_reservation( _In_ const string_t& xboxUserId, _In_ const web::json::value& memberCustomConstantsJson, _In_ bool initializeRequested ) { std::string jsonString = Utils::StringFromStringT(memberCustomConstantsJson.serialize()); auto hr = XblMultiplayerSessionAddMemberReservation( m_handle, Utils::Uint64FromStringT(xboxUserId), memberCustomConstantsJson.is_null() ? nullptr : jsonString.data(), initializeRequested ); return std::make_error_code(xbox::services::xbox_live_error_code(hr)); } xbox_live_result> multiplayer_session::join( _In_ const web::json::value& memberCustomConstantsJson, _In_ bool initializeRequested, _In_ bool joinWithActiveStatus, _In_ bool addInitializePropertyToRequest ) { UNREFERENCED_PARAMETER(addInitializePropertyToRequest); std::string jsonString = Utils::StringFromStringT(memberCustomConstantsJson.serialize()); auto hr = XblMultiplayerSessionJoin( m_handle, memberCustomConstantsJson.is_null() ? nullptr : jsonString.data(), initializeRequested, joinWithActiveStatus ); if (SUCCEEDED(hr)) { auto currentUser = std::make_shared(m_handle, XblMultiplayerSessionCurrentUser(m_handle)); return xbox_live_result>(currentUser); } return xbox_live_result>( std::make_error_code(xbox::services::xbox_live_error_code(hr)) ); } void multiplayer_session::set_visibility(_In_ multiplayer_session_visibility visibility) { XblMultiplayerSessionConstantsSetVisibility(m_handle, static_cast(visibility)); } void multiplayer_session::set_max_members_in_session(_In_ uint32_t maxMembersInSession) { XblMultiplayerSessionConstantsSetMaxMembersInSession(m_handle, maxMembersInSession); } std::error_code multiplayer_session::set_mutable_role_settings( _In_ const std::unordered_map& roleTypes ) { HRESULT finalHr = S_OK; for (const auto& roleType : roleTypes) { for (const auto& role : roleType.second.roles()) { auto max = role.second.max_members_count(); auto target = role.second.target_count(); auto hr = XblMultiplayerSessionSetMutableRoleSettings( m_handle, Utils::StringFromStringT(roleType.first).data(), Utils::StringFromStringT(role.first).data(), &max, &target ); if (FAILED(hr)) { finalHr = hr; } } } return Utils::ConvertHr(finalHr); } std::error_code multiplayer_session::set_timeouts( _In_ std::chrono::milliseconds memberReservedTimeout, _In_ std::chrono::milliseconds memberInactiveTimeout, _In_ std::chrono::milliseconds memberReadyTimeout, _In_ std::chrono::milliseconds sessionEmptyTimeout ) { auto hr = XblMultiplayerSessionConstantsSetTimeouts( m_handle, memberReservedTimeout.count(), memberInactiveTimeout.count(), memberReadyTimeout.count(), sessionEmptyTimeout.count() ); return Utils::ConvertHr(hr); } std::error_code multiplayer_session::set_quality_of_service_connectivity_metrics( _In_ bool enableLatencyMetric, _In_ bool enableBandwidthDownMetric, _In_ bool enableBandwidthUpMetric, _In_ bool enableCustomMetric ) { auto hr = XblMultiplayerSessionConstantsSetQosConnectivityMetrics( m_handle, enableLatencyMetric, enableBandwidthDownMetric, enableBandwidthUpMetric, enableCustomMetric ); return Utils::ConvertHr(hr); } std::error_code multiplayer_session::set_member_initialization( _In_ std::chrono::milliseconds joinTimeout, _In_ std::chrono::milliseconds measurementTimeout, _In_ std::chrono::milliseconds evaluationTimeout, _In_ bool externalEvaluation, _In_ uint32_t membersNeededToStart ) { auto hr = XblMultiplayerSessionConstantsSetMemberInitialization( m_handle, XblMultiplayerMemberInitialization { static_cast(joinTimeout.count()), static_cast(measurementTimeout.count()), static_cast(evaluationTimeout.count()), externalEvaluation, membersNeededToStart } ); return Utils::ConvertHr(hr); } std::error_code multiplayer_session::set_peer_to_peer_requirements( _In_ std::chrono::milliseconds latencyMaximum, _In_ uint32_t bandwidthMinimumInKilobitsPerSecond ) { auto hr = XblMultiplayerSessionConstantsSetPeerToPeerRequirements( m_handle, XblMultiplayerPeerToPeerRequirements { static_cast(latencyMaximum.count()), bandwidthMinimumInKilobitsPerSecond } ); return Utils::ConvertHr(hr); } std::error_code multiplayer_session::set_peer_to_host_requirements( _In_ std::chrono::milliseconds latencyMaximum, _In_ uint32_t bandwidthDownMinimumInKilobitsPerSecond, _In_ uint32_t bandwidthUpMinimumInKilobitsPerSecond, _In_ multiplay_metrics hostSelectionMetric ) { auto hr = XblMultiplayerSessionConstantsSetPeerToHostRequirements( m_handle, XblMultiplayerPeerToHostRequirements { static_cast(latencyMaximum.count()), bandwidthDownMinimumInKilobitsPerSecond, bandwidthUpMinimumInKilobitsPerSecond, static_cast(hostSelectionMetric) } ); return Utils::ConvertHr(hr); } std::error_code multiplayer_session::set_session_capabilities( _In_ const multiplayer_session_capabilities& capabilities ) { auto hr = XblMultiplayerSessionConstantsSetCapabilities(m_handle, capabilities.m_capabilities); return Utils::ConvertHr(hr); } std::error_code multiplayer_session::set_cloud_compute_package_json( _In_ const web::json::value& sessionCloudComputePackageConstantsJson ) { std::string jsonString = Utils::StringFromStringT(sessionCloudComputePackageConstantsJson.serialize()); auto hr = XblMultiplayerSessionConstantsSetCloudComputePackageJson(m_handle, jsonString.data()); return Utils::ConvertHr(hr); } void multiplayer_session::set_initialization_status( _In_ bool initializationSucceeded ) { XblMultiplayerSessionSetInitializationSucceeded(m_handle, initializationSucceeded); } void multiplayer_session::set_host_device_token( _In_ const string_t& hostDeviceToken ) { XblDeviceToken token; Utils::Utf8FromCharT(hostDeviceToken.data(), token.Value, sizeof(token.Value)); XblMultiplayerSessionSetHostDeviceToken(m_handle, token); } void multiplayer_session::set_matchmaking_server_connection_path( _In_ const string_t& serverConnectionPath ) { XblMultiplayerSessionSetMatchmakingServerConnectionPath( m_handle, Utils::StringFromStringT(serverConnectionPath).data() ); } void multiplayer_session::set_closed( _In_ bool closed ) { XblMultiplayerSessionSetClosed(m_handle, closed); } void multiplayer_session::set_locked( _In_ bool locked ) { XblMultiplayerSessionSetLocked(m_handle, locked); } void multiplayer_session::set_allocate_cloud_compute( _In_ bool allocateCloudCompute ) { XblMultiplayerSessionSetAllocateCloudCompute(m_handle, allocateCloudCompute); } void multiplayer_session::set_matchmaking_resubmit( _In_ bool matchResubmit ) { XblMultiplayerSessionSetMatchmakingResubmit(m_handle, matchResubmit); } void multiplayer_session::set_server_connection_string_candidates( _In_ const std::vector& serverConnectionStringCandidates ) { UTF8StringArrayRef candidates{ serverConnectionStringCandidates }; XblMultiplayerSessionSetServerConnectionStringCandidates(m_handle, candidates.Data(), static_cast(candidates.Size())); } std::error_code multiplayer_session::set_session_change_subscription( _In_ multiplayer_session_change_types changeTypes ) { return Utils::ConvertHr(XblMultiplayerSessionSetSessionChangeSubscription(m_handle, static_cast(changeTypes))); } std::error_code multiplayer_session::leave() { return Utils::ConvertHr(XblMultiplayerSessionLeave(m_handle)); } std::error_code multiplayer_session::set_current_user_status( _In_ multiplayer_session_member_status status ) { return Utils::ConvertHr(XblMultiplayerSessionCurrentUserSetStatus(m_handle, static_cast(status))); } std::error_code multiplayer_session::set_current_user_secure_device_address_base64( _In_ const string_t& value ) { auto hr = XblMultiplayerSessionCurrentUserSetSecureDeviceAddressBase64(m_handle, Utils::StringFromStringT(value).data()); return Utils::ConvertHr(hr); } std::error_code multiplayer_session::set_current_user_role_info( _In_ const std::unordered_map& roles ) { auto rolesVector = Utils::Transform(roles.begin(), roles.end(), [](const std::pair& in) { int cchRoleType = Utils::Utf8FromCharT(in.first.data(), nullptr, 0); auto roleTypeName = new char[cchRoleType]; Utils::Utf8FromCharT(in.first.data(), roleTypeName, cchRoleType); int cchRole = Utils::Utf8FromCharT(in.second.data(), nullptr, 0); auto roleName = new char[cchRole]; Utils::Utf8FromCharT(in.second.data(), roleName, cchRole); return XblMultiplayerSessionMemberRole{ roleTypeName, roleName }; }); auto hr = XblMultiplayerSessionCurrentUserSetRoles(m_handle, rolesVector.data(), static_cast(rolesVector.size())); for (auto& role : rolesVector) { delete[] role.roleTypeName; delete[] role.roleName; } return Utils::ConvertHr(hr); } std::error_code multiplayer_session::set_current_user_members_in_group( _In_ const std::vector>& membersInGroup ) { auto memberIds = Utils::Transform(membersInGroup.begin(), membersInGroup.end(), [](std::shared_ptr in) { return in->member_id(); }); auto hr = XblMultiplayerSessionCurrentUserSetMembersInGroup(m_handle, memberIds.data(), static_cast(memberIds.size())); return Utils::ConvertHr(hr); } std::error_code multiplayer_session::set_current_user_quality_of_service_measurements( _In_ std::shared_ptr> measurements ) { web::json::value json; for (const auto& measurement : *measurements) { json[measurement.m_memberDeviceToken] = measurement.m_measurementsJson; } auto hr = XblMultiplayerSessionCurrentUserSetQosMeasurements(m_handle, Utils::StringFromStringT(json.serialize()).data()); return Utils::ConvertHr(hr); } std::error_code multiplayer_session::set_current_user_quality_of_service_measurements_json( _In_ const web::json::value& serverMeasurementsJson ) { std::string jsonString = Utils::StringFromStringT(serverMeasurementsJson.serialize()); auto hr = XblMultiplayerSessionCurrentUserSetServerQosMeasurements(m_handle, jsonString.data()); return Utils::ConvertHr(hr); } std::error_code multiplayer_session::set_current_user_member_custom_property_json( _In_ const string_t& name, _In_ const web::json::value& valueJson ) { auto hr = XblMultiplayerSessionCurrentUserSetCustomPropertyJson( m_handle, Utils::StringFromStringT(name).data(), Utils::StringFromStringT(valueJson.serialize()).data() ); return Utils::ConvertHr(hr); } std::error_code multiplayer_session::delete_current_user_member_custom_property_json( _In_ const string_t& name ) { auto hr = XblMultiplayerSessionCurrentUserDeleteCustomPropertyJson( m_handle, Utils::StringFromStringT(name).data() ); return Utils::ConvertHr(hr); } std::error_code multiplayer_session::set_matchmaking_target_session_constants_json( _In_ const web::json::value& matchmakingTargetSessionConstantsJson ) { auto hr = XblMultiplayerSessionSetMatchmakingTargetSessionConstantsJson( m_handle, Utils::StringFromStringT(matchmakingTargetSessionConstantsJson.serialize()).data() ); return Utils::ConvertHr(hr); } std::error_code multiplayer_session::set_session_custom_property_json( _In_ const string_t& name, _In_ const web::json::value& valueJson ) { auto hr = XblMultiplayerSessionSetCustomPropertyJson( m_handle, Utils::StringFromStringT(name).data(), Utils::StringFromStringT(valueJson.serialize()).data() ); return Utils::ConvertHr(hr); } std::error_code multiplayer_session::delete_session_custom_property_json( _In_ const string_t& name ) { auto hr = XblMultiplayerSessionDeleteCustomPropertyJson( m_handle, Utils::StringFromStringT(name).data() ); return Utils::ConvertHr(hr); } xbox_live_result multiplayer_session::compare_multiplayer_sessions( _In_ std::shared_ptr currentSession, _In_ std::shared_ptr oldSession ) { if (currentSession == nullptr || oldSession == nullptr) { return xbox_live_result(std::make_error_code(xbox_live_error_code::invalid_argument), "Cannot compare a null session"); } auto changes = XblMultiplayerSessionCompare(currentSession->m_handle, oldSession->m_handle); return xbox_live_result(static_cast(changes)); } write_session_status multiplayer_session::convert_http_status_to_write_session_status( _In_ int32_t httpStatusCode ) { switch (httpStatusCode) { case 200: return write_session_status::updated; case 201: return write_session_status::created; case 204: return write_session_status::session_deleted; case 401: return write_session_status::access_denied; case 404: return write_session_status::handle_not_found; case 409: return write_session_status::conflict; case 412: return write_session_status::out_of_sync; default: return write_session_status::unknown; } } multiplayer_session_change_event_args::multiplayer_session_change_event_args( const XblMultiplayerSessionChangeEventArgs& args ) : m_args(args) { } multiplayer_session_reference multiplayer_session_change_event_args::session_reference() const { return multiplayer_session_reference(m_args.SessionReference); } string_t multiplayer_session_change_event_args::branch() const { return Utils::StringTFromUtf8(m_args.Branch); } uint64_t multiplayer_session_change_event_args::change_number() const { return m_args.ChangeNumber; } multiplayer_get_sessions_request::multiplayer_get_sessions_request( _In_ string_t serviceConfigurationId, _In_ uint32_t maxItems ) : m_request{} { Utils::Utf8FromCharT(serviceConfigurationId.data(), m_request.Scid, sizeof(m_request.Scid)); m_request.MaxItems = maxItems; } string_t multiplayer_get_sessions_request::service_configuration_id() { return Utils::StringTFromUtf8(m_request.Scid); } uint32_t multiplayer_get_sessions_request::max_items() { return m_request.MaxItems; } bool multiplayer_get_sessions_request::include_private_sessions() { return m_request.IncludePrivateSessions; } void multiplayer_get_sessions_request::set_include_private_sessions(_In_ bool includePrivateSessions) { m_request.IncludePrivateSessions = includePrivateSessions; } bool multiplayer_get_sessions_request::include_reservations() { return m_request.IncludeReservations; } void multiplayer_get_sessions_request::set_include_reservations(_In_ bool includeResevations) { m_request.IncludeReservations = includeResevations; } bool multiplayer_get_sessions_request::include_inactive_sessions() { return m_request.IncludeInactiveSessions; } void multiplayer_get_sessions_request::set_include_inactive_sessions(_In_ bool includeInactiveSessions) { m_request.IncludeInactiveSessions = includeInactiveSessions; } string_t multiplayer_get_sessions_request::xbox_user_id_filter() { if (m_request.XuidFiltersCount > 0) { return Utils::StringTFromUint64(m_request.XuidFilters[0]); } return string_t(); } void multiplayer_get_sessions_request::set_xbox_user_id_filter(_In_ const string_t& filter) { m_xuidFilters.clear(); if (!filter.empty()) { m_xuidFilters.push_back(Utils::Uint64FromStringT(filter)); m_request.XuidFilters = m_xuidFilters.data(); } m_request.XuidFiltersCount = m_xuidFilters.size(); } std::vector multiplayer_get_sessions_request::xbox_user_ids_filter() { return Utils::Transform(m_xuidFilters, Utils::StringTFromUint64); } void multiplayer_get_sessions_request::set_xbox_user_ids_filter(_In_ const std::vector& filters) { m_xuidFilters = Utils::Transform(filters, Utils::Uint64FromStringT); m_request.XuidFiltersCount = static_cast(m_xuidFilters.size()); m_request.XuidFilters = m_xuidFilters.data(); } string_t multiplayer_get_sessions_request::keyword_filter() { return Utils::StringTFromUtf8(m_keywordFilter.data()); } void multiplayer_get_sessions_request::set_keyword_filter(_In_ const string_t& filter) { if (!filter.empty()) { m_keywordFilter = Utils::StringFromStringT(filter); m_request.KeywordFilter = m_keywordFilter.data(); } else { m_request.KeywordFilter = nullptr; } } string_t multiplayer_get_sessions_request::session_template_name_filter() { return Utils::StringTFromUtf8(m_request.SessionTemplateNameFilter); } void multiplayer_get_sessions_request::set_session_template_name_filter(_In_ const string_t& filter) { Utils::Utf8FromCharT(filter.data(), m_request.SessionTemplateNameFilter, sizeof(m_request.SessionTemplateNameFilter)); } multiplayer_session_visibility multiplayer_get_sessions_request::visibility_filter() { return static_cast(m_request.VisibilityFilter); } void multiplayer_get_sessions_request::set_visibility_filter(_In_ multiplayer_session_visibility filter) { m_request.VisibilityFilter = static_cast(filter); } uint32_t multiplayer_get_sessions_request::contract_version_filter() { return m_request.ContractVersionFilter; } void multiplayer_get_sessions_request::set_contract_version_filter(_In_ uint32_t filter) { m_request.ContractVersionFilter = filter; } multiplayer_query_search_handle_request::multiplayer_query_search_handle_request( _In_ const string_t& serviceConfigurationId, _In_ const string_t& sessionTemplateName ) : m_scid{ serviceConfigurationId }, m_sessionTemplateName{ sessionTemplateName } { } const string_t& multiplayer_query_search_handle_request::service_configuration_id() const { return m_scid; } const string_t& multiplayer_query_search_handle_request::session_template_name() const { return m_sessionTemplateName; } const string_t& multiplayer_query_search_handle_request::order_by() const { return m_orderBy; } void multiplayer_query_search_handle_request::set_order_by(_In_ const string_t& orderBy) { m_orderBy = orderBy; } bool multiplayer_query_search_handle_request::order_ascending() const { return m_orderAscending; } void multiplayer_query_search_handle_request::set_order_ascending(_In_ bool orderAscending) { m_orderAscending = orderAscending; } const string_t& multiplayer_query_search_handle_request::search_filter() const { return m_searchFilter; } void multiplayer_query_search_handle_request::set_search_filter(_In_ const string_t& searchFilter) { m_searchFilter = searchFilter; } const string_t& multiplayer_query_search_handle_request::social_group() const { return m_socialGroup; } void multiplayer_query_search_handle_request::set_social_group(_In_ const string_t& socialGroup) { m_socialGroup = socialGroup; } multiplayer_search_handle_request::multiplayer_search_handle_request( _In_ multiplayer_session_reference sessionRef ) : m_sessionRef{ sessionRef.m_reference } { } multiplayer_session_reference multiplayer_search_handle_request::session_reference() const { return multiplayer_session_reference(m_sessionRef); } std::vector multiplayer_search_handle_request::tags() const { return Utils::Transform(m_tags, [](XblMultiplayerSessionTag tag) { return Utils::StringTFromUtf8(tag.value); }); } void multiplayer_search_handle_request::set_tags(_In_ const std::vector& value) { m_tags = Utils::Transform(value, [](const string_t& str) { XblMultiplayerSessionTag tag{}; Utils::Utf8FromCharT(str.data(), tag.value, sizeof(tag.value)); return tag; }); } std::unordered_map multiplayer_search_handle_request::numbers_metadata() const { std::unordered_map out; for (auto& attribute : m_numberAttributes) { out[Utils::StringTFromUtf8(attribute.name)] = attribute.value; } return out; } void multiplayer_search_handle_request::set_numbers_metadata( _In_ const std::unordered_map& metadata ) { m_numberAttributes = Utils::Transform(metadata.begin(), metadata.end(), [](const std::unordered_map::value_type& pair) { XblMultiplayerSessionNumberAttribute attr{ {}, pair.second }; Utils::Utf8FromCharT(pair.first.data(), attr.name, sizeof(attr.name)); return attr; }); } std::unordered_map multiplayer_search_handle_request::strings_metadata() const { std::unordered_map out; for (auto& attribute : m_stringAttributes) { out[Utils::StringTFromUtf8(attribute.name)] = Utils::StringTFromUtf8(attribute.value); } return out; } void multiplayer_search_handle_request::set_strings_metadata( _In_ const std::unordered_map& metadata ) { m_stringAttributes = Utils::Transform(metadata.begin(), metadata.end(), [](const std::unordered_map::value_type& pair) { XblMultiplayerSessionStringAttribute attr{}; Utils::Utf8FromCharT(pair.first.data(), attr.name, sizeof(attr.name)); Utils::Utf8FromCharT(pair.second.data(), attr.value, sizeof(attr.value)); return attr; }); } multiplayer_service::multiplayer_service(_In_ XblContextHandle contextHandle) { XblContextDuplicateHandle(contextHandle, &m_handle); } multiplayer_service::multiplayer_service(const multiplayer_service& other) { XblContextDuplicateHandle(other.m_handle, &m_handle); } multiplayer_service& multiplayer_service::operator=(multiplayer_service other) { std::swap(m_handle, other.m_handle); return *this; } multiplayer_service::~multiplayer_service() { XblContextCloseHandle(m_handle); } pplx::task>> multiplayer_service::write_session( _In_ std::shared_ptr multiplayerSession, _In_ multiplayer_session_write_mode writeMode ) { auto asyncWrapper = new AsyncWrapper>( [](XAsyncBlock* async, std::shared_ptr& result) { XblMultiplayerSessionHandle sessionHandle; auto hr = XblMultiplayerWriteSessionResult(async, &sessionHandle); if (sessionHandle) { XblWriteSessionStatus sessionStatus = XblMultiplayerSessionWriteStatus(sessionHandle); switch (sessionStatus) { case XblWriteSessionStatus::Updated: //200 Updated Successfully case XblWriteSessionStatus::Created: //201 Created Successfully case XblWriteSessionStatus::SessionDeleted: //204 Deleted Successfully //Intentional Fallthrough hr = S_OK; break; case XblWriteSessionStatus::AccessDenied: //403 Forbidden hr = HTTP_E_STATUS_FORBIDDEN; break; case XblWriteSessionStatus::HandleNotFound: //404 Not Found hr = HTTP_E_STATUS_NOT_FOUND; break; case XblWriteSessionStatus::Conflict: //409 Conflict hr = HTTP_E_STATUS_CONFLICT; break; case XblWriteSessionStatus::OutOfSync: //412 Not the most recent hr = HTTP_E_STATUS_PRECOND_FAILED; break; case XblWriteSessionStatus::Unknown: default: break; } result = std::shared_ptr(new multiplayer_session(sessionHandle)); XblMultiplayerSessionCloseHandle(sessionHandle); } else { result = nullptr; } return hr; }); auto hr = XblMultiplayerWriteSessionAsync(m_handle, multiplayerSession->m_handle, static_cast(writeMode), &asyncWrapper->async); return asyncWrapper->Task(hr); } pplx::task>> multiplayer_service::write_session_by_handle( _In_ std::shared_ptr multiplayerSession, _In_ multiplayer_session_write_mode writeMode, _In_ const string_t& handleId ) { auto asyncWrapper = new AsyncWrapper>( [](XAsyncBlock* async, std::shared_ptr& result) { XblMultiplayerSessionHandle sessionHandle; auto hr = XblMultiplayerWriteSessionByHandleResult(async, &sessionHandle); if (SUCCEEDED(hr)) { if (sessionHandle) { result = std::shared_ptr(new multiplayer_session(sessionHandle)); XblMultiplayerSessionCloseHandle(sessionHandle); } else { result = nullptr; } } return hr; }); auto hr = XblMultiplayerWriteSessionByHandleAsync( m_handle, multiplayerSession->m_handle, static_cast(writeMode), Utils::StringFromStringT(handleId).data(), &asyncWrapper->async ); return asyncWrapper->Task(hr); } pplx::task>> multiplayer_service::get_current_session( _In_ multiplayer_session_reference sessionReference ) { auto asyncWrapper = new AsyncWrapper>( [](XAsyncBlock* async, std::shared_ptr& result) { XblMultiplayerSessionHandle sessionHandle; auto hr = XblMultiplayerGetSessionResult(async, &sessionHandle); if (SUCCEEDED(hr)) { if (sessionHandle) { result = std::shared_ptr(new multiplayer_session(sessionHandle)); XblMultiplayerSessionCloseHandle(sessionHandle); } else { result = nullptr; } } return hr; }); auto hr = XblMultiplayerGetSessionAsync(m_handle, &sessionReference.m_reference, &asyncWrapper->async); return asyncWrapper->Task(hr); } pplx::task>> multiplayer_service::get_current_session_by_handle( _In_ const string_t& handleId ) { auto asyncWrapper = new AsyncWrapper>( [](XAsyncBlock* async, std::shared_ptr& result) { XblMultiplayerSessionHandle sessionHandle; auto hr = XblMultiplayerGetSessionByHandleResult(async, &sessionHandle); if (SUCCEEDED(hr)) { if (sessionHandle) { result = std::shared_ptr(new multiplayer_session(sessionHandle)); XblMultiplayerSessionCloseHandle(sessionHandle); } else { result = nullptr; } } return hr; }); auto hr = XblMultiplayerGetSessionByHandleAsync(m_handle, Utils::StringFromStringT(handleId).data(), &asyncWrapper->async); return asyncWrapper->Task(hr); } pplx::task>> multiplayer_service::get_sessions( _In_ multiplayer_get_sessions_request getSessionsRequest ) { auto asyncWrapper = new AsyncWrapper>( [](XAsyncBlock* async, std::vector& result) { size_t resultCount; auto hr = XblMultiplayerQuerySessionsResultCount(async, &resultCount); if (SUCCEEDED(hr)) { std::vector intermediateResults(resultCount); hr = XblMultiplayerQuerySessionsResult(async, resultCount, intermediateResults.data()); if (SUCCEEDED(hr)) { result = std::vector(intermediateResults.begin(), intermediateResults.end()); } } return hr; }); auto hr = XblMultiplayerQuerySessionsAsync(m_handle, &getSessionsRequest.m_request, &asyncWrapper->async); return asyncWrapper->Task(hr); } pplx::task> multiplayer_service::set_activity( _In_ multiplayer_session_reference sessionReference ) { auto asyncWrapper = new AsyncWrapper(); auto hr = XblMultiplayerSetActivityAsync(m_handle, &sessionReference.m_reference, &asyncWrapper->async); return asyncWrapper->Task(hr); } pplx::task> multiplayer_service::set_transfer_handle( _In_ multiplayer_session_reference targetSessionReference, _In_ multiplayer_session_reference originSessionReference ) { auto asyncWrapper = new AsyncWrapper( [](XAsyncBlock* async, string_t& result) { XblMultiplayerSessionHandleId id{}; HRESULT hr = XblMultiplayerSetTransferHandleResult(async, &id); if (SUCCEEDED(hr)) { result = xbox::services::Utils::StringTFromUtf8(id.value); } return hr; }); HRESULT hr = XblMultiplayerSetTransferHandleAsync(m_handle, targetSessionReference.m_reference, originSessionReference.m_reference, &asyncWrapper->async); return asyncWrapper->Task(hr); } pplx::task> multiplayer_service::set_search_handle( _In_ multiplayer_search_handle_request r ) { auto asyncWrapper = new AsyncWrapper( [](XAsyncBlock* async) { return XblMultiplayerCreateSearchHandleResult(async, nullptr); }); auto hr = XblMultiplayerCreateSearchHandleAsync( m_handle, &r.m_sessionRef, r.m_tags.data(), r.m_tags.size(), r.m_numberAttributes.data(), r.m_numberAttributes.size(), r.m_stringAttributes.data(), r.m_stringAttributes.size(), &asyncWrapper->async ); return asyncWrapper->Task(hr); } pplx::task> multiplayer_service::multiplayer_service::clear_activity( _In_ const string_t& serviceConfigurationId ) { auto asyncWrapper = new AsyncWrapper(); auto hr = XblMultiplayerClearActivityAsync(m_handle, Utils::StringFromStringT(serviceConfigurationId).data(), &asyncWrapper->async); return asyncWrapper->Task(hr); } pplx::task> multiplayer_service::clear_search_handle( _In_ const string_t& handleId ) { auto asyncWrapper = new AsyncWrapper(); auto hr = XblMultiplayerDeleteSearchHandleAsync(m_handle, Utils::StringFromStringT(handleId).data(), &asyncWrapper->async); return asyncWrapper->Task(hr); } pplx::task>> multiplayer_service::send_invites( _In_ multiplayer_session_reference sessionReference, _In_ const std::vector& xboxUserIds, _In_ uint32_t titleId ) { return send_invites(sessionReference, xboxUserIds, titleId, string_t(), string_t()); } pplx::task>> multiplayer_service::send_invites( _In_ multiplayer_session_reference sessionReference, _In_ const std::vector& xboxUserIds, _In_ uint32_t titleId, _In_ const string_t& contextStringId, _In_ const string_t& customActivationContext ) { auto xuids = Utils::XuidVectorFromXuidStringVector(xboxUserIds); size_t xuidCount = xboxUserIds.size(); auto asyncWrapper = new AsyncWrapper>( [xuidCount](XAsyncBlock* async, std::vector& result) { std::vector inviteHandles(xuidCount); auto hr = XblMultiplayerSendInvitesResult(async, xuidCount, inviteHandles.data()); if (SUCCEEDED(hr)) { result = Utils::Transform(inviteHandles, [](const XblMultiplayerInviteHandle& handle) { return Utils::StringTFromUtf8(handle.Data); }); } return hr; }); auto hr = XblMultiplayerSendInvitesAsync( m_handle, &sessionReference.m_reference, xuids.data(), xuidCount, titleId, contextStringId.empty() ? nullptr : Utils::StringFromStringT(contextStringId).data(), customActivationContext.empty() ? nullptr : Utils::StringFromStringT(customActivationContext).data(), &asyncWrapper->async ); return asyncWrapper->Task(hr); } pplx::task>> multiplayer_service::get_activities_for_social_group( _In_ const string_t& serviceConfigurationId, _In_ const string_t& socialGroupOwnerXboxUserId, _In_ const string_t& socialGroup ) { auto asyncWrapper = new AsyncWrapper>( [](XAsyncBlock* async, std::vector& result) { size_t resultSize; auto hr = XblMultiplayerGetActivitiesWithPropertiesForSocialGroupResultSize(async, &resultSize); if (SUCCEEDED(hr)) { size_t count{ 0 }; std::vector buffer(resultSize, 0); XblMultiplayerActivityDetails* activityDetails{}; if (resultSize > 0) { hr = XblMultiplayerGetActivitiesWithPropertiesForSocialGroupResult(async, resultSize, buffer.data(), &activityDetails, &count, nullptr); if (SUCCEEDED(hr)) { result = std::vector(activityDetails, activityDetails + count); } } } return hr; }); auto hr = XblMultiplayerGetActivitiesWithPropertiesForSocialGroupAsync( m_handle, Utils::StringFromStringT(serviceConfigurationId).data(), Utils::Uint64FromStringT(socialGroupOwnerXboxUserId), Utils::StringFromStringT(socialGroup).data(), &asyncWrapper->async ); return asyncWrapper->Task(hr); } pplx::task>> multiplayer_service::get_activities_for_users( _In_ const string_t& serviceConfigurationId, _In_ const std::vector& xboxUserIds ) { auto asyncWrapper = new AsyncWrapper>( [](XAsyncBlock* async, std::vector& result) { size_t resultSize; auto hr = XblMultiplayerGetActivitiesWithPropertiesForUsersResultSize(async, &resultSize); if (SUCCEEDED(hr)) { size_t count{ 0 }; std::vector buffer(resultSize, 0); XblMultiplayerActivityDetails* activityDetails{}; if (resultSize > 0) { hr = XblMultiplayerGetActivitiesWithPropertiesForUsersResult(async, resultSize, buffer.data(), &activityDetails, &count, nullptr); if (SUCCEEDED(hr)) { result = std::vector(activityDetails, activityDetails + count); } } } return hr; }); auto xuids = Utils::XuidVectorFromXuidStringVector(xboxUserIds); auto hr = XblMultiplayerGetActivitiesWithPropertiesForUsersAsync( m_handle, Utils::StringFromStringT(serviceConfigurationId).data(), xuids.data(), xuids.size(), &asyncWrapper->async ); return asyncWrapper->Task(hr); } pplx::task>> multiplayer_service::get_search_handles( _In_ const string_t& serviceConfigurationId, _In_ const string_t& sessionTemplateName, _In_ const string_t& orderBy, _In_ bool orderAscending, _In_ const string_t& searchFilter ) { multiplayer_query_search_handle_request request{ serviceConfigurationId, sessionTemplateName }; request.set_order_by(orderBy); request.set_order_ascending(orderAscending); request.set_search_filter(searchFilter); return get_search_handles(request); } pplx::task>> multiplayer_service::get_search_handles( _In_ const multiplayer_query_search_handle_request& r ) { auto asyncWrapper = new AsyncWrapper>( [](XAsyncBlock* async, std::vector& result) { size_t resultCount{ 0 }; auto hr = XblMultiplayerGetSearchHandlesResultCount(async, &resultCount); if (SUCCEEDED(hr)) { std::vector handles{ resultCount }; hr = XblMultiplayerGetSearchHandlesResult(async, handles.data(), resultCount); if (SUCCEEDED(hr)) { result = std::vector{ handles.begin(), handles.end() }; for (auto& handle : handles) { XblMultiplayerSearchHandleCloseHandle(handle); } } } return hr; }); auto hr = XblMultiplayerGetSearchHandlesAsync( m_handle, Utils::StringFromStringT(r.service_configuration_id()).data(), Utils::StringFromStringT(r.session_template_name()).data(), Utils::StringFromStringT(r.order_by()).data(), r.order_ascending(), Utils::StringFromStringT(r.search_filter()).data(), Utils::StringFromStringT(r.social_group()).data(), &asyncWrapper->async ); return asyncWrapper->Task(hr); } std::error_code multiplayer_service::enable_multiplayer_subscriptions() { return Utils::ConvertHr(XblMultiplayerSetSubscriptionsEnabled(m_handle, true)); } void multiplayer_service::disable_multiplayer_subscriptions() { XblMultiplayerSetSubscriptionsEnabled(m_handle, false); } bool multiplayer_service::subscriptions_enabled() { return XblMultiplayerSubscriptionsEnabled(m_handle); } struct multiplayer_service::HandlerContext { XblFunctionContext internalContext; std::function sessionChangedHandler; std::function subscriptionLostHandler; std::function connectionIdChangedHandler; }; function_context multiplayer_service::add_multiplayer_session_changed_handler( _In_ std::function handler ) { auto context = new HandlerContext{}; context->sessionChangedHandler = std::move(handler); context->internalContext = XblMultiplayerAddSessionChangedHandler(m_handle, [](_In_ void* context, _In_ XblMultiplayerSessionChangeEventArgs internalArgs) { auto handlerContext{ static_cast(context) }; handlerContext->sessionChangedHandler(multiplayer_session_change_event_args{ internalArgs }); }, context); return context; } void multiplayer_service::remove_multiplayer_session_changed_handler( _In_ function_context context ) { auto handlerContext{ static_cast(context) }; XblMultiplayerRemoveSessionChangedHandler(m_handle, handlerContext->internalContext); delete handlerContext; } function_context multiplayer_service::add_multiplayer_subscription_lost_handler( _In_ std::function handler ) { auto context = new HandlerContext{}; context->subscriptionLostHandler = std::move(handler); context->internalContext = XblMultiplayerAddSubscriptionLostHandler(m_handle, [](void* context) { auto handlerContext{ static_cast(context) }; handlerContext->subscriptionLostHandler(); }, context); return context; } void multiplayer_service::remove_multiplayer_subscription_lost_handler( _In_ function_context context ) { auto handlerContext{ static_cast(context) }; XblMultiplayerRemoveSubscriptionLostHandler(m_handle, handlerContext->internalContext); delete handlerContext; } function_context multiplayer_service::add_multiplayer_connection_id_changed_handler( _In_ std::function handler ) { auto context = new HandlerContext{}; context->connectionIdChangedHandler = std::move(handler); context->internalContext = XblMultiplayerAddConnectionIdChangedHandler(m_handle, [](void* context) { auto handlerContext{ static_cast(context) }; handlerContext->connectionIdChangedHandler(); }, context); return context; } void multiplayer_service::remove_multiplayer_connection_id_changed_handler( _In_ function_context context ) { auto handlerContext{ static_cast(context) }; XblMultiplayerRemoveConnectionIdChangedHandler(m_handle, handlerContext->internalContext); delete handlerContext; } NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_CPP_END ================================================ FILE: Include/xsapi-cpp/impl/multiplayer_manager.hpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_MANAGER_CPP_BEGIN uint32_t multiplayer_member::member_id() const { return m_internalMember.MemberId; } string_t multiplayer_member::initial_team() const { return Utils::StringTFromUtf8(m_internalMember.InitialTeam); } string_t multiplayer_member::xbox_user_id() const { return Utils::StringTFromUint64(m_internalMember.Xuid); } string_t multiplayer_member::debug_gamertag() const { return Utils::StringTFromUtf8(m_internalMember.DebugGamertag); } bool multiplayer_member::is_local() const { return m_internalMember.IsLocal; } bool multiplayer_member::is_in_lobby() const { return m_internalMember.IsInLobby; } bool multiplayer_member::is_in_game() const { return m_internalMember.IsInGame; } multiplayer_session_member_status multiplayer_member::status() const { return static_cast(m_internalMember.Status); } string_t multiplayer_member::connection_address() const { return Utils::StringTFromUtf8(m_internalMember.ConnectionAddress); } web::json::value multiplayer_member::properties() const { return Utils::ParseJson(m_internalMember.PropertiesJson); } bool multiplayer_member::is_member_on_same_device( _In_ std::shared_ptr member ) const { return XblMultiplayerManagerMemberAreMembersOnSameDevice(&m_internalMember, &member->m_internalMember); } string_t multiplayer_lobby_session::correlation_id() const { XblGuid correlationId{}; XblMultiplayerManagerLobbySessionCorrelationId(&correlationId); return Utils::StringTFromUtf8(correlationId.value); } multiplayer_session_reference multiplayer_lobby_session::session_reference() const { XblMultiplayerSessionReference sessionReference{}; XblMultiplayerManagerLobbySessionSessionReference(&sessionReference); return multiplayer_session_reference(sessionReference); } std::vector> multiplayer_lobby_session::local_members() const { size_t localMemberCount = XblMultiplayerManagerLobbySessionLocalMembersCount(); std::vector localMembers(localMemberCount); XblMultiplayerManagerLobbySessionLocalMembers(localMemberCount, localMembers.data()); return Utils::Transform>(localMembers, [](const XblMultiplayerManagerMember& member) { return std::make_shared(member); }); } std::vector> multiplayer_lobby_session::members() const { size_t memberCount = XblMultiplayerManagerLobbySessionMembersCount(); std::vector members(memberCount); XblMultiplayerManagerLobbySessionMembers(memberCount, members.data()); return Utils::Transform>(members, [](const XblMultiplayerManagerMember& member) { return std::make_shared(member); }); } std::shared_ptr multiplayer_lobby_session::host() const { XblMultiplayerManagerMember host; XblMultiplayerManagerLobbySessionHost(&host); return std::make_shared(host); } web::json::value multiplayer_lobby_session::properties() const { return Utils::ParseJson(XblMultiplayerManagerLobbySessionPropertiesJson()); } const std::shared_ptr multiplayer_lobby_session::session_constants() const { if (m_sessionConstants == nullptr) { m_sessionConstants = std::make_shared(true); } return m_sessionConstants; } xbox_live_result multiplayer_lobby_session::add_local_user( _In_ xbox_live_user_t user ) { return Utils::ConvertHr(XblMultiplayerManagerLobbySessionAddLocalUser(user)); } xbox_live_result multiplayer_lobby_session::remove_local_user( _In_ xbox_live_user_t user ) { return Utils::ConvertHr(XblMultiplayerManagerLobbySessionRemoveLocalUser(user)); } xbox_live_result multiplayer_lobby_session::set_local_member_properties( _In_ xbox_live_user_t user, _In_ const string_t& name, _In_ const web::json::value& valueJson, _In_opt_ context_t context ) { return Utils::ConvertHr(XblMultiplayerManagerLobbySessionSetLocalMemberProperties( user, Utils::StringFromStringT(name).data(), Utils::StringFromStringT(valueJson.serialize()).data(), reinterpret_cast(context) )); } xbox_live_result multiplayer_lobby_session::delete_local_member_properties( _In_ xbox_live_user_t user, _In_ const string_t& name, _In_opt_ context_t context ) { return Utils::ConvertHr(XblMultiplayerManagerLobbySessionDeleteLocalMemberProperties( user, Utils::StringFromStringT(name).data(), reinterpret_cast(context) )); } xbox_live_result multiplayer_lobby_session::set_local_member_connection_address( _In_ xbox_live_user_t user, _In_ const string_t& connectionAddress, _In_opt_ context_t context ) { return Utils::ConvertHr(XblMultiplayerManagerLobbySessionSetLocalMemberConnectionAddress( user, Utils::StringFromStringT(connectionAddress).data(), reinterpret_cast(context) )); } bool multiplayer_lobby_session::is_host( _In_ const string_t& xboxUserId ) { return XblMultiplayerManagerLobbySessionIsHost(Utils::Uint64FromStringT(xboxUserId)); } xbox_live_result multiplayer_lobby_session::set_properties( _In_ const string_t& name, _In_ const web::json::value& valueJson, _In_opt_ context_t context ) { return Utils::ConvertHr(XblMultiplayerManagerLobbySessionSetProperties( Utils::StringFromStringT(name).data(), Utils::StringFromStringT(valueJson.serialize()).data(), reinterpret_cast(context) )); } xbox_live_result multiplayer_lobby_session::set_synchronized_properties( _In_ const string_t& name, _In_ const web::json::value& valueJson, _In_opt_ context_t context ) { return Utils::ConvertHr(XblMultiplayerManagerLobbySessionSetSynchronizedProperties( Utils::StringFromStringT(name).data(), Utils::StringFromStringT(valueJson.serialize()).data(), reinterpret_cast(context) )); } xbox_live_result multiplayer_lobby_session::set_synchronized_host( _In_ std::shared_ptr gameHost, _In_opt_ context_t context ) { return Utils::ConvertHr(XblMultiplayerManagerLobbySessionSetSynchronizedHost( gameHost->m_internalMember.DeviceToken, reinterpret_cast(context) )); } #if HC_PLATFORM_IS_MICROSOFT xbox_live_result multiplayer_lobby_session::invite_friends( _In_ xbox_live_user_t user, _In_ const string_t& contextStringId, _In_ const string_t& customActivationContext ) { return Utils::ConvertHr(XblMultiplayerManagerLobbySessionInviteFriends( user, Utils::StringFromStringT(contextStringId).data(), Utils::StringFromStringT(customActivationContext).data() )); } #endif xbox_live_result multiplayer_lobby_session::invite_users( _In_ xbox_live_user_t user, _In_ const std::vector& xboxUserIds, _In_ const string_t& contextStringId, _In_ const string_t& customActivationContext ) { auto xuids = Utils::XuidVectorFromXuidStringVector(xboxUserIds); return Utils::ConvertHr(XblMultiplayerManagerLobbySessionInviteUsers( user, xuids.data(), xuids.size(), Utils::StringFromStringT(contextStringId).data(), Utils::StringFromStringT(customActivationContext).data() )); } string_t multiplayer_game_session::correlation_id() const { return Utils::StringTFromUtf8(XblMultiplayerManagerGameSessionCorrelationId()); } multiplayer_session_reference multiplayer_game_session::session_reference() const { return multiplayer_session_reference(*XblMultiplayerManagerGameSessionSessionReference()); } std::vector> multiplayer_game_session::members() const { size_t memberCount = XblMultiplayerManagerGameSessionMembersCount(); std::vector members(memberCount); XblMultiplayerManagerGameSessionMembers(memberCount, members.data()); return Utils::Transform>(members, [](const XblMultiplayerManagerMember& member) { return std::make_shared(member); }); } std::shared_ptr multiplayer_game_session::host() const { XblMultiplayerManagerMember host{}; XblMultiplayerManagerGameSessionHost(&host); return std::make_shared(host); } web::json::value multiplayer_game_session::properties() const { return Utils::ParseJson(XblMultiplayerManagerGameSessionPropertiesJson()); } const std::shared_ptr multiplayer_game_session::session_constants() const { if (m_sessionConstants == nullptr) { m_sessionConstants = std::make_shared(false); } return m_sessionConstants; } bool multiplayer_game_session::is_host( _In_ const string_t& xboxUserId ) { return XblMultiplayerManagerGameSessionIsHost(Utils::Uint64FromStringT(xboxUserId)); } xbox_live_result multiplayer_game_session::set_properties( _In_ const string_t& name, _In_ const web::json::value& valueJson, _In_opt_ context_t context ) { return Utils::ConvertHr(XblMultiplayerManagerGameSessionSetProperties( Utils::StringFromStringT(name).data(), Utils::StringFromStringT(valueJson.serialize()).data(), reinterpret_cast(context) )); } xbox_live_result multiplayer_game_session::set_synchronized_properties( _In_ const string_t& name, _In_ const web::json::value& valueJson, _In_opt_ context_t context ) { return Utils::ConvertHr(XblMultiplayerManagerGameSessionSetSynchronizedProperties( Utils::StringFromStringT(name).data(), Utils::StringFromStringT(valueJson.serialize()).data(), reinterpret_cast(context) )); } xbox_live_result multiplayer_game_session::set_synchronized_host( _In_ std::shared_ptr gameHost, _In_opt_ context_t context ) { return Utils::ConvertHr(XblMultiplayerManagerGameSessionSetSynchronizedHost( gameHost->m_internalMember.DeviceToken, reinterpret_cast(context) )); } multiplayer_event_args::multiplayer_event_args(XblMultiplayerEventArgsHandle argsHandle) : m_argsHandle(argsHandle) { } multiplayer_event_args::~multiplayer_event_args() { } string_t multiplayer_event_args::GetXuid() const { uint64_t xuid; XblMultiplayerEventArgsXuid(m_argsHandle, &xuid); return Utils::StringTFromUint64(xuid); } std::vector> multiplayer_event_args::GetMembers() const { size_t memberCount; XblMultiplayerEventArgsMembersCount(m_argsHandle, &memberCount); std::vector members(memberCount); XblMultiplayerEventArgsMembers(m_argsHandle, memberCount, members.data()); return Utils::Transform>(members, [](const XblMultiplayerManagerMember& member) { return std::make_shared(member); }); } std::shared_ptr multiplayer_event_args::GetMember() const { XblMultiplayerManagerMember member{}; XblMultiplayerEventArgsMember(m_argsHandle, &member); return std::make_shared(member); } web::json::value multiplayer_event_args::GetPropertiesJson() const { const char* json = nullptr; XblMultiplayerEventArgsPropertiesJson(m_argsHandle, &json); return Utils::ParseJson(json); } xbox::services::multiplayer::manager::match_status find_match_completed_event_args::match_status() const { XblMultiplayerMatchStatus status; XblMultiplayerEventArgsFindMatchCompleted(m_argsHandle, &status, nullptr); return static_cast(status); } multiplayer_measurement_failure find_match_completed_event_args::initialization_failure_cause() const { XblMultiplayerMeasurementFailure cause; XblMultiplayerEventArgsFindMatchCompleted(m_argsHandle, nullptr, &cause); return static_cast(cause); } std::map perform_qos_measurements_event_args::connection_address_to_device_tokens() const { XblMultiplayerPerformQoSMeasurementsArgs args{}; XblMultiplayerEventArgsPerformQoSMeasurements(m_argsHandle, &args); std::map out; for (size_t i = 0; i < args.remoteClientsSize; ++i) { out.insert(std::make_pair(Utils::StringTFromUtf8(args.remoteClients[i].connectionAddress), Utils::StringTFromUtf8(args.remoteClients[i].deviceToken.Value))); } return out; } multiplayer_event::multiplayer_event(_In_ const XblMultiplayerEvent* internalEvent) : m_internalEvent(internalEvent) { } std::error_code multiplayer_event::err() const { return std::make_error_code(static_cast(m_internalEvent->Result)); } std::string multiplayer_event::err_message() const { if (m_internalEvent->ErrorMessage) { return m_internalEvent->ErrorMessage; } return std::string(); } context_t multiplayer_event::context() { return reinterpret_cast(m_internalEvent->Context); } multiplayer_event_type multiplayer_event::event_type() const { return static_cast(m_internalEvent->EventType); } std::shared_ptr multiplayer_event::event_args() { if (m_eventArgs == nullptr) { switch (m_internalEvent->EventType) { case XblMultiplayerEventType::UserAdded: { m_eventArgs = std::make_shared(m_internalEvent->EventArgsHandle); break; } case XblMultiplayerEventType::UserRemoved: { m_eventArgs = std::make_shared(m_internalEvent->EventArgsHandle); break; } case XblMultiplayerEventType::MemberJoined: { m_eventArgs = std::make_shared(m_internalEvent->EventArgsHandle); break; } case XblMultiplayerEventType::MemberLeft: { m_eventArgs = std::make_shared(m_internalEvent->EventArgsHandle); break; } case XblMultiplayerEventType::MemberPropertyChanged: { m_eventArgs = std::make_shared(m_internalEvent->EventArgsHandle); break; } case XblMultiplayerEventType::SessionPropertyChanged: { m_eventArgs = std::make_shared(m_internalEvent->EventArgsHandle); break; } case XblMultiplayerEventType::HostChanged: { m_eventArgs = std::make_shared(m_internalEvent->EventArgsHandle); break; } case XblMultiplayerEventType::PerformQosMeasurements: { m_eventArgs = std::make_shared(m_internalEvent->EventArgsHandle); break; } case XblMultiplayerEventType::FindMatchCompleted: { m_eventArgs = std::make_shared(m_internalEvent->EventArgsHandle); break; } case XblMultiplayerEventType::JoinLobbyCompleted: { m_eventArgs = std::make_shared(m_internalEvent->EventArgsHandle); break; } default: break; } } return m_eventArgs; } multiplayer_session_type multiplayer_event::session_type() const { return static_cast(m_internalEvent->SessionType); } std::shared_ptr multiplayer_manager::get_singleton_instance() { static std::shared_ptr instance = std::shared_ptr(new multiplayer_manager()); return instance; } void multiplayer_manager::initialize( _In_ const string_t& lobbySessionTemplateName ) { XblMultiplayerManagerInitialize(Utils::StringFromStringT(lobbySessionTemplateName).data(), nullptr); } std::vector multiplayer_manager::do_work() { const XblMultiplayerEvent* eventPtr; size_t eventCount; XblMultiplayerManagerDoWork(&eventPtr, &eventCount); return Utils::Transform(eventPtr, eventCount, [](const XblMultiplayerEvent& in) { return multiplayer_event(&in); }); } std::shared_ptr multiplayer_manager::lobby_session() const { if (m_lobbySession == nullptr) { m_lobbySession = std::make_shared(); } return m_lobbySession; } std::shared_ptr multiplayer_manager::game_session() const { if (XblMultiplayerManagerGameSessionActive()) { return std::make_shared(); } return nullptr; } xbox_live_result multiplayer_manager::join_lobby( _In_ const string_t& handleId, _In_ xbox_live_user_t user ) { return Utils::ConvertHr(XblMultiplayerManagerJoinLobby(Utils::StringFromStringT(handleId).data(), user)); } #if (TV_API || UWP_API) xbox_live_result multiplayer_manager::join_lobby( _In_ Windows::ApplicationModel::Activation::IProtocolActivatedEventArgs^ eventArgs, _In_ xbox_live_user_t user ) { // TODO UNREFERENCED_PARAMETER(eventArgs); UNREFERENCED_PARAMETER(user); return xbox_live_result(xbox_live_error_code::unsupported); } #endif #if TV_API xbox_live_result multiplayer_manager::join_lobby( _In_ const string_t& handleId, _In_ std::vector users ) { // TODO UNREFERENCED_PARAMETER(handleId); UNREFERENCED_PARAMETER(users); return xbox_live_result(xbox_live_error_code::unsupported); } xbox_live_result multiplayer_manager::join_lobby( _In_ Windows::ApplicationModel::Activation::IProtocolActivatedEventArgs^ eventArgs, _In_ std::vector users ) { // TODO UNREFERENCED_PARAMETER(eventArgs); UNREFERENCED_PARAMETER(users); return xbox_live_result(xbox_live_error_code::unsupported); } void multiplayer_manager::invite_party_to_game() { // TODO } #endif xbox_live_result multiplayer_manager::join_game_from_lobby( _In_ const string_t& sessionTemplateName ) { return Utils::ConvertHr(XblMultiplayerManagerJoinGameFromLobby(Utils::StringFromStringT(sessionTemplateName).data())); } xbox_live_result multiplayer_manager::join_game( _In_ const string_t& sessionName, _In_ const string_t& sessionTemplateName, _In_ const std::vector& xboxUserIds ) { auto xuids = Utils::XuidVectorFromXuidStringVector(xboxUserIds); return Utils::ConvertHr(XblMultiplayerManagerJoinGame( Utils::StringFromStringT(sessionName).data(), Utils::StringFromStringT(sessionTemplateName).data(), xuids.data(), static_cast(xuids.size()) )); } xbox_live_result multiplayer_manager::leave_game() { return Utils::ConvertHr(XblMultiplayerManagerLeaveGame()); } xbox_live_result multiplayer_manager::find_match( _In_ const string_t& hopperName, _In_ const web::json::value& attributes, _In_ const std::chrono::seconds& timeout ) { return Utils::ConvertHr(XblMultiplayerManagerFindMatch( Utils::StringFromStringT(hopperName).data(), Utils::StringFromStringT(attributes.serialize()).data(), static_cast(timeout.count()) )); } void multiplayer_manager::cancel_match() { return XblMultiplayerManagerCancelMatch(); } xbox::services::multiplayer::manager::match_status multiplayer_manager::match_status() const { return static_cast(XblMultiplayerManagerMatchStatus()); } std::chrono::seconds multiplayer_manager::estimated_match_wait_time() const { return std::chrono::seconds(XblMultiplayerManagerEstimatedMatchWaitTime()); } bool multiplayer_manager::auto_fill_members_during_matchmaking() const { return XblMultiplayerManagerAutoFillMembersDuringMatchmaking(); } void multiplayer_manager::set_auto_fill_members_during_matchmaking( _In_ bool autoFillMembers ) { XblMultiplayerManagerSetAutoFillMembersDuringMatchmaking(autoFillMembers); } void multiplayer_manager::set_quality_of_service_measurements( _In_ std::shared_ptr> measurements ) { web::json::value measurementsJson; for (const auto& measurement : *measurements) { web::json::value jsonMeasurement; jsonMeasurement[_T("latency")] = static_cast(measurement.latency().count()); jsonMeasurement[_T("bandwidthDown")] = measurement.bandwidth_down_in_kilobits_per_second(); jsonMeasurement[_T("bandwidthUp")] = measurement.bandwidth_up_in_kilobits_per_second(); jsonMeasurement[_T("custom")] = measurement.custom_json(); measurementsJson[measurement.member_device_token()] = jsonMeasurement; } auto measurementsJsonString = Utils::StringFromStringT(measurementsJson.serialize()); XblMultiplayerManagerSetQosMeasurements(measurementsJsonString.data()); } joinability multiplayer_manager::joinability() const { return static_cast(XblMultiplayerManagerJoinability()); } xbox_live_result multiplayer_manager::set_joinability( _In_ xbox::services::multiplayer::manager::joinability value, _In_opt_ context_t context ) { return Utils::ConvertHr(XblMultiplayerManagerSetJoinability( static_cast(value), reinterpret_cast(context) )); } #if defined(XSAPI_CPPWINRT) #if TV_API xbox_live_result multiplayer_manager::join_lobby( _In_ const string_t& handleId, _In_ std::vector users ) { return join_lobby(handleId, convert_user_vector_to_cppcx(users)); } #endif #endif NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_MANAGER_CPP_END ================================================ FILE: Include/xsapi-cpp/impl/notification.hpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "public_utils.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_NOTIFICATION_CPP_BEGIN #ifdef XSAPI_NOTIFICATION_SERVICE notification_service::notification_service(_In_ XblContextHandle contextHandle) { XblContextDuplicateHandle(contextHandle, &m_xblContext); } notification_service::~notification_service() { #ifndef XSAPI_UNIT_TESTS unsubscribe_from_notifications().wait(); #endif XblContextCloseHandle(m_xblContext); } #if (HC_PLATFORM == HC_PLATFORM_IOS || HC_PLATFORM == HC_PLATFORM_ANDROID) pplx::task> notification_service::subscribe_to_notifications( _In_ const string_t deviceToken ) { auto xblContext = m_xblContext; auto asyncWrapper = new AsyncWrapper(); auto hr = XblNotificationSubscribeToNotificationsAsync( xblContext, &asyncWrapper->async, deviceToken.c_str()); return asyncWrapper->Task(hr); } #elif HC_PLATFORM == HC_PLATFORM_WIN32 && !defined(XSAPI_UNIT_TESTS) inline invite_notification_event_args::invite_notification_event_args(_In_ const XblGameInviteNotificationEventArgs& gameInviteargs) :m_gameInviteArgs(gameInviteargs) { } string_t invite_notification_event_args::invited_xbox_user_id() const { return Utils::StringTFromUint64(m_gameInviteArgs.invitedXboxUserId); } string_t invite_notification_event_args::sender_xbox_user_id() const { return Utils::StringTFromUint64(m_gameInviteArgs.senderXboxUserId); } string_t invite_notification_event_args::sender_gamertag() const { return Utils::StringTFromUtf8(m_gameInviteArgs.senderGamertag); } string_t invite_notification_event_args::invite_handle_id() const { return Utils::StringTFromUtf8(m_gameInviteArgs.inviteHandleId); } string_t invite_notification_event_args::invite_protocol() const { return Utils::StringTFromUtf8(m_gameInviteArgs.inviteProtocol); } string_t invite_notification_event_args::invite_context() const { return Utils::StringTFromUtf8(m_gameInviteArgs.inviteContext); } utility::datetime invite_notification_event_args::expiration() const { return Utils::DatetimeFromTimeT(m_gameInviteArgs.expiration); } const multiplayer::multiplayer_session_reference invite_notification_event_args::session_reference() const { return multiplayer::multiplayer_session_reference(m_gameInviteArgs.sessionReference); } inline achievement_unlocked_notification_event_args::achievement_unlocked_notification_event_args(_In_ const XblAchievementUnlockEvent& achievementUnlockEvent) : m_achievementUnlock(achievementUnlockEvent) { } string_t achievement_unlocked_notification_event_args::name() const { return Utils::StringTFromUtf8(m_achievementUnlock.achievementName); } string_t achievement_unlocked_notification_event_args::id() const { return Utils::StringTFromUtf8(m_achievementUnlock.achievementId); } string_t achievement_unlocked_notification_event_args::description() const { return Utils::StringTFromUtf8(m_achievementUnlock.achievementDescription); } string_t achievement_unlocked_notification_event_args::icon_url() const { return Utils::StringTFromUtf8(m_achievementUnlock.achievementIcon); } uint64_t achievement_unlocked_notification_event_args::gamerscore() const { return m_achievementUnlock.gamerscore; } string_t achievement_unlocked_notification_event_args::deeplink() const { return Utils::StringTFromUtf8(m_achievementUnlock.deepLink); } string_t achievement_unlocked_notification_event_args::xbox_user_id() const { return Utils::StringTFromUint64(m_achievementUnlock.xboxUserId); } utility::datetime achievement_unlocked_notification_event_args::unlockTime() const { return Utils::DatetimeFromTimeT(m_achievementUnlock.timeUnlocked); } std::function& notification_service::game_invite_handler() { return m_inviteHandler; } std::function& notification_service::achievement_unlock_handler() { return m_achievementUnlockedHandler; } pplx::task> notification_service::subscribe_to_notifications( _In_ const std::function& achievementUnlockHandler, _In_ const std::function& multiplayerInviteHandler ) { auto xblContext = m_xblContext; m_inviteHandler = multiplayerInviteHandler; m_achievementUnlockedHandler = achievementUnlockHandler; auto asyncWrapper = new AsyncWrapper(); m_gameinviteFunctionContext = XblGameInviteAddNotificationHandler( xblContext, [](_In_ const XblGameInviteNotificationEventArgs* args, _In_opt_ void* context) { invite_notification_event_args resultInviteArgs(*args); auto service = static_cast(context); service->game_invite_handler()(resultInviteArgs); }, shared_from_this().get() ); delete(asyncWrapper); asyncWrapper = new AsyncWrapper(); m_achievementUnlockFunctionContext = XblAchievementUnlockAddNotificationHandler( xblContext, [](_In_ const XblAchievementUnlockEvent* args, _In_opt_ void* context) { achievement_unlocked_notification_event_args achievementUnlockArgs(*args); auto service = static_cast(context); service->achievement_unlock_handler()(achievementUnlockArgs); }, shared_from_this().get() ); return asyncWrapper->Task(S_OK); } #endif #if (HC_PLATFORM == HC_PLATFORM_IOS || HC_PLATFORM == HC_PLATFORM_ANDROID || HC_PLATFORM == HC_PLATFORM_UWP) pplx::task> notification_service::unsubscribe_from_notifications() { auto xblContext = m_xblContext; auto asyncWrapper = new AsyncWrapper(); auto hr = XblNotificationUnsubscribeFromNotificationsAsync(xblContext, &asyncWrapper->async); return asyncWrapper->Task(hr); } #elif HC_PLATFORM == HC_PLATFORM_WIN32 && !defined(XSAPI_UNIT_TESTS) pplx::task> notification_service::unsubscribe_from_notifications() { XblGameInviteRemoveNotificationHandler(m_xblContext, m_gameinviteFunctionContext); XblAchievementUnlockRemoveNotificationHandler(m_xblContext, m_achievementUnlockFunctionContext); return pplx::task_from_result(xbox::services::xbox_live_result()); } #endif #endif NAMESPACE_MICROSOFT_XBOX_SERVICES_NOTIFICATION_CPP_END ================================================ FILE: Include/xsapi-cpp/impl/presence.hpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "public_utils.h" XBL_WARNING_PUSH XBL_WARNING_DISABLE_DEPRECATED NAMESPACE_MICROSOFT_XBOX_SERVICES_PRESENCE_CPP_BEGIN presence_data::presence_data( _In_ string_t serviceConfigurationId, _In_ string_t presenceId ) : m_serviceConfigurationId(std::move(serviceConfigurationId)), m_presenceId(std::move(presenceId)) { } presence_data::presence_data( _In_ string_t serviceConfigurationId, _In_ string_t presenceId, _In_ std::vector presenceTokenIds ) : m_serviceConfigurationId(std::move(serviceConfigurationId)), m_presenceId(std::move(presenceId)), m_presenceTokenIds(std::move(presenceTokenIds)) { } const string_t& presence_data::service_configuration_id() const { return m_serviceConfigurationId; } const string_t& presence_data::presence_id() const { return m_presenceId; } const std::vector& presence_data::presence_token_ids() const { return m_presenceTokenIds; } presence_broadcast_record::presence_broadcast_record( _In_ XblPresenceRecordHandle handle, _In_ const XblPresenceBroadcastRecord* broadcastRecord ) : m_broadcastRecord{ broadcastRecord } { XblPresenceRecordDuplicateHandle(handle, &m_handle); } presence_broadcast_record::presence_broadcast_record( const presence_broadcast_record& other ) : m_broadcastRecord{ other.m_broadcastRecord } { XblPresenceRecordDuplicateHandle(other.m_handle, &m_handle); } presence_broadcast_record& presence_broadcast_record::operator=( presence_broadcast_record other ) { std::swap(m_handle, other.m_handle); m_broadcastRecord = other.m_broadcastRecord; return *this; } presence_broadcast_record::~presence_broadcast_record() { if (m_handle) { XblPresenceRecordCloseHandle(m_handle); } } string_t presence_broadcast_record::broadcast_id() const { if (m_broadcastRecord) { return Utils::StringTFromUtf8(m_broadcastRecord->broadcastId); } return string_t(); } string_t presence_broadcast_record::session() const { if (m_broadcastRecord) { return Utils::StringTFromUtf8(m_broadcastRecord->session); } return string_t(); } string_t presence_broadcast_record::provider() const { if (m_broadcastRecord) { switch (m_broadcastRecord->provider) { case XblPresenceBroadcastProvider::Twitch: return _T("twitch"); default: return _T("unknown"); } } return _T("unknown"); } uint32_t presence_broadcast_record::viewer_count() const { if (m_broadcastRecord) { return m_broadcastRecord->viewerCount; } return 0; } utility::datetime presence_broadcast_record::start_time() const { if (m_broadcastRecord) { return Utils::DatetimeFromTimeT(m_broadcastRecord->startTime); } return utility::datetime(); } presence_title_record::presence_title_record( _In_ XblPresenceRecordHandle handle, _In_ const XblPresenceTitleRecord* titleRecord ) : m_titleRecord(titleRecord) { XblPresenceRecordDuplicateHandle(handle, &m_handle); } presence_title_record::presence_title_record( _In_ const presence_title_record& other ) : m_titleRecord(other.m_titleRecord) { XblPresenceRecordDuplicateHandle(other.m_handle, &m_handle); } presence_title_record& presence_title_record::operator=( _In_ presence_title_record other ) { std::swap(m_handle, other.m_handle); m_titleRecord = other.m_titleRecord; return *this; } presence_title_record::~presence_title_record() { XblPresenceRecordCloseHandle(m_handle); } uint32_t presence_title_record::title_id() const { return m_titleRecord->titleId; } string_t presence_title_record::title_name() const { return Utils::StringTFromUtf8(m_titleRecord->titleName); } utility::datetime presence_title_record::last_modified_date() const { return Utils::DatetimeFromTimeT(m_titleRecord->lastModified); } bool presence_title_record::is_title_active() const { return m_titleRecord->titleActive; } string_t presence_title_record::presence() const { return Utils::StringTFromUtf8(m_titleRecord->richPresenceString); } presence_title_view_state presence_title_record::presence_title_view() const { return static_cast(m_titleRecord->viewState); } presence_broadcast_record presence_title_record::broadcast_record() const { if (m_titleRecord->broadcastRecord) { return presence_broadcast_record(m_handle, m_titleRecord->broadcastRecord); } else { return presence_broadcast_record(); } } presence_device_record::presence_device_record( _In_ XblPresenceRecordHandle handle, _In_ const XblPresenceDeviceRecord* deviceRecord ) : m_deviceRecord(deviceRecord) { XblPresenceRecordDuplicateHandle(handle, &m_handle); } presence_device_record::presence_device_record( _In_ const presence_device_record& other ) : m_deviceRecord(other.m_deviceRecord) { XblPresenceRecordDuplicateHandle(other.m_handle, &m_handle); } presence_device_record& presence_device_record::operator=( _In_ presence_device_record other ) { std::swap(m_handle, other.m_handle); m_deviceRecord = other.m_deviceRecord; return *this; } presence_device_record::~presence_device_record() { XblPresenceRecordCloseHandle(m_handle); } presence_device_type presence_device_record::device_type() const { return static_cast(m_deviceRecord->deviceType); } std::vector presence_device_record::presence_title_records() const { return Utils::Transform(m_deviceRecord->titleRecords, m_deviceRecord->titleRecordsCount, [this](const XblPresenceTitleRecord& titleRecord) { return presence_title_record(m_handle, &titleRecord); }); } presence_record::presence_record( _In_ XblPresenceRecordHandle handle ) { XblPresenceRecordDuplicateHandle(handle, &m_handle); } presence_record::presence_record( _In_ const presence_record& other ) { XblPresenceRecordDuplicateHandle(other.m_handle, &m_handle); } presence_record& presence_record::operator=( presence_record other ) { std::swap(m_handle, other.m_handle); return *this; } presence_record::~presence_record() { if (m_handle) { XblPresenceRecordCloseHandle(m_handle); } } string_t presence_record::xbox_user_id() const { uint64_t xuid; XblPresenceRecordGetXuid(m_handle, &xuid); return Utils::StringTFromUint64(xuid); } user_presence_state presence_record::user_state() const { XblPresenceUserState state; XblPresenceRecordGetUserState(m_handle, &state); return static_cast(state); } std::vector presence_record::presence_device_records() const { const XblPresenceDeviceRecord* deviceRecords{ nullptr }; size_t deviceRecordsCount; XblPresenceRecordGetDeviceRecords(m_handle, &deviceRecords, &deviceRecordsCount); return Utils::Transform(deviceRecords, deviceRecordsCount, [this](const XblPresenceDeviceRecord& deviceRecord) { return presence_device_record(m_handle, &deviceRecord); }); } bool presence_record::is_user_playing_title(_In_ uint32_t titleId) const { auto userState = user_state(); if (userState == user_presence_state::offline || userState == user_presence_state::unknown) { return false; } auto deviceRecords = presence_device_records(); for (const auto& deviceRecord : deviceRecords) { auto titleRecords = deviceRecord.presence_title_records(); for (const auto& titleRecord : titleRecords) { if (titleRecord.title_id() == titleId) { return titleRecord.is_title_active(); } } } return false; } device_presence_change_event_args::device_presence_change_event_args( _In_ uint64_t xuid, _In_ XblPresenceDeviceType deviceType, _In_ bool isUserLoggedOn ) : m_deviceType(deviceType), m_isUserLoggedOn(isUserLoggedOn) { m_xuid = Utils::StringTFromUint64(xuid); } const string_t& device_presence_change_event_args::xbox_user_id() const { return m_xuid; } presence_device_type device_presence_change_event_args::device_type() const { return static_cast(m_deviceType); } bool device_presence_change_event_args::is_user_logged_on_device() const { return m_isUserLoggedOn; } device_presence_change_subscription::device_presence_change_subscription( _In_ XblRealTimeActivitySubscriptionHandle handle, _In_ const string_t& xuid ) : real_time_activity_subscription(handle), m_xuid(xuid) { stringstream_t uri; uri << _T("https://userpresence.xboxlive.com/users/xuid(") << m_xuid << _T(")/devices"); m_resourceUri = uri.str(); } const string_t& device_presence_change_subscription::xbox_user_id() const { return m_xuid; } title_presence_change_event_args::title_presence_change_event_args( _In_ uint64_t xuid, _In_ uint32_t titleId, _In_ XblPresenceTitleState titleState ) : m_titleId(titleId), m_titleState(titleState) { m_xuid = Utils::StringTFromUint64(xuid); } const string_t& title_presence_change_event_args::xbox_user_id() const { return m_xuid; } uint32_t title_presence_change_event_args::title_id() const { return m_titleId; } title_presence_state title_presence_change_event_args::title_state() const { return static_cast(m_titleState); } title_presence_change_subscription::title_presence_change_subscription( _In_ XblRealTimeActivitySubscriptionHandle handle, _In_ const string_t& xuid, _In_ uint32_t titleId ) : real_time_activity_subscription(handle), m_xuid(xuid), m_titleId(titleId) { stringstream_t uri; uri << _T("https://userpresence.xboxlive.com/users/xuid(") << m_xuid << _T(")/titles/") << m_titleId; m_resourceUri = uri.str(); } const string_t& title_presence_change_subscription::xbox_user_id() const { return m_xuid; } uint32_t title_presence_change_subscription::title_id() const { return m_titleId; } pplx::task> presence_service::set_presence( _In_ bool isUserActiveInTitle ) { auto asyncWrapper = new AsyncWrapper(); auto hr = XblPresenceSetPresenceAsync( m_xblContextHandle, isUserActiveInTitle, nullptr, &asyncWrapper->async ); return asyncWrapper->Task(hr); } pplx::task> presence_service::set_presence( _In_ bool isUserActiveInTitle, _In_ presence_data presenceData ) { auto asyncWrapper = new AsyncWrapper(); XblPresenceRichPresenceIds ids{}; Utils::Utf8FromCharT(presenceData.service_configuration_id().data(), ids.scid, sizeof(ids.scid)); std::string presenceId = Utils::StringFromStringT(presenceData.presence_id()); ids.presenceId = presenceId.data(); UTF8StringArrayRef presenceTokenIds{ presenceData.presence_token_ids() }; ids.presenceTokenIds = presenceTokenIds.Data(); ids.presenceTokenIdsCount = presenceTokenIds.Size(); auto hr = XblPresenceSetPresenceAsync( m_xblContextHandle, isUserActiveInTitle, &ids, &asyncWrapper->async ); return asyncWrapper->Task(hr); } pplx::task> presence_service::get_presence( _In_ const string_t& xboxUserId ) { auto asyncWrapper = new AsyncWrapper( [](XAsyncBlock* async, presence_record& presenceRecord) { XblPresenceRecordHandle handle; auto hr = XblPresenceGetPresenceResult(async, &handle); if (SUCCEEDED(hr)) { presenceRecord = presence_record(handle); XblPresenceRecordCloseHandle(handle); } return hr; }); auto hr = XblPresenceGetPresenceAsync( m_xblContextHandle, Utils::Uint64FromStringT(xboxUserId), &asyncWrapper->async ); return asyncWrapper->Task(hr); } pplx::task>> presence_service::get_presence_for_multiple_users( _In_ const std::vector& xboxUserIds ) { return get_presence_for_multiple_users( xboxUserIds, {}, {}, presence_detail_level::default_level, false, false ); } pplx::task>> presence_service::get_presence_for_multiple_users( _In_ const std::vector& xboxUserIds, _In_ const std::vector& deviceTypes, _In_ const std::vector& titleIds, _In_ presence_detail_level presenceDetailLevel, _In_ bool onlineOnly, _In_ bool broadcastingOnly ) { auto asyncWrapper = new AsyncWrapper>( [](XAsyncBlock* async, std::vector& presenceRecords) { size_t resultCount; auto hr = XblPresenceGetPresenceForMultipleUsersResultCount(async, &resultCount); if (SUCCEEDED(hr)) { std::vector recordHandles(resultCount); hr = XblPresenceGetPresenceForMultipleUsersResult(async, recordHandles.data(), resultCount); if (SUCCEEDED(hr)) { presenceRecords = Utils::Transform(recordHandles, [](XblPresenceRecordHandle recordHandle) { presence_record record(recordHandle); XblPresenceRecordCloseHandle(recordHandle); return record; }); } } return hr; }); XblPresenceQueryFilters filters{}; if (!deviceTypes.empty()) { filters.deviceTypes = (XblPresenceDeviceType*)(deviceTypes.data()); filters.deviceTypesCount = deviceTypes.size(); } if (!titleIds.empty()) { filters.titleIds = titleIds.data(); filters.titleIdsCount = titleIds.size(); } filters.detailLevel = static_cast(presenceDetailLevel); filters.broadcastingOnly = broadcastingOnly; filters.onlineOnly = onlineOnly; auto xuids = Utils::XuidVectorFromXuidStringVector(xboxUserIds); auto hr = XblPresenceGetPresenceForMultipleUsersAsync( m_xblContextHandle, xuids.data(), xuids.size(), &filters, &asyncWrapper->async ); return asyncWrapper->Task(hr); } pplx::task>> presence_service::get_presence_for_social_group( _In_ const string_t& socialGroup ) { return get_presence_for_social_group( socialGroup, string_t(), {}, {}, presence_detail_level::default_level, false, false ); } pplx::task>> presence_service::get_presence_for_social_group( _In_ const string_t& socialGroup, _In_ const string_t& socialGroupOwnerXboxUserId, _In_ const std::vector& deviceTypes, _In_ const std::vector& titleIds, _In_ presence_detail_level peoplehubDetailLevel, _In_ bool onlineOnly, _In_ bool broadcastingOnly ) { auto asyncWrapper = new AsyncWrapper>( [](XAsyncBlock* async, std::vector& presenceRecords) { size_t resultCount; auto hr = XblPresenceGetPresenceForSocialGroupResultCount(async, &resultCount); if (SUCCEEDED(hr)) { std::vector recordHandles(resultCount); hr = XblPresenceGetPresenceForSocialGroupResult(async, recordHandles.data(), resultCount); if (SUCCEEDED(hr)) { presenceRecords = Utils::Transform(recordHandles, [](XblPresenceRecordHandle recordHandle) { presence_record record(recordHandle); XblPresenceRecordCloseHandle(recordHandle); return record; }); } } return hr; }); XblPresenceQueryFilters filters{}; if (!deviceTypes.empty()) { filters.deviceTypes = (XblPresenceDeviceType*)(deviceTypes.data()); filters.deviceTypesCount = deviceTypes.size(); } if (!titleIds.empty()) { filters.titleIds = titleIds.data(); filters.titleIdsCount = titleIds.size(); } filters.detailLevel = static_cast(peoplehubDetailLevel); filters.broadcastingOnly = broadcastingOnly; filters.onlineOnly = onlineOnly; uint64_t groupOwnerXuid = 0; if (!socialGroupOwnerXboxUserId.empty()) { groupOwnerXuid = Utils::Uint64FromStringT(socialGroupOwnerXboxUserId); } auto hr = XblPresenceGetPresenceForSocialGroupAsync( m_xblContextHandle, Utils::StringFromStringT(socialGroup).data(), groupOwnerXuid ? &groupOwnerXuid : nullptr, &filters, &asyncWrapper->async ); return asyncWrapper->Task(hr); } xbox_live_result> presence_service::subscribe_to_device_presence_change( _In_ const string_t& xboxUserId ) { XblRealTimeActivitySubscriptionHandle subHandle{}; auto hr = XblPresenceSubscribeToDevicePresenceChange( m_xblContextHandle, Utils::Uint64FromStringT(xboxUserId), &subHandle ); if (FAILED(hr)) { return xbox_live_result>(Utils::ConvertHr(hr)); } return xbox_live_result>(std::make_shared(subHandle, xboxUserId)); } xbox_live_result presence_service::unsubscribe_from_device_presence_change( _In_ std::shared_ptr subscription ) { return Utils::ConvertHr(XblPresenceUnsubscribeFromDevicePresenceChange(m_xblContextHandle, subscription->m_handle)); } xbox_live_result> presence_service::subscribe_to_title_presence_change( _In_ const string_t& xboxUserId, _In_ uint32_t titleId ) { XblRealTimeActivitySubscriptionHandle subHandle{}; auto hr = XblPresenceSubscribeToTitlePresenceChange( m_xblContextHandle, Utils::Uint64FromStringT(xboxUserId), titleId, &subHandle ); if (FAILED(hr)) { return xbox_live_result>(Utils::ConvertHr(hr)); } return xbox_live_result>(std::make_shared(subHandle, xboxUserId, titleId)); } xbox_live_result presence_service::unsubscribe_from_title_presence_change( _In_ std::shared_ptr subscription ) { return Utils::ConvertHr(XblPresenceUnsubscribeFromTitlePresenceChange(m_xblContextHandle, subscription->m_handle)); } struct presence_service::HandlerContext { XblFunctionContext internalContext; std::function devicePresenceChangedHandler; std::function titlePresenceChangedHandler; }; function_context presence_service::add_device_presence_changed_handler( _In_ std::function handler ) { auto context = new HandlerContext{}; context->devicePresenceChangedHandler = std::move(handler); context->internalContext = XblPresenceAddDevicePresenceChangedHandler(m_xblContextHandle, [](void* context, uint64_t xuid, XblPresenceDeviceType deviceType, bool isUserLoggedOnDevice) { auto handlerContext{ static_cast(context) }; handlerContext->devicePresenceChangedHandler(device_presence_change_event_args{ xuid, deviceType, isUserLoggedOnDevice }); }, context); return context; } void presence_service::remove_device_presence_changed_handler( _In_ function_context context ) { auto handlerContext{ static_cast(context) }; XblPresenceRemoveDevicePresenceChangedHandler(m_xblContextHandle, handlerContext->internalContext); delete handlerContext; } function_context presence_service::add_title_presence_changed_handler( _In_ std::function handler ) { auto context = new HandlerContext{}; context->titlePresenceChangedHandler = std::move(handler); context->internalContext = XblPresenceAddTitlePresenceChangedHandler(m_xblContextHandle, [](void* context, uint64_t xuid, uint32_t titleId, XblPresenceTitleState titleState) { auto handlerContext{ static_cast(context) }; handlerContext->titlePresenceChangedHandler(title_presence_change_event_args{ xuid, titleId, titleState }); }, context); return context; } void presence_service::remove_title_presence_changed_handler( _In_ function_context context ) { auto handlerContext{ static_cast(context) }; XblPresenceRemoveTitlePresenceChangedHandler(m_xblContextHandle, handlerContext->internalContext); delete handlerContext; } presence_service::presence_service(XblContextHandle xblContextHandle) { XblContextDuplicateHandle(xblContextHandle, &m_xblContextHandle); } presence_service::presence_service(const presence_service& other) { XblContextDuplicateHandle(other.m_xblContextHandle, &m_xblContextHandle); } presence_service& presence_service::operator=(presence_service other) { std::swap(m_xblContextHandle, other.m_xblContextHandle); return *this; } presence_service::~presence_service() { XblContextCloseHandle(m_xblContextHandle); } NAMESPACE_MICROSOFT_XBOX_SERVICES_PRESENCE_CPP_END XBL_WARNING_POP ================================================ FILE: Include/xsapi-cpp/impl/privacy.hpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "public_utils.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_PRIVACY_CPP_BEGIN permission_deny_reason::permission_deny_reason( const XblPermissionDenyReasonDetails& reasonDetails ) : m_reasonDetails(reasonDetails) { } string_t permission_deny_reason::reason() const { static const std::unordered_map reasonsMap = { { static_cast(XblPermissionDenyReason::Unknown), _T("Unknown") }, { static_cast(XblPermissionDenyReason::NotAllowed), _T("NotAllowed") }, { static_cast(XblPermissionDenyReason::MissingPrivilege), _T("MissingPrivilege") }, { static_cast(XblPermissionDenyReason::PrivilegeRestrictsTarget), _T("PrivilegeRestrictsTarget") }, { static_cast(XblPermissionDenyReason::BlockListRestrictsTarget), _T("BlockListRestrictsTarget") }, { static_cast(XblPermissionDenyReason::MuteListRestrictsTarget), _T("MuteListRestrictsTarget") }, { static_cast(XblPermissionDenyReason::PrivacySettingRestrictsTarget), _T("PrivacySettingRestrictsTarget") } }; return reasonsMap.at(static_cast(m_reasonDetails.reason)); } string_t permission_deny_reason::restricted_setting() const { static const std::unordered_map settingsMap = { // Privacy settings { static_cast(XblPrivacySetting::ShareFriendList), _T("ShareFriendList") }, { static_cast(XblPrivacySetting::ShareGameHistory), _T("ShareGameHistory") }, { static_cast(XblPrivacySetting::CommunicateUsingTextAndVoice), _T("CommunicateUsingTextAndVoice") }, { static_cast(XblPrivacySetting::SharePresence), _T("SharePresence") }, { static_cast(XblPrivacySetting::ShareProfile), _T("ShareProfile") }, { static_cast(XblPrivacySetting::ShareVideoAndMusicStatus), _T("ShareVideoAndMusicStatus") }, { static_cast(XblPrivacySetting::CommunicateUsingVideo), _T("CommunicateUsingVideo") }, { static_cast(XblPrivacySetting::CollectVoiceData), _T("CollectVoiceData") }, { static_cast(XblPrivacySetting::ShareXboxMusicActivity), _T("ShareXboxMusicActivity") }, { static_cast(XblPrivacySetting::ShareExerciseInfo), _T("ShareExerciseInfo") }, { static_cast(XblPrivacySetting::ShareIdentity), _T("ShareIdentity") }, { static_cast(XblPrivacySetting::ShareIdentityInGame), _T("ShareIdentityInGame") }, { static_cast(XblPrivacySetting::ShareRecordedGameSessions), _T("ShareRecordedGameSessions") }, { static_cast(XblPrivacySetting::CollectLiveTvData), _T("CollectLiveTvData") }, { static_cast(XblPrivacySetting::CollectXboxVideoData), _T("CollectXboxVideoData") }, { static_cast(XblPrivacySetting::ShareIdentityTransitively), _T("ShareIdentityTransitively") }, { static_cast(XblPrivacySetting::ShareVideoHistory), _T("ShareVideoHistory") }, { static_cast(XblPrivacySetting::ShareMusicHistory), _T("ShareMusicHistory") }, { static_cast(XblPrivacySetting::AllowUserCreatedContentViewing), _T("AllowUserCreatedContentViewing") }, { static_cast(XblPrivacySetting::AllowProfileViewing), _T("AllowProfileViewing") }, { static_cast(XblPrivacySetting::ShowRealTimeActivity), _T("ShowRealTimeActivity") }, { static_cast(XblPrivacySetting::CollectVoiceDataXboxOneFull), _T("CollectVoiceDataXboxOneFull") }, { static_cast(XblPrivacySetting::CanShareIdentity), _T("CanShareIdentity") }, { static_cast(XblPrivacySetting::ShareContentToExternalNetworks), _T("ShareContentToExternalNetworks") }, { static_cast(XblPrivacySetting::CollectVoiceSearchData), _T("CollectVoiceSearchData") }, { static_cast(XblPrivacySetting::ShareClubMembership), _T("ShareClubMembership") }, { static_cast(XblPrivacySetting::CollectVoiceGameChatData), _T("CollectVoiceGameChatData") }, { static_cast(XblPrivacySetting::ShareActivityFeed), _T("ShareActivityFeed") }, { static_cast(XblPrivacySetting::CommunicateDuringCrossNetworkPlay), _T("CommunicateDuringCrossNetworkPlay") }, }; auto iter = settingsMap.find(static_cast(m_reasonDetails.restrictedPrivacySetting)); if (iter != settingsMap.end()) { return iter->second; } static const std::unordered_map privMap = { // Permissions { static_cast(XblPrivilege::AllowIngameVoiceCommunications), _T("AllowIngameVoiceCommunications") }, { static_cast(XblPrivilege::AllowVideoCommunications), _T("PrivilegeVideoCommunications") }, { static_cast(XblPrivilege::AllowProfileViewing), _T("AllowProfileViewing") }, { static_cast(XblPrivilege::AllowCommunications), _T("AllowCommunications") }, { static_cast(XblPrivilege::AllowMultiplayer), _T("AllowMultiplayer") }, { static_cast(XblPrivilege::AllowAddFriend), _T("AllowAddFriend") } }; auto iter2 = privMap.find(static_cast(m_reasonDetails.restrictedPrivacySetting)); if (iter2 != privMap.end()) { return iter2->second; } return string_t(); } permission_check_result::permission_check_result( const XblPermissionCheckResult* result ) : m_result(*result) { for (auto i = 0u; i < m_result.reasonsCount; ++i) { m_reasons.push_back(permission_deny_reason(m_result.reasons[i])); } } bool permission_check_result::is_allowed() const { return m_result.isAllowed; } string_t permission_check_result::permission_requested() const { static const std::unordered_map permissionsMap = { { static_cast(XblPermission::CommunicateUsingText), permission_id_constants::communicate_using_text() }, { static_cast(XblPermission::CommunicateUsingVideo), permission_id_constants::communicate_using_video() }, { static_cast(XblPermission::CommunicateUsingVoice), permission_id_constants::communicate_using_voice() }, { static_cast(XblPermission::ViewTargetProfile), permission_id_constants::view_target_profile() }, { static_cast(XblPermission::ViewTargetGameHistory), permission_id_constants::view_target_game_history() }, { static_cast(XblPermission::ViewTargetVideoHistory), permission_id_constants::view_target_video_history() }, { static_cast(XblPermission::ViewTargetMusicHistory), permission_id_constants::view_target_music_history() }, { static_cast(XblPermission::ViewTargetExerciseInfo), permission_id_constants::view_target_exercise_info() }, { static_cast(XblPermission::ViewTargetPresence), permission_id_constants::view_target_presence() }, { static_cast(XblPermission::ViewTargetVideoStatus), permission_id_constants::view_target_video_status() }, { static_cast(XblPermission::ViewTargetMusicStatus), permission_id_constants::view_target_music_status() }, { static_cast(XblPermission::PlayMultiplayer), permission_id_constants::play_multiplayer() }, { static_cast(XblPermission::ViewTargetUserCreatedContent), permission_id_constants::view_target_user_created_content() }, { static_cast(XblPermission::BroadcastWithTwitch), permission_id_constants::broadcast_with_twitch() } }; return permissionsMap.at(static_cast(m_result.permissionRequested)); } const std::vector& permission_check_result::deny_reasons() const { return m_reasons; } inline XblAnonymousUserType AnonymousUserTypeFromString( const string_t& anonymousUserTypeString ) { static const std::unordered_map anonymousUserTypes { { anonymous_user_type_constants::cross_network_user(), XblAnonymousUserType::CrossNetworkUser }, { anonymous_user_type_constants::crost_network_friend(), XblAnonymousUserType::CrossNetworkFriend } }; auto iter = anonymousUserTypes.find(anonymousUserTypeString); if (iter != anonymousUserTypes.end()) { return iter->second; } return XblAnonymousUserType::Unknown; } multiple_permissions_check_result::multiple_permissions_check_result( const XblPermissionCheckResult* results, size_t resultCount, string_t target ) : m_target{ std::move(target) } { for (auto i = 0u; i < resultCount; ++i) { XblAnonymousUserType anonymousUserType{ AnonymousUserTypeFromString(m_target) }; if ((anonymousUserType != XblAnonymousUserType::Unknown && results[i].targetUserType == anonymousUserType) || results[i].targetXuid == Utils::Uint64FromStringT(m_target)) { m_items.push_back(permission_check_result(results + i)); } } } const string_t& multiple_permissions_check_result::xbox_user_id() const { return m_target; } const std::vector& multiple_permissions_check_result::items() const { return m_items; } privacy_service::privacy_service(_In_ XblContextHandle contextHandle) { XblContextDuplicateHandle(contextHandle, &m_xblContext); } privacy_service::privacy_service(const privacy_service& other) { XblContextDuplicateHandle(other.m_xblContext, &m_xblContext); } privacy_service& privacy_service::operator=(privacy_service other) { std::swap(m_xblContext, other.m_xblContext); return *this; } privacy_service::~privacy_service() { XblContextCloseHandle(m_xblContext); } pplx::task>> privacy_service::get_avoid_list() { auto asyncWrapper = new AsyncWrapper>( [](XAsyncBlock* async, std::vector& result) { size_t xuidCount; auto hr = XblPrivacyGetAvoidListResultCount(async, &xuidCount); if (SUCCEEDED(hr)) { std::vector xuids(xuidCount); hr = XblPrivacyGetAvoidListResult(async, xuidCount, xuids.data()); for (auto& xuid : xuids) { result.push_back(Utils::StringTFromUint64(xuid)); } } return hr; }); auto hr = XblPrivacyGetAvoidListAsync(m_xblContext, &asyncWrapper->async); return asyncWrapper->Task(hr); } inline XblPermission XblPermissionFromString(const string_t& permission) { static const std::unordered_map map = { { permission_id_constants::communicate_using_text(), static_cast(XblPermission::CommunicateUsingText) }, { permission_id_constants::communicate_using_video(), static_cast(XblPermission::CommunicateUsingVideo) }, { permission_id_constants::communicate_using_voice(), static_cast(XblPermission::CommunicateUsingVoice) }, { permission_id_constants::view_target_profile(), static_cast(XblPermission::ViewTargetProfile) }, { permission_id_constants::view_target_game_history(), static_cast(XblPermission::ViewTargetGameHistory) }, { permission_id_constants::view_target_video_history(), static_cast(XblPermission::ViewTargetVideoHistory) }, { permission_id_constants::view_target_music_history(), static_cast(XblPermission::ViewTargetMusicHistory) }, { permission_id_constants::view_target_exercise_info(), static_cast(XblPermission::ViewTargetExerciseInfo) }, { permission_id_constants::view_target_presence(), static_cast(XblPermission::ViewTargetPresence) }, { permission_id_constants::view_target_video_status(), static_cast(XblPermission::ViewTargetVideoStatus) }, { permission_id_constants::view_target_music_status(), static_cast(XblPermission::ViewTargetMusicStatus) }, { permission_id_constants::play_multiplayer(), static_cast(XblPermission::PlayMultiplayer) }, { permission_id_constants::view_target_user_created_content(), static_cast(XblPermission::ViewTargetUserCreatedContent) }, { permission_id_constants::broadcast_with_twitch(), static_cast(XblPermission::BroadcastWithTwitch) } }; auto iter = map.find(permission); if (iter == map.end()) { return XblPermission::Unknown; } return static_cast(iter->second); } pplx::task> privacy_service::check_permission_with_target_user( _In_ const string_t& permissionId, _In_ const string_t& target ) { auto permission = XblPermissionFromString(permissionId); if (permission == XblPermission::Unknown || target.empty()) { return pplx::task_from_result(xbox_live_result(std::make_error_code(xbox_live_error_code::invalid_argument))); } auto asyncWrapper = new AsyncWrapper( [](XAsyncBlock* async, permission_check_result& result) { size_t bufferSize; auto hr = XblPrivacyCheckPermissionResultSize(async, &bufferSize); if (SUCCEEDED(hr)) { auto buffer = new char[bufferSize]; XblPermissionCheckResult* resultPtr; hr = XblPrivacyCheckPermissionResult(async, bufferSize, buffer, &resultPtr, nullptr); result = permission_check_result(resultPtr); delete[] buffer; } return hr; }); HRESULT hr{ S_OK }; XblAnonymousUserType anonymousUserType{ AnonymousUserTypeFromString(target) }; if (anonymousUserType != XblAnonymousUserType::Unknown) { hr = XblPrivacyCheckPermissionForAnonymousUserAsync( m_xblContext, permission, anonymousUserType, &asyncWrapper->async ); } else { hr = XblPrivacyCheckPermissionAsync( m_xblContext, permission, Utils::Uint64FromStringT(target), &asyncWrapper->async ); } return asyncWrapper->Task(hr); } pplx::task>> privacy_service::check_multiple_permissions_with_multiple_target_users( _In_ const std::vector& permissionIds, _In_ const std::vector& targets ) { std::vector xuids{}; std::vector userTypes{}; for (auto& target : targets) { XblAnonymousUserType anonymousUserType{ AnonymousUserTypeFromString(target) }; if (anonymousUserType == XblAnonymousUserType::Unknown) { xuids.push_back(Utils::Uint64FromStringT(target)); } else { userTypes.push_back(anonymousUserType); } } auto asyncWrapper = new AsyncWrapper>( [targets](XAsyncBlock* async, std::vector& results) { size_t bufferSize; auto hr = XblPrivacyBatchCheckPermissionResultSize(async, &bufferSize); if (SUCCEEDED(hr)) { auto buffer = new char[bufferSize]; XblPermissionCheckResult* resultPtr; size_t resultCount; hr = XblPrivacyBatchCheckPermissionResult(async, bufferSize, buffer, &resultPtr, &resultCount, nullptr); if (SUCCEEDED(hr)) { for (auto& target : targets) { results.push_back(multiple_permissions_check_result(resultPtr, resultCount, target)); } } delete[] buffer; } return hr; }); auto hr = XblPrivacyBatchCheckPermissionAsync( m_xblContext, Utils::Transform(permissionIds, XblPermissionFromString).data(), permissionIds.size(), xuids.data(), xuids.size(), userTypes.data(), userTypes.size(), &asyncWrapper->async ); return asyncWrapper->Task(hr); } pplx::task>> privacy_service::get_mute_list() { auto asyncWrapper = new AsyncWrapper>( [](XAsyncBlock* async, std::vector& result) { size_t xuidCount; auto hr = XblPrivacyGetMuteListResultCount(async, &xuidCount); if (SUCCEEDED(hr)) { std::vector xuids(xuidCount); hr = XblPrivacyGetMuteListResult(async, xuidCount, xuids.data()); for (auto& xuid : xuids) { result.push_back(Utils::StringTFromUint64(xuid)); } } return hr; }); auto hr = XblPrivacyGetMuteListAsync(m_xblContext, &asyncWrapper->async); return asyncWrapper->Task(hr); } pplx::task>> privacy_service::get_avoid_or_mute_list( _In_ const string_t& subPathName ) { if (Utils::Stricmp(subPathName, _T("mute")) == 0) { return get_mute_list(); } else if (Utils::Stricmp(subPathName, _T("avoid")) == 0) { return get_avoid_list(); } return pplx::task_from_result(xbox_live_result>(std::make_error_code(xbox_live_error_code::invalid_argument))); } NAMESPACE_MICROSOFT_XBOX_SERVICES_PRIVACY_CPP_END ================================================ FILE: Include/xsapi-cpp/impl/profile.hpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "public_utils.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_SOCIAL_CPP_BEGIN xbox_user_profile::xbox_user_profile( const XblUserProfile& profile ) : m_profile{ profile } { } string_t xbox_user_profile::app_display_name() const { return Utils::StringTFromUtf8(m_profile.appDisplayName); } web::uri xbox_user_profile::app_display_picture_resize_uri() const { return web::uri{ Utils::StringTFromUtf8(m_profile.appDisplayPictureResizeUri) }; } string_t xbox_user_profile::game_display_name() const { return Utils::StringTFromUtf8(m_profile.gameDisplayName); } web::uri xbox_user_profile::game_display_picture_resize_uri() const { return web::uri{ Utils::StringTFromUtf8(m_profile.gameDisplayPictureResizeUri) }; } string_t xbox_user_profile::gamerscore() const { return Utils::StringTFromUtf8(m_profile.gamerscore); } string_t xbox_user_profile::gamertag() const { return Utils::StringTFromUtf8(m_profile.gamertag); } string_t xbox_user_profile::xbox_user_id() const { return Utils::StringTFromUint64(m_profile.xboxUserId); } profile_service::profile_service(XblContextHandle xblContextHandle) { XblContextDuplicateHandle(xblContextHandle, &m_xblContextHandle); } profile_service::profile_service(const profile_service& other) { XblContextDuplicateHandle(other.m_xblContextHandle, &m_xblContextHandle); } profile_service& profile_service::operator=(profile_service other) { std::swap(m_xblContextHandle, other.m_xblContextHandle); return *this; } profile_service::~profile_service() { XblContextCloseHandle(m_xblContextHandle); } pplx::task> profile_service::get_user_profile( _In_ string_t xboxUserId ) { auto asyncWrapper = new AsyncWrapper{ [](XAsyncBlock* async, xbox_user_profile& result) { XblUserProfile profile{}; auto hr = XblProfileGetUserProfileResult(async, &profile); if (SUCCEEDED(hr)) { result = xbox_user_profile{ profile }; } return hr; } }; auto hr = XblProfileGetUserProfileAsync(m_xblContextHandle, Utils::Uint64FromStringT(std::move(xboxUserId)), &asyncWrapper->async); return asyncWrapper->Task(hr); } pplx::task>> profile_service::get_user_profiles( _In_ const std::vector& xboxUserIds ) { auto asyncWrapper = new AsyncWrapper>{ [](XAsyncBlock* async, std::vector& result) { size_t resultCount{ 0 }; auto hr = XblProfileGetUserProfilesResultCount(async, &resultCount); if (SUCCEEDED(hr)) { auto profiles = new XblUserProfile[resultCount]; hr = XblProfileGetUserProfilesResult(async, resultCount, profiles); if (SUCCEEDED(hr)) { for (size_t i = 0; i < resultCount; ++i) { result.push_back(profiles[i]); } } delete[] profiles; } return hr; } }; auto xuids{ Utils::Transform(xboxUserIds, Utils::Uint64FromStringT) }; auto hr = XblProfileGetUserProfilesAsync(m_xblContextHandle, xuids.data(), xuids.size(), &asyncWrapper->async); return asyncWrapper->Task(hr); } pplx::task>> profile_service::get_user_profiles_for_social_group( _In_ const string_t& socialGroup ) { auto asyncWrapper = new AsyncWrapper>{ [](XAsyncBlock* async, std::vector& result) { size_t resultCount{ 0 }; auto hr = XblProfileGetUserProfilesForSocialGroupResultCount(async, &resultCount); if (SUCCEEDED(hr)) { auto profiles = new XblUserProfile[resultCount]; hr = XblProfileGetUserProfilesForSocialGroupResult(async, resultCount, profiles); if (SUCCEEDED(hr)) { for (size_t i = 0; i < resultCount; ++i) { result.push_back(profiles[i]); } } delete[] profiles; } return hr; } }; auto hr = XblProfileGetUserProfilesForSocialGroupAsync(m_xblContextHandle, Utils::StringFromStringT(socialGroup).data(), &asyncWrapper->async); return asyncWrapper->Task(hr); } NAMESPACE_MICROSOFT_XBOX_SERVICES_SOCIAL_CPP_END ================================================ FILE: Include/xsapi-cpp/impl/public_utils.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "cpprest/json.h" #include "xsapi-c/types_c.h" #include "xsapi-cpp/errors.h" #if !XSAPI_XAL_AUTH && HC_PLATFORM != HC_PLATFORM_XDK #include "xsapi-cpp/types.h" #endif #ifndef MAKE_HTTP_HRESULT #define MAKE_HTTP_HRESULT(code) MAKE_HRESULT(1, 0x019, code) #endif namespace xbox { namespace services { struct Utils { static xbox_live_error_code ConvertHrToXblErrorCode(HRESULT hr) { switch (hr) { case S_OK: return xbox_live_error_code::no_error; case E_OUTOFMEMORY: return xbox_live_error_code::bad_alloc; case E_INVALIDARG: return xbox_live_error_code::invalid_argument; case E_XBL_RUNTIME_ERROR: return xbox_live_error_code::runtime_error; case __HRESULT_FROM_WIN32(ERROR_BAD_LENGTH): return xbox_live_error_code::length_error; case E_BOUNDS: return xbox_live_error_code::out_of_range; case E_NOINTERFACE: return xbox_live_error_code::bad_cast; case E_UNEXPECTED: return xbox_live_error_code::logic_error; case WEB_E_INVALID_JSON_STRING: return xbox_live_error_code::json_error; case WEB_E_UNEXPECTED_CONTENT: return xbox_live_error_code::uri_error; case ONL_E_ACTION_REQUIRED: return xbox_live_error_code::auth_user_interaction_required; case E_XBL_RTA_GENERIC_ERROR: return xbox_live_error_code::rta_generic_error; case E_XBL_RTA_SUBSCRIPTION_LIMIT_REACHED: return xbox_live_error_code::rta_subscription_limit_reached; case E_XBL_RTA_ACCESS_DENIED: return xbox_live_error_code::rta_access_denied; case E_XBL_RTA_NOT_ACTIVATED: return xbox_live_error_code::rta_not_activated; case E_XBL_AUTH_UNKNOWN_ERROR: return xbox_live_error_code::auth_unknown_error; case E_XBL_AUTH_RUNTIME_ERROR: return xbox_live_error_code::auth_runtime_error; case E_XBL_AUTH_NO_TOKEN: return xbox_live_error_code::auth_no_token_error; case __HRESULT_FROM_WIN32(ERROR_NO_SUCH_USER): return xbox_live_error_code::auth_user_not_signed_in; case __HRESULT_FROM_WIN32(ERROR_CANCELLED): return xbox_live_error_code::auth_user_cancel; case __HRESULT_FROM_WIN32(ERROR_BAD_CONFIGURATION): return xbox_live_error_code::invalid_config; case E_NOTIMPL: return xbox_live_error_code::unsupported; // HTTP errors case __HRESULT_FROM_WIN32(ERROR_RESOURCE_DATA_NOT_FOUND): return xbox_live_error_code::http_status_204_resource_data_not_found; case HTTP_E_STATUS_AMBIGUOUS: return xbox_live_error_code::http_status_300_multiple_choices; case HTTP_E_STATUS_MOVED: return xbox_live_error_code::http_status_301_moved_permanently; case HTTP_E_STATUS_REDIRECT: return xbox_live_error_code::http_status_302_found; case HTTP_E_STATUS_REDIRECT_METHOD: return xbox_live_error_code::http_status_303_see_other; case HTTP_E_STATUS_NOT_MODIFIED: return xbox_live_error_code::http_status_304_not_modified; case HTTP_E_STATUS_USE_PROXY: return xbox_live_error_code::http_status_305_use_proxy; case HTTP_E_STATUS_REDIRECT_KEEP_VERB: return xbox_live_error_code::http_status_307_temporary_redirect; case HTTP_E_STATUS_BAD_REQUEST: return xbox_live_error_code::http_status_400_bad_request; case HTTP_E_STATUS_DENIED: return xbox_live_error_code::http_status_401_unauthorized; case HTTP_E_STATUS_PAYMENT_REQ: return xbox_live_error_code::http_status_402_payment_required; case HTTP_E_STATUS_FORBIDDEN: return xbox_live_error_code::http_status_403_forbidden; case HTTP_E_STATUS_NOT_FOUND: return xbox_live_error_code::http_status_404_not_found; case HTTP_E_STATUS_BAD_METHOD: return xbox_live_error_code::http_status_405_method_not_allowed; case HTTP_E_STATUS_NONE_ACCEPTABLE: return xbox_live_error_code::http_status_406_not_acceptable; case HTTP_E_STATUS_PROXY_AUTH_REQ: return xbox_live_error_code::http_status_407_proxy_authentication_required; case HTTP_E_STATUS_REQUEST_TIMEOUT: return xbox_live_error_code::http_status_408_request_timeout; case HTTP_E_STATUS_CONFLICT: return xbox_live_error_code::http_status_409_conflict; case HTTP_E_STATUS_GONE: return xbox_live_error_code::http_status_410_gone; case HTTP_E_STATUS_LENGTH_REQUIRED: return xbox_live_error_code::http_status_411_length_required; case HTTP_E_STATUS_PRECOND_FAILED: return xbox_live_error_code::http_status_412_precondition_failed; case HTTP_E_STATUS_REQUEST_TOO_LARGE: return xbox_live_error_code::http_status_413_request_entity_too_large; case HTTP_E_STATUS_URI_TOO_LONG: return xbox_live_error_code::http_status_414_request_uri_too_long; case HTTP_E_STATUS_UNSUPPORTED_MEDIA: return xbox_live_error_code::http_status_415_unsupported_media_type; case HTTP_E_STATUS_RANGE_NOT_SATISFIABLE: return xbox_live_error_code::http_status_416_requested_range_not_satisfiable; case HTTP_E_STATUS_EXPECTATION_FAILED: return xbox_live_error_code::http_status_417_expectation_failed; case MAKE_HTTP_HRESULT(421): return xbox_live_error_code::http_status_421_misdirected_request; case MAKE_HTTP_HRESULT(422): return xbox_live_error_code::http_status_422_unprocessable_entity; case MAKE_HTTP_HRESULT(423): return xbox_live_error_code::http_status_423_locked; case MAKE_HTTP_HRESULT(424): return xbox_live_error_code::http_status_424_failed_dependency; case MAKE_HTTP_HRESULT(426): return xbox_live_error_code::http_status_426_upgrade_required; case MAKE_HTTP_HRESULT(428): return xbox_live_error_code::http_status_428_precondition_required; case MAKE_HTTP_HRESULT(429): return xbox_live_error_code::http_status_429_too_many_requests; case MAKE_HTTP_HRESULT(431): return xbox_live_error_code::http_status_431_request_header_fields_too_large; case MAKE_HTTP_HRESULT(449): return xbox_live_error_code::http_status_449_retry_with; case MAKE_HTTP_HRESULT(451): return xbox_live_error_code::http_status_451_unavailable_for_legal_reasons; case HTTP_E_STATUS_SERVER_ERROR: return xbox_live_error_code::http_status_500_internal_server_error; case HTTP_E_STATUS_NOT_SUPPORTED: return xbox_live_error_code::http_status_501_not_implemented; case HTTP_E_STATUS_BAD_GATEWAY: return xbox_live_error_code::http_status_502_bad_gateway; case HTTP_E_STATUS_SERVICE_UNAVAIL: return xbox_live_error_code::http_status_503_service_unavailable; case HTTP_E_STATUS_GATEWAY_TIMEOUT: return xbox_live_error_code::http_status_504_gateway_timeout; case HTTP_E_STATUS_VERSION_NOT_SUP: return xbox_live_error_code::http_status_505_http_version_not_supported; case MAKE_HTTP_HRESULT(506): return xbox_live_error_code::http_status_506_variant_also_negotiates; case MAKE_HTTP_HRESULT(507): return xbox_live_error_code::http_status_507_insufficient_storage; case MAKE_HTTP_HRESULT(508): return xbox_live_error_code::http_status_508_loop_detected; case MAKE_HTTP_HRESULT(510): return xbox_live_error_code::http_status_510_not_extended; case MAKE_HTTP_HRESULT(511): return xbox_live_error_code::http_status_511_network_authentication_required; default: return xbox_live_error_code::generic_error; } } static std::error_code ConvertHr(HRESULT hr) { return std::make_error_code(ConvertHrToXblErrorCode(hr)); } static string_t StringTFromUtf8(_In_z_ const char* utf8) { if (utf8 == nullptr) { return string_t(); } #if HC_PLATFORM_IS_MICROSOFT auto cchOutString = CharTFromUft8(utf8, nullptr, 0); string_t out(static_cast(cchOutString) - 1, '\0'); CharTFromUft8(utf8, &out[0], cchOutString); return out; #else return string_t(utf8); #endif } static std::string StringFromStringT(_In_ const string_t& stringt) { #if HC_PLATFORM_IS_MICROSOFT auto cchOutString = Utf8FromCharT(stringt.data(), nullptr, 0); std::string out(static_cast(cchOutString) - 1, '\0'); Utf8FromCharT(stringt.data(), &out[0], cchOutString); return out; #else return std::string(stringt.data()); #endif } static int Utf8FromCharT( _In_z_ const char_t* inArray, _Out_writes_z_(cchOutArray) char* outArray, _In_ int cchOutArray ) { #if HC_PLATFORM_IS_MICROSOFT // query for the buffer size auto queryResult = WideCharToMultiByte( CP_UTF8, WC_ERR_INVALID_CHARS, inArray, -1, nullptr, 0, nullptr, nullptr ); if (queryResult > cchOutArray && cchOutArray == 0) { return queryResult; } else if (queryResult == 0 || queryResult > cchOutArray) { throw std::exception("utf8_from_char_t failed"); } auto conversionResult = WideCharToMultiByte( CP_UTF8, WC_ERR_INVALID_CHARS, inArray, -1, outArray, cchOutArray, nullptr, nullptr ); if (conversionResult == 0) { throw std::exception("utf8_from_char_t failed"); } return conversionResult; #else int len = (int)strlen(inArray); if (len < cchOutArray && outArray != nullptr) { strlcpy(outArray, inArray, len + 1); } else if (cchOutArray > 0) { return 0; } return len + 1; #endif } static int CharTFromUft8( _In_z_ const char* inArray, _Out_writes_z_(cchOutArray) char_t* outArray, _In_ int cchOutArray ) { #if HC_PLATFORM_IS_MICROSOFT // query for the buffer size auto queryResult = MultiByteToWideChar( CP_UTF8, MB_ERR_INVALID_CHARS, inArray, -1, nullptr, 0 ); if (queryResult > cchOutArray && cchOutArray == 0) { return queryResult; } else if (queryResult == 0 || queryResult > cchOutArray) { throw std::exception("char_t_from_utf8 failed"); } auto conversionResult = MultiByteToWideChar( CP_UTF8, MB_ERR_INVALID_CHARS, inArray, -1, outArray, cchOutArray ); if (conversionResult == 0) { throw std::exception("char_t_from_utf8 failed"); } return conversionResult; #else int len = (int)strlen(inArray); if (len < cchOutArray && outArray != nullptr) { strlcpy(outArray, inArray, len + 1); } else if (cchOutArray > 0) { return 0; } return len + 1; #endif } static size_t CopyUtf8( _In_ char* destinationCharArr, _In_ size_t sizeInWords, _In_ const char* sourceCharArr ) { #if HC_PLATFORM_IS_MICROSOFT return strcpy_s(destinationCharArr, sizeInWords, sourceCharArr); #else return strlcpy(destinationCharArr, sourceCharArr, sizeInWords); #endif } static string_t StringTFromUint64(_In_ uint64_t val) { stringstream_t ss; ss << val; return ss.str(); } static std::string StringFromUint64(_In_ uint64_t val) { std::stringstream ss; ss << val; return ss.str(); } static uint64_t Uint64FromStringT(_In_ const string_t& str) { #if HC_PLATFORM_IS_MICROSOFT return _wtoi64(str.data()); #else return strtoull(str.data(), nullptr, 0); #endif } static int Stricmp(const char* left, const char* right) noexcept { #if HC_PLATFORM_IS_MICROSOFT return _stricmp(left, right); #else return strcasecmp(left, right); #endif } static int Stricmp(const string_t& left, const string_t& right) { #if HC_PLATFORM_IS_MICROSOFT return _wcsicmp(left.data(), right.data()); #else return strcasecmp(left.data(), right.data()); #endif } static web::json::value ParseJson(const char* jsonString) { try { if (jsonString) { return web::json::value::parse(Utils::StringTFromUtf8(jsonString)); } } catch (web::json::json_exception) { } return web::json::value::null(); } static web::json::value ExtractJsonField( _In_ const web::json::value& json, _In_ const string_t& name, _In_ bool required ) { if (json.is_object()) { auto& jsonObj = json.as_object(); auto it = jsonObj.find(name); if (it != jsonObj.end()) { return it->second; } } if (required) { utility::stringstream_t ss; ss << name; ss << " not found"; throw web::json::json_exception(ss.str().c_str()); } return web::json::value::null(); } static string_t ExtractJsonString( _In_ const web::json::value& jsonValue, _In_ const string_t& stringName, _In_ bool required = false, _In_ const string_t& defaultValue = string_t() ) { web::json::value field(ExtractJsonField(jsonValue, stringName, required)); if ((!field.is_string() && !required) || field.is_null()) { return defaultValue; } return field.as_string(); } static uint64_t ExtractJsonUint64( _In_ const web::json::value& jsonValue, _In_ const string_t& name, _In_ bool required = false, _In_ uint64_t defaultValue = 0 ) { web::json::value field(ExtractJsonField(jsonValue, name, required)); if (!field.is_number() && !required) { return defaultValue; } return field.as_number().to_uint64(); } template static std::vector Transform(InputIt first, InputIt last, Transformer op) { std::vector out; std::transform(first, last, std::back_inserter(out), op); return out; } template static std::vector Transform(const std::vector& in, Transformer op) { return Transform(in.begin(), in.end(), op); } template static std::vector Transform(TIn* inArray, size_t inArrayCount, Transformer op) { return Transform(inArray, inArray + inArrayCount, op); } template static std::vector Transform(TIn* inArray, size_t inArrayCount) { return Transform(inArray, inArrayCount, [](const TIn& in) { return TOut(in); }); } static std::vector XuidStringVectorFromXuidArray(const uint64_t* xuids, size_t xuidsCount) { return Transform(xuids, xuidsCount, StringTFromUint64); } static std::vector XuidVectorFromXuidStringVector(const std::vector& xuidStrings) { return Transform(xuidStrings, Uint64FromStringT); } static std::vector StringTVectorFromCStringArray(const char** stringArray, size_t arrayCount) { return Transform(stringArray, arrayCount, StringTFromUtf8); } #define MS_TICKS (10000) #define SECOND_TICKS (1000 * MS_TICKS) #define MINUTE_TICKS (60 * SECOND_TICKS) #define HOUR_TICKS (60 * MINUTE_TICKS) #define DAY_TICKS (24 * HOUR_TICKS) static utility::datetime DatetimeFromTimeT(time_t time) { const uint64_t epoch_offset = 11644473600LL; uint64_t result = epoch_offset + time; result *= SECOND_TICKS; // convert to 10e-7 return utility::datetime() + result; } static time_t TimeTFromDatetime(const utility::datetime& datetime) { const uint64_t epoch_offset = 11644473600LL; uint64_t seconds = datetime.to_interval() / SECOND_TICKS; if (seconds >= epoch_offset) { return (time_t)(seconds - epoch_offset); } else { // If time is before epoch, 0 is returned. return 0; } } static char_t ToLower(char_t c) { return std::tolower(c, std::locale()); } }; #if !XSAPI_NO_PPL template struct AsyncWrapper { typedef std::function ResultExtractor; AsyncWrapper(ResultExtractor resultExtractor) : m_resultExtractor(std::move(resultExtractor)) { async.queue = XblGetAsyncQueue(); async.context = this; async.callback = [](XAsyncBlock* async) { auto thisPtr = static_cast*>(async->context); T result; auto hr = thisPtr->m_resultExtractor(async, result); if (SUCCEEDED(hr)) { thisPtr->m_taskCompletionEvent.set(xbox_live_result(result)); } else { thisPtr->m_taskCompletionEvent.set(xbox_live_result(Utils::ConvertHr(hr))); } delete thisPtr; }; } XAsyncBlock async{}; // If the Async API fails, the callback will never be invoked. Return a failure task and self destruct. pplx::task> Task(HRESULT asyncApiResult) { if (SUCCEEDED(asyncApiResult)) { return pplx::task>(m_taskCompletionEvent); } else { delete this; return pplx::task_from_result(xbox_live_result(Utils::ConvertHr(asyncApiResult))); } } private: AsyncWrapper(const AsyncWrapper&) = delete; AsyncWrapper& operator=(AsyncWrapper) = delete; ResultExtractor m_resultExtractor; pplx::task_completion_event> m_taskCompletionEvent; }; template<> struct AsyncWrapper { typedef std::function ResultExtractor; AsyncWrapper() : AsyncWrapper{ [](XAsyncBlock* async) { return XAsyncGetStatus(async, false); } } { } AsyncWrapper(ResultExtractor resultExtractor) : m_resultExtractor{ std::move(resultExtractor) } { async.queue = XblGetAsyncQueue(); async.context = this; async.callback = [](XAsyncBlock* async) { auto thisPtr = static_cast*>(async->context); auto hr = thisPtr->m_resultExtractor(async); thisPtr->m_taskCompletionEvent.set(xbox_live_result(Utils::ConvertHr(hr))); delete thisPtr; }; } XAsyncBlock async{}; pplx::task> Task(HRESULT asyncApiResult) { if (SUCCEEDED(asyncApiResult)) { return pplx::task>(m_taskCompletionEvent); } else { // If the Async API fails, the callback will never be invoked. Return a failure task and self destruct. delete this; return pplx::task_from_result(xbox_live_result(Utils::ConvertHr(asyncApiResult))); } } private: AsyncWrapper(const AsyncWrapper&) = delete; AsyncWrapper& operator=(AsyncWrapper) = delete; ResultExtractor m_resultExtractor; pplx::task_completion_event> m_taskCompletionEvent; }; #endif // #if !XSAPI_NO_PPL // RAII class used to create an array of C-Strings from a std::vector. // On Microsoft platforms where string_t is using wide characters, we will allocate memory for the // UTF-8 string array. On other platforms, UTF8StringArrayRef will only reference the existing memory. class UTF8StringArrayRef { public: #if HC_PLATFORM_IS_MICROSOFT UTF8StringArrayRef(const std::vector& string_tVector) { std::transform(string_tVector.begin(), string_tVector.end(), std::back_inserter(m_strings), [](const string_t& in) { auto cchOut = Utils::Utf8FromCharT(in.data(), nullptr, 0); auto out = new char[cchOut]; Utils::Utf8FromCharT(in.data(), out, cchOut); return out; }); } ~UTF8StringArrayRef() noexcept { for (auto string : m_strings) { delete[] string; } } #else UTF8StringArrayRef(const std::vector& string_tVector) noexcept { std::transform(string_tVector.begin(), string_tVector.end(), std::back_inserter(m_strings), [](const string_t& in) { return in.data(); }); } ~UTF8StringArrayRef() noexcept = default; #endif UTF8StringArrayRef(UTF8StringArrayRef&& other) noexcept : m_strings{ std::move(other.m_strings) } { } UTF8StringArrayRef(const UTF8StringArrayRef&) = delete; UTF8StringArrayRef& operator=(UTF8StringArrayRef) = delete; const char** Data() noexcept { return m_strings.data(); } size_t Size() const noexcept { return m_strings.size(); } private: std::vector m_strings; }; } } // xbox::services ================================================ FILE: Include/xsapi-cpp/impl/real_time_activity.hpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "public_utils.h" #include "xsapi-c/real_time_activity_c.h" XBL_WARNING_PUSH XBL_WARNING_DISABLE_DEPRECATED NAMESPACE_MICROSOFT_XBOX_SERVICES_RTA_CPP_BEGIN real_time_activity_subscription::real_time_activity_subscription( XblRealTimeActivitySubscriptionHandle handle ) : m_handle{ handle } { } real_time_activity_subscription_state real_time_activity_subscription::state() const { XblRealTimeActivitySubscriptionState state; XblRealTimeActivitySubscriptionGetState(m_handle, &state); return static_cast(state); } const string_t& real_time_activity_subscription::resource_uri() const { return m_resourceUri; } uint32_t real_time_activity_subscription::subscription_id() const { uint32_t id; XblRealTimeActivitySubscriptionGetId(m_handle, &id); return id; } real_time_activity_subscription_error_event_args::real_time_activity_subscription_error_event_args( XblRealTimeActivitySubscriptionHandle subscriptionHandle, HRESULT subscriptionError ) : m_subscription{ subscriptionHandle } { m_err = Utils::ConvertHr(subscriptionError); } const real_time_activity_subscription& real_time_activity_subscription_error_event_args::subscription() { return m_subscription; } std::error_code real_time_activity_subscription_error_event_args::err() const { return m_err; } std::string real_time_activity_subscription_error_event_args::err_message() const { return std::string(); } real_time_activity_service::real_time_activity_service(_In_ XblContextHandle contextHandle) { XblContextDuplicateHandle(contextHandle, &m_xblContext); } real_time_activity_service::real_time_activity_service(const real_time_activity_service& other) { XblContextDuplicateHandle(other.m_xblContext, &m_xblContext); } real_time_activity_service& real_time_activity_service::operator=(real_time_activity_service other) { std::swap(m_xblContext, other.m_xblContext); return *this; } real_time_activity_service::~real_time_activity_service() { XblContextCloseHandle(m_xblContext); } void real_time_activity_service::activate() { XblRealTimeActivityActivate(m_xblContext); } void real_time_activity_service::deactivate() { XblRealTimeActivityDeactivate(m_xblContext); } struct real_time_activity_service::HandlerContext { XblFunctionContext internalContext; std::function connectionStateChangeHandler; std::function subscriptionErrorHandler; std::function resyncHandler; }; function_context real_time_activity_service::add_connection_state_change_handler( _In_ std::function handler ) { auto context = new HandlerContext{}; context->connectionStateChangeHandler = std::move(handler); context->internalContext = XblRealTimeActivityAddConnectionStateChangeHandler(m_xblContext, [](_In_ void* context, _In_ XblRealTimeActivityConnectionState connectionState) { auto handlerContext{ static_cast(context) }; handlerContext->connectionStateChangeHandler(static_cast(connectionState)); }, context); return context; } void real_time_activity_service::remove_connection_state_change_handler( _In_ function_context remove ) { auto handlerContext{ static_cast(remove) }; XblRealTimeActivityRemoveConnectionStateChangeHandler(m_xblContext, handlerContext->internalContext); delete handlerContext; } function_context real_time_activity_service::add_subscription_error_handler( _In_ std::function handler ) { auto context = new HandlerContext{}; context->subscriptionErrorHandler = std::move(handler); context->internalContext = XblRealTimeActivityAddSubscriptionErrorHandler(m_xblContext, [](_In_ void* context, _In_ XblRealTimeActivitySubscriptionHandle subscription, HRESULT subscriptionError) { auto handlerContext{ static_cast(context) }; handlerContext->subscriptionErrorHandler( real_time_activity_subscription_error_event_args{ subscription, subscriptionError } ); }, context); return context; } void real_time_activity_service::remove_subscription_error_handler( _In_ function_context remove ) { auto handlerContext{ static_cast(remove) }; XblRealTimeActivityRemoveSubscriptionErrorHandler(m_xblContext, handlerContext->internalContext); delete handlerContext; } function_context real_time_activity_service::add_resync_handler( _In_ std::function handler ) { auto context = new HandlerContext{}; context->resyncHandler = std::move(handler); context->internalContext = XblRealTimeActivityAddResyncHandler(m_xblContext, [](_In_ void* context) { auto handlerContext{ static_cast(context) }; handlerContext->resyncHandler(); }, context); return context; } void real_time_activity_service::remove_resync_handler( _In_ function_context remove ) { auto handlerContext{ static_cast(remove) }; XblRealTimeActivityRemoveResyncHandler(m_xblContext, handlerContext->internalContext); delete handlerContext; } NAMESPACE_MICROSOFT_XBOX_SERVICES_RTA_CPP_END XBL_WARNING_POP ================================================ FILE: Include/xsapi-cpp/impl/service_call_logging_config.hpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "public_utils.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN #if HC_PLATFORM_IS_MICROSOFT std::shared_ptr service_call_logging_config::get_singleton_instance() { static std::shared_ptr s_instance = std::shared_ptr(new service_call_logging_config); return s_instance; } void service_call_logging_config::enable() {} void service_call_logging_config::disable() {} #if HC_PLATFORM == HC_PLATFORM_XDK || HC_PLATFORM == HC_PLATFORM_UWP || defined(XSAPI_UNIT_TESTS) void service_call_logging_config::_Register_for_protocol_activation() {} #endif #endif NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END ================================================ FILE: Include/xsapi-cpp/impl/social.hpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "public_utils.h" XBL_WARNING_PUSH XBL_WARNING_DISABLE_DEPRECATED NAMESPACE_MICROSOFT_XBOX_SERVICES_SOCIAL_CPP_BEGIN xbox_social_relationship::xbox_social_relationship( const XblSocialRelationship& socialRelationship ) : m_xuid{ Utils::StringTFromUint64(socialRelationship.xboxUserId) }, m_isFavorite{ socialRelationship.isFavorite }, m_isFollowingCaller{ socialRelationship.isFollowingCaller }, m_isFriend{ socialRelationship.isFriend }, m_socialNetworks{ Utils::Transform(socialRelationship.socialNetworks, socialRelationship.socialNetworksCount, Utils::StringTFromUtf8) } { } const string_t& xbox_social_relationship::xbox_user_id() const { return m_xuid; } bool xbox_social_relationship::is_favorite() const { return m_isFavorite; } bool xbox_social_relationship::is_following_caller() const { return m_isFollowingCaller; } bool xbox_social_relationship::is_friend() const { return m_isFriend; } const std::vector& xbox_social_relationship::social_networks() const { return m_socialNetworks; } xbox_social_relationship_result::xbox_social_relationship_result( XblSocialRelationshipResultHandle resultHandle, XblContextHandle xblContextHandle ) { assert(resultHandle); assert(xblContextHandle); XblSocialRelationshipResultDuplicateHandle(resultHandle, &m_resultHandle); XblContextDuplicateHandle(xblContextHandle, &m_xblContextHandle); } xbox_social_relationship_result::xbox_social_relationship_result( const xbox_social_relationship_result& other ) { if (other.m_resultHandle) { XblSocialRelationshipResultDuplicateHandle(other.m_resultHandle, &m_resultHandle); } if (other.m_xblContextHandle) { XblContextDuplicateHandle(other.m_xblContextHandle, &m_xblContextHandle); } } xbox_social_relationship_result& xbox_social_relationship_result::operator=( xbox_social_relationship_result other ) { std::swap(m_resultHandle, other.m_resultHandle); std::swap(m_xblContextHandle, other.m_xblContextHandle); return *this; } xbox_social_relationship_result::~xbox_social_relationship_result() { if (m_resultHandle) { XblSocialRelationshipResultCloseHandle(m_resultHandle); } if (m_xblContextHandle) { XblContextCloseHandle(m_xblContextHandle); } } std::vector xbox_social_relationship_result::items() const { const XblSocialRelationship* relationships{ nullptr }; size_t relationshipsCount{ 0 }; XblSocialRelationshipResultGetRelationships(m_resultHandle, &relationships, &relationshipsCount); return Utils::Transform(relationships, relationshipsCount); } uint32_t xbox_social_relationship_result::total_count() const { size_t totalCount{ 0 }; XblSocialRelationshipResultGetTotalCount(m_resultHandle, &totalCount); return static_cast(totalCount); } bool xbox_social_relationship_result::has_next() const { bool hasNext{ false }; XblSocialRelationshipResultHasNext(m_resultHandle, &hasNext); return hasNext; } pplx::task> xbox_social_relationship_result::get_next( _In_ uint32_t maxItems ) { auto asyncWrapper = new AsyncWrapper( [this](XAsyncBlock* async, xbox_social_relationship_result& result) { XblSocialRelationshipResultHandle resultHandle{ nullptr }; auto hr = XblSocialRelationshipResultGetNextResult(async, &resultHandle); if (SUCCEEDED(hr)) { result = xbox_social_relationship_result(resultHandle, m_xblContextHandle); XblSocialRelationshipResultCloseHandle(resultHandle); } return hr; }); auto hr = XblSocialRelationshipResultGetNextAsync(m_xblContextHandle, m_resultHandle, static_cast(maxItems), &asyncWrapper->async); return asyncWrapper->Task(hr); } social_relationship_change_event_args::social_relationship_change_event_args( const XblSocialRelationshipChangeEventArgs* args ) : m_callerXuid{ Utils::StringTFromUint64(args->callerXboxUserId) }, m_notificationType{ static_cast(args->socialNotification) }, m_xuids{ Utils::Transform(args->xboxUserIds, args->xboxUserIdsCount, Utils::StringTFromUint64) } { } const string_t& social_relationship_change_event_args::caller_xbox_user_id() const { return m_callerXuid; } social_notification_type social_relationship_change_event_args::social_notification() const { return m_notificationType; } const std::vector& social_relationship_change_event_args::xbox_user_ids() const { return m_xuids; } social_relationship_change_subscription::social_relationship_change_subscription( _In_ XblRealTimeActivitySubscriptionHandle handle, _In_ uint64_t xuid ) : real_time_activity_subscription(handle), m_xuid{ Utils::StringTFromUint64(xuid) } { stringstream_t uri; uri << _T("http://social.xboxlive.com/users/xuid(") << m_xuid << ")/friends"; m_resourceUri = uri.str(); } const string_t& social_relationship_change_subscription::xbox_user_id() const { return m_xuid; } social_service::social_service(XblContextHandle xblContextHandle) { XblContextDuplicateHandle(xblContextHandle, &m_xblContextHandle); XblContextGetXboxUserId(m_xblContextHandle, &m_xuid); } social_service::social_service(const social_service& other) : m_xuid{ other.m_xuid } { XblContextDuplicateHandle(other.m_xblContextHandle, &m_xblContextHandle); } social_service& social_service::operator=(social_service other) { std::swap(m_xblContextHandle, other.m_xblContextHandle); m_xuid = other.m_xuid; return *this; } social_service::~social_service() { XblContextCloseHandle(m_xblContextHandle); } pplx::task> social_service::get_social_relationships( _In_ uint64_t xuid, _In_ XblSocialRelationshipFilter filter, _In_ size_t startIndex, _In_ size_t maxItems ) { XblContextHandle xblContextHandle{ nullptr }; XblContextDuplicateHandle(m_xblContextHandle, &xblContextHandle); auto asyncWrapper = new AsyncWrapper( [xblContextHandle](XAsyncBlock* async, xbox_social_relationship_result& result) { XblSocialRelationshipResultHandle resultHandle{ nullptr }; auto hr = XblSocialGetSocialRelationshipsResult(async, &resultHandle); if (SUCCEEDED(hr)) { result = xbox_social_relationship_result(resultHandle, xblContextHandle); XblSocialRelationshipResultCloseHandle(resultHandle); } XblContextCloseHandle(xblContextHandle); return hr; }); auto hr = XblSocialGetSocialRelationshipsAsync(m_xblContextHandle, xuid, filter, startIndex, maxItems, &asyncWrapper->async); if (FAILED(hr)) { //Close the duplicated handle since it won't be closed in the callback XblContextCloseHandle(xblContextHandle); } return asyncWrapper->Task(hr); } pplx::task> social_service::get_social_relationships() { return get_social_relationships(m_xuid, XblSocialRelationshipFilter::All, 0, 0); } pplx::task> social_service::get_social_relationships( _In_ xbox_social_relationship_filter socialRelationshipFilter ) { return get_social_relationships(m_xuid, static_cast(socialRelationshipFilter), 0, 0); } pplx::task> social_service::get_social_relationships( _In_ const string_t& xuid ) { return get_social_relationships(Utils::Uint64FromStringT(xuid), XblSocialRelationshipFilter::All, 0, 0); } pplx::task> social_service::get_social_relationships( _In_ xbox_social_relationship_filter filter, _In_ uint32_t startIndex, _In_ uint32_t maxItems ) { return get_social_relationships(m_xuid, static_cast(filter), startIndex, maxItems); } xbox_live_result> social_service::subscribe_to_social_relationship_change( _In_ const string_t& xuidString ) { auto xuid{ Utils::Uint64FromStringT(xuidString) }; XblRealTimeActivitySubscriptionHandle subHandle{}; auto hr = XblSocialSubscribeToSocialRelationshipChange(m_xblContextHandle, xuid, &subHandle); if (FAILED(hr)) { return Utils::ConvertHr(hr); } return std::make_shared(subHandle, xuid); } xbox_live_result social_service::unsubscribe_from_social_relationship_change( _In_ std::shared_ptr subscription ) { return Utils::ConvertHr(XblSocialUnsubscribeFromSocialRelationshipChange(m_xblContextHandle, subscription->m_handle)); } struct social_service::HandlerContext { XblFunctionContext token{ 0 }; std::function handler; }; function_context social_service::add_social_relationship_changed_handler( _In_ std::function handler ) { auto context = new HandlerContext{}; context->handler = std::move(handler); context->token = XblSocialAddSocialRelationshipChangedHandler(m_xblContextHandle, [](const XblSocialRelationshipChangeEventArgs* args, void* context) { auto handlerContext{ static_cast(context) }; handlerContext->handler(args); }, context); return context; } void social_service::remove_social_relationship_changed_handler( _In_ function_context context ) { auto handlerContext{ static_cast(context) }; XblSocialRemoveSocialRelationshipChangedHandler(m_xblContextHandle, handlerContext->token); delete handlerContext; } reputation_feedback_item::reputation_feedback_item( _In_ const string_t& xboxUserId, _In_ reputation_feedback_type reputationFeedbackType, _In_ xbox::services::multiplayer::multiplayer_session_reference sessionRef, _In_ const string_t& reasonMessage, _In_ const string_t& evidenceResourceId ) : m_xboxUserId{ Utils::Uint64FromStringT(xboxUserId) }, m_reputationFeedbackType{ reputationFeedbackType }, m_sessionRef{ std::move(sessionRef) }, m_reasonMessage{ Utils::StringFromStringT(reasonMessage) }, m_evidenceResourceId{ Utils::StringFromStringT(evidenceResourceId) } { } string_t reputation_feedback_item::xbox_user_id() const { return Utils::StringTFromUint64(m_xboxUserId); } reputation_feedback_type reputation_feedback_item::feedback_type() const { return m_reputationFeedbackType; } const xbox::services::multiplayer::multiplayer_session_reference& reputation_feedback_item::session_reference() const { return m_sessionRef; } string_t reputation_feedback_item::reason_message() const { return Utils::StringTFromUtf8(m_reasonMessage.data()); } string_t reputation_feedback_item::evidence_resource_id() const { return Utils::StringTFromUtf8(m_evidenceResourceId.data()); } reputation_service::reputation_service(XblContextHandle xblContextHandle) { XblContextDuplicateHandle(xblContextHandle, &m_xblContextHandle); } reputation_service::reputation_service(const reputation_service& other) { XblContextDuplicateHandle(other.m_xblContextHandle, &m_xblContextHandle); } reputation_service& reputation_service::operator=(reputation_service other) { std::swap(m_xblContextHandle, other.m_xblContextHandle); return *this; } reputation_service::~reputation_service() { XblContextCloseHandle(m_xblContextHandle); } pplx::task> reputation_service::submit_reputation_feedback( _In_ const string_t& xboxUserId, _In_ reputation_feedback_type reputationFeedbackType, _In_ const string_t& sessionName, _In_ const string_t& reasonMessage, _In_ const string_t& evidenceResourceId ) { UNREFERENCED_PARAMETER(sessionName); auto asyncWrapper = new AsyncWrapper( [](XAsyncBlock* async) { return XAsyncGetStatus(async, false); }); auto hr = XblSocialSubmitReputationFeedbackAsync( m_xblContextHandle, Utils::Uint64FromStringT(xboxUserId), static_cast(reputationFeedbackType), nullptr, Utils::StringFromStringT(reasonMessage).data(), evidenceResourceId.size() > 0 ? Utils::StringFromStringT(evidenceResourceId).data() : nullptr, &asyncWrapper->async ); return asyncWrapper->Task(hr); } pplx::task> reputation_service::submit_batch_reputation_feedback( _In_ const std::vector& feedbackItems ) { auto asyncWrapper = new AsyncWrapper( [](XAsyncBlock* async) { return XAsyncGetStatus(async, false); }); auto items = Utils::Transform(feedbackItems, [](const reputation_feedback_item& i) { XblReputationFeedbackItem item{}; item.xboxUserId = i.m_xboxUserId; item.feedbackType = static_cast(i.m_reputationFeedbackType); item.sessionReference = &i.m_sessionRef.m_reference; if (i.m_reasonMessage.size() > 0) { item.reasonMessage = i.m_reasonMessage.data(); } if (i.m_evidenceResourceId.size() > 0) { item.evidenceResourceId = i.m_evidenceResourceId.data(); } return item; }); auto hr = XblSocialSubmitBatchReputationFeedbackAsync( m_xblContextHandle, items.data(), items.size(), &asyncWrapper->async ); return asyncWrapper->Task(hr); } NAMESPACE_MICROSOFT_XBOX_SERVICES_SOCIAL_CPP_END XBL_WARNING_POP ================================================ FILE: Include/xsapi-cpp/impl/social_manager.hpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "public_utils.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_SOCIAL_MANAGER_CPP_BEGIN title_history::title_history( const XblTitleHistory& titleHistory ) : m_titleHistory{ &titleHistory } { } title_history::title_history( const title_history& other ) : m_owningPtr{ std::make_shared(*other.m_titleHistory) }, m_titleHistory{ m_owningPtr.get() } { } title_history& title_history::operator=(title_history other) { m_owningPtr = other.m_owningPtr; m_titleHistory = m_owningPtr.get(); return *this; } bool title_history::has_user_played() const { return m_titleHistory->hasUserPlayed; } utility::datetime title_history::last_time_user_played() const { return Utils::DatetimeFromTimeT(m_titleHistory->lastTimeUserPlayed); } preferred_color::preferred_color( const XblPreferredColor& preferredColor ) { m_primaryColor = Utils::StringTFromUtf8(preferredColor.primaryColor); m_secondaryColor = Utils::StringTFromUtf8(preferredColor.secondaryColor); m_tertiaryColor = Utils::StringTFromUtf8(preferredColor.tertiaryColor); } const char_t* preferred_color::primary_color() const { return m_primaryColor.data(); } const char_t* preferred_color::secondary_color() const { return m_secondaryColor.data(); } const char_t* preferred_color::tertiary_color() const { return m_tertiaryColor.data(); } bool preferred_color::operator!=( const preferred_color& rhs ) const { return Utils::Stricmp(m_primaryColor, rhs.m_primaryColor) != 0 || Utils::Stricmp(m_secondaryColor, rhs.m_secondaryColor) != 0 || Utils::Stricmp(m_tertiaryColor, rhs.m_tertiaryColor) != 0; } social_manager_presence_title_record::social_manager_presence_title_record( const XblSocialManagerPresenceTitleRecord& titleRecord ) : m_titleRecord{ &titleRecord } { m_presenceText = Utils::StringTFromUtf8(m_titleRecord->presenceText); } social_manager_presence_title_record::social_manager_presence_title_record( const social_manager_presence_title_record& other ) : m_owningPtr{ std::make_shared(*other.m_titleRecord) }, m_titleRecord{ m_owningPtr.get() }, m_presenceText{ other.m_presenceText } { } social_manager_presence_title_record& social_manager_presence_title_record::operator=( social_manager_presence_title_record other ) { m_owningPtr = other.m_owningPtr; m_titleRecord = m_owningPtr.get(); m_presenceText = other.m_presenceText; return *this; } uint32_t social_manager_presence_title_record::title_id() const { return m_titleRecord->titleId; } bool social_manager_presence_title_record::is_title_active() const { return m_titleRecord->isTitleActive; } const char_t* social_manager_presence_title_record::presence_text() const { return m_presenceText.data(); } bool social_manager_presence_title_record::is_broadcasting() const { return m_titleRecord->isBroadcasting; } xbox::services::presence::presence_device_type social_manager_presence_title_record::device_type() const { return static_cast(m_titleRecord->deviceType); } bool social_manager_presence_title_record::is_primary() const { return m_titleRecord->isPrimary; } social_manager_presence_record::social_manager_presence_record( const XblSocialManagerPresenceRecord& presenceRecord ) : m_presenceRecord{ &presenceRecord } { m_titleRecords.reserve(XBL_NUM_PRESENCE_RECORDS); for (size_t i = 0; i < m_presenceRecord->presenceTitleRecordCount; ++i) { m_titleRecords.push_back(social_manager_presence_title_record{ m_presenceRecord->presenceTitleRecords[i] }); } } social_manager_presence_record::social_manager_presence_record( const social_manager_presence_record& other ) : m_owningPtr{ std::make_shared(*other.m_presenceRecord) }, m_presenceRecord{ m_owningPtr.get() } { m_titleRecords.reserve(XBL_NUM_PRESENCE_RECORDS); for (size_t i = 0; i < m_presenceRecord->presenceTitleRecordCount; ++i) { m_titleRecords.push_back(social_manager_presence_title_record{ m_presenceRecord->presenceTitleRecords[i] }); } } social_manager_presence_record& social_manager_presence_record::operator=( social_manager_presence_record other ) { m_owningPtr = other.m_owningPtr; m_presenceRecord = m_owningPtr.get(); m_titleRecords.clear(); for (size_t i = 0; i < m_presenceRecord->presenceTitleRecordCount; ++i) { m_titleRecords.push_back(social_manager_presence_title_record{ m_presenceRecord->presenceTitleRecords[i] }); } return *this; } xbox::services::presence::user_presence_state social_manager_presence_record::user_state() const { return static_cast(m_presenceRecord->userState); } const std::vector& social_manager_presence_record::presence_title_records() const { return m_titleRecords; } bool social_manager_presence_record::is_user_playing_title( _In_ uint32_t titleId ) const { for (auto& titleRecord : m_titleRecords) { if (titleRecord.title_id() == titleId) { return true; } } return false; } xbox_social_user::xbox_social_user( const XblSocialManagerUser& user ) : m_user{ &user }, m_titleHistory{ m_user->titleHistory }, m_preferredColor{ m_user->preferredColor }, m_presenceRecord{ m_user->presenceRecord } { m_gamerscore = Utils::StringTFromUtf8(m_user->gamerscore); m_gamertag = Utils::StringTFromUtf8(m_user->gamertag); m_modernGamertag = Utils::StringTFromUtf8(m_user->modernGamertag); m_modernGamertagSuffix = Utils::StringTFromUtf8(m_user->modernGamertagSuffix); m_uniqueModernGamertag = Utils::StringTFromUtf8(m_user->uniqueModernGamertag); m_xboxUserId = Utils::StringTFromUint64(m_user->xboxUserId); m_displayName = Utils::StringTFromUtf8(m_user->displayName); m_realName = Utils::StringTFromUtf8(m_user->realName); m_displayPicUrlRaw = Utils::StringTFromUtf8(m_user->displayPicUrlRaw); } xbox_social_user::xbox_social_user( const xbox_social_user& other ) : m_owningPtr{ std::make_shared(*other.m_user) }, m_user{ m_owningPtr.get() }, m_gamerscore{ other.m_gamerscore }, m_gamertag{ other.m_gamertag }, m_modernGamertag{ other.m_modernGamertag }, m_modernGamertagSuffix{ other.m_modernGamertagSuffix }, m_uniqueModernGamertag{ other.m_uniqueModernGamertag }, m_xboxUserId{ other.m_xboxUserId }, m_displayName{ other.m_displayName }, m_realName{ other.m_realName }, m_displayPicUrlRaw{ other.m_displayPicUrlRaw }, m_titleHistory{ m_user->titleHistory }, m_preferredColor{ m_user->preferredColor }, m_presenceRecord{ m_user->presenceRecord } { } xbox_social_user& xbox_social_user::operator=( xbox_social_user other ) { m_owningPtr = std::make_shared(*other.m_user); m_user = other.m_user; m_gamerscore = other.m_gamerscore; m_gamertag = other.m_gamertag; m_modernGamertag = other.m_modernGamertag; m_modernGamertagSuffix = other.m_modernGamertagSuffix; m_uniqueModernGamertag = other.m_uniqueModernGamertag; m_xboxUserId = other.m_xboxUserId; m_displayName = other.m_displayName; m_realName = other.m_realName; m_displayPicUrlRaw = other.m_displayPicUrlRaw; m_titleHistory = xbox::services::social::manager::title_history{ m_user->titleHistory }; m_preferredColor = xbox::services::social::manager::preferred_color{ m_user->preferredColor }; m_presenceRecord = xbox::services::social::manager::social_manager_presence_record{ m_user->presenceRecord }; return *this; } const char_t* xbox_social_user::xbox_user_id() const { return m_xboxUserId.data(); } bool xbox_social_user::is_favorite() const { return m_user->isFavorite; } bool xbox_social_user::is_following_user() const { return m_user->isFollowingUser; } bool xbox_social_user::is_followed_by_caller() const { return m_user->isFollowedByCaller; } const char_t* xbox_social_user::display_name() const { return m_displayName.data(); } const char_t* xbox_social_user::real_name() const { return m_realName.data(); } const char_t* xbox_social_user::display_pic_url_raw() const { return m_displayPicUrlRaw.data(); } bool xbox_social_user::use_avatar() const { return m_user->useAvatar; } const char_t* xbox_social_user::gamerscore() const { return m_gamerscore.data(); } const char_t* xbox_social_user::gamertag() const { return m_gamertag.data(); } const char_t* xbox_social_user::modern_gamertag() const { return m_modernGamertag.data(); } const char_t* xbox_social_user::modern_gamertag_suffix() const { return m_modernGamertagSuffix.data(); } const char_t* xbox_social_user::unique_modern_gamertag() const { return m_uniqueModernGamertag.data(); } const social_manager_presence_record& xbox_social_user::presence_record() const { return m_presenceRecord; } const xbox::services::social::manager::title_history& xbox_social_user::title_history() const { return m_titleHistory; } const preferred_color& xbox_social_user::preferred_color() const { return m_preferredColor; } xbox_user_id_container::xbox_user_id_container( _In_ uint64_t xuid ) : m_xboxUserId{ Utils::StringTFromUint64(xuid) } { } const char_t* xbox_user_id_container::xbox_user_id() const { return m_xboxUserId.data(); } social_event::social_event( const XblSocialManagerEvent& event ) : m_event{ event }, m_eventType{ static_cast(m_event.eventType) } { if (m_event.groupAffected) { auto groupAffected = social_manager::get_singleton_instance()->m_groups[m_event.groupAffected]; assert(groupAffected); m_args = std::make_shared(groupAffected); } } social_event::social_event( const xbox_live_user_t& removedUser ) : m_eventType{ social_event_type::local_user_removed } { m_event.user = removedUser; m_event.hr = S_OK; } xbox_live_user_t social_event::user() const { return m_event.user; } social_event_type social_event::event_type() const { return m_eventType; } std::vector social_event::users_affected() const { std::vector usersAffected; for (auto& user : m_event.usersAffected) { if (user) { usersAffected.push_back(user->xboxUserId); } } return usersAffected; } std::shared_ptr social_event::event_args() const { return m_args; } std::error_code social_event::err() const { return Utils::ConvertHr(m_event.hr); } std::string social_event::err_message() const { return std::string{}; } xbox_social_user_group::xbox_social_user_group( XblSocialManagerUserGroupHandle group ) : m_group{ group } { } std::vector xbox_social_user_group::users() const { PopulateUsers(); std::vector userPointers; for (auto& user : m_users) { userPointers.push_back(&user); } return userPointers; } xbox_live_result xbox_social_user_group::get_copy_of_users( _Inout_ std::vector& socialUserVector ) { PopulateUsers(); for (auto& user : m_users) { socialUserVector.push_back(user); } return xbox_live_result{}; } social_user_group_type xbox_social_user_group::social_user_group_type() const { XblSocialUserGroupType type{}; auto hr = XblSocialManagerUserGroupGetType(m_group, &type); assert(SUCCEEDED(hr)); UNREFERENCED_PARAMETER(hr); return static_cast(type); } std::vector xbox_social_user_group::users_tracked_by_social_user_group() const { const uint64_t* trackedXuids{ nullptr }; size_t count{ 0 }; auto hr = XblSocialManagerUserGroupGetUsersTrackedByGroup(m_group, &trackedXuids, &count); assert(SUCCEEDED(hr)); UNREFERENCED_PARAMETER(hr); return Utils::Transform(trackedXuids, count); } xbox_live_user_t xbox_social_user_group::local_user() const { XblUserHandle userHandle{ nullptr }; auto hr = XblSocialManagerUserGroupGetLocalUser(m_group, &userHandle); assert(SUCCEEDED(hr)); UNREFERENCED_PARAMETER(hr); return userHandle; } void xbox_social_user_group::PopulateUsers() const { const XblSocialManagerUser* const * users{ nullptr }; size_t usersCount{ 0 }; XblSocialManagerUserGroupGetUsers(m_group, &users, &usersCount); m_users.clear(); for (size_t i = 0; i < usersCount; ++i) { m_users.push_back(xbox_social_user{ *users[i] }); } } presence_filter xbox_social_user_group::presence_filter_of_group() const { XblPresenceFilter filter{ XblPresenceFilter::Unknown }; XblSocialManagerUserGroupGetFilters(m_group, &filter, nullptr); return static_cast(filter); } relationship_filter xbox_social_user_group::relationship_filter_of_group() const { XblRelationshipFilter filter{ XblRelationshipFilter::Friends }; XblSocialManagerUserGroupGetFilters(m_group, nullptr, &filter); return static_cast(filter); } std::vector xbox_social_user_group::get_users_from_xbox_user_ids( _In_ const std::vector& xboxUserIds ) { PopulateUsers(); std::vector userPointers; for (auto& xuid : xboxUserIds) { for (auto& user : m_users) { if (Utils::Stricmp(user.xbox_user_id(), xuid.xbox_user_id()) == 0) { userPointers.push_back(&user); break; } } } return userPointers; } social_user_group_loaded_event_args::social_user_group_loaded_event_args( std::shared_ptr group ) : m_group{ std::move(group) } { } std::shared_ptr social_user_group_loaded_event_args::social_user_group() const { return m_group; } std::shared_ptr social_manager::get_singleton_instance() { static std::shared_ptr instance = std::shared_ptr(new social_manager{}); return instance; } xbox_live_result social_manager::add_local_user( _In_ xbox_live_user_t user, _In_ social_manager_extra_detail_level extraDetailLevel ) { return Utils::ConvertHr(XblSocialManagerAddLocalUser( user, static_cast(extraDetailLevel), nullptr )); } xbox_live_result social_manager::remove_local_user( _In_ xbox_live_user_t user ) { auto hr = XblSocialManagerRemoveLocalUser(user); if (SUCCEEDED(hr)) { // Track removed users to raise user removed event in next do_work call. This is to maintain // legacy behavior in c++ interface since removing a user no longer yields an event. m_removedUsers.push_back(user); } return Utils::ConvertHr(hr); } std::vector social_manager::do_work() { const XblSocialManagerEvent* events{ nullptr }; size_t eventCount{ 0 }; auto hr = XblSocialManagerDoWork(&events, &eventCount); assert(SUCCEEDED(hr)); UNREFERENCED_PARAMETER(hr); auto legacyEvents{ Utils::Transform(events, eventCount) }; for (auto& user : m_removedUsers) { legacyEvents.push_back(social_event{ user }); } m_removedUsers.clear(); return legacyEvents; } xbox_live_result> social_manager::create_social_user_group_from_filters( _In_ xbox_live_user_t user, _In_ presence_filter presenceDetailLevel, _In_ relationship_filter filter ) { XblSocialManagerUserGroupHandle groupHandle{ nullptr }; HRESULT hr = XblSocialManagerCreateSocialUserGroupFromFilters( user, static_cast(presenceDetailLevel), static_cast(filter), &groupHandle ); if (SUCCEEDED(hr)) { auto group = std::make_shared(groupHandle); m_groups[groupHandle] = group; return group; } return Utils::ConvertHr(hr); } xbox_live_result> social_manager::create_social_user_group_from_list( _In_ xbox_live_user_t user, _In_ std::vector xboxUserIdList ) { XblSocialManagerUserGroupHandle groupHandle{ nullptr }; auto xuids{ Utils::XuidVectorFromXuidStringVector(xboxUserIdList) }; HRESULT hr = XblSocialManagerCreateSocialUserGroupFromList( user, xuids.data(), xuids.size(), &groupHandle ); if (SUCCEEDED(hr)) { auto group = std::make_shared(groupHandle); m_groups[groupHandle] = group; return group; } return Utils::ConvertHr(hr); } xbox_live_result social_manager::destroy_social_user_group( _In_ std::shared_ptr socialUserGroup ) { auto hr = XblSocialManagerDestroySocialUserGroup(socialUserGroup->m_group); if (SUCCEEDED(hr)) { m_groups.erase(socialUserGroup->m_group); } return Utils::ConvertHr(hr); } std::vector social_manager::local_users() const { std::vector users{}; size_t localUsersCount{ XblSocialManagerGetLocalUserCount() }; users = std::vector(localUsersCount, nullptr); XblSocialManagerGetLocalUsers(localUsersCount, users.data()); return users; } xbox_live_result social_manager::update_social_user_group( _In_ const std::shared_ptr& group, _In_ const std::vector& users ) { auto xuids{ Utils::XuidVectorFromXuidStringVector(users) }; return Utils::ConvertHr(XblSocialManagerUpdateSocialUserGroup(group->m_group, xuids.data(), xuids.size())); } xbox_live_result social_manager::set_rich_presence_polling_status( _In_ xbox_live_user_t user, _In_ bool shouldEnablePolling ) { return Utils::ConvertHr(XblSocialManagerSetRichPresencePollingStatus(user, shouldEnablePolling)); } NAMESPACE_MICROSOFT_XBOX_SERVICES_SOCIAL_MANAGER_CPP_END ================================================ FILE: Include/xsapi-cpp/impl/string_verify.hpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "public_utils.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_SYSTEM_CPP_BEGIN verify_string_result::verify_string_result() : m_resultCode {verify_string_result_code::success}, m_first_offending_substring{} { } verify_string_result::verify_string_result( XblVerifyStringResultCode resultCode, char const* firstOffendingSubstring ) : m_resultCode{ static_cast(resultCode)}, m_first_offending_substring{ Utils::StringTFromUtf8(firstOffendingSubstring ? firstOffendingSubstring : "") } { } verify_string_result_code verify_string_result::result_code() const { return m_resultCode; } const string_t& verify_string_result::first_offending_substring() const { return m_first_offending_substring; } string_service::string_service(_In_ XblContextHandle contextHandle) { XblContextDuplicateHandle(contextHandle, &m_xblContext); } string_service::string_service(const string_service& other) { XblContextDuplicateHandle(other.m_xblContext, &m_xblContext); } string_service& string_service::operator=(string_service other) { std::swap(m_xblContext, other.m_xblContext); return *this; } string_service::~string_service() { XblContextCloseHandle(m_xblContext); } pplx::task> string_service::verify_string(_In_ const string_t& stringToVerify) { auto xblContext = m_xblContext; auto asyncWrapper = new AsyncWrapper( [](XAsyncBlock* async, verify_string_result& result) { size_t bufferSize{ 0 }; auto hr = XblStringVerifyStringResultSize(async, &bufferSize); if (SUCCEEDED(hr)) { std::shared_ptr buffer(new char[bufferSize], std::default_delete()); XblVerifyStringResult* resultResponse; hr = XblStringVerifyStringResult(async, bufferSize, buffer.get(), &resultResponse, nullptr); if (SUCCEEDED(hr)) { auto internalResult = reinterpret_cast(buffer.get()); result = verify_string_result(internalResult->resultCode, internalResult->firstOffendingSubstring); } } return hr; }); auto hr = XblStringVerifyStringAsync( xblContext, Utils::StringFromStringT(stringToVerify).c_str(), &asyncWrapper->async ); return asyncWrapper->Task(hr); } pplx::task>> string_service::verify_strings(_In_ const std::vector& stringsToVerify) { UTF8StringArrayRef stringsToVerifyInternal{ stringsToVerify }; auto xblContext = m_xblContext; auto asyncWrapper = new AsyncWrapper>( [](XAsyncBlock* async, std::vector& results) { size_t bufferSize; auto hr = XblStringVerifyStringsResultSize(async, &bufferSize); if (SUCCEEDED(hr)) { std::shared_ptr buffer(new char[bufferSize], std::default_delete()); XblVerifyStringResult* resultResponses = nullptr; size_t stringsCount{ 0 }; hr = XblStringVerifyStringsResult(async, bufferSize, buffer.get(), &resultResponses, &stringsCount, nullptr); if (SUCCEEDED(hr)) { for (size_t i = 0; i < stringsCount; i++) { results.push_back(verify_string_result(resultResponses[i].resultCode, resultResponses[i].firstOffendingSubstring)); } } } return hr; }); auto hr = XblStringVerifyStringsAsync( xblContext, stringsToVerifyInternal.Data(), stringsToVerifyInternal.Size(), &asyncWrapper->async ); return asyncWrapper->Task(hr); } NAMESPACE_MICROSOFT_XBOX_SERVICES_SYSTEM_CPP_END ================================================ FILE: Include/xsapi-cpp/impl/system.hpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "public_utils.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_SYSTEM_CPP_BEGIN std::shared_ptr xbox_live_services_settings::get_singleton_instance(_In_ bool createIfRequired) { UNREFERENCED_PARAMETER(createIfRequired); static std::shared_ptr instance = std::shared_ptr(new xbox_live_services_settings); return instance; } void xbox_live_services_settings::set_memory_allocation_hooks( _In_ const std::function<_Ret_maybenull_ _Post_writable_byte_size_(dwSize) void*(_In_ size_t dwSize)>& memAllocHandler, _In_ const std::function& memFreeHandler ) { static std::function allocHook{ nullptr }; static std::function freeHook{ nullptr }; if (memAllocHandler != nullptr && memFreeHandler != nullptr) { allocHook = memAllocHandler; freeHook = memFreeHandler; XblMemSetFunctions( [](size_t size, HCMemoryType) { return allocHook(size); }, [](void* pointer, HCMemoryType) { freeHook(pointer); } ); } else if (memAllocHandler == nullptr && memFreeHandler == nullptr) { XblMemSetFunctions(nullptr, nullptr); } } function_context xbox_live_services_settings::add_logging_handler( std::function handler ) { UNREFERENCED_PARAMETER(handler); return function_context{}; } void xbox_live_services_settings::remove_logging_handler(_In_ function_context context) { UNREFERENCED_PARAMETER(context); } xbox_services_diagnostics_trace_level xbox_live_services_settings::diagnostics_trace_level() const { HCTraceLevel traceLevel{}; HCSettingsGetTraceLevel(&traceLevel); switch (traceLevel) { case HCTraceLevel::Off: return xbox_services_diagnostics_trace_level::off; case HCTraceLevel::Error: return xbox_services_diagnostics_trace_level::error; case HCTraceLevel::Warning: return xbox_services_diagnostics_trace_level::warning; case HCTraceLevel::Important: return xbox_services_diagnostics_trace_level::info; case HCTraceLevel::Verbose: return xbox_services_diagnostics_trace_level::verbose; default: return xbox_services_diagnostics_trace_level::off; } } void xbox_live_services_settings::set_diagnostics_trace_level(_In_ xbox_services_diagnostics_trace_level value) { HCTraceLevel traceLevel{ HCTraceLevel::Off }; switch (value) { case xbox_services_diagnostics_trace_level::off: traceLevel = HCTraceLevel::Off; break; case xbox_services_diagnostics_trace_level::error: traceLevel = HCTraceLevel::Error; break; case xbox_services_diagnostics_trace_level::warning: traceLevel = HCTraceLevel::Warning; break; case xbox_services_diagnostics_trace_level::info: traceLevel = HCTraceLevel::Information; break; case xbox_services_diagnostics_trace_level::verbose: traceLevel = HCTraceLevel::Verbose; break; default: break; } HCSettingsSetTraceLevel(traceLevel); } function_context xbox_live_services_settings::add_wns_handler(_In_ const std::function& handler) { UNREFERENCED_PARAMETER(handler); return function_context{}; } void xbox_live_services_settings::remove_wns_handler(_In_ function_context context) { UNREFERENCED_PARAMETER(context); } NAMESPACE_MICROSOFT_XBOX_SERVICES_SYSTEM_CPP_END ================================================ FILE: Include/xsapi-cpp/impl/title_storage.hpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "public_utils.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_TITLE_STORAGE_CPP_BEGIN inline title_storage_quota::title_storage_quota( string_t scid, title_storage_type storageType, uint64_t xuid, uint64_t usedByted, uint64_t quotaBytes ) : m_serviceConfigurationId{ scid }, m_storageType{ storageType }, m_xboxUserId{ xuid }, m_usedBytes{ usedByted }, m_quotaBytes{ quotaBytes } { } inline const string_t& title_storage_quota::service_configuration_id() const { return m_serviceConfigurationId; } inline title_storage_type title_storage_quota::storage_type() const { return m_storageType; } inline string_t title_storage_quota::xbox_user_id() const { return Utils::StringTFromUint64(m_xboxUserId); } inline uint64_t title_storage_quota::used_bytes() const { return m_usedBytes; } inline uint64_t title_storage_quota::quota_bytes() const { return m_quotaBytes; } inline title_storage_blob_metadata::title_storage_blob_metadata( XblTitleStorageBlobMetadata blobMetadata ) : m_blobMetadata{blobMetadata} { } inline title_storage_blob_metadata::title_storage_blob_metadata( _In_ string_t serviceConfigurationId, _In_ title_storage_type storageType, _In_ string_t blobPath, _In_ title_storage_blob_type blobType, _In_ string_t xboxUserId ) : title_storage_blob_metadata( serviceConfigurationId, storageType, blobPath, blobType, xboxUserId, _T(""), _T("") ) { } inline title_storage_blob_metadata::title_storage_blob_metadata( _In_ string_t serviceConfigurationId, _In_ title_storage_type storageType, _In_ string_t blobPath, _In_ title_storage_blob_type blobType, _In_ string_t xboxUserId, _In_ string_t /*displayName*/, _In_ string_t /*eTag*/ ) : title_storage_blob_metadata( serviceConfigurationId, storageType, blobPath, blobType, xboxUserId, _T(""), _T(""), utility::datetime() ) { } inline title_storage_blob_metadata::title_storage_blob_metadata( _In_ string_t serviceConfigurationId, _In_ title_storage_type storageType, _In_ string_t blobPath, _In_ title_storage_blob_type blobType, _In_ string_t xboxUserId, _In_ string_t displayName, _In_ string_t eTag, _In_ utility::datetime clientTimestamp ) { Utils::Utf8FromCharT(serviceConfigurationId.c_str(), m_blobMetadata.serviceConfigurationId, sizeof(m_blobMetadata.serviceConfigurationId)); m_blobMetadata.storageType = static_cast(storageType); Utils::Utf8FromCharT(blobPath.c_str(), m_blobMetadata.blobPath, sizeof(m_blobMetadata.blobPath)); m_blobMetadata.blobType = static_cast(blobType); m_blobMetadata.xboxUserId = Utils::Uint64FromStringT(xboxUserId); Utils::Utf8FromCharT(displayName.c_str(), m_blobMetadata.displayName, sizeof(m_blobMetadata.displayName)); Utils::Utf8FromCharT(eTag.c_str(), m_blobMetadata.eTag, sizeof(m_blobMetadata.eTag)); m_blobMetadata.clientTimestamp = Utils::TimeTFromDatetime(clientTimestamp); } inline string_t title_storage_blob_metadata::blob_path() const { return Utils::StringTFromUtf8(m_blobMetadata.blobPath); } inline title_storage_blob_type title_storage_blob_metadata::blob_type() const { return static_cast(m_blobMetadata.blobType); } inline title_storage_type title_storage_blob_metadata::storage_type() const { return static_cast(m_blobMetadata.storageType); } inline string_t title_storage_blob_metadata::display_name() const { return Utils::StringTFromUtf8(m_blobMetadata.displayName); } inline string_t title_storage_blob_metadata::e_tag() const { return Utils::StringTFromUtf8(m_blobMetadata.eTag); } inline utility::datetime title_storage_blob_metadata::client_timestamp() const { return Utils::DatetimeFromTimeT(m_blobMetadata.clientTimestamp); } inline void title_storage_blob_metadata::set_client_timestamp(_In_ utility::datetime value) { m_blobMetadata.clientTimestamp = Utils::TimeTFromDatetime(value); } inline uint64_t title_storage_blob_metadata::length() const { return m_blobMetadata.length; } inline string_t title_storage_blob_metadata::service_configuration_id() const { return Utils::StringTFromUtf8(m_blobMetadata.serviceConfigurationId); } inline string_t title_storage_blob_metadata::xbox_user_id() const { return Utils::StringTFromUint64(m_blobMetadata.xboxUserId); } inline title_storage_blob_metadata_result::title_storage_blob_metadata_result( XblTitleStorageBlobMetadataResultHandle handle ) { XblTitleStorageBlobMetadataResultDuplicateHandle(handle, &m_handle); size_t itemsCount; const XblTitleStorageBlobMetadata* items{ nullptr }; XblTitleStorageBlobMetadataResultGetItems(m_handle, &items, &itemsCount); for (size_t i = 0; i < itemsCount; i++) { m_items.push_back(title_storage_blob_metadata(items[i])); } } title_storage_blob_metadata_result::title_storage_blob_metadata_result( _In_ const title_storage_blob_metadata_result& other ) { XblTitleStorageBlobMetadataResultDuplicateHandle(other.m_handle, &m_handle); size_t itemsCount; const XblTitleStorageBlobMetadata* items{ nullptr }; XblTitleStorageBlobMetadataResultGetItems(m_handle, &items, &itemsCount); for (size_t i = 0; i < itemsCount; i++) { m_items.push_back(title_storage_blob_metadata(items[i])); } } title_storage_blob_metadata_result& title_storage_blob_metadata_result::operator=( title_storage_blob_metadata_result other ) { std::swap(m_handle, other.m_handle); std::swap(m_items, other.m_items); return *this; } inline title_storage_blob_metadata_result::~title_storage_blob_metadata_result() { if (m_handle) { XblTitleStorageBlobMetadataResultCloseHandle(m_handle); } } inline const std::vector& title_storage_blob_metadata_result::items() const { return m_items; } inline pplx::task> title_storage_blob_metadata_result::get_next( _In_ uint32_t maxItems ) const { auto asyncWrapper = new AsyncWrapper( [](XAsyncBlock* async, title_storage_blob_metadata_result& result) { XblTitleStorageBlobMetadataResultHandle handle; auto hr = XblTitleStorageBlobMetadataResultGetNextResult(async, &handle); if (SUCCEEDED(hr)) { // title_storage_blob_metadata_result owns the lifetime of the handle result = title_storage_blob_metadata_result(handle); XblTitleStorageBlobMetadataResultCloseHandle(handle); } return hr; }); auto hr = XblTitleStorageBlobMetadataResultGetNextAsync( m_handle, maxItems, &asyncWrapper->async ); return asyncWrapper->Task(hr); } inline bool title_storage_blob_metadata_result::has_next() const { bool hasNext = false; XblTitleStorageBlobMetadataResultHasNext(m_handle, &hasNext); return hasNext; } inline title_storage_blob_result::title_storage_blob_result( std::shared_ptr> blobBuffer, title_storage_blob_metadata blobMetadata ) : m_blobBuffer{ std::move(blobBuffer) }, m_blobMetadata{ std::move(blobMetadata) } { } inline std::shared_ptr> const title_storage_blob_result::blob_buffer() const { return m_blobBuffer; } inline const title_storage_blob_metadata& title_storage_blob_result::blob_metadata() const { return m_blobMetadata; } inline title_storage_service::title_storage_service(_In_ XblContextHandle contextHandle) { XblContextDuplicateHandle(contextHandle, &m_xblContext); } inline title_storage_service::title_storage_service(const title_storage_service& other) { XblContextDuplicateHandle(other.m_xblContext, &m_xblContext); } inline title_storage_service& title_storage_service::operator=(title_storage_service other) { std::swap(m_xblContext, other.m_xblContext); return *this; } inline title_storage_service::~title_storage_service() { XblContextCloseHandle(m_xblContext); } inline pplx::task> title_storage_service::get_quota( _In_ string_t serviceConfigurationId, _In_ title_storage_type storageType ) { uint64_t xuid{ 0 }; XblContextGetXboxUserId(m_xblContext, &xuid); auto asyncWrapper = new AsyncWrapper( [serviceConfigurationId, storageType, xuid](XAsyncBlock* async, title_storage_quota& result) { size_t usedBytes; size_t quotaBytes; auto hr = XblTitleStorageGetQuotaResult(async, &usedBytes, "aBytes); if (SUCCEEDED(hr)) { result = title_storage_quota(serviceConfigurationId, storageType, xuid, static_cast(usedBytes), static_cast(quotaBytes)); } return hr; }); char scid[XBL_SCID_LENGTH]; Utils::Utf8FromCharT(serviceConfigurationId.c_str(), scid, sizeof(scid)); auto hr = XblTitleStorageGetQuotaAsync( m_xblContext, scid, static_cast(storageType), &asyncWrapper->async ); return asyncWrapper->Task(hr); } inline pplx::task> title_storage_service::get_blob_metadata( _In_ string_t serviceConfigurationId, _In_ title_storage_type storageType, _In_ string_t blobPath, _In_ string_t xboxUserId, _In_ uint32_t skipItems, _In_ uint32_t maxItems ) { auto asyncWrapper = new AsyncWrapper( [](XAsyncBlock* async, title_storage_blob_metadata_result& result) { XblTitleStorageBlobMetadataResultHandle handle; auto hr = XblTitleStorageGetBlobMetadataResult(async, &handle); if (SUCCEEDED(hr)) { // title_storage_blob_metadata_result owns the lifetime of the handle result = title_storage_blob_metadata_result(handle); XblTitleStorageBlobMetadataResultCloseHandle(handle); } return hr; }); char scid[XBL_SCID_LENGTH] = { 0 }; Utils::Utf8FromCharT(serviceConfigurationId.c_str(), scid, sizeof(scid)); char blobPathUTF8[XBL_TITLE_STORAGE_BLOB_PATH_MAX_LENGTH] = { 0 }; Utils::Utf8FromCharT(blobPath.c_str(), blobPathUTF8, sizeof(blobPathUTF8)); auto hr = XblTitleStorageGetBlobMetadataAsync( m_xblContext, scid, static_cast(storageType), blobPathUTF8, Utils::Uint64FromStringT(xboxUserId), skipItems, maxItems, &asyncWrapper->async ); return asyncWrapper->Task(hr); } inline pplx::task> title_storage_service::delete_blob( _In_ const title_storage_blob_metadata& blobMetadata, _In_ bool deleteOnlyIfEtagMatches ) { auto asyncWrapper = new AsyncWrapper(); auto hr = XblTitleStorageDeleteBlobAsync( m_xblContext, blobMetadata.m_blobMetadata, deleteOnlyIfEtagMatches, &asyncWrapper->async ); return asyncWrapper->Task(hr); } inline pplx::task> title_storage_service::download_blob( _In_ title_storage_blob_metadata blobMetadata, _In_ std::shared_ptr> blobBuffer, _In_ title_storage_e_tag_match_condition etagMatchCondition, _In_ string_t selectQuery ) { return download_blob( blobMetadata, blobBuffer, etagMatchCondition, selectQuery, XBL_TITLE_STORAGE_DEFAULT_DOWNLOAD_BLOCK_SIZE ); } inline pplx::task> title_storage_service::download_blob( _In_ title_storage_blob_metadata blobMetadata, _In_ std::shared_ptr> blobBuffer, _In_ title_storage_e_tag_match_condition etagMatchCondition, _In_ string_t selectQuery, _In_ uint32_t preferredDownloadBlockSize ) { auto asyncWrapper = new AsyncWrapper( [blobBuffer](XAsyncBlock* async, title_storage_blob_result& result) { XblTitleStorageBlobMetadata blobMetadata; auto hr = XblTitleStorageDownloadBlobResult(async, &blobMetadata); if (SUCCEEDED(hr)) { result = title_storage_blob_result(blobBuffer, blobMetadata); } return hr; }); auto hr = XblTitleStorageDownloadBlobAsync( m_xblContext, blobMetadata.m_blobMetadata, static_cast(blobBuffer->data()), blobBuffer->size(), static_cast(etagMatchCondition), Utils::StringFromStringT(selectQuery).c_str(), static_cast(preferredDownloadBlockSize), &asyncWrapper->async ); return asyncWrapper->Task(hr); } inline pplx::task> title_storage_service::upload_blob( _In_ title_storage_blob_metadata blobMetadata, _In_ std::shared_ptr> blobBuffer, _In_ title_storage_e_tag_match_condition etagMatchCondition, _In_ uint32_t preferredUploadBlockSize ) { auto asyncWrapper = new AsyncWrapper( [](XAsyncBlock* async, title_storage_blob_metadata& result) { XblTitleStorageBlobMetadata blobMetadata; auto hr = XblTitleStorageUploadBlobResult(async, &blobMetadata); if (SUCCEEDED(hr)) { result = title_storage_blob_metadata(blobMetadata); } return hr; }); auto hr = XblTitleStorageUploadBlobAsync( m_xblContext, blobMetadata.m_blobMetadata, static_cast(blobBuffer->data()), blobBuffer->size(), static_cast(etagMatchCondition), static_cast(preferredUploadBlockSize), &asyncWrapper->async ); return asyncWrapper->Task(hr); } NAMESPACE_MICROSOFT_XBOX_SERVICES_TITLE_STORAGE_CPP_END ================================================ FILE: Include/xsapi-cpp/impl/user_statistics.hpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "public_utils.h" XBL_WARNING_PUSH XBL_WARNING_DISABLE_DEPRECATED NAMESPACE_MICROSOFT_XBOX_SERVICES_USERSTATISTICS_CPP_BEGIN statistic::statistic(const XblStatistic& statistic) : m_statName(Utils::StringTFromUtf8(statistic.statisticName)), m_statType(Utils::StringTFromUtf8(statistic.statisticType)), m_value(Utils::StringTFromUtf8(statistic.value)) { } statistic::statistic( const string_t& name, const string_t& type, const string_t& value ) : m_statName{ name }, m_statType{ type }, m_value{ value } { } const string_t& statistic::statistic_name() const { return m_statName; } const string_t& statistic::statistic_type() const { return m_statType; } const string_t& statistic::value() const { return m_value; } service_configuration_statistic::service_configuration_statistic(const XblServiceConfigurationStatistic& serviceConfigurationStatistic) : m_serviceConfigurationId(Utils::StringTFromUtf8(serviceConfigurationStatistic.serviceConfigurationId)) { for (uint32_t i = 0; i < serviceConfigurationStatistic.statisticsCount; i++) { m_statistics.push_back(serviceConfigurationStatistic.statistics[i]); } } const string_t& service_configuration_statistic::service_configuration_id() const { return m_serviceConfigurationId; } const std::vector& service_configuration_statistic::statistics() const { return m_statistics; } user_statistics_result::user_statistics_result(const XblUserStatisticsResult& userStatisticsResult) : m_xboxUserId(Utils::StringTFromUint64(userStatisticsResult.xboxUserId)) { for (uint32_t i = 0; i < userStatisticsResult.serviceConfigStatisticsCount; i++) { m_serviceConfigurationStatistics.push_back(userStatisticsResult.serviceConfigStatistics[i]); } } const string_t& user_statistics_result::xbox_user_id() const { return m_xboxUserId; } const std::vector& user_statistics_result::service_configuration_statistics() const { return m_serviceConfigurationStatistics; } requested_statistics::requested_statistics( _In_ string_t serviceConfigurationId, _In_ const std::vector& statistics ) : m_scid(serviceConfigurationId), m_statistics(statistics), m_statisticsC{ statistics } { Utils::Utf8FromCharT(serviceConfigurationId.c_str(), m_requestedStatistics.serviceConfigurationId, XBL_SCID_LENGTH); m_requestedStatistics.statistics = m_statisticsC.Data(); m_requestedStatistics.statisticsCount = (uint32_t)m_statisticsC.Size(); } requested_statistics::requested_statistics(_In_ const requested_statistics& other) : m_scid(other.m_scid), m_statistics(other.m_statistics), m_statisticsC{ other.m_statistics } { Utils::Utf8FromCharT(m_scid.c_str(), m_requestedStatistics.serviceConfigurationId, XBL_SCID_LENGTH); m_requestedStatistics.statistics = m_statisticsC.Data(); m_requestedStatistics.statisticsCount = (uint32_t)m_statisticsC.Size(); } const string_t& requested_statistics::service_configuration_id() const { return m_scid; } const std::vector& requested_statistics::statistics() const { return m_statistics; } const XblRequestedStatistics& requested_statistics::_requested_statistics() const { return m_requestedStatistics; } statistic_change_event_args::statistic_change_event_args(const XblStatisticChangeEventArgs& statisticEventArgs) : m_xboxUserId { Utils::StringTFromUint64(statisticEventArgs.xboxUserId) }, m_serviceConfigurationId { Utils::StringTFromUtf8(statisticEventArgs.serviceConfigurationId) }, m_statistic { statisticEventArgs.latestStatistic } { } const string_t& statistic_change_event_args::xbox_user_id() const { return m_xboxUserId; } const string_t& statistic_change_event_args::service_configuration_id() const { return m_serviceConfigurationId; } const statistic& statistic_change_event_args::latest_statistic() const { return m_statistic; } statistic_change_subscription::statistic_change_subscription( _In_ string_t xboxUserId, _In_ string_t serviceConfigurationId, _In_ xbox::services::user_statistics::statistic newStat, _In_ XblRealTimeActivitySubscriptionHandle handle ) : real_time_activity_subscription(handle), m_xboxUserId{ xboxUserId }, m_serviceConfigurationId{ serviceConfigurationId }, m_statistic{ newStat } { } const string_t& statistic_change_subscription::xbox_user_id() const { return m_xboxUserId; } const string_t& statistic_change_subscription::service_configuration_id() const { return m_serviceConfigurationId; } const xbox::services::user_statistics::statistic& statistic_change_subscription::statistic() const { return m_statistic; } user_statistics_service::user_statistics_service(_In_ XblContextHandle contextHandle) { XblContextDuplicateHandle(contextHandle, &m_xblContext); } user_statistics_service::user_statistics_service(const user_statistics_service& other) { XblContextDuplicateHandle(other.m_xblContext, &m_xblContext); } user_statistics_service& user_statistics_service::operator=(user_statistics_service other) { std::swap(m_xblContext, other.m_xblContext); return *this; } user_statistics_service::~user_statistics_service() { XblContextCloseHandle(m_xblContext); } pplx::task> user_statistics_service::get_single_user_statistics( _In_ const string_t& xboxUserId, _In_ const string_t& serviceConfigurationId, _In_ const string_t& statisticName ) { auto asyncWrapper = new AsyncWrapper( [](XAsyncBlock* async, user_statistics_result& result) { size_t bufferSize; auto hr = XblUserStatisticsGetSingleUserStatisticResultSize(async, &bufferSize); if (SUCCEEDED(hr)) { auto buffer = new char[bufferSize]; XblUserStatisticsResult* resultPtr; hr = XblUserStatisticsGetSingleUserStatisticResult(async, bufferSize, buffer, &resultPtr, nullptr); if (SUCCEEDED(hr)) { result = user_statistics_result(*resultPtr); } delete[] buffer; } return hr; }); auto hr = XblUserStatisticsGetSingleUserStatisticAsync( m_xblContext, Utils::Uint64FromStringT(xboxUserId), Utils::StringFromStringT(serviceConfigurationId).c_str(), Utils::StringFromStringT(statisticName).c_str(), &asyncWrapper->async ); return asyncWrapper->Task(hr); } pplx::task> user_statistics_service::get_single_user_statistics( _In_ const string_t& xboxUserId, _In_ const string_t& serviceConfigurationId, _In_ const std::vector& statisticNames ) { UTF8StringArrayRef statisticNamesC{ statisticNames }; auto asyncWrapper = new AsyncWrapper( [](XAsyncBlock* async, user_statistics_result& result) { size_t bufferSize; auto hr = XblUserStatisticsGetSingleUserStatisticsResultSize(async, &bufferSize); if (SUCCEEDED(hr)) { auto buffer = new char[bufferSize]; XblUserStatisticsResult* resultPtr; hr = XblUserStatisticsGetSingleUserStatisticsResult(async, bufferSize, buffer, &resultPtr, nullptr); if (SUCCEEDED(hr)) { result = user_statistics_result(*resultPtr); } delete[] buffer; } return hr; }); auto hr = XblUserStatisticsGetSingleUserStatisticsAsync( m_xblContext, Utils::Uint64FromStringT(xboxUserId), Utils::StringFromStringT(serviceConfigurationId).c_str(), statisticNamesC.Data(), statisticNamesC.Size(), &asyncWrapper->async ); return asyncWrapper->Task(hr); } pplx::task>> user_statistics_service::get_multiple_user_statistics( _In_ const std::vector& xboxUserIds, _In_ const string_t& serviceConfigurationId, _In_ std::vector& statisticNames ) { std::vector xboxUserIdsC = Utils::XuidVectorFromXuidStringVector(xboxUserIds); UTF8StringArrayRef statisticNamesC{ statisticNames }; auto asyncWrapper = new AsyncWrapper>( [](XAsyncBlock* async, std::vector& results) { size_t bufferSize; auto hr = XblUserStatisticsGetMultipleUserStatisticsResultSize(async, &bufferSize); if (SUCCEEDED(hr)) { auto buffer = new char[bufferSize]; XblUserStatisticsResult* resultsPtr; size_t resultsCount; hr = XblUserStatisticsGetMultipleUserStatisticsResult(async, bufferSize, buffer, &resultsPtr, &resultsCount, nullptr); if (SUCCEEDED(hr)) { for (size_t i = 0; i < resultsCount; i++) { results.push_back(user_statistics_result(resultsPtr[i])); } } delete[] buffer; } return hr; }); auto hr = XblUserStatisticsGetMultipleUserStatisticsAsync( m_xblContext, xboxUserIdsC.data(), xboxUserIdsC.size(), Utils::StringFromStringT(serviceConfigurationId).c_str(), statisticNamesC.Data(), statisticNamesC.Size(), &asyncWrapper->async ); return asyncWrapper->Task(hr); } pplx::task>> user_statistics_service::get_multiple_user_statistics_for_multiple_service_configurations( _In_ const std::vector& xboxUserIds, _In_ const std::vector& requestedServiceConfigurationStatisticsCollection ) { std::vector xboxUserIdsC = Utils::XuidVectorFromXuidStringVector(xboxUserIds); std::vector requestedStatsC; for (size_t i = 0; i < requestedServiceConfigurationStatisticsCollection.size(); i++) { requestedStatsC.push_back(requestedServiceConfigurationStatisticsCollection[i]._requested_statistics()); } auto asyncWrapper = new AsyncWrapper>( [](XAsyncBlock* async, std::vector& results) { size_t bufferSize; auto hr = XblUserStatisticsGetMultipleUserStatisticsForMultipleServiceConfigurationsResultSize(async, &bufferSize); if (SUCCEEDED(hr)) { auto buffer = new char[bufferSize]; XblUserStatisticsResult* resultsPtr; size_t resultsCount; hr = XblUserStatisticsGetMultipleUserStatisticsForMultipleServiceConfigurationsResult(async, bufferSize, buffer, &resultsPtr, &resultsCount, nullptr); if (SUCCEEDED(hr)) { for (size_t i = 0; i < resultsCount; i++) { results.push_back(user_statistics_result(resultsPtr[i])); } } delete[] buffer; } return hr; }); auto hr = XblUserStatisticsGetMultipleUserStatisticsForMultipleServiceConfigurationsAsync( m_xblContext, xboxUserIdsC.data(), (uint32_t)xboxUserIdsC.size(), requestedStatsC.data(), (uint32_t)requestedStatsC.size(), &asyncWrapper->async ); return asyncWrapper->Task(hr); } xbox_live_result> user_statistics_service::subscribe_to_statistic_change( _In_ const string_t& xboxUserId, _In_ const string_t& serviceConfigurationId, _In_ const string_t& statisticName ) { XblRealTimeActivitySubscriptionHandle subscription; HRESULT hr = XblUserStatisticsSubscribeToStatisticChange( m_xblContext, Utils::Uint64FromStringT(xboxUserId), Utils::StringFromStringT(serviceConfigurationId).c_str(), Utils::StringFromStringT(statisticName).c_str(), &subscription ); auto statistic = xbox::services::user_statistics::statistic(statisticName, Utils::StringTFromUtf8(""), Utils::StringTFromUtf8("")); auto result = std::make_shared(xboxUserId, serviceConfigurationId, statistic, subscription); return xbox_live_result>(result, Utils::ConvertHr(hr), ""); } xbox_live_result user_statistics_service::unsubscribe_from_statistic_change( _In_ std::shared_ptr subscription ) { if (subscription == nullptr) { return xbox_live_result(Utils::ConvertHr(E_INVALIDARG), ""); } HRESULT hr = XblUserStatisticsUnsubscribeFromStatisticChange( m_xblContext, subscription->m_handle ); return xbox_live_result(Utils::ConvertHr(hr), ""); } struct user_statistics_service::HandlerContext { XblFunctionContext internalContext; std::function statisticChangedHandler; }; function_context user_statistics_service::add_statistic_changed_handler(_In_ std::function handler) { auto context = new HandlerContext{}; context->statisticChangedHandler = std::move(handler); context->internalContext = XblUserStatisticsAddStatisticChangedHandler( m_xblContext, [](XblStatisticChangeEventArgs args, void* context) { auto handler = static_cast(context); if (handler && handler->statisticChangedHandler) { handler->statisticChangedHandler(args); } }, context); return context; } void user_statistics_service::remove_statistic_changed_handler(_In_ function_context functionContext) { if (functionContext != nullptr) { auto context{ static_cast(functionContext) }; XblUserStatisticsRemoveStatisticChangedHandler(m_xblContext, context->internalContext); delete context; } } NAMESPACE_MICROSOFT_XBOX_SERVICES_USERSTATISTICS_CPP_END XBL_WARNING_POP ================================================ FILE: Include/xsapi-cpp/impl/xbox_live_app_config.hpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "public_utils.h" #include "Xal/xal.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN std::shared_ptr xbox_live_app_config::get_app_config_singleton() { static std::shared_ptr instance = std::shared_ptr(new xbox_live_app_config()); return instance; } uint32_t xbox_live_app_config::title_id() const { uint32_t titleId{}; XalGetTitleId(&titleId); return titleId; } string_t xbox_live_app_config::scid() const { const char* scid{ nullptr }; XblGetScid(&scid); return Utils::StringTFromUtf8(scid); } string_t xbox_live_app_config::environment() const { // Not exposed from C APIs. return string_t(); } string_t xbox_live_app_config::sandbox() const { size_t sandboxSize = XalGetSandboxSize(); string_t sandboxStr; char* sandbox = new (std::nothrow) char[sandboxSize]; if (sandbox != nullptr) { if (SUCCEEDED(XalGetSandbox(sandboxSize, sandbox, nullptr))) { sandboxStr = Utils::StringTFromUtf8(sandbox); } delete[] sandbox; } return sandboxStr; } NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END ================================================ FILE: Include/xsapi-cpp/impl/xbox_live_context.hpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. // TODO exclude this from build and include in xbox_live_context.h. Circular dependency currently prevents this, // but after services are migrated that will work. #if !XSAPI_NO_PPL #include "xsapi-c/xbox_live_context_c.h" #include "public_utils.h" #include "xsapi-cpp/xbox_live_context.h" #include "xsapi-cpp/events.h" #ifdef XSAPI_NOTIFICATION_SERVICE #include "xsapi-cpp/notification_service.h" #endif NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN xbox_live_context::xbox_live_context(_In_ XblUserHandle user) { HRESULT hr = XblContextCreateHandle(user, &m_handle); if (FAILED(hr)) throw std::runtime_error("XblContextCreateHandle failed"); XblSetApiType(XblApiType::XblCPPApi); #ifdef XSAPI_NOTIFICATION_SERVICE // Unlike the rest of the services, notification_service needs to maintain state // That is because of the differences between C-APIs for subscribing for RTA events // and the C++ subscribe_to_notifications call m_notificationService = std::make_shared(m_handle); #endif } xbox_live_context::xbox_live_context(_In_ XblContextHandle xboxLiveContextHandle) { HRESULT hr = XblContextDuplicateHandle(xboxLiveContextHandle, &m_handle); if (FAILED(hr)) throw std::runtime_error("XblContextCreateHandle failed"); XblSetApiType(XblApiType::XblCPPApi); #ifdef XSAPI_NOTIFICATION_SERVICE // Unlike the rest of the services, notification_service needs to maintain state // That is because of the differences between C-APIs for subscribing for RTA events // and the C++ subscribe_to_notifications call m_notificationService = std::make_shared(m_handle); #endif } xbox_live_context::~xbox_live_context() { XblContextCloseHandle(m_handle); } XblUserHandle xbox_live_context::user() { XblUserHandle userHandle = nullptr; XblContextGetUser(m_handle, &userHandle); return userHandle; } XblContextHandle xbox_live_context::handle() { XblContextHandle contextHandle = nullptr; XblContextDuplicateHandle(m_handle, &contextHandle); return contextHandle; } string_t xbox_live_context::xbox_live_user_id() { stringstream_t ss; uint64_t xuid; XblContextGetXboxUserId(m_handle, &xuid); ss << xuid; return ss.str(); } std::shared_ptr xbox_live_context::settings() { return std::make_shared(m_handle); } std::shared_ptr xbox_live_context::application_config() { return xbox_live_app_config::get_app_config_singleton(); } title_storage::title_storage_service xbox_live_context::title_storage_service() { return title_storage::title_storage_service(m_handle); } social::profile_service xbox_live_context::profile_service() { return social::profile_service(m_handle); } privacy::privacy_service xbox_live_context::privacy_service() { return privacy::privacy_service(m_handle); } #if !defined(XBOX_LIVE_CREATORS_SDK) leaderboard::leaderboard_service xbox_live_context::leaderboard_service() { return leaderboard::leaderboard_service(m_handle); } social::social_service xbox_live_context::social_service() { return social::social_service(m_handle); } social::reputation_service xbox_live_context::reputation_service() { return social::reputation_service(m_handle); } achievements::achievement_service xbox_live_context::achievement_service() { return achievements::achievement_service(m_handle); } user_statistics::user_statistics_service xbox_live_context::user_statistics_service() { return user_statistics::user_statistics_service(m_handle); } multiplayer::multiplayer_service xbox_live_context::multiplayer_service() { return multiplayer::multiplayer_service(m_handle); } matchmaking::matchmaking_service xbox_live_context::matchmaking_service() { return matchmaking::matchmaking_service(m_handle); } std::shared_ptr xbox_live_context::real_time_activity_service() { return std::shared_ptr(new real_time_activity::real_time_activity_service(m_handle)); } system::string_service xbox_live_context::string_service() { return system::string_service(m_handle); } presence::presence_service xbox_live_context::presence_service() { return presence::presence_service(m_handle); } #ifdef XSAPI_NOTIFICATION_SERVICE std::shared_ptr xbox_live_context::notification_service() { return m_notificationService; } #endif #ifdef XSAPI_EVENTS_SERVICE events::events_service xbox_live_context::events_service() { return events::events_service(m_handle); } #endif // XSAPI_EVENTS_SERVICE #endif // !defined(XBOX_LIVE_CREATORS_SDK) NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END #endif ================================================ FILE: Include/xsapi-cpp/impl/xbox_live_context_settings.hpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "xsapi-c/xbox_live_global_c.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN xbox_live_context_settings::xbox_live_context_settings(XblContextHandle xblContextHandle) { XblContextDuplicateHandle(xblContextHandle, &m_xblContextHandle); } xbox_live_context_settings::~xbox_live_context_settings() { XblContextCloseHandle(m_xblContextHandle); } struct xbox_live_context_settings::HandlerContext { XblFunctionContext internalContext{ 0 }; std::function handler; }; function_context xbox_live_context_settings::add_service_call_routed_handler( _In_ std::function handler ) { auto context = new HandlerContext{}; context->handler = std::move(handler); context->internalContext = XblAddServiceCallRoutedHandler( [](XblServiceCallRoutedArgs internalArgs, void* context) { auto handlerContext{ static_cast(context) }; handlerContext->handler(xbox_service_call_routed_event_args{ internalArgs }); }, context); return context; } void xbox_live_context_settings::remove_service_call_routed_handler(_In_ function_context context) { auto handlerContext{ static_cast(context) }; XblRemoveServiceCallRoutedHandler(handlerContext->internalContext); delete handlerContext; } std::chrono::seconds xbox_live_context_settings::long_http_timeout() const { uint32_t timeout; XblContextSettingsGetLongHttpTimeout(m_xblContextHandle, &timeout); return std::chrono::seconds(timeout); } void xbox_live_context_settings::set_long_http_timeout(_In_ std::chrono::seconds value) { XblContextSettingsSetLongHttpTimeout(m_xblContextHandle, static_cast(value.count())); } std::chrono::seconds xbox_live_context_settings::xbox_live_context_settings::http_retry_delay() const { uint32_t delay; XblContextSettingsGetHttpRetryDelay(m_xblContextHandle, &delay); return std::chrono::seconds(delay); } void xbox_live_context_settings::set_http_retry_delay(_In_ std::chrono::seconds value) { XblContextSettingsSetHttpRetryDelay(m_xblContextHandle, static_cast(value.count())); } std::chrono::seconds xbox_live_context_settings::http_timeout_window() const { uint32_t window; XblContextSettingsGetHttpTimeoutWindow(m_xblContextHandle, &window); return std::chrono::seconds(window); } void xbox_live_context_settings::set_http_timeout_window(_In_ std::chrono::seconds value) { XblContextSettingsSetHttpTimeoutWindow(m_xblContextHandle, static_cast(value.count())); } std::chrono::seconds xbox_live_context_settings::websocket_timeout_window() const { uint32_t window; XblContextSettingsGetWebsocketTimeoutWindow(m_xblContextHandle, &window); return std::chrono::seconds(window); } void xbox_live_context_settings::set_websocket_timeout_window(_In_ std::chrono::seconds value) { XblContextSettingsSetWebsocketTimeoutWindow(m_xblContextHandle, static_cast(value.count())); } bool xbox_live_context_settings::use_core_dispatcher_for_event_routing() const { return false; } void xbox_live_context_settings::set_use_core_dispatcher_for_event_routing(_In_ bool value) { UNREFERENCED_PARAMETER(value); } void xbox_live_context_settings::disable_asserts_for_xbox_live_throttling_in_dev_sandboxes( _In_ xbox_live_context_throttle_setting setting ) { UNREFERENCED_PARAMETER(setting); XblDisableAssertsForXboxLiveThrottlingInDevSandboxes(XblConfigSetting::ThisCodeNeedsToBeChanged); } void xbox_live_context_settings::disable_asserts_for_maximum_number_of_websockets_activated( _In_ xbox_live_context_recommended_setting setting ) { UNREFERENCED_PARAMETER(setting); } bool xbox_live_context_settings::use_crossplatform_qos_servers() const { bool value; XblContextSettingsGetUseCrossPlatformQosServers(m_xblContextHandle, &value); return value; } void xbox_live_context_settings::set_use_crossplatform_qos_servers(_In_ bool value) { XblContextSettingsSetUseCrossPlatformQosServers(m_xblContextHandle, value); } NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END ================================================ FILE: Include/xsapi-cpp/impl/xbox_service_call_routed_event_args.hpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "public_utils.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN xbox_service_call_routed_event_args::xbox_service_call_routed_event_args( _In_ const XblServiceCallRoutedArgs& internalArgs ) { m_callHandle = HCHttpCallDuplicateHandle(internalArgs.call); m_responseCount = static_cast(internalArgs.responseCount); m_fullResponseFormatted = Utils::StringTFromUtf8(internalArgs.fullResponseFormatted); } xbox_service_call_routed_event_args::xbox_service_call_routed_event_args(const xbox_service_call_routed_event_args& other) : m_responseCount(other.m_responseCount), m_fullResponseFormatted(other.m_fullResponseFormatted) { m_callHandle = HCHttpCallDuplicateHandle(other.m_callHandle); } xbox_service_call_routed_event_args& xbox_service_call_routed_event_args::operator=(xbox_service_call_routed_event_args other) { std::swap(m_callHandle, other.m_callHandle); m_responseCount = other.m_responseCount; m_fullResponseFormatted = other.m_fullResponseFormatted; return *this; } xbox_service_call_routed_event_args::~xbox_service_call_routed_event_args() { HCHttpCallCloseHandle(m_callHandle); } string_t xbox_service_call_routed_event_args::xbox_user_id() const { // TODO a lot of these fields are not exposed from the hc_call_handle. Need to expose in libHttpClient to expose them from xsapi. return string_t{}; } string_t xbox_service_call_routed_event_args::http_method() const { return string_t(); } string_t xbox_service_call_routed_event_args::uri() const { const char* url; HCHttpCallGetRequestUrl(m_callHandle, &url); return Utils::StringTFromUtf8(url); } string_t xbox_service_call_routed_event_args::request_headers() const { return string_t(); } http_call_request_message xbox_service_call_routed_event_args::request_body() const { return http_call_request_message(); } uint32_t xbox_service_call_routed_event_args::response_count() const { return static_cast(m_responseCount); } string_t xbox_service_call_routed_event_args::response_headers() const { uint32_t numHeaders; HCHttpCallResponseGetNumHeaders(m_callHandle, &numHeaders); stringstream_t ss; for (uint32_t i = 0; i < numHeaders; ++i) { const char* headerName; const char* headerValue; HCHttpCallResponseGetHeaderAtIndex(m_callHandle, i, &headerName, &headerValue); ss << Utils::StringTFromUtf8(headerName) << _T(" : ") << Utils::StringTFromUtf8(headerValue) << _T("\r\n"); } return ss.str(); } string_t xbox_service_call_routed_event_args::response_body() const { const char* responseBody; HCHttpCallResponseGetResponseString(m_callHandle, &responseBody); return Utils::StringTFromUtf8(responseBody); } string_t xbox_service_call_routed_event_args::etag() const { const char* etag; HCHttpCallResponseGetHeader(m_callHandle, "ETag", &etag); return Utils::StringTFromUtf8(etag); } string_t xbox_service_call_routed_event_args::token() const { const char* token; HCHttpCallResponseGetHeader(m_callHandle, "Authorization", &token); return Utils::StringTFromUtf8(token); } string_t xbox_service_call_routed_event_args::signature() const { const char* signature; HCHttpCallResponseGetHeader(m_callHandle, "Signature", &signature); return Utils::StringTFromUtf8(signature); } uint32_t xbox_service_call_routed_event_args::http_status() const { uint32_t httpStatus; HCHttpCallResponseGetStatusCode(m_callHandle, &httpStatus); return httpStatus; } const string_t& xbox_service_call_routed_event_args::full_response_formatted() const { return m_fullResponseFormatted; } chrono_clock_t::time_point xbox_service_call_routed_event_args::request_time() const { return chrono_clock_t::time_point(); } chrono_clock_t::time_point xbox_service_call_routed_event_args::response_time() const { return chrono_clock_t::time_point(); } std::chrono::milliseconds xbox_service_call_routed_event_args::elapsed_call_time() const { return std::chrono::duration_cast(request_time() - request_time()); } NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END ================================================ FILE: Include/xsapi-cpp/leaderboard.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "types.h" #include #include #include "xbox_live_app_config.h" #include "xsapi-c/leaderboard_c.h" #define NO_SKIP_XUID (_T("")) #define NO_XUID (_T("")) #define NO_SOCIAL_GROUP (_T("")) #define NO_SKIP_RANK (0) #define NO_MAX_ITEMS (0) #define NO_CONTINUATION (_T("")) #define NO_SORT_ORDER (_T("")) namespace xbox { namespace services { class xbox_live_context; /// /// Contains classes and enumerations that let you retrieve /// leaderboard information from Xbox Live. /// namespace leaderboard { /// Enumerates the data type of a leaderboard statistic. enum class leaderboard_stat_type { /// Unsigned 64 bit integer. stat_uint64, /// Boolean. stat_boolean, /// Double. stat_double, /// String. stat_string, /// Unknown. stat_other }; #if !XSAPI_NO_PPL /// /// Represents a column in a collection of leaderboard items. /// class leaderboard_column { public: inline leaderboard_column( std::shared_ptr buffer, const XblLeaderboardColumn& leaderboardColumn ); /// /// The name the statistic displayed in the column. /// inline string_t stat_name() const; /// /// The property type of the statistic in the column. /// inline leaderboard_stat_type stat_type() const; private: std::shared_ptr m_buffer; XblLeaderboardColumn m_leaderboardColumn; }; /// /// Represents a row in a collection of leaderboard items. /// class leaderboard_row { public: inline leaderboard_row( std::shared_ptr buffer, const XblLeaderboardRow& leaderboardRow ); /// /// The Gamertag of the player. /// inline string_t gamertag() const; /// /// The Xbox user ID of the player. /// inline string_t xbox_user_id() const; /// /// The percentile rank of the player. /// inline double percentile() const; /// /// The rank of the player. /// inline uint32_t rank() const; /// /// Values for each column in the leaderboard row for the player. /// inline std::vector column_values() const; private: std::shared_ptr m_buffer; XblLeaderboardRow m_leaderboardRow; }; /// /// Represents the results of a leaderboard request. /// class leaderboard_result { public: inline leaderboard_result() { } inline leaderboard_result( std::shared_ptr buffer, const XblContextHandle& xblContext ); /// /// The total number of rows in the leaderboard results. /// inline uint32_t total_row_count() const; /// /// The collection of columns in the leaderboard results. /// inline const std::vector& columns() const; /// /// The collection of rows in the leaderboard results. /// inline const std::vector& rows() const; /// /// Indicates whether there is a next page of results. /// /// True if there is another page of results; otherwise false. inline bool has_next() const; #if !defined(XBOX_LIVE_CREATORS_SDK) /// /// Get the next page of a previous leaderboard call using the same service config Id and leaderboard name. /// /// The maximum number of items to return. /// A leaderboard_results object that contains the next set of results. /// /// This query is only to be used to retrieve a leaderboard in a pre stats 2017 system /// Returns a concurrency::task<T> object that represents the state of the asynchronous operation. /// /// Calls V1 GET /scids/{scid}/leaderboards/{leaderboardname}? /// [maxItems={maxItems}] /// [continuationToken={token}] /// inline pplx::task> get_next(_In_ uint32_t maxItems); #endif private: std::shared_ptr m_buffer; XblLeaderboardResult m_leaderboardResult{}; XblContextHandle m_xblContext{}; std::vector m_columns; std::vector m_rows; }; #if !defined(XBOX_LIVE_CREATORS_SDK) /// /// Represents the leaderboard service. /// class leaderboard_service { public: /// /// Get a leaderboard for a single leaderboard given a service configuration ID and a leaderboard name. /// /// The service configuration ID (SCID) of the title /// The name of the leaderboard. /// The name of the stats for the additionalColumns. (Optional) /// /// A leaderboard_result object containing a collection of the leaderboard columns and rows, /// ordered by player rank descending. /// /// /// Returns a concurrency::task<T> object that represents the state of the asynchronous operation. /// /// Calls V1 GET /scids/{scid}/leaderboards/{leaderboardname} /// inline pplx::task> get_leaderboard( _In_ const string_t& scid, _In_ const string_t& name, _In_ const std::vector& additionalColumnNames = std::vector() ); /// /// Get a leaderboard for a single leaderboard given a service configuration ID and a leaderboard name. /// /// The service configuration ID (SCID) of the title /// The name of the leaderboard. /// The Xbox user ID of the requesting user. /// The name of the group of users to get get leaderboard results for. /// See Microsoft::Xbox::Services::Social::SocialGroupConstants for the latest options. /// The name of the stats for the additionalColumns. (Optional) /// /// A leaderboard_result object containing a collection of the leaderboard columns and rows, /// ordered by player rank descending. /// /// /// Returns a concurrency::task<T> object that represents the state of the asynchronous operation. /// /// Calls V3 GET /scids/{scid}/leaderboards/{leaderboardname}?include=valuemetadata&xuid={xuid}&viewTarget=people&view=people /// inline pplx::task> get_leaderboard( _In_ const string_t& scid, _In_ const string_t& name, _In_ const string_t& xuid, _In_ const string_t& socialGroup, _In_ uint32_t maxItems = 0, _In_ const std::vector& additionalColumnNames = std::vector() ); /// /// Get a page of leaderboard results for a single leaderboard given a service configuration ID /// and a leaderboard name. /// /// The service configuration ID (SCID) of the title /// The name of the leaderboard. /// The number of ranks to skip before returning results. /// The maximum number of items to retrieve. If this value is 0, the server defaults to 10. (Optional) /// The name of the stats for the additionalColumns. (Optional) /// A leaderboard_result object containing a collection of the leaderboard columns and rows. /// /// Returns a concurrency::task<T> object that represents the state of the asynchronous operation. /// /// Calls V1 GET /// /scids/{scid}/leaderboards/{leaderboardname}?[&skipItems={skipItems}][&maxItems={maxItems}] /// inline pplx::task> get_leaderboard( _In_ const string_t& scid, _In_ const string_t& name, _In_ uint32_t skipToRank, _In_ uint32_t maxItems = 0, _In_ const std::vector& additionalColumnNames = std::vector() ); /// /// Get a page of leaderboard results for a single leaderboard given a service configuration ID /// and a leaderboard name. /// /// The service configuration ID (SCID) of the title /// The name of the leaderboard. /// The number of ranks to skip before returning results. /// The Xbox user ID of the requesting user. /// The name of the group of users to get get leaderboard results for. /// See Microsoft::Xbox::Services::Social::SocialGroupConstants for the latest options. /// The maximum number of items to retrieve. If this value is 0, the server defaults to 10. (Optional) /// The name of the stats for the additionalColumns. (Optional) /// A leaderboard_result object containing a collection of the leaderboard columns and rows. /// /// Returns a concurrency::task<T> object that represents the state of the asynchronous operation. /// /// Calls V3 GET /// /scids/{scid}/leaderboards/{leaderboardname}?[&skipItems={skipItems}][&maxItems={maxItems}]&include=valuemetadata&xuid={xuid}&viewTarget=people&view=people /// inline pplx::task> get_leaderboard( _In_ const string_t& scid, _In_ const string_t& name, _In_ uint32_t skipToRank, _In_ const string_t& xuid, _In_ const string_t& socialGroup, _In_ uint32_t maxItems = 0, _In_ const std::vector& additionalColumnNames = std::vector() ); /// /// Get a leaderboard starting at a specified player, regardless of the player's rank or score, ordered by /// the player's percentile rank. /// /// The service configuration ID (SCID) of the title /// The name of the leaderboard. /// The Xbox user ID of the player to skip to before retrieving results. /// The maximum number of items to retrieve. If this value is 0, the server defaults to 10. (Optional) /// The name of the stats for the additionalColumns. (Optional) /// /// A leaderboard_result object containing a collection of the leaderboard columns and rows. /// The result page is ordered by percentile rank, with the specified player in the last position of /// the page for predefined views, or in the middle for stat leaderboard views. /// /// /// Returns a concurrency::task<T> object that represents the state of the asynchronous operation. /// /// There is no continuation Token provided for this query. /// /// Calls V1 GET /// /scids/{scid}/leaderboards/{leaderboardname}?[&skipToUser={xuid}][&maxItems={maxItems}] /// inline pplx::task> get_leaderboard_skip_to_xuid( _In_ const string_t& scid, _In_ const string_t& name, _In_ const string_t& skipToXuid = string_t(), _In_ uint32_t maxItems = 0, _In_ const std::vector& additionalColumnNames = std::vector() ); /// /// Get a leaderboard starting at a specified player, regardless of the player's rank or score, ordered by /// the player's percentile rank. /// /// The service configuration ID (SCID) of the title /// The name of the leaderboard. /// The Xbox user ID of the requesting user. /// The name of the group of users to get get leaderboard results for. /// See Microsoft::Xbox::Services::Social::SocialGroupConstants for the latest options. /// The Xbox user ID of the player to skip to before retrieving results. /// A value indicating the sort order for the returned leaderboard result. /// The possible values are 'ascending' or 'descending', without quotes. /// The maximum number of items to retrieve. If this value is 0, the server defaults to 10. (Optional) /// The name of the stats for the additionalColumns. (Optional) /// /// A leaderboard_result object containing a collection of the leaderboard columns and rows. /// The result page is ordered by percentile rank, with the specified player in the last position of /// the page for predefined views, or in the middle for stat leaderboard views. /// /// /// Returns a concurrency::task<T> object that represents the state of the asynchronous operation. /// /// There is no continuation Token provided for this query. /// /// Calls V1 GET /// /scids/{scid}/leaderboards/{leaderboardname}?[&skipToUser={xuid}][&maxItems={maxItems}] /// inline pplx::task> get_leaderboard_skip_to_xuid( _In_ const string_t& scid, _In_ const string_t& name, _In_ const string_t& xuid, _In_ const string_t& socialGroup, _In_ const string_t& skipToXuid, _In_ uint32_t maxItems = 0, _In_ const std::vector& additionalColumnNames = std::vector() ); /// /// Get an unsorted leaderboard that shows members of a specified social group. /// /// The Xbox user ID of the requesting user. /// The service configuration ID (SCID) of the title /// The name of the statistic to get a leaderboard for. /// The name of the group of users to get get leaderboard results for. /// You can pass either "Favorites" or "People" /// The maximum number of items to retrieve. If this value is 0, the server defaults to 10. (Optional) /// /// A LeaderboardResult object containing a collection of the leaderboard columns and rows. /// /// /// Returns a concurrency::task<T> object that represents the state of the asynchronous operation. /// /// Calls V1 GET /// https://leaderboards.xboxlive.com/users/xuid({xuid})/scids/{scid}/stats/{statname}/people/{all|favorites} /// inline pplx::task> get_leaderboard_for_social_group( _In_ const string_t& xuid, _In_ const string_t& scid, _In_ const string_t& statName, _In_ const string_t& socialGroup, _In_ uint32_t maxItems = 0 ); /// /// Get a sorted leaderboard that shows members of a specified social group. /// /// The Xbox user ID of the requesting user. /// The service configuration ID (SCID) of the title /// The name of the statistic to get a leaderboard for. /// The name of the group of users to get get leaderboard results for. /// You can pass either "Favorites" or "People" /// A value indicating the sort order for the returned leaderboard result. /// The possible values are 'ascending' or 'descending', without quotes. /// The maximum number of items to retrieve. If this value is 0, the server defaults to 10. (Optional) /// An object containing a collection of the leaderboard columns and rows /// /// Returns a concurrency::task<T> object that represents the state of the asynchronous operation. /// /// Calls V1 GET /// https://leaderboards.xboxlive.com/users/xuid({xuid})/scids/{scid}/stats/{statname}/people/{all|favorites}[?sort=descending|ascending] /// inline pplx::task> get_leaderboard_for_social_group( _In_ const string_t& xuid, _In_ const string_t& scid, _In_ const string_t& statName, _In_ const string_t& socialGroup, _In_ const string_t& sortOrder, _In_ uint32_t maxItems = 0 ); /// /// Get a sorted leaderboard, starting at a specified rank, that shows members of a specified social group. /// /// The Xbox user ID of the requesting user. /// The service configuration ID (SCID) of the title /// The name of the statistic to get a leaderboard for. /// The name of the group of users to get get leaderboard results for. /// See Microsoft::Xbox::Services::Social::SocialGroupConstants for the latest options. /// The number of ranks to skip before retrieving results. /// A value indicating the sort order for the returned leaderboard result. /// The possible values are 'ascending' or 'descending', without quotes. /// The maximum number of items to retrieve. If this value is 0, the server defaults to 10. (Optional) /// The name of the stats for the additionalColumns. (Optional) /// /// A leaderboard_result object containing a collection of the leaderboard columns and rows. /// /// /// Returns a concurrency::task<T> object that represents the state of the asynchronous operation. /// /// Calls V1 GET /// https://leaderboards.xboxlive.com/users/xuid({xuid})/scids/{scid}/stats/{statname}/people/{all|favorites}[?sort=descending|ascending]&skipToRank={skipToUser} /// inline pplx::task> get_leaderboard_for_social_group_skip_to_rank( _In_ const string_t& xuid, _In_ const string_t& scid, _In_ const string_t& statName, _In_ const string_t& socialGroup, _In_ uint32_t skipToRank, _In_ const string_t& sortOrder, _In_ uint32_t maxItems = 0 ); /// /// Get a sorted leaderboard, starting at a specified player, that shows members of a specified social group. /// /// The Xbox user ID of the requesting user. /// The service configuration ID (SCID) of the title /// The name of the statistic to get a leaderboard for. /// The name of the group of users to get get leaderboard results for. /// See Microsoft::Xbox::Services::Social::SocialGroupConstants for the latest options. /// The Xbox user ID of the player to skip to before retrieving results. /// A value indicating the sort order for the returned leaderboard result. /// The possible values are 'ascending' or 'descending', without quotes. /// The maximum number of items to retrieve. If this value is 0, the server defaults to 10. (Optional) /// /// A leaderboard_result object that contains a page of leaderboard results around the specified player regardless /// of that player's rank or score. /// The result page is ordered by percentile rank, with the specified player in the last position of /// the page for predefined views, or in the middle for stat leaderboard views. /// /// /// Returns a concurrency::task<T> object that represents the state of the asynchronous operation. /// /// Calls V1 GET /// https://leaderboards.xboxlive.com/users/xuid({xuid})/scids/{scid}/stats/{statname}/people/{all|favorites}[?sort=descending|ascending]&skipToUser={skipToUser} /// inline pplx::task> get_leaderboard_for_social_group_skip_to_xuid( _In_ const string_t& xuid, _In_ const string_t& scid, _In_ const string_t& statName, _In_ const string_t& socialGroup, _In_ const string_t& skipToXuid, _In_ const string_t& sortOrder, _In_ uint32_t maxItems = 0 ); inline leaderboard_service(const leaderboard_service& other); inline leaderboard_service& operator=(leaderboard_service other); inline ~leaderboard_service(); private: inline leaderboard_service(_In_ XblContextHandle contextHandle); XblContextHandle m_xblContext; inline XblSocialGroupType XblSocialGroupTypeFromString(string_t socialGroup); inline XblLeaderboardSortOrder XblLeaderboardSortOrderFromString(string_t socialGroup); friend xbox_live_context; }; #endif #endif }}} #if !XSAPI_NO_PPL #include "impl/leaderboard.hpp" #endif ================================================ FILE: Include/xsapi-cpp/matchmaking.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "xsapi-c/matchmaking_c.h" #include "xsapi-cpp/multiplayer.h" namespace xbox { namespace services { class xbox_live_context; /// /// Contains classes and enumerations that let you match /// players for a multiplayer session. /// namespace matchmaking { /// /// Defines values used to indicate whether a match ticket is for a new /// game session or an existing session. /// enum class preserve_session_mode { /// /// The server returned an unrecognized response. /// unknown, /// /// Always use an existing game session. This is for matching more players /// for a game session that is already created or in progress. /// always, /// /// Never use an existing game session. This is for matching players /// for a new game session. /// never }; /// /// Defines values used to indicate the status of the match request. /// enum class ticket_status { /// /// The status of the match request has not been returned by the server yet /// or the server returned an unrecognized response. /// unknown, /// /// Matchmaking has not found a match and the search /// request has expired according to its give up duration. /// expired, /// /// Matchmaking has not found a match yet and it is /// still searching. /// searching, /// /// Matchmaking has found a match and the ticket contains a /// reference to the session that is to be created. /// found, /// /// Matchmaking has been canceled for this ticket. /// canceled }; /// /// Represents a server response to a create match ticket request. /// class create_match_ticket_response { public: /// /// Internal function /// inline create_match_ticket_response(); /// /// Internal function /// inline create_match_ticket_response( XblCreateMatchTicketResponse response ); /// /// Ticket ID of a match request. /// inline string_t match_ticket_id() const; /// /// Estimated wait time for a match request to be matched with other players. /// inline std::chrono::seconds estimated_wait_time() const; private: XblCreateMatchTicketResponse m_createMatchTicketResponse; }; /// /// Represents a server response to a request for match ticket details. /// class match_ticket_details_response { public: /// /// Internal function /// inline match_ticket_details_response(); /// /// Internal function /// inline match_ticket_details_response( std::shared_ptr buffer ); /// /// Status of a match request. /// inline ticket_status match_status() const; /// /// Estimated wait time for a match request to be matched with other players. /// inline std::chrono::seconds estimated_wait_time() const; /// /// An enum value to specify whether the match should preserve the session on which the match has been requested. /// inline preserve_session_mode preserve_session() const; /// /// The session on which the match was requested. /// inline xbox::services::multiplayer::multiplayer_session_reference ticket_session() const; /// /// The session on which a match request has been found. /// inline xbox::services::multiplayer::multiplayer_session_reference target_session() const; /// /// The attributes of a match request. /// inline web::json::value ticket_attributes() const; private: static inline ticket_status convert_string_to_ticket_status(_In_ const string_t& value); static inline preserve_session_mode convert_string_to_preserve_session_mode(_In_ const string_t& value); std::shared_ptr m_buffer; XblMatchTicketDetailsResponse m_matchTicketDetailsResponse; }; /// /// Represents a server response to a hopper statistics request. /// class hopper_statistics_response { public: /// /// Internal function /// inline hopper_statistics_response(); /// /// Internal function /// inline hopper_statistics_response( std::shared_ptr buffer ); /// /// Name of the hopper in which a match was requested. /// inline string_t hopper_name() const; /// /// Estimated wait time for a match request to be matched with other players. /// inline std::chrono::seconds estimated_wait_time() const; /// /// The number of players in the hopper waiting to be matched. /// inline uint32_t players_waiting_to_match() const; private: std::shared_ptr m_buffer; XblHopperStatisticsResponse m_hopperStatisticsResponse; }; /// /// Represents the Matchmaking Service. /// class matchmaking_service { public: /// /// Sends a matchmaking request to the server and returns the match ticket with a ticket id. /// /// The multiplayer session to use for the match. /// The service configuration ID for the match. /// The name of the hopper. /// The maximum time to wait for players to join the session. /// Indicates if the session should be preserved. /// The ticket attributes for the session. (Optional) /// The async object for notifying when the operation is completed. With the handler, a new match ticket /// object is returned. The match ticket object contains server returned information such as ticket id and wait /// time, and also contains copies of the title specified data from the ticket data object. /// Calls V103 POST /serviceconfigs/{serviceConfigId}/hoppers/{hopperName} inline pplx::task> create_match_ticket( _In_ const xbox::services::multiplayer::multiplayer_session_reference& ticketSessionReference, _In_ const string_t& matchmakingServiceConfigurationId, _In_ const string_t& hopperName, _In_ const std::chrono::seconds& ticketTimeout, _In_ preserve_session_mode preserveSession, _In_ const web::json::value& ticketAttributesJson = web::json::value() ); /// /// Deletes a the match ticket on the server. /// /// The service config id that is specific for the title. /// The name of the hopper where the match ticket is located. /// The id of the ticket to delete on the server. /// The async object for notifying when the operation has been completed. /// Calls V103 DELETE /serviceconfigs/{serviceConfigId}/hoppers/{hopperName}/tickets/{ticketId} inline pplx::task> delete_match_ticket( _In_ const string_t& serviceConfigurationId, _In_ const string_t& hopperName, _In_ const string_t& ticketId ); /// /// Retrieves the properties of a match ticket from the server. /// /// The service config id that is specific for the title. /// The name of the hopper where the match ticket is located. /// The ticket id of the match ticket to retrieve. /// The async object for notifying when the operation is completed. With the handler, the match /// ticket object with the data for the ticket, including ticket id and wait time information, is returned /// returned from the server. /// Calls V103 GET /serviceconfigs/{serviceConfigId}/hoppers/{hopperName}/tickets/{ticketId} inline pplx::task> get_match_ticket_details( _In_ const string_t& serviceConfigurationId, _In_ const string_t& hopperName, _In_ const string_t& ticketId ); /// /// Gets statistics about a hopper such as how many players are in it. /// /// The service config id that is specific for the title. /// The name of the hopper to query stats for. /// The async object for notifying when the operation is completed. With the handler, an object /// containing statistics about the hopper is returned. /// Calls V103 GET /serviceconfigs/{serviceConfigId}/hoppers/{hopperName}/stats inline pplx::task> get_hopper_statistics( _In_ const string_t& serviceConfigurationId, _In_ const string_t& hopperName ); inline matchmaking_service(const matchmaking_service& other); inline matchmaking_service& operator=(matchmaking_service other); inline ~matchmaking_service(); private: inline matchmaking_service (_In_ XblContextHandle contextHandle); XblContextHandle m_xblContext; friend xbox_live_context; }; }}} #include "impl/matchmaking.hpp" ================================================ FILE: Include/xsapi-cpp/mem.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include #include #include #include #include #include #include #include #include NAMESPACE_MICROSOFT_XBOX_SERVICES_SYSTEM_CPP_BEGIN class xsapi_memory { public: static inline _Ret_maybenull_ _Post_writable_byte_size_(dwSize) void* mem_alloc( _In_ size_t dwSize ); static inline void mem_free( _In_opt_ void* pAddress ); private: xsapi_memory() = delete; }; class xsapi_memory_buffer { public: xsapi_memory_buffer(_In_ size_t dwSize) { m_pBuffer = xsapi_memory::mem_alloc(dwSize); } ~xsapi_memory_buffer() { xsapi_memory::mem_free(m_pBuffer); m_pBuffer = nullptr; } void* get() { return m_pBuffer; } private: void* m_pBuffer; }; NAMESPACE_MICROSOFT_XBOX_SERVICES_SYSTEM_CPP_END #include "impl/mem.hpp" ================================================ FILE: Include/xsapi-cpp/multiplayer.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #if !XSAPI_NO_PPL #include "xsapi-cpp/real_time_activity.h" #include "xsapi-c/multiplayer_c.h" namespace xbox { namespace services { class xbox_live_context; namespace social { class reputation_service; } }} namespace xbox { namespace services { /// /// Contains classes and enumerations for managing a multiplayer session. /// namespace multiplayer { /// /// Defines values used to indicate status for visibility or accessibility of a session. /// enum class multiplayer_session_visibility { /// Unknown unknown, /// Ignore the SessionVisibility filter. any, /// /// The session is private and it's not visible to other users who /// aren't in the session. Joining a visible or private session is a HTTP_E_STATUS_FORBIDDEN. /// private_session, /// /// The session is visible to other users who aren't in the session, but the session is read-only to them and they can't join. /// Joining an visible causes the service to return HTTP_E_STATUS_BAD_REQUEST (403). /// visible, /// /// The session is full and cannot be joined by anyone. /// Joining an open but full session causes the service to return HTTP_E_STATUS_BAD_REQUEST (400). /// full, /// The session is open and can be joined by anyone. open }; /// /// Defines values used to indicate status for the initialization stage of a session during managed initialization. /// enum class multiplayer_initialization_stage { /// Unknown unknown, /// Initialization stage not set. none, /// /// Joining initialization stage. /// Typically matchmaking creates session and puts users into it. /// SPC has up to the joining timeout to join the session during this phase. /// joining, /// /// Measuring initialization stage. Stage where QoS measurement happens. /// If the title is manually managing QoS, then title will do this stage. /// Otherwise the Party system will do this when calling RegisterGameSession or RegisterMatchSession. /// measuring, /// /// Evaluating initialization stage. /// If auto evaluate is true, then this stage is skipped. /// Otherwise the title will do its own evaluation. /// This stage is applied even with the SPC is managing QoS. /// evaluating, /// /// Failed initialization stage. /// If episode 1 didn't succeed, then goes into failed permanently. /// failed }; /// /// Defines values used to indicate the type of metric used to measure matchmaking QoS for a session. /// enum class multiplay_metrics { /// Unknown metric unknown, /// Bandwidth host selection metric bandwidth_up, /// Bandwidth down host selection metric bandwidth_down, /// Bandwidth host selection metric bandwidth, /// Latency host selection metric latency }; /// /// Defines values used to indicate the current network address translation (NAT) settings for a console connecting to Xbox Live. /// enum class network_address_translation_setting { /// The server returned an unrecognized response. unknown, /// Can connect with any other consoles regardless of their NAT setting. open, /// Consoles using Moderate NAT settings can only connect with other consoles using Moderate or Open settings. moderate, /// Consoles using Strict NAT settings can only connect with other consoles using Open NAT settings. strict }; /// /// Defines values used to indicate types measurement failures for a session member on the network. /// enum class multiplayer_measurement_failure { /// Unknown measurement failure unknown, /// This player has no measurement failure. none, /// This player failed because timeout measurement test failed. timeout, /// This player failed because latency measurement test failed. latency, /// This player failed because bandwidth up measurement test failed. bandwidth_up, /// This player failed because bandwidth down measurement test failed. bandwidth_down, /// This player failed cause someone failed in their group failed. group, /// This player failed due to a network error such as the user was unreachable. network, /// This player failed because your episode failed. This likely happened because there wasn't enough users in the session. episode }; /// /// Defines values used to indicate current status values for a session. /// enum class multiplayer_session_status { /// /// The server returned an unrecognized response. /// unknown, /// /// The session is active and there is at least one user. /// active, /// /// The session is inactive. This means no users in the session are /// active or all users left the session. /// inactive, /// /// The session is reserved. This means one for more users have not /// accepted the session invite. /// reserved }; /// /// Defines values used to indicate restrictions on the users who can join a session. /// enum class multiplayer_session_restriction { /// The unrecognized restriction type. unknown, /// Default value, no restriction none, /// If "local", only users whose token's DeviceId matches someone else already in the session and "active": true. local, /// If "followed", only local users (as defined above) and users who are followed by an existing (not reserved) member of the session can join without a reservation. followed }; /// /// Defines values used to indicate status for a matchmaking request for a session. /// enum class matchmaking_status { /// The server returned an unrecognized response. unknown, /// Indicates the matchmaking search is not specified. This status is optional and requires the clientMatchmaking capability. none, /// Indicates the matchmaking search is still searching. searching, /// Indicates the matchmaking search has expired. expired, /// Indicates the matchmaking search has found a session. found, /// Indicates the matchmaking search has been canceled. canceled }; /// /// Defines values used to indicate status for member of a session. /// enum class multiplayer_session_member_status { /// /// Member is reserved for a specific Xbox User ID. /// This specific member must join the session to fill the reservation. /// If a reserved member doesn't join before the JoinTimeout they will be removed. /// reserved, /// /// The member is inactive in the current title. /// The member may be active in another title as specified by ActiveTitleId. /// If an inactive member doesn't mark themselves as Active within the MemberInactiveTimeout they will be removed from the session. /// inactive, /// /// When the shell launches the title to start a multiplayer game, the member is marked as ready. /// If a ready member doesn't mark themselves as Active within the MemberReadyTimeout they will be marked as inactive. /// ready, /// The member is active in the current title. active }; /// /// Defines values used to indicate the mode used when creating or writing to a new Multiplayer service session. /// enum class multiplayer_session_write_mode { /// /// Create a new multiplayer session. Fails if the session already exists. /// create_new, /// /// Either update or create a new session. Doesn't care whether the session exists. /// update_or_create_new, /// /// Updates an existing multiplayer session; fails if the session doesn't exist. /// update_existing, /// /// Updates an existing multiplayer session. Fails with HTTP_E_STATUS_PRECOND_FAILED (HTTP status 412) if eTag on local session doesn't match eTag on server. /// Fails if the session does not exist. /// synchronized_update, }; enum class write_session_status { /// /// Unknown write result /// unknown, /// /// HTTP Result 403- User does not have proper permission to write a session /// access_denied, /// /// HTTP Result 201- Write created session successfully /// created, /// /// HTTP Result 409- Conflict occurred during write about session document /// conflict, /// /// HTTP Result 404- Session not found /// handle_not_found, /// /// HTTP Result 412- Session document is not the most recent /// out_of_sync, /// /// HTTP Result 204- Session deleted successfully /// session_deleted, /// /// HTTP Result 200- Session updated successfully /// updated }; /// /// Defines values used to indicate change types for a multiplayer session. /// enum multiplayer_session_change_types { /// /// None /// none = 0x0000, /// /// Changes to anything in the session. /// everything = 0x0001, /// /// Changes to the host device token. /// host_device_token_change = 0x0002, /// /// Changes to the stage of initialization has changed. /// initialization_state_change = 0x0004, /// /// Changes to the matchmaking status (e.g. match found or expired) /// matchmaking_status_change = 0x0008, /// /// A member joined the session /// member_list_change = 0x0010, /// /// A member left the session /// member_status_change = 0x0020, /// /// Changes to the joinability of the session. /// session_joinability_change = 0x0040, /// /// Changes within properties/custom /// custom_property_change = 0x0080, /// /// Changes within member/properties/custom, for any of the members. /// member_custom_property_change = 0x0100, }; /// /// Defines values used to indicate mutable_role_setting types for a multiplayer role. /// Note: Only the session owner can modify role settings and only those that are set as multiplayer_role_type::mutable_role_settings(). /// The mutable_role_settings can be set in the session template. /// enum class mutable_role_setting { /// /// Allows you to set a max count for the multiplayer role /// max, /// /// Allows you to set a target count for the multiplayer role /// target }; /// /// Represents requirements that apply to each connection between a host candidate and session members. /// class multiplayer_peer_to_host_requirements { public: /// /// The maximum latency for the peer to host connection. /// inline std::chrono::milliseconds latency_maximum() const; /// /// The minimum bandwidth down in kilobits per second for the peer to host connection. /// inline uint64_t bandwidth_down_minimum_in_kilobits_per_second() const; /// /// The minimum bandwidth up in kilobits per second for the peer to host connection. /// inline uint64_t bandwidth_up_minimum_in_kilobits_per_second() const; /// /// Indicates which metric was used to select the host. /// inline multiplay_metrics host_selection_metric() const; /// /// Internal function /// inline multiplayer_peer_to_host_requirements(_In_ const XblMultiplayerPeerToHostRequirements& requirements); private: XblMultiplayerPeerToHostRequirements m_requirements; }; /// /// Represents requirements for a connection between session members. /// class multiplayer_peer_to_peer_requirements { public: /// /// The maximum latency for the peer to peer connection. /// inline uint64_t bandwidth_minimum_in_kilobits_per_second() const; /// /// The minimum bandwidth in kilobits per second for the peer to peer connection. /// inline std::chrono::milliseconds latency_maximum() const; /// /// Internal function /// inline multiplayer_peer_to_peer_requirements(_In_ const XblMultiplayerPeerToPeerRequirements& requirements); private: XblMultiplayerPeerToPeerRequirements m_requirements; }; /// /// Used to configure requirements and initialize a new Multiplayer session. /// class multiplayer_member_initialization { public: /// /// Indicates if the ManagedInitializion object is set. /// inline bool member_initialization_set() const; /// /// Returns the timeout for the first stage of the QoS process which is the joining stage. /// inline std::chrono::milliseconds join_timeout() const; /// /// Returns the timeout for the measurement stage of the QoS process. /// inline std::chrono::milliseconds measurement_timeout() const; /// /// Returns the timeout for the evaluation stage of the QoS process. /// inline std::chrono::milliseconds evaluation_timeout() const; /// /// This is an optional evaluate stage for title. The title can do evaluation when set to true. /// inline bool external_evaluation() const; /// /// Defaults to 2. Must be between 1 and maxMemberCount. Only applies to initialization episode zero. /// inline uint32_t members_need_to_start() const; /// /// Internal function /// inline multiplayer_member_initialization(_In_opt_ const XblMultiplayerMemberInitialization* memberInitialization); private: bool m_initializationSet{ false }; XblMultiplayerMemberInitialization m_memberInitialization{}; }; /// /// Represents the capabilities of a multiplayer session. /// /// /// Session capabilities are boolean values that are optionally set in the session template. If no capabilities are needed, an empty SessionCapabilities object should be in the template to prevent capabilities from being specified on session creation, unless the title requires dynamic session capabilities. /// class multiplayer_session_capabilities { public: /// /// Constructor /// inline multiplayer_session_capabilities(); /// /// If false, the session can't enable any metrics and the session members can not set their SecureDeviceAddress. /// inline bool connectivity() const; /// /// If false, the session can't enable any metrics and the session members can not set their SecureDeviceAddress. /// inline void set_connectivity(_In_ bool connectivity); /// /// By default (if false), active users are required to remain online playing the title, otherwise they get demoted to /// inactive status. Setting this flag to true disables this check so that members stay active indefinitely. /// inline bool suppress_presence_activity_check() const; /// /// By default (if false), active users are required to remain online playing the title, otherwise they get demoted to /// inactive status. Setting this flag to true disables this check so that members stay active indefinitely. /// inline void set_suppress_presence_activity_check(_In_ bool suppressPresenceActivityCheck); /// /// Indicates whether the session represents actual gameplay, as opposed to setup/menu time like a lobby or matchmaking. /// If true, then the session is in gameplay mode. /// inline bool gameplay() const; /// /// Indicates whether the session represents actual gameplay, as opposed to setup/menu time like a lobby or matchmaking. /// If true, then the session is in gameplay mode. /// inline void set_gameplay(_In_ bool gameplay); /// /// If true, this session can host a large number of users, which has impact on other session properties (see documentation) /// inline bool large() const; /// /// If true, this session can host a large number of users, which has impact on other session properties (see documentation) /// inline void set_large(_In_ bool large); /// /// If true, this connection is required to have a member be active (see documentation) /// inline bool connection_required_for_active_members() const; /// /// If true, this connection is required to have a member be active (see documentation) /// inline void set_connection_required_for_active_members(_In_ bool connectionRequired); /// /// Session supports calls from platforms without strong title identity. This capability can't be set on large sessions. /// inline bool user_authorization_style() const; /// /// Session supports calls from platforms without strong title identity. This capability can't be set on large sessions. /// inline void set_user_authorization_style(_In_ bool userAuthorizationStyle); /// /// Session supports cross play between PC and Xbox /// inline bool crossplay() const; /// /// Session supports cross play between PC and Xbox /// inline void set_crossplay(_In_ bool crossplay); /// /// True, if the session can be linked to a search handle for searching. /// inline bool searchable() const; /// /// Allows the session to be linked to a search handle for searching. /// inline void set_searchable(_In_ bool searchable); /// /// True, if the session has owners. If you have user_authorization_style set, then in order to be searchable, you must have owners set. /// inline bool has_owners() const; /// /// If you have user_authorization_style set, then in order to be searchable, you must have owners set. /// inline void set_has_owners(_In_ bool hasOwners); private: XblMultiplayerSessionCapabilities m_capabilities; friend class multiplayer_session; }; /// /// Represents matchmaking quality of service (QoS) measurements for the network used by a session member. /// class multiplayer_quality_of_service_measurements { public: /// /// Creates a new MultiplayerQualityOfServiceMeasurements object. /// /// The device token of the member that this measurement is for. /// The time of the latency measurement. /// The bandwidth down in kilobits per second. /// The bandwidth up in kilobits per second. /// JSON string that specify the custom properties. inline multiplayer_quality_of_service_measurements( _In_ const string_t& memberDeviceToken, _In_ std::chrono::milliseconds latency, _In_ uint64_t bandwidthDownInKilobitsPerSecond, _In_ uint64_t bandwidthUpInKilobitsPerSecond, _In_ const string_t& customJson ); /// /// The device token of the member that this measurement is for. /// inline const string_t& member_device_token() const; /// /// The time of the latency measurement. /// inline const std::chrono::milliseconds& latency() const; /// /// The bandwidth down in kilobits per second. /// inline uint64_t bandwidth_down_in_kilobits_per_second() const; /// /// The bandwidth up in kilobits per second. /// inline uint64_t bandwidth_up_in_kilobits_per_second() const; /// /// JSON string that specify the custom properties. /// inline const web::json::value& custom_json() const; /// /// Internal function /// inline multiplayer_quality_of_service_measurements( _In_ const string_t& memberDeviceToken, _In_ const web::json::value& measurementsJson ); private: string_t m_memberDeviceToken; std::chrono::milliseconds m_latency; uint64_t m_bandwidthDown; uint64_t m_bandwidthUp; web::json::value m_customJson; web::json::value m_measurementsJson; friend class multiplayer_session; }; /// /// Represents constant values for a multiplayer session. /// class multiplayer_session_constants { public: /// /// The maximum number of members in this session. /// inline uint32_t max_members_in_session() const; /// /// The maximum number of members in this session. /// inline void set_max_members_in_session(_In_ uint32_t maxMembersInSession); /// /// The visibility of this session. /// inline multiplayer_session_visibility visibility() const; /// /// The visibility of this session. /// inline void set_visibility(_In_ multiplayer_session_visibility visibility); /// /// A collection of Xbox User IDs indicating who initiated the session. (Optional) /// inline std::vector initiator_xbox_user_ids() const; /// /// JSON string that specify the custom constants for the session. These can not be changed after the session is created. (Optional) /// inline web::json::value session_custom_constants_json() const; /// /// JSON string that specify the cloud compute package constants for the session. These can not be changed after the session is created. (Optional) /// inline web::json::value session_cloud_compute_package_constants_json() const; /// /// If a member reservation does not join within this timeout, then reservation is removed. /// inline std::chrono::milliseconds member_reserved_time_out() const; /// /// If an inactive member reservation does not become active within this timeout, then the inactive member is removed from the session. /// inline std::chrono::milliseconds member_inactive_timeout() const; /// /// If a member who is marked as ready doesn't mark themselves as active within this timeout, then member becomes inactive. /// When the shell launches the title to start a multiplayer game, the member is marked as ready. /// inline std::chrono::milliseconds member_ready_timeout() const; /// /// If the session is empty for this timeout, then the session is deleted. /// inline std::chrono::milliseconds session_empty_timeout() const; /// /// Indicates if the title wants latency measured for determining connectivity /// Requires CapabilitiesConnectivity capability. /// inline bool enable_metrics_latency() const; /// /// Indicates if the title wants bandwidth down measured for determining connectivity /// Requires CapabilitiesConnectivity capability. /// inline bool enable_metrics_bandwidth_down() const; /// /// Indicates if the title wants bandwidth up measured for determining connectivity /// Requires CapabilitiesConnectivity capability. /// inline bool enable_metrics_bandwidth_up() const; /// /// Indicates if the title wants a custom measurement measured for determining connectivity /// Requires CapabilitiesConnectivity capability. /// inline bool enable_metrics_custom() const; /// /// If a MemberInitialization object is set, the session expects the client system or title to perform /// initialization following session creation. The timeouts and initialization stages are automatically tracked by /// the session, including initial QoS if any metrics are set. /// inline multiplayer_member_initialization member_initialization() const; /// /// Peer to peer QoS requirements /// inline multiplayer_peer_to_peer_requirements peer_to_peer_requirements() const; /// /// Peer to host QoS requirements /// inline multiplayer_peer_to_host_requirements peer_to_host_requirements() const; /// /// The set of potential server connection strings that should be evaluated. /// inline web::json::value measurement_server_addresses_json() const; /// /// Indicates whether the matchmaking status fields can be written to. /// inline bool client_matchmaking_capable() const; /// /// If false, the session can't enable any metrics and the session members can not set their SecureDeviceAddress. /// inline bool capabilities_connectivity() const; /// /// By default (if false), active users are required to remain online playing the title, otherwise they get demoted to /// inactive status. Setting this flag to true disables this check so that members stay active indefinitely. /// inline bool capabilities_suppress_presence_activity_check() const; /// /// Indicates whether the session represents actual gameplay, as opposed to setup/menu time like a lobby or matchmaking. /// If true, then the session is in gameplay mode. /// inline bool capabilities_gameplay() const; /// /// If true, this session can host a large number of users, which has impact on other session properties. /// inline bool capabilities_large() const; /// /// If true, this connection is required to have a member be active (see documentation) /// inline bool capabilities_connection_required_for_active_member() const; /// /// Session supports cross play between PC and Xbox /// inline bool capabilities_crossplay() const; /// /// Session supports calls from platforms without strong title identity. This capability can't be set on large sessions. /// Using this capability will cause both 'readRestriction' and 'joinRestriction' to default to "local". /// inline bool capabilities_user_authorization_style() const; /// /// True, if the session can be linked to a search handle for searching. /// inline bool capabilities_searchable() const; /// /// Internal function /// inline multiplayer_session_constants(_In_ XblMultiplayerSessionHandle sessionHandle); /// /// Internal function /// inline multiplayer_session_constants(bool isLobbySession); inline ~multiplayer_session_constants(); private: multiplayer_session_constants(const multiplayer_session_constants&) = delete; multiplayer_session_constants& operator=(multiplayer_session_constants) = delete; XblMultiplayerSessionHandle m_sessionHandle; const XblMultiplayerSessionConstants* m_constants; }; /// /// Represents a reference to a multiplayer session. /// class multiplayer_session_reference { public: /// /// Constructs a null MultiplayerSession object. /// inline multiplayer_session_reference(); /// /// Constructs the MultiplayerSession object with data about the session. /// /// A service configuration ID appropriate for the title. /// The name of the template for the session to be based on. /// A unique name for the session. inline multiplayer_session_reference( _In_ const string_t& serviceConfigurationId, _In_ const string_t& sessionTemplateName, _In_ const string_t& sessionName ); /// /// The service configuration ID specific to the title. /// inline string_t service_configuration_id() const; /// /// The name of the template for the session. /// inline string_t session_template_name() const; /// /// The name of the session. /// inline string_t session_name() const; /// /// Whether this object has been properly constructed /// inline bool is_null() const; /// /// Returns a URI path representation of the session reference. /// inline string_t to_uri_path() const; /// /// Returns the session reference parsed from URI. /// static inline multiplayer_session_reference parse_from_uri_path(_In_ const string_t& path); /// /// Internal function /// inline multiplayer_session_reference(_In_ const XblMultiplayerSessionReference& reference); private: mutable XblMultiplayerSessionReference m_reference; friend class multiplayer_session; friend class multiplayer_service; friend class multiplayer_search_handle_request; friend class social::reputation_service; }; /// /// Represents the matchmaking server supporting the multiplayer session. /// class multiplayer_session_matchmaking_server { public: /// /// The Matchmaking Status of the Multiplayer Session Server. /// inline matchmaking_status status() const; /// /// The Matchmaking Status Details of the Multiplayer Session Server. /// inline string_t status_details() const; /// /// The Typical Wait of the Multiplayer Session Server. /// inline std::chrono::seconds typical_wait() const; /// /// The Target Session Reference of the Multiplayer Session Server. /// inline multiplayer_session_reference target_session_ref() const; /// /// Returns true if this object is blank /// inline bool is_null() const; /// /// Internal function /// inline multiplayer_session_matchmaking_server(_In_ XblMultiplayerSessionHandle sessionHandle); inline multiplayer_session_matchmaking_server(const multiplayer_session_matchmaking_server& other); inline multiplayer_session_matchmaking_server& operator=(multiplayer_session_matchmaking_server other); inline ~multiplayer_session_matchmaking_server(); private: XblMultiplayerSessionHandle m_sessionHandle; const XblMultiplayerMatchmakingServer* m_server; }; /// /// Represents role info for a multiplayer role. /// class multiplayer_role_info { public: inline multiplayer_role_info(); /// /// Member xbox_user_ids currently assigned for this role. /// inline const std::vector& member_xbox_user_ids() const; /// /// Number of slots occupied for this role. /// inline uint32_t members_count() const; /// /// Number of target slots assigned for this role. /// inline uint32_t target_count() const; /// /// Maximum number of slots available for this role. /// inline uint32_t max_members_count() const; /// /// Set the max member count for this role. /// Note: Only the session owner can modify role settings and only those that are multiplayer_role_type::mutable_role_settings() /// In your session template, you also need to set 'hasOwners' capability and 'ownerManaged' to true for the specific role type /// that you want to modify the mutable_role_setting off. /// /// The max member count for this role. inline void set_max_members_count(_In_ uint32_t maxCount); /// /// Set the target member count for this role. /// Note: Only the session owner can modify role settings and only those that are multiplayer_role_type::mutable_role_settings() /// In your session template, you also need to set 'hasOwners' capability and 'ownerManaged' to true for the specific role type /// that you want to modify the mutable_role_setting off. /// /// The max member count for this role. inline void set_target_count(_In_ uint32_t targetCount); /// /// Internal function /// inline multiplayer_role_info(const XblMultiplayerRole* role); private: std::vector m_memberXuids; uint32_t m_maxMembersCount{ 0 }; uint32_t m_membersCount{ 0 }; uint32_t m_targetCount{ 0 }; }; /// /// Represents a collection of roles for this role type. /// class multiplayer_role_type { public: inline multiplayer_role_type(); /// /// True if ownerManaged is set on the roleType. /// inline bool owner_managed() const; /// /// Mutable role settings for this role. /// inline const std::vector& mutable_role_settings() const; /// /// A collection of roles for this role type. /// inline const std::unordered_map& roles() const; /// /// Set a collection of roles for this role type. /// Note: Only the session owner can modify role settings and only those that are multiplayer_role_type::mutable_role_settings() /// In your session template, you also need to set 'hasOwners' capability and 'ownerManaged' to true for the specific role type /// that you want to modify the mutable_role_setting off. /// inline void set_roles(_In_ const std::unordered_map& roles); /// /// Internal function /// inline multiplayer_role_type(const XblMultiplayerRoleType* roleType); private: bool m_ownerManaged{ false }; std::vector m_mutableRoleSettings; std::unordered_map m_roles; }; /// /// Represents session role type values for a multiplayer session. /// class multiplayer_session_role_types { public: /// /// A collection of role types. /// inline const std::unordered_map& role_types() const; /// /// Internal function /// inline multiplayer_session_role_types(const XblMultiplayerRoleType* roleTypes, size_t roleTypesCount); private: std::unordered_map m_roleTypes; }; /// /// Represents a users current multiplayer activity, along with some details about the corresponding session. /// class multiplayer_activity_details { public: /// /// Object containing identifying information for the session. /// inline multiplayer_session_reference session_reference() const; /// /// HandleId corresponding to this activity. /// inline string_t handle_id() const; /// /// TitleId that should be launched in order to join this activity. /// inline uint32_t title_id() const; /// /// The visibility state of the session. Whether other users can see, or join, etc. /// inline multiplayer_session_visibility visibility() const; /// /// The join restriction of the session, which applies if visibility is "open". /// inline multiplayer_session_restriction join_restriction() const; /// /// Indicates whether the session is temporarily closed for joining. /// inline bool closed() const; /// /// Xbox User ID of the member whose activity this is. /// inline string_t owner_xbox_user_id() const; /// /// Number of total slots. /// inline uint32_t max_members_count() const; /// /// Number of slots occupied. /// inline uint32_t members_count() const; /// /// Custom session properties for the session. /// inline web::json::value custom_session_properties_json() const; /// /// Internal function /// inline multiplayer_activity_details(const XblMultiplayerActivityDetails& activityDetails); inline multiplayer_activity_details(const multiplayer_activity_details&); inline multiplayer_activity_details& operator=(multiplayer_activity_details); /// /// Internal function /// inline ~multiplayer_activity_details(); private: XblMultiplayerActivityDetails m_activityDetails; }; /// /// Represents a users current search handle, along with some details about the corresponding session. /// class multiplayer_search_handle_details { public: /// /// Object containing identifying information for the session. /// inline multiplayer_session_reference session_reference() const; /// /// HandleId corresponding to this activity. /// inline string_t handle_id() const; /// /// Owners of the session. /// inline std::vector session_owner_xbox_user_ids() const; /// /// The tags that are currently set on the session. /// inline std::vector tags() const; /// /// The numbers metadata that is currently set on the session. /// inline std::unordered_map numbers_metadata() const; /// /// The strings metadata that is currently set on the session. /// inline std::unordered_map strings_metadata() const; /// /// Deprecated. Note that the role types are still exposed through the multiplayer_session object. /// _XSAPICPP_DEPRECATED inline std::unordered_map role_types() const; /// /// The visibility state of the session. Whether other users can see, or join, etc. /// inline multiplayer_session_visibility visibility() const; /// /// The join restriction of the session, which applies if visibility is "open". /// inline multiplayer_session_restriction join_restriction() const; /// /// Indicates whether the session is temporarily closed for joining. /// inline bool closed() const; /// /// Number of total slots. /// inline uint32_t max_members_count() const; /// /// Number of slots occupied. /// inline uint32_t members_count() const; /// /// Custom session properties for the session. /// inline web::json::value custom_session_properties_json() const; /// /// The time when the search handle was created. /// inline utility::datetime handle_creation_time() const; inline multiplayer_search_handle_details(XblMultiplayerSearchHandle handle); inline multiplayer_search_handle_details(const multiplayer_search_handle_details&); inline multiplayer_search_handle_details& operator=(multiplayer_search_handle_details); inline ~multiplayer_search_handle_details(); private: XblMultiplayerSearchHandle m_handle; }; /// /// Represents a reference to a multiplayer session. It /// contains mostly just ids. /// class multiplayer_session_states { public: /// /// The time that the session began. /// inline utility::datetime start_time() const; /// /// Object containing identifying information for the session. /// inline multiplayer_session_reference session_reference() const; /// /// The current status of the session. /// inline multiplayer_session_status status() const; /// /// The visibility state of the session. Whether other users can see, or join, etc. /// inline multiplayer_session_visibility visibility() const; /// /// Indicates if it is my turn. /// inline bool is_my_turn() const; /// /// Xbox User ID of the member. /// inline string_t xbox_user_id() const; /// /// Approximate number of non-reserved members. /// inline uint32_t accepted_member_count() const; /// /// Join restriction for the session. /// inline multiplayer_session_restriction join_restriction() const; /// /// DEPRECATED - Keywords can be obtained with get_current_session. /// _XSAPICPP_DEPRECATED inline std::vector keywords() const; /// /// Internal function /// inline multiplayer_session_states(_In_ const XblMultiplayerSessionQueryResult& state); private: XblMultiplayerSessionQueryResult m_state; }; /// /// Represents a reference to member in a multiplayer session. /// class multiplayer_session_member { public: /// /// Id for this member. /// inline uint32_t member_id() const; /// /// Initial team assignment from SmartMatch. /// inline string_t initial_team() const; /// /// Xbox User ID of the member. Only known if the member has accepted. /// inline string_t xbox_user_id() const; /// /// JSON string that specify the custom constants for the member. /// inline web::json::value member_custom_constants_json() const; /// /// The base64 encoded secure device address of the member. (Optional) /// inline string_t secure_device_base_address64() const; /// /// A collection of role types to role names for this member. (Optional) /// inline std::unordered_map roles() const; /// /// JSON string that specify the custom properties for the member. /// inline web::json::value member_custom_properties_json() const; /// /// The Gamertag of the member. Only known if the member has accepted. (Optional) /// inline string_t gamertag() const; /// /// The status of this member. /// inline multiplayer_session_member_status status() const; /// /// Only true if this member is ready for turn. /// inline bool is_turn_available() const; /// /// Indicates if this MultiplayerSessionMember is for the current user. /// inline bool is_current_user() const; /// /// Indicates to run QoS initialization for this user. Defaults to false. /// Ignored if there is not a "memberInitialization" section for the session. /// inline bool initialize_requested() const; /// /// When match adds a user to a session, it can provide some context around how and why they were matched into the session. /// This is a copy of the user's serverMeasurements from the matchmaking session. /// inline web::json::value matchmaking_result_server_measurements_json() const; /// /// QoS measurements by game-server connection string. /// Like all fields, "serverMeasurements" must be updated as a whole, so it should be set once when measurement is complete. /// If empty, it means that none of the measurements completed within the "serverMeasurementTimeout". /// inline web::json::value member_server_measurements_json() const; /// /// A collection of members in my group /// If a "initializationGroup" list is set, the member's own index will always be added if it isn't already present. /// During managed initialization, if any members in the list fail, this member will also fail. /// inline std::vector> members_in_group() const; /// /// A collection of members in my group /// If a "initializationGroup" list is set, the member's own index will always be added if it isn't already present. /// During managed initialization, if any members in the list fail, this member will also fail. /// inline std::error_code set_members_list(_In_ const std::vector>& members); /// /// QoS measurements by secure device address. Like all fields, "measurements" must be updated as a whole. It should be set once when measurement is complete, not incrementally. /// If a "measurements" object is set, it can't contain an entry for the member's own address. /// inline std::shared_ptr> member_measurements() const; /// /// This is set when the member uploads a secure device address. It's a case-insensitive string that can be used for equality comparisons. /// inline string_t device_token() const; /// /// This is the device's NAT setting when the member uploads a secure device address. /// inline network_address_translation_setting nat() const; /// /// If the member is active, this is the title ID in which they are active. /// inline uint32_t active_title_id() const; /// /// This value is only useful to read when the title is manually managing their own QoS. /// If the "memberInitialization" section is set and the member was added with "initialize":true, /// this is set to the initialization episode that the member will participate in otherwise it is 0. /// Users join sessions in batches. /// The initialization episode number indicates a set of users that QoS needs to be performed against. /// Initialization episode 1 is a special value used for the members added to a new session at create time. /// inline uint32_t initialization_episode() const; /// /// The time the user joined the session. If "reserved" is true, this is the time the reservation was made. /// inline utility::datetime join_time() const; /// /// The cause of why the initialization failed, or MultiplayerMeasurementFailure::None if there was no failure. /// Set when transitioning out of the "joining" or "measuring" stage if this member doesn't pass. /// inline multiplayer_measurement_failure initialization_failure_cause() const; /// /// Gets or sets a string vector of group names for the current user indicating which groups that user was part of during a multiplayer session. /// inline std::vector groups() const; /// /// Gets or sets a string vector of group names for the current user indicating which groups that user was part of during a multiplayer session. /// inline void set_groups(_In_ const std::vector& groups); /// /// Gets a list of group names for the current user indicating which groups that user encountered during a multiplayer session. /// inline std::vector encounters() const; /// /// Gets a list of group names for the current user indicating which groups that user encountered during a multiplayer session. /// inline void set_encounters(_In_ const std::vector& encounters); /// /// Internal function /// inline multiplayer_session_member(_In_ XblMultiplayerSessionHandle session, _In_ const XblMultiplayerSessionMember* member); inline ~multiplayer_session_member(); private: multiplayer_session_member(const multiplayer_session_member&) = delete; multiplayer_session_member& operator=(multiplayer_session_member) = delete; XblMultiplayerSessionHandle m_session; const XblMultiplayerSessionMember* m_member; }; /// /// Represents multiplayer session properties. /// class multiplayer_session_properties { public: /// /// A collection of keywords associated with the session. (Optional, might be empty) /// When changing, call multiplayer_service::write_session to write the changes to the service. /// inline std::vector keywords() const; /// /// A collection of keywords associated with the session. (Optional, might be empty) /// When changing, call multiplayer_service::write_session to write the changes to the service. /// inline void set_keywords(_In_ const std::vector& keywords); /// /// Restricts who can join "open" sessions. (Has no effect on reservations, which means it has no impact on "private" and "visible" sessions.) /// Defaults to "none". /// If "local", only users whose token's DeviceId matches someone else already in the session and "active": true. /// If "followed", only local users (as defined above) and users who are followed by an existing (not reserved) member of the session can join without a reservation. /// inline multiplayer_session_restriction join_restriction() const; /// /// Restricts who can join "open" sessions. (Has no effect on reservations, which means it has no impact on "private" and "visible" sessions.) /// Defaults to "none". /// If "local", only users whose token's DeviceId matches someone else already in the session and "active": true. /// If "followed", only local users (as defined above) and users who are followed by an existing (not reserved) member of the session can join without a reservation. /// inline std::error_code set_join_restriction(_In_ multiplayer_session_restriction joinRestriction); /// /// Restricts who can read "open" sessions. (Has no effect on reservations, which means it has no impact on "private" and "visible" sessions.) /// Defaults to "none". /// If "local", only users whose token's DeviceId matches someone else already in the session and "active": true. /// If "followed", only local users (as defined above) and users who are followed by an existing (not reserved) member of the session can read without a reservation. /// The read restriction applies to sessions with "open" or "visible" visibility and determines who can read the session without an invite. /// The read restriction must be at least as accessible as the join restriction, i.e. 'joinRestriction' can't be set to "followed" without also setting 'readRestriction'." /// inline multiplayer_session_restriction read_restriction() const; /// /// Restricts who can read "open" sessions. (Has no effect on reservations, which means it has no impact on "private" and "visible" sessions.) /// Defaults to "none". /// If "local", only users whose token's DeviceId matches someone else already in the session and "active": true. /// If "followed", only local users (as defined above) and users who are followed by an existing (not reserved) member of the session can read without a reservation. /// inline std::error_code set_read_restriction(_In_ multiplayer_session_restriction readRestriction); /// /// A collection of MultiplayerSessionMember objects indicating whose turn it is. /// When changing, call multiplayer_service::write_session to write the changes to the service. /// inline std::vector> turn_collection() const; /// /// A collection of MultiplayerSessionMember objects indicating whose turn it is. /// When changing, call multiplayer_service::write_session to write the changes to the service. /// inline std::error_code set_turn_collection(_In_ const std::vector>& turnCollection); /// /// A JSON string representing the target session constants. /// inline web::json::value matchmaking_target_session_constants_json() const; /// /// JSON string that specify the custom properties for the session. These can be changed anytime. /// When changing, call multiplayer_service::write_session to write the changes to the service. /// inline web::json::value session_custom_properties_json() const; /// /// Force a specific connection string to be used. This is useful for session in progress join scenarios. /// inline string_t matchmaking_server_connection_string() const; /// /// The ordered list of connection strings that the session could use to connect to a game server. Generally titles should use the first on /// the list, but sophisticated titles could use a custom mechanism for choosing one of the others (e.g. based on load). /// inline std::vector server_connection_string_candidates() const; /// /// Member index of owners of the session. /// inline std::vector session_owner_indices() const; /// /// Device token of the host. /// Must match the "deviceToken" of at least one member, otherwise this field is deleted. /// If "peerToHostRequirements" is set and "host" is set, the measurement stage assumes the given host is the correct host and only measures metrics to that host. /// inline string_t host_device_token() const; /// /// Controls whether a session is joinable, independent of visibility, joinrestriction, and available space in the session. /// Does not affect reservations. Defaults to false. /// inline bool closed() const; /// /// If true, it would allow the members of the session to be locked, such that if a user leaves they are able to /// come back into the session but no other user could take that spot. Defaults to false. /// inline bool locked() const; /// /// Setting to true by a client triggers a Xbox Live Compute allocation attempt by MPSD. /// Defaults to false. /// inline bool allocate_cloud_compute() const; /// /// Internal function /// inline multiplayer_session_properties(_In_ XblMultiplayerSessionHandle session); inline ~multiplayer_session_properties(); private: multiplayer_session_properties(const multiplayer_session_properties&) = delete; multiplayer_session_properties& operator=(multiplayer_session_properties) = delete; XblMultiplayerSessionHandle m_session; const XblMultiplayerSessionProperties* m_properties; }; /// /// Represents a multiplayer session. /// class multiplayer_session { public: /// /// Call multiplayer_service::write_session after this to write batched local changes to the service. /// If this is called without multiplayer_service::write_session, this will only create a local session /// object but does not commit it to the service. /// Creates a new MultiplayerSession without any constants or session reference. This override is intended /// to be used when the session (serviceconfigid/template/name) are not known. A MultiplayerSession created /// using this constructor must we retrieved/written using the "ByHandle" overrides. /// (e.g. WriteSessionByHandleAsync and GetCurrentSessionByHandleAsync) /// /// The Xbox User ID of the user who is creating this session /// A reference that uniquely identifies the session. inline multiplayer_session( _In_ const string_t& xboxUserId ); /// /// Call multiplayer_service::write_session after this to write batched local changes to the service. /// If this is called without multiplayer_service::write_session, this will only create a local session object but does not commit it to the service. /// Creates a new MultiplayerSession without any constants, which allows the request to simply use whatever constants are already specified in the session /// template on the service. Those constants are returned in the response session data. /// /// The Xbox User ID of the user who is creating this session /// A reference that uniquely identifies the session. inline multiplayer_session( _In_ const string_t& xboxUserId, _In_ multiplayer_session_reference sessionReference ); /// /// Call multiplayer_service::write_session after this to write batched local changes to the service. /// If this is called without multiplayer_service::write_session, this will only create a local session object but does not commit it to the service. /// Creates a new MultiplayerSession using the specified session constants. /// /// The Xbox User ID of the user who is creating this session /// A reference that uniquely identifies the session. /// The maximum number of members in this session. This value can only be set if the maximum is not specified in the title's multiplayer session template. /// If the maximum is specified in the title's multiplayer session template, then set to 0 to ignore this parameter /// The visibility of this session /// A collection of Xbox User IDs indicating who initiated the session (Optional) /// JSON that specify the custom constants for the session. These can not be changed after the session is created. (Optional) inline multiplayer_session( _In_ const string_t& xboxUserId, _In_ multiplayer_session_reference multiplayerSessionReference, _In_ uint32_t maxMembersInSession, _In_ multiplayer_session_visibility multiplayerSessionVisibility, _In_ std::vector initiatorXboxUserIds = std::vector(), _In_ const web::json::value& sessionCustomConstantsJson = web::json::value() ); /// /// A unique ID to the session used to query trace logs for entries that relate to the session. /// inline string_t multiplayer_correlation_id() const; /// /// A unique search handle ID to the session. /// inline string_t search_handle_id() const; /// /// The time that the session began. /// inline utility::datetime start_time() const; /// /// If any timeouts are in progress, this is the date when the next timer will fire. /// inline utility::datetime date_of_next_timer() const; /// /// The date when the server returned the session. /// inline utility::datetime date_of_session() const; /// /// Present during managed initialization. /// The "stage" goes from "joining" to "measuring" to "evaluating". /// If episode 1 fails, then "stage" is set to "failed" and the session cannot be initialized. /// Otherwise, when an initialization episode completes, the "initialization" object is removed. /// If "externalEvaluation" is not set, "evaluating" is skipped. If "metrics" isn't set, "measuring" is skipped. /// inline multiplayer_initialization_stage initialization_stage() const; /// /// The time with the initialization stage started. /// inline utility::datetime initializing_stage_start_time() const; /// /// If MemberInitialization set and Initialize is true on the member, then the member gets assigned to an InitializingEpisode. /// An episode is a set of users that need to have QoS metrics applied to them. /// Will be 0 when the InitializingEpisode is not set. /// This value is only useful when manually managing QoS. /// inline uint32_t intializing_episode() const; /// /// Returns an OR'd set of MultiplayerSessionChangeTypes values representing the aspects of /// the session that the current xboxlivecontext is subscribed to, of None if there is none. /// inline multiplayer_session_change_types subscribed_change_types() const; /// /// Host candidates are an ordered list of device tokens, ordered by preference as specified by MultiplayerSessionConstants::PeerToHostRequirements::HostSelectionMetric. /// inline std::vector host_candidates() const; /// /// The uniquely identifying information for the session. /// inline multiplayer_session_reference session_reference() const; /// /// A set of constants associated with this session. These can only be set when creating the session. /// inline std::shared_ptr session_constants() const; /// /// A set of properties associated with this session. Any player can modify these properties. /// inline std::shared_ptr session_properties() const; /// /// A set of role types associated with this session. /// inline std::shared_ptr session_role_types() const; /// /// A collection of members that are in the session or entering the session together. /// Call MultiplayerSession::Join or MultiplayerSession::Leave to add or remove yourself from this list. /// Call MultiplayerSession::AddMemberReservation to add a reservation for another user on this list. /// Call multiplayer_service::write_session to write these changes to the service. /// inline std::vector> members() const; /// /// A multiplayer session servers that contains properties associated with a target session reference. /// inline multiplayer_session_matchmaking_server matchmaking_server() const; /// /// The number of members that have accepted and are added to the session and are no longer pending. /// inline uint32_t members_accepted() const; /// /// Call multiplayer_service::write_session after this to write batched local changes to the service. /// If this is called without multiplayer_service::write_session, this will only change the local session object but does not commit it to the service. /// A JSON string containing a collection of servers for this multiplayer session. /// inline web::json::value servers_json() const; /// /// Call multiplayer_service::write_session after this to write batched local changes to the service. /// If this is called without multiplayer_service::write_session, this will only change the local session object but does not commit it to the service. /// A JSON string containing a collection of servers for this multiplayer session. /// inline void set_servers_json(_In_ const web::json::value& serversJson); /// /// The ETag returned with this session. /// inline string_t e_tag() const; /// /// Returns the current User in the session. A nullptr will be returned if there is no current user in the session. /// inline std::shared_ptr current_user() const; /// /// The branch of the session used to scope change numbers. /// inline string_t branch() const; /// /// The change number of the session. /// inline uint64_t change_number() const; /// /// On writing a session, the status of the write /// inline write_session_status write_status() const; /// /// Call multiplayer_service::write_session after this to write batched local changes to the service. /// If this is called without multiplayer_service::write_session, this will only change the local session object but does not commit it to the service. /// Add a new member reservation on the session for the specified xboxUserId and member constants. /// /// The Xbox User ID to add a reservation for. /// The custom constants to set for this member. This is the only time the member's constants can be set. (Optional) inline std::error_code add_member_reservation( _In_ const string_t& xboxUserId, _In_ const web::json::value& memberCustomConstantsJson = web::json::value() ); /// /// Call multiplayer_service::write_session after this to write batched local changes to the service. /// If this is called without multiplayer_service::write_session, this will only change the local session object but does not commit it to the service. /// Add a new member reservation on the session for the specified xboxUserId and member constants. /// /// The Xbox User ID to add a reservation for. /// The custom constants to set for this member. This is the only time the member's constants can be set. (Optional) /// True if the system should perform managed initialization, and false otherwise. inline std::error_code add_member_reservation( _In_ const string_t& xboxUserId, _In_ const web::json::value& memberCustomConstantsJson, _In_ bool initializeRequested ); /// /// Joins this user to the session, sets the user to active. /// /// The custom constants to set for this member. /// This is the only time the member's constants can be set. /// True if the system should perform managed /// initialization, and false otherwise. /// True if player should join with active status. /// True to write the 'initialize' property to the request. False otherwise and that means initializeRequested will be ignored. inline xbox_live_result> join( _In_ const web::json::value& memberCustomConstantsJson = web::json::value(), _In_ bool initializeRequested = true, _In_ bool joinWithActiveStatus = true, _In_ bool addInitializePropertyToRequest = true ); /// /// Call multiplayer_service::write_session after this to write batched local changes to the service. /// This can only be set when creating a new session. /// Sets the visibility of the session. /// /// Set the visibility setting of the session. inline void set_visibility(_In_ multiplayer_session_visibility visibility); /// /// Call multiplayer_service::write_session after this to write batched local changes to the service. /// This can only be set when creating a new session. /// Sets the max member count of the session. /// /// Set the max member count of the session. inline void set_max_members_in_session(_In_ uint32_t maxMembersInSession); /// /// Call multiplayer_service::write_session after this to write batched local changes to the service. /// If this is called without multiplayer_service::write_session, this will only change the local session object but does not commit it to the service. /// Sets the max member count per role. /// Note: Only the session owner can modify role settings and only those that are multiplayer_role_type::mutable_role_settings() /// In your session template, you also need to set 'hasOwners' capability and 'ownerManaged' to true for the specific role type /// that you want to modify the mutable_role_setting off. /// /// A map of role type names to multiplayer role type inline std::error_code set_mutable_role_settings( _In_ const std::unordered_map& roleTypes ); /// /// Call multiplayer_service::write_session after this to write batched local changes to the service. /// This can only be set when creating a new session. /// Sets the timeouts for the session. /// /// The timeout for a member reservation, in milliseconds. A value of 0 is allowed and indicates an immediate timeout. If the timeout is not specified, it is considered infinite. /// The timeout for a member to be considered inactive, in milliseconds. A value of 0 is allowed and indicates an immediate timeout. If the timeout is not specified, it is considered infinite. /// The timeout for a member to be considered ready, in milliseconds. A value of 0 is allowed and indicates an immediate timeout. If the timeout is not specified, it is considered infinite. /// The timeout for an empty session, in milliseconds. A value of 0 is allowed and indicates an immediate timeout. If the timeout is not specified, it is considered infinite. inline std::error_code set_timeouts( _In_ std::chrono::milliseconds memberReservedTimeout, _In_ std::chrono::milliseconds memberInactiveTimeout, _In_ std::chrono::milliseconds memberReadyTimeout, _In_ std::chrono::milliseconds sessionEmptyTimeout ); /// /// Call multiplayer_service::write_session after this to write batched local changes to the service. /// This can only be set when creating a new session. /// Enables or disables connectivity metrics for the session. /// For ones that are enabled, they must be sufficient to satisfy the QoS requirements. /// /// True to enable the measuring of latency, and false to disable latency measurement. /// True to enable the measuring of bandwidth down, and false to disable bandwidth down measurement. /// True to enable the measuring of bandwidth up, and false to disable bandwidth up measurement. /// True to enable custom metrics, and false to disable them. inline std::error_code set_quality_of_service_connectivity_metrics( _In_ bool enableLatencyMetric, _In_ bool enableBandwidthDownMetric, _In_ bool enableBandwidthUpMetric, _In_ bool enableCustomMetric ); /// /// Call multiplayer_service::write_session after this to write batched local changes to the service. /// This can only be set when creating a new session. /// If a 'memberInitialization' object is set, the session expects the client system or title to perform /// initialization following session creation and/or as new members join the session. /// The timeouts and initialization stages are automatically tracked by the session, including QoS /// measurements if any metrics are set. These timeouts override the session's reservation and ready /// timeouts for members that have 'initializationEpisode' set. /// /// The period of time, in milliseconds, that the Xbox system waits for a member to join the session. /// The period of time, in milliseconds, that the Xbox system waits for a measuring operation during managed initialization. /// The period of time, in milliseconds, that the Xbox system waits for an evaluation. /// False if the Xbox system should auto-evaluate the session service, and true if the title performs the evaluation. /// The number of members needed to start the session, for initialization episode zero only. inline std::error_code set_member_initialization( _In_ std::chrono::milliseconds joinTimeout, _In_ std::chrono::milliseconds measurementTimeout, _In_ std::chrono::milliseconds evaluationTimeout, _In_ bool externalEvaluation, _In_ uint32_t membersNeededToStart ); /// /// Call multiplayer_service::write_session after this to write batched local changes to the service. /// This can only be set when creating a new session. /// These thresholds apply to each pairwise connection for all members in a session. /// /// The maximum latency, in milliseconds, between session members. /// The minimum bandwidth, in kilobits per second, between members. inline std::error_code set_peer_to_peer_requirements( _In_ std::chrono::milliseconds latencyMaximum, _In_ uint32_t bandwidthMinimumInKilobitsPerSecond ); /// /// Call multiplayer_service::write_session after this to write batched local changes to the service. /// This can only be set when creating a new session. /// These thresholds apply to each connection from a host candidate. /// /// The maximum latency time, in milliseconds. /// The minimum bandwidth, in kilobits per second, for information sent from the host to the session member. /// The minimum bandwidth, in kilobits per second, for information sent from the session member to the host. /// An enumeration value indicating the metric for the Xbox system to use in selecting a host. inline std::error_code set_peer_to_host_requirements( _In_ std::chrono::milliseconds latencyMaximum, _In_ uint32_t bandwidthDownMinimumInKilobitsPerSecond, _In_ uint32_t bandwidthUpMinimumInKilobitsPerSecond, _In_ multiplay_metrics hostSelectionMetric ); /// /// Call multiplayer_service::write_session after this to write batched local changes to the service. /// This can only be set when creating a new session. /// /// A collection of MultiplayerSessionCapabilities flags that apply to the MultiplayerSessionConstant's capabilities JSON object inline std::error_code set_session_capabilities( _In_ const multiplayer_session_capabilities& capabilities ); /// /// Call multiplayer_service::write_session after this to write batched local changes to the service. /// This can only be set when creating a new session. /// Can only be specified if the 'cloudCompute' capability is set. Enables clients to request that a cloud compute instance be allocated on behalf of the session. /// /// Cloud compute instance be allocated on behalf of the session. inline std::error_code set_cloud_compute_package_json( _In_ const web::json::value& sessionCloudComputePackageConstantsJson ); /// /// Call multiplayer_service::write_session after this to write batched local changes to the service. /// This can only be set when creating a new session. /// The set of potential server connection strings that should be evaluated. /// /// True if initialization succeeded, and false otherwise. inline void set_initialization_status( _In_ bool initializationSucceeded ); /// /// Call multiplayer_service::write_session after this to write batched local changes to the service. /// If this is called without multiplayer_service::write_session, this will only change the local session object but does not commit it to the service. /// Sets the device token of the host. /// If "peerToHostRequirements" is set and this is set, the measurement stage assumes the given host is the correct host and only measures metrics to that host. /// /// The host device token. inline void set_host_device_token( _In_ const string_t& hostDeviceToken ); /// /// Call multiplayer_service::write_session after this to write batched local changes to the service. /// If this is called without multiplayer_service::write_session, this will only change the local session object but does not commit it to the service. /// Forces a specific server connection string to be used, useful in preserveSession=always cases. /// /// The server connection path. Setting this path can be useful when the session is preserved. inline void set_matchmaking_server_connection_path( _In_ const string_t& serverConnectionPath ); /// /// Call multiplayer_service::write_session after this to write batched local changes to the service. /// If this is called without multiplayer_service::write_session, this will only change the local session object but does not commit it to the service. /// If set to true, makes the session "closed", meaning that new users will not be able to join unless they already have a reservation. /// inline void set_closed( _In_ bool closed ); /// /// Call multiplayer_service::write_session after this to write batched local changes to the service. /// If this is called without multiplayer_service::write_session, this will only change the local session object but does not commit it to the service. /// If set to true, it would allow the members of the session to be locked, such that if a user leaves they are able to come back into the session but /// no other user could take that spot. If the session is locked, it must also be set to closed. /// inline void set_locked( _In_ bool locked ); /// /// Call multiplayer_service::write_session after this to write batched local changes to the service. /// If this is called without multiplayer_service::write_session, this will only change the local session object but does not commit it to the service. /// If set to true, makes the session "closed", meaning that new users will not be able to join unless they already have a reservation. /// inline void set_allocate_cloud_compute( _In_ bool allocateCloudCompute ); /// /// Call multiplayer_service::write_session after this to write batched local changes to the service. /// If this is called without multiplayer_service::write_session, this will only change the local session object but does not commit it to the service. /// Set matchResubmit to true if the match that was found didn't work out and needs to be resubmitted. /// Set matchResubmit to false to signal that the match did work, and the matchmaking service can release the session. /// /// True if the match that was found was not successful and needs to be resubmitted. inline void set_matchmaking_resubmit( _In_ bool matchResubmit ); /// /// Call multiplayer_service::write_session after this to write batched local changes to the service. /// If this is called without multiplayer_service::write_session, this will only change the local session object but does not commit it to the service. /// The ordered list of case-insensitive connection strings that the session could use to connect to /// a game server. Generally titles should use the first on the list, but sophisticated titles could use /// a custom mechanism for choosing one of the others (e.g. based on load). /// /// The collection of connection paths. inline void set_server_connection_string_candidates( _In_ const std::vector& serverConnectionStringCandidates ); /// /// Call multiplayer_service::write_session after this to write batched local changes to the service. /// If this is called without multiplayer_service::write_session, this will only change the local /// session object but does not commit it to the service. Configures the set of session changes that /// this client will be subscribed to. Set to "MultiplayerSessionChangeTypes::None" to clear the subscription. /// /// Or'd set of MultiplayerSessionChangeType enum values representing the change types to subscribe to. inline std::error_code set_session_change_subscription( _In_ multiplayer_session_change_types changeTypes ); /// /// Call multiplayer_service::write_session after this to write batched local changes to the service. /// If this is called without multiplayer_service::write_session, this will only change the local session object but does not commit it to the service. /// With the user who either created or got the session, leave the session. If the session is deleted as a result of this action, a 204 response with a nullptr for the MultiplayerSession object will be returned. /// inline std::error_code leave(); /// /// Call multiplayer_service::write_session after this to write batched local changes to the service. /// If this is called without multiplayer_service::write_session, this will only change the local session object but does not commit it to the service. /// Set the current user to active or inactive. /// You cannot set the user to reserved or ready in this manner. /// Use AddMemberReservation() to add a member reservation. /// The member must first be joined to the session. /// /// Indicates the current user status. inline std::error_code set_current_user_status( _In_ multiplayer_session_member_status status ); /// /// Call multiplayer_service::write_session after this to write batched local changes to the service. /// If this is called without multiplayer_service::write_session, this will only change the local session object but does not commit it to the service. /// Set the base64 encoded secure device address of the member /// The member must first be joined to the session. /// /// Indicates the value of the current user's secure device address encoded in base64. inline std::error_code set_current_user_secure_device_address_base64( _In_ const string_t& value ); /// /// Call multiplayer_service::write_session after this to write batched local changes to the service. /// If this is called without multiplayer_service::write_session, this will only change the local session object but does not commit it to the service. /// Set the role info of the member. /// The member must first be joined to the session. /// /// Indicates a collection of role types to role names for the current user. inline std::error_code set_current_user_role_info( _In_ const std::unordered_map& roles ); /// /// Call multiplayer_service::write_session after this to write batched local changes to the service. /// If this is called without multiplayer_service::write_session, this will only change the local session object but does not commit it to the service. /// Set a collection of members in the group /// The member must first be joined to the session. /// /// Indicates the value of the current user's secure device address encoded in base64. inline std::error_code set_current_user_members_in_group( _In_ const std::vector>& membersInGroup ); /// /// Call multiplayer_service::write_session after this to write batched local changes to the service. /// If this is called without multiplayer_service::write_session, this will only change the local session object but does not commit it to the service. /// Sets a collection of MultiplayerQualityOfServiceMeasurements for the members. /// This is only useful when the title is manually managing QoS. /// If the platform is automatically performing QoS, this does not need to be called. /// /// A collection of objects representing the QoS measurements. inline std::error_code set_current_user_quality_of_service_measurements( _In_ std::shared_ptr> measurements ); /// /// Call multiplayer_service::write_session after this to write batched local changes to the service. /// If this is called without multiplayer_service::write_session, this will only change the local session object but does not commit it to the service. /// Sets measurements JSON for the servers. /// This is only useful when the title is manually managing QoS. /// If the platform is automatically performing QoS, this does not need to be called. /// /// The JSON that represents the server measurements. inline std::error_code set_current_user_quality_of_service_measurements_json( _In_ const web::json::value& serverMeasurementsJson ); /// /// Call multiplayer_service::write_session after this to write batched local changes to the service. /// If this is called without multiplayer_service::write_session, this will only change the local session object but does not commit it to the service. /// Set a custom property on the current user to the specified JSON string /// The member must first be joined to the session. /// /// The name of the property to set. /// The JSON value to assign to the property. (Optional) inline std::error_code set_current_user_member_custom_property_json( _In_ const string_t& name, _In_ const web::json::value& valueJson = web::json::value() ); /// /// Call multiplayer_service::write_session after this to write batched local changes to the service. /// If this is called without multiplayer_service::write_session, this will only change the local session object but does not commit it to the service. /// Delete a custom property on the current user /// /// The name of the property to set inline std::error_code delete_current_user_member_custom_property_json( _In_ const string_t& name ); /// /// Call multiplayer_service::write_session after this to write batched local changes to the service. /// If this is called without multiplayer_service::write_session, this will only change the local session object but does not commit it to the service. /// Sets the properties of the matchmaking. This should only be set by a client acting as a matchmaking service. /// /// A JSON string representing the target session constants. inline std::error_code set_matchmaking_target_session_constants_json( _In_ const web::json::value& matchmakingTargetSessionConstantsJson ); /// /// Call multiplayer_service::write_session after this to write batched local changes to the service. /// If this is called without multiplayer_service::write_session, this will only change the local session object but does not commit it to the service. /// Set a session custom property to the specified JSON string. /// /// The name of the property to set. /// The JSON value to assign to the property. (Optional) inline std::error_code set_session_custom_property_json( _In_ const string_t& name, _In_ const web::json::value& valueJson = web::json::value() ); /// /// Call multiplayer_service::write_session after this to write batched local changes to the service. /// If this is called without multiplayer_service::write_session, this will only change the local session object but does not commit it to the service. /// Deletes a session custom property. /// /// The name of the property to set. inline std::error_code delete_session_custom_property_json( _In_ const string_t& name ); /// /// Static compare method that allows comparison between 2 sessions and returns a Or'ed MultiplayerSessionChangeType. /// /// A session to compare to the other. /// A session to compare to the other. /// An OR'ed MultiplayerSessionChangeType that contains all of the differences. inline static xbox_live_result compare_multiplayer_sessions( _In_ std::shared_ptr currentSession, _In_ std::shared_ptr oldSession ); /// /// Static method that converts an HTTP Status code to a write_session_status /// /// Status code of a http result /// A write_session_status which gives more specific information about the status code in regards to the Write Session Call inline static write_session_status convert_http_status_to_write_session_status( _In_ int32_t httpStatusCode ); /// /// Internal function. /// inline multiplayer_session(XblMultiplayerSessionHandle handle); inline ~multiplayer_session(); private: multiplayer_session(const multiplayer_session&) = delete; multiplayer_session& operator=(multiplayer_session) = delete; XblMultiplayerSessionHandle m_handle; const XblMultiplayerSessionInfo* m_sessionInfo; mutable std::shared_ptr m_sessionConstants; mutable std::shared_ptr m_sessionProperties; friend class multiplayer_service; }; /// /// Arguments passed to the event handler when a session change occurs. /// class multiplayer_session_change_event_args { public: /// /// The session that triggered this event. /// inline multiplayer_session_reference session_reference() const; /// /// The branch of the session used to scope change numbers. /// inline string_t branch() const; /// /// The change number of the session. /// inline uint64_t change_number() const; /// /// Internal function /// inline multiplayer_session_change_event_args(const XblMultiplayerSessionChangeEventArgs& args); private: XblMultiplayerSessionChangeEventArgs m_args; }; /// /// Gets the visible multiplayer sessions based on the configuration of this request. /// class multiplayer_get_sessions_request { public: /// Creates a GetSessionsRequest object. /// The service configuration id that the sessions part of. /// The maximum number of items to return. inline multiplayer_get_sessions_request( _In_ string_t serviceConfigurationId, _In_ uint32_t maxItems = 0 ); /// /// The service configuration id that the sessions part of. /// inline string_t service_configuration_id(); /// /// The maximum number of items to return. /// inline uint32_t max_items(); /// /// Include private sessions to the result. /// inline bool include_private_sessions(); /// /// Sets the query to include private sessions in the result. /// /// Whether to include private sessions. inline void set_include_private_sessions(_In_ bool includePrivateSessions); /// /// Include sessions that the user hasn't accepted. Must specify xboxUserIdFilter to use. /// inline bool include_reservations(); /// /// Sets the query to include session reservations for members in the result. /// /// Whether to include reservations. inline void set_include_reservations(_In_ bool includeResevations); /// /// Include inactive sessions to the result. Must specify xboxUserIdFilter to use. /// inline bool include_inactive_sessions(); /// /// Sets the query to include inactive sessions in the result. /// /// Whether to include inactive sessions. inline void set_include_inactive_sessions(_In_ bool includeInactiveSessions); /// /// Filter result to just sessions this Xbox User ID in it. /// inline string_t xbox_user_id_filter(); /// /// Sets the xbox user id filter. (The xboxUserIdFilter, xboxUserIdsFilter, or keywordFilter must be specified) /// /// The xbox user id to filter by. inline void set_xbox_user_id_filter(_In_ const string_t& filter); /// /// Filter result to just sessions these Xbox User IDs in it. /// inline std::vector xbox_user_ids_filter(); /// /// Sets the xbox user ids filter. (The xboxUserIdFilter, xboxUserIdsFilter, or keywordFilter must be specified) /// /// The xbox user ids to filter by. inline void set_xbox_user_ids_filter(_In_ const std::vector& filter); /// /// Filter result to just sessions with this keyword. /// inline string_t keyword_filter(); /// /// Sets the xbox user ids filter. (The xboxUserIdFilter, xboxUserIdsFilter, or keywordFilter must be specified) /// /// The keyword to filter by. inline void set_keyword_filter(_In_ const string_t& filter); /// /// The name of the template for the multiplayer session to filter on. /// inline string_t session_template_name_filter(); /// /// Sets the session template to filter by. /// /// The session template name to filter by. inline void set_session_template_name_filter(_In_ const string_t& filter); /// /// Filter result to just sessions with the specified visibility. /// inline multiplayer_session_visibility visibility_filter(); /// /// Sets the session template to filter by. /// /// The session template name to filter by. inline void set_visibility_filter(_In_ multiplayer_session_visibility filter); /// /// Filter result to just sessions with this major version or less of the contract. (use 0 to ignore) /// inline uint32_t contract_version_filter(); /// /// Sets the contract version to filter by. /// /// The contract version to filter by. inline void set_contract_version_filter(_In_ uint32_t filter); private: XblMultiplayerSessionQuery m_request; std::vector m_xuidFilters; std::string m_keywordFilter; friend class multiplayer_service; }; /// /// Queries for the all search handles that references the searchable sessions given the specific query. /// There is no paging or continuation, and the multiplayer service will limit the number of items returned to 100. /// class multiplayer_query_search_handle_request { public: /// Creates a multiplayer_query_search_handle_request object. /// The scid within which to query for search handles. /// The name of the template to query for search handles. /// inline multiplayer_query_search_handle_request( _In_ const string_t& serviceConfigurationId, _In_ const string_t& sessionTemplateName ); /// /// The service configuration id that the sessions part of. /// inline const string_t& service_configuration_id() const; /// /// The name of the template that the sessions part of. /// inline const string_t& session_template_name() const; /// /// The attribute to sort the search handles by. /// inline const string_t& order_by() const; /// /// Specify the attribute to sort the search handles by. /// Valid values are "Timestamp desc", "Timestamp asc" or any Numbers search attribute followed by 'asc or 'desc' (ex: 'Numbers/gamerank asc') /// /// Pass empty string to default to ordering by 'Timestamp asc'. inline void set_order_by(_In_ const string_t& orderBy); /// /// The order to sort the search handles by. /// inline bool order_ascending() const; /// /// Specify the order to sort the search handles by. /// /// Pass true to order ascending, false to order descending inline void set_order_ascending(_In_ bool orderAscending); /// /// The filter to search for. /// inline const string_t& search_filter() const; /// /// Specify the filter to search for. /// The filter syntax is an OData like syntax with only the following operators supported EQ, NE, GE, GT, LE and LT along with the logical operators of AND and OR. /// /// Example 1: /// To search for search handles for a specific XboxUserId use /// "MemberXuids/any(d:d eq '12345678')" or "OwnerXuids/any(d:d eq '12345678')" /// /// Example 2: /// To search for search handles for a title defined string metadata use /// "Strings/stringMetadataType eq 'value'" /// /// Example 3: /// To search for search handles for a title defined numbers metadata AND a tag type value use /// "Numbers/numberMetadataType eq 53 AND Tags/tagType eq 'value'" /// /// The filter string to search for. inline void set_search_filter(_In_ const string_t& searchFilter); /// /// The social group to get the search handles for. /// inline const string_t& social_group() const; /// /// Specify the social group to get the search handles for. /// /// The social group to use in order to get the list of users. (e.g. "people" or "favorites") inline void set_social_group(_In_ const string_t& socialGroup); private: string_t m_scid; string_t m_sessionTemplateName; string_t m_orderBy; bool m_orderAscending{ false }; string_t m_searchFilter; string_t m_socialGroup; }; /// /// Sets the search handle based on the configuration of this request. /// class multiplayer_search_handle_request { public: /// Creates a multiplayer_search_handle_request object. /// The session referenceid that the sessions part of. inline multiplayer_search_handle_request( _In_ multiplayer_session_reference sessionRef ); /// /// The session reference that the sessions part of. /// inline multiplayer_session_reference session_reference() const; /// /// Filter result to just sessions with the tags set. /// inline std::vector tags() const; /// /// Sets the tags to filter by. /// /// The tags to filter by. inline void set_tags(_In_ const std::vector& value); /// /// Filter result to just sessions with the numbers metadata. /// inline std::unordered_map numbers_metadata() const; /// /// Sets the numbers metadata to filter by. /// /// The tags to filter by. inline void set_numbers_metadata(_In_ const std::unordered_map& metadata); /// /// Filter result to just sessions with the strings metadata. /// inline std::unordered_map strings_metadata() const; /// /// Sets the strings metadata to filter by. /// /// The tags to filter by. inline void set_strings_metadata(_In_ const std::unordered_map& metadata); private: XblMultiplayerSessionReference m_sessionRef; std::vector m_tags; std::vector m_stringAttributes; std::vector m_numberAttributes; friend class multiplayer_service; }; /// /// Used to handle interactions with an Xbox Live service endpoint on a server. /// class multiplayer_service { public: /// /// Writes a new or updated multiplayer session to the service. /// The passed multiplayerSession must have a valid multiplayer_session_reference set on it. /// /// A MultiplayerSession object that has been modified with the changes to write. /// The type of write operation /// The async object for notifying when the operation is completed. With the handler, a MultiplayerSession /// object is returned that contains the response returned from the server. Note that if you leave a session that you are the /// the last member of and the sessionEmptyTimeout is equal to 0, then the session will be deleted immediately and a nullptr will be returned. /// Calls V105 PUT /serviceconfigs/{serviceConfigurationId}/sessionTemplates/{sessiontemplateName}/sessions/{sessionName} inline pplx::task>> write_session( _In_ std::shared_ptr multiplayerSession, _In_ multiplayer_session_write_mode writeMode ); /// /// Writes a new or updated multiplayer session to the service, using the specified handle to the session. /// A handle is a service-side pointer to a session. The handleid is a GUID identifier of the handle. Callers will /// usually get the handleid either from another player's MultiplayerActivityDetails, or from an invite. /// /// Use this method only if your MultiplayerSession object doesn't have a multiplayer_session_reference, as /// a handle's lifetime may be shorter than that of the session it points to. /// /// /// A MultiplayerSession object that has been modified with the changes to write /// The type of write operation /// The ID of the handle that should be used when writing the session. /// The async object for notifying when the operation is completed. With the handler, a MultiplayerSession /// object is returned that contains the response returned from the server. The returned MultiplayerSession will /// contain a multiplayer_session_reference, so may be used when calling write_session. Note that if you leave a session that you are the /// the last member of and the sessionEmptyTimeout is equal to 0, then the session will be deleted immediately and a nullptr will be returned. /// Calls V105 PUT /handles/{handleid}/session inline pplx::task>> write_session_by_handle( _In_ std::shared_ptr multiplayerSession, _In_ multiplayer_session_write_mode multiplayerSessionWriteMode, _In_ const string_t& handleId ); /// /// Gets a session object with all its attributes from the server. /// /// A multiplayer_session_reference object containing identifying information for the session. /// The async object for notifying when the operation is completed. With the handler, a MultiplayerSession /// object is returned that contains the response returned from the server. /// Calls V102 GET /serviceconfigs/{serviceConfigurationId}/sessionTemplates/{sessiontemplateName}/sessions/{sessionName} inline pplx::task>> get_current_session( _In_ multiplayer_session_reference sessionReference ); /// /// Gets a session object with all its attributes from the server, given a session handle id. /// A handle is a service-side pointer to a session. The handleid is a GUID identifier of the handle. Callers will /// usually get the handleid either from another player's MultiplayerActivityDetails, or from an invite. /// /// A multiplayer handle id, which uniquely identifies the session. /// The async object for notifying when the operation is completed. With the handler, a MultiplayerSession /// object is returned that contains the response returned from the server. /// Calls GET /handles/{handleId}/session inline pplx::task>> get_current_session_by_handle( _In_ const string_t& handleId ); /// /// Retrieve a list of sessions with various filters /// /// A session request object that sends a query for the session /// The async object for notifying when the operation is completed. With the handler, a collection of /// SessionStates objects are returned where each contains metadata about one session. /// Calls V102 GET /serviceconfigs/{scid}/sessions or /serviceconfigs/{scid}/sessiontemplates/{session-template-name}/sessions inline pplx::task>> get_sessions( _In_ multiplayer_get_sessions_request getSessionsRequest ); /// /// Sets the passed session as the user's current activity, which will be displayed in Xbox /// dashboard user experiences (e.g. friends and gamercard) as associated with the currently /// running title. If the session is joinable, it may also be displayed as such in those /// user experiences. /// /// A multiplayer_session_reference for the session of the activity /// The async object for notifying when the operation is completed. inline pplx::task> set_activity(_In_ multiplayer_session_reference sessionReference); /// /// The access rights the caller has to the origin session are extended to the target session. /// For example, in a title with a lobby session and a game session, the title could put a transfer handle /// linking the lobby to the game inside the lobby session. Users invited to the lobby can use the handle to join the game session as well. /// The transfer handle is deleted once the target session is deleted. /// /// Target multiplayer_session_reference for the session you want to extend the access rights for /// Origin multiplayer_session_reference for the session that grants access to the target session /// The async object for notifying when the operation is completed. This contains the transfer handle ID string. inline pplx::task> set_transfer_handle( _In_ multiplayer_session_reference targetSessionReference, _In_ multiplayer_session_reference originSessionReference ); /// /// Creates a search handle associated with the session. The visibility of the session is dependent on its search handle. /// While you can create an searchable session, it is not queryable and visible to others unless you have the associated search handle committed as well. /// /// A search handle request object for the associated session /// The async object for notifying when the operation is completed. inline pplx::task> set_search_handle( _In_ multiplayer_search_handle_request searchHandleRequest ); /// /// Clears the user's current activity session for the specified serviceConfigurationId /// /// A string containing the serviceConfigurationId in which to clear activity. /// The async object for notifying when the operation is completed. inline pplx::task> clear_activity(_In_ const string_t& serviceConfigurationId); /// /// Clears the search handle that is associated with the session. /// /// The handleId associated with the session to clear. /// The async object for notifying when the operation is completed. inline pplx::task> clear_search_handle(_In_ const string_t& handleId); /// /// Invites the specified users to a session. This will result in a notification being shown to /// each invited user using standard invite text. If a user accepts that notification the title will be activated. /// /// A multiplayer_session_reference object representing the session the target users will be invited to. /// The list of xbox user IDs who will be invited. /// The ID of the title that the invited user will activate in order to join the session. /// The async object for notifying when the operation is completed. This contains a vectorview of handle ID strings corresponding to the invites that have been sent. inline pplx::task>> send_invites( _In_ multiplayer_session_reference sessionReference, _In_ const std::vector& xboxUserIds, _In_ uint32_t titleId ); /// /// Invites the specified users to a session. This will result in a notification being shown to /// each invited user. If a user accepts that notification the title will be activated. /// /// A multiplayer_session_reference object representing the session the target users will be invited to. /// The list of xbox user IDs who will be invited. /// The ID of the title that the invited user will activate in order to join the session. /// The custom context string ID. This string ID is defined /// during Xbox Live ingestion to identify the invitation text that is additional to the standard /// invitation text. The ID string must be prefixed with "///". Pass an empty string if /// you don't want a custom string added to the invite. /// The activation context string. A game defined string that is passed to the invited game client and interpreted as desired. (Optional) /// The async object for notifying when the operation is completed. This contains a vectorview of handle ID strings corresponding to the invites that have been sent. inline pplx::task>> send_invites( _In_ multiplayer_session_reference sessionReference, _In_ const std::vector& xboxUserIds, _In_ uint32_t titleId, _In_ const string_t& contextStringId, _In_ const string_t& customActivationContext ); /// /// Queries for the current activity for a socialgroup of users associated with a particular /// "owner" user. There is no paging or continuation, and the multiplayer service will /// limit the number of items returned to 100. /// /// The scid within which to query for activities. /// The person whose social group will be used for the query. /// The social group to use in order to get the list of users. (e.g. "friends" or "favorites") /// The async object for notifying when the operation is completed. This contains a vectorview of MultiplayerActivityDetails objects, containing the details of the activities of the targeted users. inline pplx::task>> get_activities_for_social_group( _In_ const string_t& serviceConfigurationId, _In_ const string_t& socialGroupOwnerXboxUserId, _In_ const string_t& socialGroup ); /// /// Queries for the current activity for a set of users specified by xuid. /// There is no paging or continuation, and the multiplayer service will limit the /// number of items returned to 100. /// /// The scid within which to query for activities. /// The list of user ids to find activities for. /// The async object for notifying when the operation is completed. This contains a vectorview of MultiplayerActivityDetails objects, containing the details of the activities of the targeted users. inline pplx::task>> get_activities_for_users( _In_ const string_t& serviceConfigurationId, _In_ const std::vector& xboxUserIds ); /// /// Queries for the all search handles that references the searchable sessions given the specific query. /// There is no paging or continuation, and the multiplayer service will limit the number of items returned to 100. /// Call get_search_handles(multiplayer_query_search_handle_request) instead. /// /// The scid within which to query for search handles. /// The name of the template to query for search handles. /// This specifies the attribute to sort the search handles by. Pass empty string to default to ordering by 'Timestamp asc'. (Optional) /// Pass true to order ascending, false to order descending /// The query string to get the search handles for. (Optional) /// The search query. /// The query syntax is an OData like syntax with only the following operators supported EQ, NE, GE, GT, LE and LT along with the logical operators of AND and OR. /// /// Example 1: /// To search for search handles for a specific XboxUserId use /// "MemberXuids/any(d:d eq '12345678')" or "OwnerXuids/any(d:d eq '12345678')" /// /// Example 2: /// To search for search handles for a title defined string metadata use /// "Strings/stringMetadataType eq 'value'" /// /// Example 3: /// To search for search handles for a title defined numbers metadata AND a tag type value use /// "Numbers/numberMetadataType eq 53 AND Tags/tagType eq 'value'" /// /// The async object for notifying when the operation is completed. This contains a vectorview of multiplayer_search_handle_details objects, containing the details of the search handles. inline pplx::task>> get_search_handles( _In_ const string_t& serviceConfigurationId, _In_ const string_t& sessionTemplateName, _In_ const string_t& orderBy, _In_ bool orderAscending, _In_ const string_t& searchFilter ); /// /// Queries for the all search handles that references the searchable sessions given the specific query. /// There is no paging or continuation, and the multiplayer service will limit the number of items returned to 100. /// /// A search handle request object that queries for the all search handles. /// The async object for notifying when the operation is completed. This contains a vectorview of multiplayer_search_handle_details objects, containing the details of the search handles. inline pplx::task>> get_search_handles( _In_ const multiplayer_query_search_handle_request& searchHandleRequest ); /// /// Starts multiplayerservice connectivity via RTA, for two purposes: /// 1. subscriptions to changes on specific sessions, using the MultiplayerSession object. /// 2. automatic removal of members from sessions when the collection underlying this multiplayer subscription is broken. /// This method does not actually make the connection, but enables the connection, and helps track its lifetime. /// This method will fail if called twice, unless the multiplayerconnection has been lost or stopped in the interim. /// This can be detected by listening for the MultiplayerSubscriptionsLost event. /// inline std::error_code enable_multiplayer_subscriptions(); /// /// Stops multiplayerservice connectivity via RTA. /// When stopping multiplayer is complete, a MultiplayerSubscriptionsLost event will fire. /// It is not necessary to wait for this event to fire, unless you intend to call EnableMultiplayerSubscriptions() to re-start it, /// in which case you must wait for the stop to complete. /// inline void disable_multiplayer_subscriptions(); /// /// Indicates whether multiplayer subscriptions are currently enabled. /// inline bool subscriptions_enabled(); /// /// Registers an event handler for notifications when a multiplayer session changes. /// Event handlers receive a multiplayer_session_change_event_args& object. /// /// The callback function that receives notifications. /// /// A function_context object that can be used to unregister the event handler. /// /// /// Session subscriptions are created using the multiplayer_session object within the multiplayer namespace. /// inline function_context add_multiplayer_session_changed_handler(_In_ std::function handler); /// /// Unregisters an event handler for multiplayer session change notifications. /// /// The function_context object that was returned when the event handler was registered. /// The callback function that receives notifications. inline void remove_multiplayer_session_changed_handler(_In_ function_context context); /// /// Registers an event handler for notifications when a multiplayer subscription is lost. /// /// The callback function that receives notifications. /// /// A function_context object that can be used to unregister the event handler. /// /// /// Session subscriptions are created using the multiplayer_session object within the multiplayer namespace. /// inline function_context add_multiplayer_subscription_lost_handler(_In_ std::function handler); /// /// Unregisters an event handler for multiplayer subscription lost notifications. /// /// The function_context object that was returned when the event handler was registered. /// The callback function that receives notifications. inline void remove_multiplayer_subscription_lost_handler(_In_ function_context context); /// /// Registers an event handler for notifications when a multiplayer connection id is changed. /// /// The callback function that receives notifications. /// /// A function_context object that can be used to unregister the event handler. /// /// /// Connection id maps the multiplayer service to rta. /// inline function_context add_multiplayer_connection_id_changed_handler(_In_ std::function handler); /// /// Unregisters an event handler for multiplayer connection id changed notifications. /// /// The function_context object that was returned when the event handler was registered. /// The callback function that receives notifications. inline void remove_multiplayer_connection_id_changed_handler(_In_ function_context context); inline multiplayer_service(const multiplayer_service& other); inline multiplayer_service& operator=(multiplayer_service other); inline ~multiplayer_service(); private: inline multiplayer_service(_In_ XblContextHandle contextHandle); XblContextHandle m_handle; struct HandlerContext; friend xbox_live_context; }; }}} #include "impl/multiplayer.hpp" #endif ================================================ FILE: Include/xsapi-cpp/multiplayer_manager.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include #include "pplx/pplxtasks.h" #include "xsapi-cpp/types.h" #include "xsapi-c/multiplayer_manager_c.h" typedef void* context_t; namespace xbox { namespace services { /// /// Contains classes related to multiplayer. /// namespace multiplayer { /// /// Contains classes and enumerations for more easily managing multiplayer /// scenarios. /// namespace manager { /// /// Defines values used to indicate who can join your lobby. /// enum class joinability { /// /// Joinability not set or no lobby exists yet. /// none, /// /// Default value. The lobby is joinable by users who are followed by an existing member of the session. /// joinable_by_friends, /// /// The lobby is joinable only via an invite. /// invite_only, /// /// This option will close the lobby only when a game is in progress. All other times, /// it will keep the lobby open for invite_only so invitees can join when no game is in progress. /// disable_while_game_in_progress, /// /// This option will close the lobby immediately. /// closed }; /// /// Defines values used to indicate status for the matchmaking stages. /// enum match_status { /// /// Indicates no matchmaking search has been started. /// none, /// /// Indicates that a match ticket was submitted for matchmaking. /// submitting_match_ticket, /// /// Indicates that matchmaking is still searching. /// searching, /// /// Indicates that matchmaking search has found a match. /// found, /// /// Joining initialization stage. /// Matchmaking creates the game session and adds users to it. /// The client has up to the joining timeout to join the session during this phase. /// joining, /// /// Waiting for remote clients to join the game session. /// The client has up to the joining timeout to join the session during this phase. /// waiting_for_remote_clients_to_join, /// /// Measuring initialization stage. /// Stage where QoS measurement happens. /// The client has up to the measurement timeout to upload qos measurements to the service during this phase. /// measuring, /// /// Uploading QoS measurement results to the service. /// The client has up to the measurement timeout to upload qos measurements to the service during this phase. /// uploading_qos_measurements, /// /// Waiting for remote clients to upload QoS measurement results to the service. /// The client has up to the measurement timeout to upload qos measurements to the service during this phase. /// waiting_for_remote_clients_to_upload_qos, /// /// Evaluating initialization stage. /// If auto evaluate is true, then this stage is skipped. /// Otherwise the title will do its own evaluation. /// evaluating, /// /// Match was found and QoS measurement was successful. /// completed, /// /// If the match that was found was not successful and is resubmitting. /// resubmitting, /// /// Indicates that matchmaking search has expired. /// expired, /// /// Indicates that matchmaking is in the process of canceling the search. /// canceling, /// /// Indicates that matchmaking search has been canceled. /// canceled, /// /// Failed initialization stage. /// The initialization failed. /// failed, }; /// /// Defines values used to indicate event types for a multiplayer lobby or game. /// enum class multiplayer_event_type { /// /// Indicates the user was added. /// user_added, /// /// Indicates the user was removed. /// user_removed, /// /// Indicates a new member has joined the session. /// The event_args object should be cast to a /// member_joined_event_args object for more information. /// member_joined, /// /// Indicates a member has left the session. /// The event_args object should be cast to a /// member_left_event_args object for more information. /// member_left, /// /// Indicates a member property has changed. /// The event_args object should be cast to a /// member_property_changed_event_args object for more information. /// member_property_changed, /// /// Indicates that the set_local_member_properties() or delete_local_member_properties() operation has completed. /// Upon completion, the game can view the err() to see if the write succeeded. /// A game can be write local member properties by calling the set_local_member_properties() operation. /// local_member_property_write_completed, /// /// Indicates that the set_local_member_connection_address() operation has completed. /// Upon completion, the game can view the err() to see if the write succeeded. /// A game can be write local member properties by calling the set_local_member_connection_address() operation. /// local_member_connection_address_write_completed, /// /// Indicates a session (lobby or game) property has changed. /// The event_args object should be cast to a /// session_property_changed_event_args object for more information. /// session_property_changed, /// /// Indicates that the set_synchronized_properties() operation has completed. /// Upon completion, the game can view the err() to see if the write succeeded. /// A game can be write synchronized properties by calling the set_properties() operation. /// session_property_write_completed, /// /// Indicates that the set_synchronized_properties() operation has completed. /// Upon completion, the game can view the err() to see if the write succeeded. /// A game can be write synchronized properties by calling the set_synchronized_properties() operation. /// session_synchronized_property_write_completed, /// /// Indicates host has changed. /// The event_args object should be cast to a /// host_changed_event_args object for more information. /// host_changed, /// /// Indicates that the set_synchronized_host() operation has completed. /// Upon completion, the game can view the err() to see if the write succeeded. /// A game can be write synchronized host by calling the set_synchronized_host() operation. /// synchronized_host_write_completed, /// /// Indicates that the joinability value has changed. /// A game can be change the state by calling the set_joinability() operation. /// joinability_state_changed, /// /// Fired when a match has been found, and the client has joined the target game session. /// When this event occurs, title should provide qos measurement results (via set_quality_of_service_measurements) between itself and a list of remote clients. /// Note: If your title does not require QoS (based on the session template), this event will not be triggered. /// perform_qos_measurements, /// /// Indicates that the find_match() operation has completed. /// The event_args object should be cast to a /// find_match_completed_event_args object for more information. /// find_match_completed, /// /// Indicates that the join_game() operation has completed. Once the join succeeds, /// the member is now part of the game session, and can use data in the session /// to connect to other game members. /// join_game_completed, /// /// Indicates that the leave_game() operation has completed. After receiving this event, /// the game session object will be set to null. You can join another game by calling /// join_game() or join_game_from_lobby(). /// leave_game_completed, /// /// Indicates that the join_lobby() operation has completed. Once the join succeeds, /// the member is now part of the lobby session, and can use data in the session /// to connect to other lobby members. The event_args object should be cast to a /// join_lobby_completed_event_args object for more information. /// join_lobby_completed, /// /// Fired when the title's connection to MPSD using the real-time activity service is lost. /// When this event occurs, the title should shut down the multiplayer. /// client_disconnected_from_multiplayer_service, /// /// Indicates that the invite API operation has been completed. /// invite_sent }; /// /// Defines values used to indicate types for multiplayer sessions. /// enum class multiplayer_session_type { /// /// The session type is unknown. /// unknown, /// /// Indicates multiplayer lobby session. /// lobby_session, /// /// Indicates multiplayer game session. /// game_session, /// /// Indicates multiplayer match session. /// match_session }; /// /// Represents a reference to a member in a multiplayer game. /// class multiplayer_member { public: /// /// Internal function /// inline multiplayer_member(_In_ const XblMultiplayerManagerMember& internalMember) : m_internalMember(internalMember) {} /// /// Id for the member. /// inline uint32_t member_id() const; /// /// Only applicable if you are using Team rules with Smart Match. /// Initial team assignment suggested by Smart Match. /// inline string_t initial_team() const; /// /// Xbox User ID of the member. /// inline string_t xbox_user_id() const; /// /// The Gamertag of the member. This is only to be used for debugging purposes as this gamertag may be out of date. /// It is recommended you use social manager or the profile service to get this information. /// inline string_t debug_gamertag() const; /// /// Indicates if this member is playing on the local device. /// inline bool is_local() const; /// /// Indicates if this member is part of the lobby. /// inline bool is_in_lobby() const; /// /// Indicates if this member is part of the game. /// inline bool is_in_game() const; /// /// The status of this member. /// inline xbox::services::multiplayer::multiplayer_session_member_status status() const; /// /// The address used for network connection. /// This can be used for secure socket connection. /// inline string_t connection_address() const; /// /// JSON value that specify the custom properties of the member. /// inline web::json::value properties() const; /// /// Determines whether the member is on the same device. /// inline bool is_member_on_same_device(_In_ std::shared_ptr member) const; private: const XblMultiplayerManagerMember m_internalMember; friend class multiplayer_lobby_session; friend class multiplayer_game_session; }; /// /// Represents a multiplayer lobby. This is also where you would manage members that are local to this device. /// There are 2 game objects when using a multiplayer manager. One represents the lobby_session() which is where friends you invite will join. /// Another is the game_session() which contains people that your lobby has been matched with. /// class multiplayer_lobby_session { public: /// /// A unique ID to the session used to query trace logs for entries that relate to the session. /// inline string_t correlation_id() const; /// /// Object containing identifying information for the session. /// inline xbox::services::multiplayer::multiplayer_session_reference session_reference() const; /// /// A collection of members that are local to this device. /// inline std::vector> local_members() const; /// /// A collection of members that are in the lobby. When a friend accepts a game invite, /// members will be added to the lobby. /// inline std::vector> members() const; /// /// Returns the host member for the lobby. /// The host is defined as the user with the lowest index on the host device. /// inline std::shared_ptr host() const; /// /// JSON string that specify the custom properties for the game. These can be changed anytime. /// inline web::json::value properties() const; /// /// A set of constants associated with this session. These can only be set through the session template. /// inline const std::shared_ptr session_constants() const; /// /// Changes are batched and written to the service on the next do_work(). All session properties and members /// contain updated response returned from the server upon calling do_work(). /// Hosts a new lobby when the first user is added. For all other users, they will be added to the existing lobby /// as secondary users. This API will also advertise the lobby for friends to join. /// You can send invites, set lobby properties, access lobby members via lobby() only once you've added the local user. /// While joining a lobby via an invite, or a handleId, you can skip adding the local user to avoid creating a lobby and /// instead pass in the list of users in the join_lobby() API. /// /// The associated system User. inline xbox_live_result add_local_user( _In_ xbox_live_user_t user ); /// /// Changes are batched and written to the service on the next do_work(). All session properties and members /// contain updated response returned from the server upon calling do_work(). /// Removes the local user from the lobby and game session. /// After this method is called, if no local users are active, title will not be able to perform any further multiplayer operations. /// You can join another game or re-add the local user. /// /// The associated system User. inline xbox_live_result remove_local_user( _In_ xbox_live_user_t user ); /// /// Set a custom property on the local member to the specified JSON string /// Changes are batched and written to the service on the next do_work(). All session properties and members /// contain updated response returned from the server upon calling do_work(). /// The result is delivered via multiplayer_event callback of type local_member_property_write_completed through do_work(). /// /// The associated system User you want to set the property for. /// The name of the property to set. /// The JSON value to assign to the property. (Optional) /// The application-defined data to correlate the multiplayer_event to the initiating call. (Optional) inline xbox_live_result set_local_member_properties( _In_ xbox_live_user_t user, _In_ const string_t& name, _In_ const web::json::value& valueJson, _In_opt_ context_t context = nullptr ); /// /// Delete a custom property on the local member /// Changes are batched and written to the service on the next do_work(). All session properties and members /// contain updated response returned from the server upon calling do_work(). /// The result is delivered via multiplayer_event callback of type local_member_property_write_completed through do_work(). /// /// The associated system User you want to delete the property for. /// The name of the property to delete /// The application-defined data to correlate the multiplayer_event to the initiating call. (Optional) inline xbox_live_result delete_local_member_properties( _In_ xbox_live_user_t user, _In_ const string_t& name, _In_opt_ context_t context = nullptr ); /// /// Set connection address for the local member. The address can be used for network and secure socket connection. /// Changes are batched and written to the service on the next do_work(). All session properties and members /// contain updated response returned from the server upon calling do_work(). /// The result is delivered via multiplayer_event callback of type local_member_connection_address_write_completed through do_work(). /// /// The associated system User you want to set the property for. /// The network connection address to set. /// The application-defined data to correlate the multiplayer_event to the initiating call. (Optional) inline xbox_live_result set_local_member_connection_address( _In_ xbox_live_user_t user, _In_ const string_t& connectionAddress, _In_opt_ context_t context = nullptr ); /// /// Whether or not the Xbox User ID is the host. /// /// The Xbox User ID of the user inline bool is_host( _In_ const string_t& xboxUserId ); /// /// Set a custom game property to the specified JSON string. /// Changes are batched and written to the service on the next do_work(). All session properties and members /// contain updated response returned from the server upon calling do_work(). /// The result is delivered via multiplayer_event callback of type session_property_write_completed through do_work(). /// /// The name of the property to set. /// The JSON value to assign to the property. (Optional) /// The application-defined data to correlate the multiplayer_event to the initiating call. (Optional) inline xbox_live_result set_properties( _In_ const string_t& name, _In_ const web::json::value& valueJson, _In_opt_ context_t context = nullptr ); /// /// Sets a custom property to the specified JSON string using multiplayer_session_write_mode::synchronized_update. /// Use this method to resolve any conflicts between devices while trying to set properties to a shared portion that other /// devices can also modify. It ensures that updates to the session are atomic. If writing to non-sharable properties, use set_properties() instead. /// The service may reject your request if a race condition occurred (due to a conflict) resulting in error_code /// http_status_412_precondition_failed (HTTP status 412). To resolve this, evaluate the need to write again and re-submit if needed. /// The result is delivered via multiplayer_event callback of type session_synchronized_property_write_completed through do_work(). /// /// The name of the property to set. /// The JSON value to assign to the property. (Optional) /// The application-defined data to correlate the multiplayer_event to the initiating call. (Optional) inline xbox_live_result set_synchronized_properties( _In_ const string_t& name, _In_ const web::json::value& valueJson, _In_opt_ context_t context = nullptr ); /// /// Sets the host for the game using multiplayer_session_write_mode::synchronized_update. Use this method to resolve /// any conflicts between devices trying to set the host at the same time. It ensures that updates to the session are atomic. /// The service may reject your request if a race condition occurred(due to a conflict) resulting in error_code /// http_status_412_precondition_failed (HTTP status 412). To resolve this, evaluate the need to write again and re-submit if needed. /// The result is delivered via multiplayer_event callback of type synchronized_host_write_completed through do_work(). /// /// The host member. /// The application-defined data to correlate the multiplayer_event to the initiating call. (Optional) inline xbox_live_result set_synchronized_host( _In_ std::shared_ptr gameHost, _In_opt_ context_t context = nullptr ); #if HC_PLATFORM_IS_MICROSOFT /// /// Displays the invite UI and allows the user to select people from the user's people list and invite them to join the user's party /// If a user accepts that notification the title will be activated. /// /// The associated system User. /// The custom context string ID. This string ID is defined /// during Xbox Live ingestion to identify the invitation text that is additional to the standard /// invitation text. The ID string must be prefixed with "///". (Optional) /// The activation context string. A game defined string that is passed to the invited game client and interpreted as desired. (Optional) inline xbox_live_result invite_friends( _In_ xbox_live_user_t user, _In_ const string_t& contextStringId = string_t(), _In_ const string_t& customActivationContext = string_t() ); #endif /// /// Invites the specified users to a game. This will result in a notification being shown to /// each invited user using standard invite text. If a user accepts that notification the title will be activated. /// /// The associated system User. /// The list of xbox user IDs who will be invited. /// The custom context string ID. This string ID is defined /// during Xbox Live ingestion to identify the invitation text that is additional to the standard /// invitation text. The ID string must be prefixed with "///". (Optional) /// The activation context string. A game defined string that is passed to the invited game client and interpreted as desired. (Optional) /// The async object for notifying when the operation is completed. inline xbox_live_result invite_users( _In_ xbox_live_user_t user, _In_ const std::vector& xboxUserIds, _In_ const string_t& contextStringId = string_t(), _In_ const string_t& customActivationContext = string_t() ); private: mutable std::shared_ptr m_sessionConstants; }; /// /// Represents a multiplayer game. /// There are 2 game objects when using a multiplayer manager. One represents the lobby_session() which is where friends you invite will join. /// Another is the game_session() which contains people that your lobby has been matched with. /// class multiplayer_game_session { public: /// /// A unique ID to the session used to query trace logs for entries that relate to the session. /// inline string_t correlation_id() const; /// /// Object containing identifying information for the session. /// inline xbox::services::multiplayer::multiplayer_session_reference session_reference() const; /// /// A collection of members that are in the game. When a friend accepts a game invite, /// members will be added to the lobby and the game session members list. /// inline std::vector> members() const; /// /// Returns the host member for the game. /// The host is defined as the user with the lowest index on the host device. /// inline std::shared_ptr host() const; /// /// JSON string that specify the custom properties for the game. These can be changed anytime. /// inline web::json::value properties() const; /// /// A set of constants associated with this session. These can only be set through the session template. /// inline const std::shared_ptr session_constants() const; /// /// Whether or not the Xbox User ID is the host. /// /// The Xbox User ID of the user inline bool is_host( _In_ const string_t& xboxUserId ); /// /// Set a custom game property to the specified JSON string. /// Changes are batched and written to the service on the next do_work(). All session properties and members /// contain updated response returned from the server upon calling do_work(). /// /// The name of the property to set. /// The JSON value to assign to the property. (Optional) /// The application-defined data to correlate the multiplayer_event to the initiating call. (Optional) inline xbox_live_result set_properties( _In_ const string_t& name, _In_ const web::json::value& valueJson, _In_opt_ context_t context = nullptr ); /// /// Sets a custom property to the specified JSON string using multiplayer_session_write_mode::synchronized_update. /// Use this method to resolve any conflicts between devices while trying to set properties to a shared portion that other /// devices can also modify. It ensures that updates to the session are atomic. If writing to non-sharable properties, use set_properties() instead. /// The service may reject your request if a race condition occurred (due to a conflict) resulting in error_code /// http_status_412_precondition_failed (HTTP status 412). To resolve this, evaluate the need to write again and re-submit if needed. /// The result is delivered via multiplayer_event callback of type synchronized_property_write_completed through do_work(). /// /// The name of the property to set. /// The JSON value to assign to the property. (Optional) /// The application-defined data to correlate the multiplayer_event to the initiating call. (Optional) inline xbox_live_result set_synchronized_properties( _In_ const string_t& name, _In_ const web::json::value& valueJson, _In_opt_ context_t context = nullptr ); /// /// Sets the host for the game using multiplayer_session_write_mode::synchronized_update. Use this method to resolve /// any conflicts between devices trying to set the host at the same time. It ensures that updates to the session are atomic. /// The service may reject your request if a race condition occurred(due to a conflict) resulting in error_code /// http_status_412_precondition_failed (HTTP status 412). To resolve this, evaluate the need to write again and re-submit if needed. /// The result is delivered via multiplayer_event callback of type synchronized_host_write_completed through do_work(). /// /// The host member. /// The application-defined data to correlate the multiplayer_event to the initiating call. (Optional) inline xbox_live_result set_synchronized_host( _In_ std::shared_ptr gameHost, _In_opt_ context_t context = nullptr ); private: mutable std::shared_ptr m_sessionConstants; }; class multiplayer_event_args { public: inline multiplayer_event_args(XblMultiplayerEventArgsHandle argsHandle); inline virtual ~multiplayer_event_args(); protected: inline string_t GetXuid() const; inline std::vector> GetMembers() const; inline std::shared_ptr GetMember() const; inline web::json::value GetPropertiesJson() const; XblMultiplayerEventArgsHandle m_argsHandle; }; /// /// Notifies the title when a new user was added. /// class user_added_event_args : public multiplayer_event_args { public: /// /// Internal function /// user_added_event_args(XblMultiplayerEventArgsHandle handle) : multiplayer_event_args(handle) {} /// /// Xbox User ID of the member that that was added. /// string_t xbox_user_id() const { return GetXuid(); } }; /// /// Notifies the title when a user was removed. /// class user_removed_event_args : public multiplayer_event_args { public: /// /// Internal function /// user_removed_event_args(XblMultiplayerEventArgsHandle handle) : multiplayer_event_args(handle) {} /// /// Xbox User ID of the member that that was removed. /// string_t xbox_user_id() const { return GetXuid(); } }; /// /// Notifies the title when a new game member joins the game. /// class member_joined_event_args : public multiplayer_event_args { public: /// /// Internal function /// member_joined_event_args(XblMultiplayerEventArgsHandle handle) : multiplayer_event_args(handle) {} /// /// A list of members that joined the game. /// std::vector> members() { return GetMembers(); } }; /// /// Notifies the title when an existing game member leaves the game. /// class member_left_event_args : public multiplayer_event_args { public: /// /// Internal function /// member_left_event_args(XblMultiplayerEventArgsHandle handle) : multiplayer_event_args(handle) {} /// /// A list of members that left the game. /// std::vector> members() { return GetMembers(); } }; /// /// Notifies the title when a new host member has been set. /// class host_changed_event_args : public multiplayer_event_args { public: /// /// Internal function /// host_changed_event_args(XblMultiplayerEventArgsHandle handle) : multiplayer_event_args(handle) {} /// /// The new host member. If an existing host leaves, the host_member() will be nullptr. /// std::shared_ptr host_member() { return GetMember(); } }; /// /// Notifies the title when a game member property has been added or modified. /// class member_property_changed_event_args : public multiplayer_event_args { public: /// /// Internal function /// member_property_changed_event_args(XblMultiplayerEventArgsHandle handle) : multiplayer_event_args(handle) {} /// /// The member whose property changed. /// std::shared_ptr member() { return GetMember(); } /// /// The JSON of the property that changed. /// web::json::value properties() { return GetPropertiesJson(); } }; /// /// Notifies the title when a session property has been added or modified. /// class session_property_changed_event_args : public multiplayer_event_args { public: /// /// Internal function /// session_property_changed_event_args(XblMultiplayerEventArgsHandle handle) : multiplayer_event_args(handle) {} /// /// The JSON of the property that changed. /// web::json::value properties() { return GetPropertiesJson(); } }; /// /// Notifies the title when the client joins a lobby. Once join succeeds, the member is now /// part of the lobby session, and can use data in the session to connect to other lobby members. /// To join a friend's lobby, call join_lobby(handleId) operation using the handleId you back get from /// multiplayer_service::get_activities_for_social_group. If the user accepts an invite or joined a friends' game via the shell, /// the title will get protocol activated, in which case you should call join_lobby(IProtocolActivatedEventArgs^). /// /// For scenarios where the local user has not been added, you can pass the local user object part of the join_lobby API. /// If the invited user is not added via add_local_user() or through join_lobby, then join_lobby() will fail and /// provide the invited_xbox_user_id() that the invite was sent for as part of the join_lobby_completed_event_args(). /// class join_lobby_completed_event_args : public multiplayer_event_args { public: /// /// Internal function /// join_lobby_completed_event_args(XblMultiplayerEventArgsHandle handle) : multiplayer_event_args(handle) {} /// /// Invited Xbox User ID of the member that the invite was sent for. /// string_t invited_xbox_user_id() const { return GetXuid(); } }; /// /// Contains information for an event that indicates when a multiplayer match is found. /// class find_match_completed_event_args : public multiplayer_event_args { public: find_match_completed_event_args(XblMultiplayerEventArgsHandle handle) : multiplayer_event_args(handle) {} /// /// Provides the current matchmaking status. /// inline xbox::services::multiplayer::manager::match_status match_status() const; /// /// The cause of why the initialization failed, or multiplayer_measurement_failure::None if there was no failure. /// Set when transitioning out of the "joining" or "measuring" stage if this member doesn't pass. /// inline xbox::services::multiplayer::multiplayer_measurement_failure initialization_failure_cause() const; }; /// /// Notifies the title when it should provide qos measurement results between itself and a list of remote clients. /// class perform_qos_measurements_event_args : public multiplayer_event_args { public: /// /// Internal function /// perform_qos_measurements_event_args(XblMultiplayerEventArgsHandle handle) : multiplayer_event_args(handle) {} /// /// A map of connection addresses and device tokens to run qos on. /// inline std::map connection_address_to_device_tokens() const; }; /// /// Base class for all event arg classes. Based on the multiplayer_event_type, you would need to cast the args /// to the appropriate class. Note that the event will only be valid until the next call to multiplayer_manager::do_work(). /// class multiplayer_event { public: /// /// Internal function /// inline multiplayer_event(_In_ const XblMultiplayerEvent* internalEvent); /// /// The error code indicating the result of the operation. /// inline std::error_code err() const; /// /// Returns call specific debug information if join fails /// It is not localized, so only use for debugging purposes. /// inline std::string err_message() const; /// /// A pointer to the application-defined data passed into the initiating method. /// context_t inline context(); /// /// Type of the event triggered. /// inline multiplayer_event_type event_type() const; /// /// You need to cast this to one of the event arg classes to retrieve the data for that particular event. /// inline std::shared_ptr event_args(); /// /// The multiplayer session type this event was triggered for. Depending upon the session type, /// you can then retrieve the latest lobby or game session from the multiplayer_manager class. /// inline multiplayer_session_type session_type() const; private: const XblMultiplayerEvent* m_internalEvent; std::shared_ptr m_eventArgs; }; /// /// APIs for matchmaking, player roster and multiplayer session management. /// class multiplayer_manager { public: /// /// Gets the multiplayer_manager singleton instance /// inline static std::shared_ptr get_singleton_instance(); /// /// Initializes the object. /// /// The name of the template for the lobby session to be based on. inline void initialize( _In_ const string_t& lobbySessionTemplateName ); /// /// Ensures proper game state updates are maintained between the title and the Xbox Live Multiplayer Service. /// To ensure best performance, do_work() must be called frequently, such as once per frame. /// Title needs to be thread safe when calling do_work() since this is when the state will change. /// For example, if you looping through the list of members() on a different thread than your calling do_work() on, /// it may change when do_work() is called. /// /// A list of all callback events for the game to handle. Empty if no events are triggered during this update. inline std::vector do_work(); /// /// Represents a lobby session used for managing members that are local to this device and your invited friends. /// When a member accepts a game invite, they will be added to the lobby and the game session (if it exists). /// Users found via matchmaking will not be added in the lobby. /// inline std::shared_ptr lobby_session() const; /// /// Represents a game session used for your active gameplay. When a member accepts an invite, /// they will be added to the lobby and the game session (if there is room). /// You can call leave_game() to leave the game session. /// inline std::shared_ptr game_session() const; /// /// Joins a game given a session handle id. A handle is a service-side pointer to a session. /// The handleId is a GUID identifier of the handle. Callers will usually get the handleId from /// another member's multiplayer_activity_details. Optionally, if you haven't added the local users through /// lobby_session()::add_local_user(), you can pass in the list of users via the join_lobby() API. /// The result is delivered via multiplayer_event callback of type join_lobby_completed through do_work(). /// After joining, you can set the host via set_synchronized_host if one doesn't exist. /// /// A multiplayer handle id, which uniquely identifies the game session you want to join. /// The system User joining the lobby. inline xbox_live_result join_lobby( _In_ const string_t& handleId, _In_ xbox_live_user_t user ); #if (HC_PLATFORM == HC_PLATFORM_XDK || HC_PLATFORM == HC_PLATFORM_UWP) /// /// Joins a game via the specified IProtocolActivatedEventArgs. /// The IProtocolActivatedEventArgs provides arguments for protocol activation. If the user accepts an invite or /// joined a friends game via a shell UI, the title will get a protocol activation. The result is delivered via /// join_lobby_completed_event_args() callback through do_work(). /// Optionally, if you haven't added the local users through lobby_session()::add_local_user(), you can pass in /// the list of users via the join_lobby() API. If the invited user is not added either via /// lobby_session()::add_local_user() or through join_lobby(), then join_lobby() will fail and provide the /// invited_xbox_user_id() that the invite was sent for as part of the join_lobby_completed_event_args() /// After joining, you can set the host via set_synchronized_host if one doesn't exist. /// /// The IProtocolActivatedEventArgs when the title is protocol activated. /// The system User joining the lobby. inline xbox_live_result join_lobby( _In_ Windows::ApplicationModel::Activation::IProtocolActivatedEventArgs^ eventArgs, _In_ xbox_live_user_t user ); #endif #if HC_PLATFORM == HC_PLATFORM_XDK /// /// Joins a game given a session handle id. A handle is a service-side pointer to a game session. /// The handleId is a GUID identifier of the handle. Callers will usually get the handleId from /// another member's multiplayer_activity_details. Optionally, if you haven't added the local users through /// lobby_session()::add_local_user(), you can pass in the list of users via the join_lobby() API. /// The result is delivered via multiplayer_event callback of type join_lobby_completed through do_work(). /// After joining, you can set the host via set_synchronized_host if one doesn't exist. /// /// A multiplayer handle id, which uniquely identifies the game session you want to join. /// List of system Users joining the lobby. inline xbox_live_result join_lobby( _In_ const string_t& handleId, _In_ std::vector users ); /// /// Joins a game via the specified IProtocolActivatedEventArgs. /// The IProtocolActivatedEventArgs provides arguments for protocol activation. If the user accepts an invite or /// joined a friends game via a shell UI, the title will get a protocol activation. The result is delivered via /// join_lobby_completed_event_args() callback through do_work(). /// Optionally, if you haven't added the local users through lobby_session()::add_local_user(), you can pass in /// the list of users via the join_lobby() API. If the invited user is not added either via /// lobby_session()::add_local_user() or through join_lobby(), then join_lobby() will fail and provide the /// invited_xbox_user_id() that the invite was sent for as part of the join_lobby_completed_event_args() /// After joining, you can set the host via set_synchronized_host if one doesn't exist. /// /// The IProtocolActivatedEventArgs when the title is protocol activated. /// List of system Users joining the lobby. inline xbox_live_result join_lobby( _In_ Windows::ApplicationModel::Activation::IProtocolActivatedEventArgs^ eventArgs, _In_ std::vector users ); /// /// Send invites to your party to join your game. /// inline void invite_party_to_game(); #endif /// /// Join the lobby's game session if one exists and if there is room. If the session doesn't exist, it creates a new game session /// with the existing lobby members. The result is delivered via multiplayer_event callback of /// type join_game_completed through do_work(). This does not migrate existing lobby session properties over to the game session. /// After joining, you can set the properties or the host via game_session()::set_synchronized APIs. /// /// The name of the template for the game session to be based on. inline xbox_live_result join_game_from_lobby( _In_ const string_t& sessionTemplateName ); /// /// Joins a game given a globally unique session name. Callers can get the unique session name /// as a result of the title's third party matchmaking. Call this on all clients that needs to join this game. /// The result is delivered via multiplayer_event callback of type join_game_completed through do_work(). /// After joining, you can set the properties or the host via game_session()::set_synchronized APIs. /// /// A unique name for the session. /// The name of the template for the game session to be based on. /// The list of xbox user IDs you want to be part of the game. inline xbox_live_result join_game( _In_ const string_t& sessionName, _In_ const string_t& sessionTemplateName, _In_ const std::vector& xboxUserIds = std::vector() ); /// /// Leaving the game will put you back into the lobby. This will remove all local users from the game. /// The result is delivered via multiplayer_event callback of type leave_game_completed through do_work(). /// inline xbox_live_result leave_game(); /// /// Sends a matchmaking request to the server. When a match is found, the manager will join /// the game and notify the title via find_match_completed_event(). /// /// The name of the hopper. /// The ticket attributes for the match. (Optional) /// The maximum time to wait for members to join the match. (Optional) inline xbox_live_result find_match( _In_ const string_t& hopperName, _In_ const web::json::value& attributes = web::json::value(), _In_ const std::chrono::seconds& timeout = std::chrono::seconds(60) ); /// /// Cancels the match request on the server, if one exists. /// inline void cancel_match(); /// /// Provides the current status of matchmaking. 'None' if no matchmaking is in progress. /// inline xbox::services::multiplayer::manager::match_status match_status() const; /// /// Estimated wait time for a match request to be matched with other members. /// Only applies after find_match() has been called. /// inline std::chrono::seconds estimated_match_wait_time() const; /// /// Indicates whether the game should auto fill open slots during gameplay. /// inline bool auto_fill_members_during_matchmaking() const; /// /// If set to true, it finds members via matchmaking to fill open slots during gameplay. /// This can be changed anytime. /// /// True to search for members during matchmaking if the game has open slots. inline void set_auto_fill_members_during_matchmaking( _In_ bool autoFillMembers ); /// /// Sets a collection of multiplayer_quality_of_service_measurements between itself and a list of remote clients. /// This is only used when the title is manually managing QoS. /// inline void set_quality_of_service_measurements( _In_ std::shared_ptr> measurements ); /// /// Indicates who can join your game via the lobby. /// inline joinability joinability() const; /// /// Restricts who can join the game. Defaults to "joinable_by_friends", meaning only local users and users who are followed /// by an existing member of the lobby can join without an invite. /// Changes are batched and written to the service on the next do_work(). All session properties and members /// contain updated response returned from the server upon calling do_work(). /// The result is delivered via multiplayer_event callback of type joinability_state_changed through do_work(). /// /// The joinability value you want to set. /// The application-defined data to correlate the multiplayer_event to the initiating call. (Optional) inline xbox_live_result set_joinability( _In_ xbox::services::multiplayer::manager::joinability value, _In_opt_ context_t context = nullptr ); private: multiplayer_manager() {} multiplayer_manager(multiplayer_manager const&) = delete; void operator=(multiplayer_manager const&) = delete; mutable std::shared_ptr m_lobbySession; }; }}}} #include "impl/multiplayer_manager.hpp" ================================================ FILE: Include/xsapi-cpp/notification_helper.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #if XSAPI_I #include "errors.h" namespace xbox { namespace services { enum class xbox_live_notification_type { unknown = 2, game_invite = 1, achievement_unlocked = 0 }; struct xbox_live_notification { xbox_live_notification_type type; string_t title; string_t body; string_t data; }; xbox_live_result parse_notification(NSDictionary* notificationInfo); }} #endif ================================================ FILE: Include/xsapi-cpp/notification_service.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include #if HC_PLATFORM == HC_PLATFORM_WIN32 #include #include #endif NAMESPACE_MICROSOFT_XBOX_SERVICES_NOTIFICATION_CPP_BEGIN class xbox_live_context; #if HC_PLATFORM == HC_PLATFORM_WIN32 class invite_notification_event_args { public: inline string_t invited_xbox_user_id() const; inline string_t sender_xbox_user_id() const; inline string_t sender_gamertag() const; inline string_t invite_handle_id() const; inline string_t invite_protocol() const; inline string_t invite_context() const; inline utility::datetime expiration() const; inline const multiplayer::multiplayer_session_reference session_reference() const; /// /// Internal function /// inline invite_notification_event_args(_In_ const XblGameInviteNotificationEventArgs& gameInviteargs); private: XblGameInviteNotificationEventArgs m_gameInviteArgs; }; class achievement_unlocked_notification_event_args { public: inline string_t name() const; inline string_t id() const; inline string_t description() const; inline string_t icon_url() const; inline uint64_t gamerscore() const; inline string_t deeplink() const; inline string_t xbox_user_id() const; inline utility::datetime unlockTime() const; achievement_unlocked_notification_event_args(_In_ const XblAchievementUnlockEvent& achievementUnlockEvent); private: XblAchievementUnlockEvent m_achievementUnlock; }; #endif // WIN32 enum notification_filter_source_type { media_presence = 1, presence_online = 2, broadcast = 3, message = 4, party_invite_360 = 5, multiplayer = 6, achievements = 8 }; struct notification_filter { notification_filter_source_type sourceType; uint32_t type; }; class notification_service : public std::enable_shared_from_this { public: inline notification_service( _In_ XblContextHandle contextHandle); inline ~notification_service(); inline pplx::task> subscribe_to_notifications( #if HC_PLATFORM == HC_PLATFORM_WIN32 && !defined(XSAPI_UNIT_TESTS) _In_ const std::function& achievementUnlockHandler, _In_ const std::function& multiplayerInviteHandler #elif (HC_PLATFORM == HC_PLATFORM_IOS || HC_PLATFORM == HC_PLATFORM_ANDROID) _In_ const string_t deviceToken #endif ); inline pplx::task> unsubscribe_from_notifications(); #if HC_PLATFORM == HC_PLATFORM_WIN32 && !defined(XSAPI_UNIT_TESTS) inline std::function& game_invite_handler(); inline std::function& achievement_unlock_handler(); #endif private: XblContextHandle m_xblContext; #if HC_PLATFORM == HC_PLATFORM_WIN32 && !defined(XSAPI_UNIT_TESTS) XblFunctionContext m_gameinviteFunctionContext; XblFunctionContext m_achievementUnlockFunctionContext; std::function m_achievementUnlockedHandler; std::function m_inviteHandler; #endif }; NAMESPACE_MICROSOFT_XBOX_SERVICES_NOTIFICATION_CPP_END #if !XSAPI_NO_PPL #include "impl/notification.hpp" #endif ================================================ FILE: Include/xsapi-cpp/presence.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "xsapi-c/presence_c.h" #include "xsapi-cpp/real_time_activity.h" namespace xbox { namespace services { class xbox_live_context; /// /// Contains classes and enumerations that let you retrieve /// information about player's online presence on Xbox Live. /// namespace presence { /// Defines values used to indicate the device type associate with a PresenceTitleRecord. enum class presence_device_type { /// Unknown device unknown, /// Windows Phone device windows_phone, /// Windows Phone 7 device windows_phone_7, /// Web device, like Xbox.com web, /// Xbox360 device xbox_360, /// Games for Windows LIVE device pc, /// Xbox LIVE for Windows device windows_8, /// Xbox One device xbox_one, /// Windows One Core devices windows_one_core, /// Windows One Core Mobile devices windows_one_core_mobile }; /// /// Defines values used to indicate the state of the user with regard to the presence service. /// enum class user_presence_state { /// The state is unknown. unknown, /// User is signed in to Xbox LIVE and active in a title. online, /// User is signed-in to Xbox LIVE, but inactive in all titles. away, /// User is not signed in to Xbox LIVE. offline }; /// /// Defines values used to indicate the states of the screen view of presence information. /// enum class presence_title_view_state { /// Unknown view state. unknown, /// The title's view is using the full screen. full_screen, /// The title's view is using part of the screen with another application snapped. filled, /// The title's view is snapped with another application using a part of the screen. snapped, /// The title's running in the background and is not visible. background }; /// Defines values used to set the level of presence detail return from the service. Choosing proper detail level could help the performance of the API. enum class presence_detail_level { /// Default detail level. default_level, /// User detail level. User presence info only, no device, title or rich presence info. user, /// Device detail level. User and device presence info only, no title or rich presence info. device, /// Title detail level. User, device and title presence info only, no rich presence info. title, /// All detail possible. User, device, title and rich presence info will be provided. all }; /// Defines values used to indicate the media id types for media presence data. enum class presence_media_id_type { /// Unknown media Id. unknown, /// Bing media Id. bing, /// MediaProvider media Id. media_provider }; /// Defines values used to indicate the title presence state for a user. enum class title_presence_state { /// /// Indicates this is a Unknown state. /// unknown, /// /// Indicates the user started playing the title. /// started, /// /// Indicates the user ended playing the title. /// ended }; /// /// Represents data supporting Rich Presence features. /// class presence_data { public: /// /// Initializes a new instance of the PresenceData class, which identifies where the presence strings are defined and which of the defined strings is used. /// /// The service configuration ID (SCID) that identifies where the presence strings are defined by Id. /// Id of the presence string that should be used. inline presence_data( _In_ string_t serviceConfigurationId, _In_ string_t presenceId ); /// /// Initializes a new instance of the PresenceData class, which identifies where the presence strings are defines, which string is used, and IDs of strings to be used in place of tokens within the presence string. /// /// The service configuration ID (SCID) that identifies where the presence strings are defined by Id. /// Id of the presence string that should be used. /// Ids of the strings that should be used to replace the tokens in the presence string. inline presence_data( _In_ string_t serviceConfigurationId, _In_ string_t presenceId, _In_ std::vector presenceTokenIds ); /// /// ID of the service configuration containing the presence strings. /// inline const string_t& service_configuration_id() const; /// /// The ID of a presence string that is defined in the service configuration. /// For example, PresenceId = "1" could equal "Playing {0} on {1}" in the service configuration. /// The service configuration might map token 0 to Maps and token 1 to MapId. /// inline const string_t& presence_id() const; /// /// The IDs of the strings to replace the format string tokens found in the presence string. These strings are also defined in the service configuration. /// The ID values in the collection map to the strings associated with the token arguments found in the PresenceId. /// For example let's say this vector view contained the values "4" and "1" and PresenceId = "1" equals "Playing {0} on {1}" in the service configuration. /// The service configuration might map Token 0 = Maps, where MapId = "4" equals "Hometown". /// The service configuration might map Token 1 = Difficulty, where DifficultyId = "1" equals "Casual". /// inline const std::vector& presence_token_ids() const; private: string_t m_serviceConfigurationId; string_t m_presenceId; std::vector m_presenceTokenIds; }; class presence_broadcast_record { public: /// /// Id for this broadcast as defined by the broadcasting service. /// inline string_t broadcast_id() const; /// /// The GUID uniquely identifying the broadcasting session. /// inline string_t session() const; /// /// Name of the streaming provider. /// inline string_t provider() const; /// /// Approximate number of current viewers. /// inline uint32_t viewer_count() const; /// /// UTC timestamp when the broadcast was started. /// inline utility::datetime start_time() const; /// /// Internal function /// inline presence_broadcast_record() = default; inline presence_broadcast_record(_In_ XblPresenceRecordHandle handle, _In_ const XblPresenceBroadcastRecord* broadcastRecord); inline presence_broadcast_record(const presence_broadcast_record& other); inline presence_broadcast_record& operator=(presence_broadcast_record other); inline ~presence_broadcast_record(); private: XblPresenceRecordHandle m_handle{}; const XblPresenceBroadcastRecord* m_broadcastRecord{ nullptr }; }; class presence_title_record { public: /// /// The title ID. /// inline uint32_t title_id() const; /// /// The title name. /// inline string_t title_name() const; /// /// The UTC timestamp when the record was last updated. /// inline utility::datetime last_modified_date() const; /// /// The active state for the title. /// inline bool is_title_active() const; /// /// The formatted and localized presence string. /// inline string_t presence() const; /// /// The title view state. /// inline presence_title_view_state presence_title_view() const; /// /// The broadcast information of what the user is broadcasting. /// inline presence_broadcast_record broadcast_record() const; /// /// Internal function /// inline presence_title_record(_In_ XblPresenceRecordHandle handle, _In_ const XblPresenceTitleRecord* titleRecord); inline presence_title_record(_In_ const presence_title_record& other); inline presence_title_record& operator=(_In_ presence_title_record other); inline ~presence_title_record(); private: XblPresenceRecordHandle m_handle; const XblPresenceTitleRecord* m_titleRecord; }; class presence_device_record { public: /// /// The device type associated with this record. /// inline presence_device_type device_type() const; /// /// The record containing title presence data. /// inline std::vector presence_title_records() const; /// /// Internal function /// inline presence_device_record(_In_ XblPresenceRecordHandle handle, _In_ const XblPresenceDeviceRecord* deviceRecord); inline presence_device_record(_In_ const presence_device_record& other); inline presence_device_record& operator=(_In_ presence_device_record other); inline ~presence_device_record(); private: XblPresenceRecordHandle m_handle; const XblPresenceDeviceRecord* m_deviceRecord; }; class presence_record { public: /// /// The Xbox user ID. /// inline string_t xbox_user_id() const; /// /// The user's presence state. /// inline user_presence_state user_state() const; /// /// Collection of presence_device_record objects returned by a request. /// inline std::vector presence_device_records() const; /// /// Returns whether the user is playing this title id /// inline bool is_user_playing_title(_In_ uint32_t titleId) const; presence_record() = default; inline presence_record(_In_ XblPresenceRecordHandle handle); inline presence_record(_In_ const presence_record& other); inline presence_record& operator=(presence_record other); inline ~presence_record(); private: XblPresenceRecordHandle m_handle{ nullptr }; }; /// /// Used to identify the Xbox user, device, and log-in status presence values. /// class device_presence_change_event_args { public: /// /// The Xbox user ID. /// inline const string_t& xbox_user_id() const; /// /// The type of device. /// inline presence_device_type device_type() const; /// /// Value used to indicate if the Xbox user is logged onto the device. /// inline bool is_user_logged_on_device() const; inline device_presence_change_event_args(_In_ uint64_t xuid, _In_ XblPresenceDeviceType deviceType, _In_ bool isUserLoggedOn); private: string_t m_xuid; XblPresenceDeviceType m_deviceType; bool m_isUserLoggedOn; }; /// /// Subscribes to changes to an Xbox user's presence on a device. /// class device_presence_change_subscription : public xbox::services::real_time_activity::real_time_activity_subscription { public: /// /// The Xbox user ID. /// inline const string_t& xbox_user_id() const; /// /// Internal function /// inline device_presence_change_subscription(_In_ XblRealTimeActivitySubscriptionHandle handle, _In_ const string_t& xuid); private: string_t m_xuid; friend class presence_service; }; /// /// Used to identify a Xbox user, title, and presence states that can be subscribed to. /// class title_presence_change_event_args { public: /// /// The Xbox user ID. /// inline const string_t& xbox_user_id() const; /// /// The title ID. /// inline uint32_t title_id() const; /// /// Object that defines possible presence states. /// inline title_presence_state title_state() const; inline title_presence_change_event_args(_In_ uint64_t xuid, _In_ uint32_t titleId, _In_ XblPresenceTitleState titleState); private: string_t m_xuid; uint32_t m_titleId; XblPresenceTitleState m_titleState; }; /// /// Subscribes to presence change events for the specified title and user. /// class title_presence_change_subscription : public real_time_activity::real_time_activity_subscription { public: /// /// The Xbox user ID. /// inline const string_t& xbox_user_id() const; /// /// The title ID. /// inline uint32_t title_id() const; inline title_presence_change_subscription(_In_ XblRealTimeActivitySubscriptionHandle handle, _In_ const string_t& xuid, _In_ uint32_t titleId); private: string_t m_xuid; uint32_t m_titleId; friend class presence_service; }; #if !XSAPI_NO_PPL class presence_service { public: /// /// Sets presence info for the current user context. /// /// Indicates if the current user context is currently active or inactive in the title. /// The application can choose to set this based on an amount of inactivity. /// Current user's presence data. /// Calls V1 POST /users/xuid({xuid})/devices/current/titles/current inline pplx::task> set_presence( _In_ bool isUserActiveInTitle ); /// /// Sets presence info for the current user context. /// /// Indicates if the current user context is currently active or inactive in the title. /// The application can choose to set this based on an amount of inactivity. /// Current user's presence data. /// Calls V1 POST /users/xuid({xuid})/devices/current/titles/current inline pplx::task> set_presence( _In_ bool isUserActiveInTitle, _In_ presence_data presenceData ); /// /// Gets presence info for a specific Xbox User Id. /// /// The Xbox User ID of the user to get presence for /// Calls V3 GET /users/xuid({xuid}) inline pplx::task> get_presence( _In_ const string_t& xboxUserId ); /// /// Gets presence info for multiple users. This returns all possible titles on all device, /// defaults to presence_detail_level::default which is equivalent to /// presence_detail_level::title (get basic title level information), /// and does not filter out users who are offline or broadcasting. /// /// The name of the users to get presence for. /// Calls V3 POST /users/batch inline pplx::task>> get_presence_for_multiple_users( _In_ const std::vector& xboxUserIds ); /// /// Gets presence info for multiple users with filters. /// /// The name of the users to get presence for. /// List of device types. If the input is an empty vector, defaults to all possible deviceTypes. /// List of titleIds for filtering the result. If the input is an empty vector, defaults to all possible titles. /// Detail level of the result. Defaults to all details /// If true, API will filter out records for users that are offline /// If true, API will filter out records for users that are not broadcasting. /// Calls V3 POST /users/batch inline pplx::task>> get_presence_for_multiple_users( _In_ const std::vector& xboxUserIds, _In_ const std::vector& deviceTypes, _In_ const std::vector& titleIds, _In_ presence_detail_level presenceDetailLevel, _In_ bool onlineOnly, _In_ bool broadcastingOnly ); /// /// Gets presence info for a specific group of users. /// /// The name of the group of users to get presence for. /// See Microsoft::Xbox::Services::Social::SocialGroupConstants for the latest options. /// Calls V3 GET /users/xuid({xuid})/groups/{socialGroup} inline pplx::task>> get_presence_for_social_group( _In_ const string_t& socialGroup ); /// /// Gets presence info for a specific group of users. /// /// The name of the group of users to get presence for. /// The user whose group should be targeted. If the input is null, current user will be used. /// List of device types. If the input is null; defaults to all possible deviceTypes. (Optional) /// List of titleIds for filtering the result. If the input is null, defaults to all possible titles. (Optional) /// Detail level of the result. Defaults to all details. (Optional) /// If true, API will filter out records for users that are offline. /// If true, API will filter out records for users that are not broadcasting. /// Calls V3 POST /users/batch inline pplx::task>> get_presence_for_social_group( _In_ const string_t& socialGroup, _In_ const string_t& socialGroupOwnerXboxUserId, _In_ const std::vector& deviceTypes, _In_ const std::vector& titleIds, _In_ presence_detail_level peoplehubDetailLevel, _In_ bool onlineOnly, _In_ bool broadcastingOnly ); /// /// Subscribes to device presence change notifications via the DevicePresenceChanged event. /// /// The Xbox User ID of the person of the subscription. /// RealTimeActivityDevicePresenceChangeSubscription containing the initial value of the PresenceDeviceRecord. /// Register for device presence changes via the DevicePresenceChanged event. inline xbox_live_result> subscribe_to_device_presence_change( _In_ const string_t& xboxUserId ); /// /// Unsubscribes a previously created device presence change subscription. /// /// The subscription object to unsubscribe. inline xbox_live_result unsubscribe_from_device_presence_change( _In_ std::shared_ptr subscription ); /// /// Subscribes to title presence change notifications via the TitlePresenceChanged event. /// /// The Xbox User ID of the person of the subscription. /// The title ID of the subscription. /// RealTimeActivityDevicePresenceChangeSubscription containing the initial value of the PresenceDeviceRecord. /// Register for device presence changes via the DevicePresenceChanged event. inline xbox_live_result> subscribe_to_title_presence_change( _In_ const string_t& xboxUserId, _In_ uint32_t titleId ); /// /// Registers for title presence change notifications. Event handlers will receive RealTimeActivityTitlePresenceChangeEventArgs^. /// inline xbox_live_result unsubscribe_from_title_presence_change( _In_ std::shared_ptr subscription ); /// /// Registers an event handler for device presence change notifications. /// Event handlers receive a device_presence_change_event_args& object. /// /// The callback function that receives notifications. /// /// A function_context object that can be used to unregister the event handler. /// inline function_context add_device_presence_changed_handler(_In_ std::function handler); /// /// Unregisters an event handler for device presence change notifications. /// /// The function_context object that was returned when the event handler was registered. /// The callback function that receives notifications. inline void remove_device_presence_changed_handler(_In_ function_context context); /// /// Registers an event handler for title presence change notifications. /// Event handlers receive a title_presence_change_event_args& object. /// /// The callback function that receives notifications. /// /// A function_context object that can be used to unregister the event handler. /// inline function_context add_title_presence_changed_handler(_In_ std::function handler); /// /// Unregisters an event handler for title presence change notifications. /// /// The function_context object that was returned when the event handler was registered. /// The callback function that receives notifications. inline void remove_title_presence_changed_handler(_In_ function_context context); inline presence_service(const presence_service& other); inline presence_service& operator=(presence_service other); inline ~presence_service(); private: inline presence_service(XblContextHandle xblContextHandle); XblContextHandle m_xblContextHandle; struct HandlerContext; friend xbox_live_context; }; #endif // !XSAPI_NO_PPL }}} #if !XSAPI_NO_PPL #include "impl/presence.hpp" #endif ================================================ FILE: Include/xsapi-cpp/privacy.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "xsapi-c/privacy_c.h" namespace xbox { namespace services { class xbox_live_context; /// /// Contains classes and enumerations that let you retrieve /// information about a player's privacy settings from Xbox Live. /// namespace privacy { /// /// Contains the reason why permission was denied. /// class permission_deny_reason { public: inline permission_deny_reason(const XblPermissionDenyReasonDetails& reasonDetails); /// /// The reason why permission was denied. /// inline string_t reason() const; /// /// If the deny reason is privilege check, this indicates which privilege failed. /// inline string_t restricted_setting() const; private: XblPermissionDenyReasonDetails m_reasonDetails; }; /// /// Contains the result of a permission check. /// class permission_check_result { public: inline permission_check_result() {} inline permission_check_result(const XblPermissionCheckResult* result); /// /// Indicates if the user is allowed the requested access. /// inline bool is_allowed() const; /// /// The permission requested. /// inline string_t permission_requested() const; /// /// If IsAllowed is false, contains the reasons why the permissions were denied. /// inline const std::vector& deny_reasons() const; private: XblPermissionCheckResult m_result{}; std::vector m_reasons; }; /// /// Contains the results of multiple permission checks. /// class multiple_permissions_check_result { public: inline multiple_permissions_check_result(const XblPermissionCheckResult* results, size_t resultCount, string_t target); /// /// Xbox User Id OR anonymous user type for the permission request. /// inline const string_t& xbox_user_id() const; /// /// Contains a collection of results returned when checking multiple permissions for a user. /// inline const std::vector& items() const; private: string_t m_target; std::vector m_items; }; /// /// Manages constant values for permission IDs. /// class permission_id_constants { public: /// /// Check whether or not the user can send a message with text content to the target user. /// static const string_t communicate_using_text() { return _T("CommunicateUsingText"); } /// /// Check whether or not the user can communicate using video with the target user. /// static const string_t communicate_using_video() { return _T("CommunicateUsingVideo"); } /// /// Check whether or not the user can communicate using voice with the target user. /// static const string_t communicate_using_voice() { return _T("CommunicateUsingVoice"); } /// /// Check whether or not the user can view the profile of the target user. /// static const string_t view_target_profile() { return _T("ViewTargetProfile"); } /// /// Check whether or not the user can view the game history of the target user. /// static const string_t view_target_game_history() { return _T("ViewTargetGameHistory"); } /// /// Check whether or not the user can view the details video watching history of the target user. /// static const string_t view_target_video_history() { return _T("ViewTargetVideoHistory"); } /// /// Check whether or not the user can view the detailed music listening history of the target user. /// static const string_t view_target_music_history() { return _T("ViewTargetMusicHistory"); } /// /// Check whether or not the user can view the exercise info of the target user. /// static const string_t view_target_exercise_info() { return _T("ViewTargetExerciseInfo"); } /// /// Check whether or not the user can view the online status of the target user. /// static const string_t view_target_presence() { return _T("ViewTargetPresence"); } /// /// Check whether or not the user can view the details of the targets video status (extended online presence). /// static const string_t view_target_video_status() { return _T("ViewTargetVideoStatus"); } /// /// Check whether or not the user can view the details of the targets music status (extended online presence). /// static const string_t view_target_music_status() { return _T("ViewTargetMusicStatus"); } /// /// Check whether or not a user can play multiplayer with the target user. /// static const string_t play_multiplayer() { return _T("PlayMultiplayer"); } /// /// Checks whether or not the user can view information about how audio buffers are broadcast for the target user. /// static const string_t broadcast_with_twitch() { return _T("BroadcastWithTwitch"); } /// /// Check whether or not the user can view the user-created content of other users. /// static const string_t view_target_user_created_content() { return _T("ViewTargetUserCreatedContent"); } }; /// /// Special strings that can be passed in as to check_permission APIs to check permission for different /// classes of non-Xbox Live users. /// class anonymous_user_type_constants { public: /// /// A non Xbox Live user /// static string_t cross_network_user() { return _T("crossNetworkUser"); } /// /// A non Xbox Live user that a title recognizes as an in-game friend. /// static string_t crost_network_friend() { return _T("crossNetworkFriend"); } }; /// /// Provides an endpoint for managing privacy settings. /// class privacy_service { public: /// /// Get the list of Xbox Live Ids the calling user should avoid during multiplayer matchmaking. /// /// A collection of XboxUserIds that correspond to the calling user's avoid list. /// /// Returns a concurrency::task<T> object that represents the state of the asynchronous operation. /// /// Calls V1 GET /users/xuid({xuid})/people/avoid /// inline pplx::task>> get_avoid_list(); /// /// Check a single permission with a single target user. /// /// The ID of the permission to check. /// See microsoft::xbox::services::privacy::permission_id_constants for the latest options. /// The target user's Xbox Live ID OR an anonymous user type string from anonymous_user_type_constants. /// The permission check result against a single user. /// /// Returns a concurrency::task<T> object that represents the state of the asynchronous operation. /// /// Calls V1 GET /users/{requestorId}/permission/validate /// inline pplx::task> check_permission_with_target_user( _In_ const string_t& permissionId, _In_ const string_t& target ); /// /// Check multiple permissions with multiple target users. /// /// The collection of IDs of the permissions to check. /// See microsoft::xbox::services::privacy::permission_id_constants for the latest options. /// The collection of target Xbox user IDs and/or anonymous user types to check permissions against. /// A multiple permission check result which contains a collection of permission information. /// /// Returns a concurrency::task<T> object that represents the state of the asynchronous operation. /// /// Calls V1 POST /users/{requestorId}/permission/validate /// inline pplx::task>> check_multiple_permissions_with_multiple_target_users( _In_ const std::vector& permissionIds, _In_ const std::vector& targets ); /// /// Get the list of Xbox Live Ids that the calling user should not hear (mute) during multiplayer matchmaking. /// /// The collection of Xbox user IDs that represent the mute list for a user. /// /// Returns a concurrency::task<T> object that represents the state of the asynchronous operation. /// /// Calls V1 GET /users/xuid({xuid})/people/mute /// inline pplx::task>> get_mute_list(); /// /// Get either a user's avoid list or a user's mute list. /// /// A string indicating what type of list to get for the user. /// The valid values are "avoid" and "mute". /// The collection of Xbox user IDs that represent the either the avoid list or the mute list for a user. /// /// Returns a concurrency::task<T> object that represents the state of the asynchronous operation. /// /// Calls V1 GET /users/xuid({xuid})/people/mute /// inline pplx::task>> get_avoid_or_mute_list(_In_ const string_t& subPathName); inline privacy_service(const privacy_service& other); inline privacy_service& operator=(privacy_service other); inline ~privacy_service(); private: inline privacy_service(_In_ XblContextHandle contextHandle); XblContextHandle m_xblContext; friend xbox_live_context; }; } } } #include "impl/privacy.hpp" ================================================ FILE: Include/xsapi-cpp/profile.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "xsapi-c/profile_c.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN class xbox_live_context; /// /// Contains classes and enumerations that let you retrieve /// information about a player's Xbox Live profile. /// namespace social { /// /// Represents a user's Xbox Live profile. /// class xbox_user_profile { public: /// /// Name for displaying in apps. This will be the user's gamertag. /// inline string_t app_display_name() const; /// /// Uri for the user's display picture to be used in application UI. /// The Uri is a resizable Uri. It can be used to specify one of the following sizes and formats by appending '&format={format}&w={width}&h={height}: /// Format: png /// Width Height /// 64 64 /// 208 208 /// 424 424 /// inline web::uri app_display_picture_resize_uri() const; /// /// Name for displaying in games. This will be the user's gamertag. /// inline string_t game_display_name() const; /// /// Uri for the user's display picture to be used in games. /// The Uri is a resizable Uri. It can be used to specify one of the following sizes and formats by appending '&format={format}&w={width}&h={height}: /// Format: png /// Width Height /// 64 64 /// 208 208 /// 424 424 /// inline web::uri game_display_picture_resize_uri() const; /// /// The user's Gamerscore. /// inline string_t gamerscore() const; /// /// The user's Gamertag. /// inline string_t gamertag() const; /// /// The user's Xbox user ID. /// inline string_t xbox_user_id() const; inline xbox_user_profile() = default; inline xbox_user_profile(const XblUserProfile& profile); private: XblUserProfile m_profile{}; }; /// /// Services that manage user profile. /// class profile_service { public: /// /// Gets a user profile for a specific Xbox user. /// /// The Xbox User ID of the user to get the profile for. /// /// Returns a concurrency::task<T> object that represents the state of the asynchronous operation. /// The result of the asynchronous operation is an xbox_user_profile object. /// /// Calls V2 GET /users/batch/profile/settings inline pplx::task> get_user_profile( _In_ string_t xboxUserId ); /// /// Gets one or more user profiles for a collection of specified Xbox users. /// /// The collection of Xbox User IDs of the users to get profiles for /// /// Returns a concurrency::task<T> object that represents the state of the asynchronous operation. /// The result of the asynchronous operation is a collection of xbox_user_profile objects. /// /// Calls V2 GET /users/batch/profile/settings inline pplx::task>> get_user_profiles( _In_ const std::vector& xboxUserIds ); /// /// Gets user profiles for users in a specified social group. /// /// The name of the social group of users to search. /// See xbox::services::social::social_group_constants for the latest options. /// /// Returns a concurrency::task<T> object that represents the state of the asynchronous operation. /// The result of the asynchronous operation is a collection of xbox_user_profile objects. /// /// Calls V2 GET /users/{userId}/profile/settings/people/{socialGroup} inline pplx::task>> get_user_profiles_for_social_group( _In_ const string_t& socialGroup ); inline profile_service(const profile_service& other); inline profile_service& operator=(profile_service other); inline ~profile_service(); private: inline profile_service(XblContextHandle xblContextHandle); XblContextHandle m_xblContextHandle; friend xbox_live_context; }; } NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END #include "impl/profile.hpp" ================================================ FILE: Include/xsapi-cpp/real_time_activity.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include #include "xsapi-c/real_time_activity_c.h" #include "xsapi-cpp/types.h" namespace xbox { namespace services { class xbox_live_context; }} namespace xbox { namespace services { /// /// Contains classes and enumerations that let you subscribe /// to real time activity information for a player statistic /// on Xbox Live. /// namespace real_time_activity { /// /// Enumeration for the message types for the Xbox Live service. /// The value of each enum should not be changed. /// enum class real_time_activity_message_type { /// /// Indicates that this is a Subscribe message. /// subscribe = 1, /// /// Indicates that this is a Unsubscribe message. /// unsubscribe = 2, /// /// Indicates that this is a ChangeEvent message. /// change_event = 3, /// /// Indicates that this is a Resync message. /// resync = 4 }; /// /// Enumeration for the possible states of a statistic subscription request /// to the real-time activity service. /// enum real_time_activity_subscription_state { /// /// The subscription state is unknown. /// unknown, /// /// Waiting for the server to respond to the subscription request. /// pending_subscribe, /// /// Subscription confirmed. /// subscribed, /// /// Waiting for the server to respond to the unsubscribe request. /// pending_unsubscribe, /// /// Unsubscribe confirmed. /// closed }; /// /// Enumeration for the possible connection states of the connection /// to the real-time activity service. /// enum real_time_activity_connection_state { /// /// Currently connected to the real-time activity service. /// connected, /// /// Currently connecting to the real-time activity service. /// connecting, /// /// Currently disconnected from the real-time activity service. /// disconnected }; /// /// The base class for real time activity subscriptions. /// class real_time_activity_subscription { public: /// /// The state of the subscription request. /// DEPRECATED. The state of RTA subscriptions is no longer exposed publicly. real_time_activity_subscription_state::unknown /// will always be returned. /// _XSAPICPP_DEPRECATED inline real_time_activity_subscription_state state() const; /// /// The resource uri for the request. /// DEPRECATED. The state of RTA subscriptions is no longer exposed publicly. /// _XSAPICPP_DEPRECATED inline const string_t& resource_uri() const; /// /// The unique subscription id for the request. /// DEPRECATED. The state of RTA subscriptions is no longer exposed publicly. This API will return a unique /// client side ID, but it is in no way related to the ID assigned by the RTA service. /// _XSAPICPP_DEPRECATED inline uint32_t subscription_id() const; inline real_time_activity_subscription(XblRealTimeActivitySubscriptionHandle handle); protected: string_t m_resourceUri; XblRealTimeActivitySubscriptionHandle m_handle; }; class real_time_activity_subscription_error_event_args { public: /// /// The subscription this refers to /// inline const real_time_activity_subscription& subscription(); /// /// The error returned by the operation. /// inline std::error_code err() const; /// /// The error message /// _XSAPICPP_DEPRECATED inline std::string err_message() const; /// /// Internal function /// inline real_time_activity_subscription_error_event_args( XblRealTimeActivitySubscriptionHandle subscriptionHandle, HRESULT subscriptionError ); private: real_time_activity_subscription m_subscription; std::error_code m_err; }; /// /// Represents a client side service that handles connections and communications with /// the Xbox Live real-time activity service. /// class real_time_activity_service : public std::enable_shared_from_this { public: /// /// Starts a background task that creates and initializes a websocket connection to the Xbox Live real-time activity service. /// Its recommended that titles do not activate more than MAXIMUM_WEBSOCKETS_ACTIVATIONS_ALLOWED_PER_USER (5) per user per title instance. /// Upon reaching the limit, titles will hit an assert during development that can be temporarily disabled via /// xbox_live_context_settings::disable_asserts_for_maximum_number_of_websockets_activated(). /// DEPRECATED. Calling this API is no longer required. The WebSocket connection will be made automatically by /// XSAPI as necessary. This API will be removed in a future release. /// _XSAPICPP_DEPRECATED inline void activate(); /// /// Cancels all existing subscriptions to the Xbox Live real-time activity service, /// unhooks from the websocket connection, and stops the background task. /// DEPRECATED. Calling this API is no longer required. The WebSocket connection will be cleaned up automatically /// by XSAPI when it is no longer needed. This API will be removed in a future release. /// _XSAPICPP_DEPRECATED inline void deactivate(); /// /// Registers a handler function to receive a notification that is sent when the client service /// loses or gains connectivity to the real time activity service. /// Event handlers receive a real_time_activity_connection_state object. /// /// The callback function that receives notifications. /// /// A function_context object that can be used to unregister the event handler. /// inline function_context add_connection_state_change_handler(_In_ std::function handler); /// /// Unregisters an event handler for real time activity connectivity state changes. /// /// The function_context object that was returned when the event handler was registered. inline void remove_connection_state_change_handler(_In_ function_context remove); /// /// Registers a handler function to receive a notification that is sent when there is an /// error in the real time activity service. /// Event handlers receive a real_time_activity_subscription_error_event_args& object. /// DEPRECATED. RTA service errors will now be handled by XSAPI internally and callback will no longer be invoked. /// /// The callback function that receives notifications. /// /// A function_context object that can be used to unregister the event handler. /// _XSAPICPP_DEPRECATED inline function_context add_subscription_error_handler(_In_ std::function handler); /// /// Unregisters an event handler for real time activity error notifications. /// DEPRECATED. RTA service errors will now be handled by XSAPI internally and callback will no longer be invoked. /// /// The function_context object that was returned when the event handler was registered. _XSAPICPP_DEPRECATED inline void remove_subscription_error_handler(_In_ function_context remove); /// /// Registers a handler function to receive a notification that is sent when there is a /// resync message from the real time activity service. /// This message indicates that data may have been lost and to resync all data by calling /// corresponding REST API's /// /// The callback function that receives notifications. /// /// A function_context object that can be used to unregister the event handler. /// inline function_context add_resync_handler(_In_ std::function handler); /// /// Unregisters an event handler for real time activity resync notifications. /// /// The function_context object that was returned when the event handler was registered. inline void remove_resync_handler(_In_ function_context remove); inline real_time_activity_service(const real_time_activity_service& other); inline real_time_activity_service& operator=(real_time_activity_service other); inline ~real_time_activity_service(); private: inline real_time_activity_service(_In_ XblContextHandle contextHandle); XblContextHandle m_xblContext; struct HandlerContext; friend xbox_live_context; }; }}} #include "impl/real_time_activity.hpp" ================================================ FILE: Include/xsapi-cpp/service_call_logging_config.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN #if HC_PLATFORM_IS_MICROSOFT class service_call_logging_config { public: /// /// Deprecated. Service call logging feature is no longer supported. /// _XSAPICPP_DEPRECATED inline static std::shared_ptr get_singleton_instance(); /// /// Deprecated. Service call logging feature is no longer supported. /// _XSAPICPP_DEPRECATED inline void enable(); /// /// Deprecated. Service call logging feature is no longer supported. /// _XSAPICPP_DEPRECATED inline void disable(); #if HC_PLATFORM == HC_PLATFORM_XDK || HC_PLATFORM == HC_PLATFORM_UWP || defined(XSAPI_UNIT_TESTS) /// /// Deprecated /// _XSAPICPP_DEPRECATED inline void _Register_for_protocol_activation(); #endif private: service_call_logging_config() = default; service_call_logging_config(const service_call_logging_config&) = delete; void operator=(const service_call_logging_config&) = delete; }; #endif // HC_PLATFORM_IS_MICROSOFT NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END #include "impl/service_call_logging_config.hpp" ================================================ FILE: Include/xsapi-cpp/services.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #if HC_PLATFORM != HC_PLATFORM_ANDROID #pragma warning(disable: 4265) #pragma warning(disable: 4266) #pragma warning(disable: 4062) #endif #ifndef _LIBCPP_ENABLE_CXX17_REMOVED_AUTO_PTR #define _LIBCPP_ENABLE_CXX17_REMOVED_AUTO_PTR #endif // cpprest assumed to be part of XSAPI static lib #ifndef _ASYNCRT_IMPORT // define this if cpprest is actually a dll #ifndef _ASYNCRT_EXPORT #ifndef _NO_ASYNCRTIMP #define _NO_ASYNCRTIMP // by default disable cpprest dll fn decorations #endif #endif #endif #include "xsapi-c/services_c.h" #include "xsapi-cpp/types.h" #include "xsapi-cpp/errors.h" #include "xsapi-cpp/mem.h" #include "cpprest/http_msg.h" #include "xsapi-cpp/system.h" #include "xsapi-cpp/service_call_logging_config.h" #if !XSAPI_NO_PPL #include "xsapi-cpp/leaderboard.h" #include "xsapi-cpp/title_storage.h" #include "xsapi-cpp/privacy.h" #include "xsapi-cpp/profile.h" #endif // !XSAPI_NO_PPL #include "xsapi-cpp/social_manager.h" #include "xsapi-cpp/http_call.h" #include "xsapi-cpp/xbox_live_context_settings.h" #if !defined(XBOX_LIVE_CREATORS_SDK) #include "xsapi-cpp/social.h" #include "xsapi-cpp/achievements.h" #include "xsapi-cpp/real_time_activity.h" #include "xsapi-cpp/presence.h" #if !XSAPI_NO_PPL #include "xsapi-cpp/events.h" #include "xsapi-cpp/user_statistics.h" #include "xsapi-cpp/multiplayer.h" #include "xsapi-cpp/matchmaking.h" #include "xsapi-cpp/multiplayer_manager.h" #include "xsapi-cpp/notification_service.h" #include "xsapi-cpp/string_verify.h" #endif // !XSAPI_NO_PPL #endif // !defined(XBOX_LIVE_CREATORS_SDK) #include "xsapi-cpp/title_callable_ui.h" #include "xsapi-cpp/xbox_live_context.h" #ifdef U #undef U // clean up cpprest's global define in case it's used by app #endif ================================================ FILE: Include/xsapi-cpp/social.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "xsapi-c/social_c.h" #include "xsapi-cpp/real_time_activity.h" #include "xsapi-cpp/multiplayer.h" #include "xsapi-cpp/system.h" namespace xbox { namespace services { class xbox_live_context; /// /// Contains classes and enumerations that let you retrieve /// information about player reputation and relationship with /// other players from Xbox Live. /// namespace social { enum class xbox_social_relationship_filter { /// All the people on the user's people list. all = static_cast(XblSocialRelationshipFilter::All), /// Filters to only the people on the user's people list that have the attribute "Favorite" associated with them. favorite = static_cast(XblSocialRelationshipFilter::Favorite), /// Filters to only the people on the user's people list that are also legacy Xbox Live friends. legacy_xbox_live_friends = static_cast(XblSocialRelationshipFilter::LegacyXboxLiveFriends) }; /// /// Defines values used to identify the type of reputation feedback. /// enum class reputation_feedback_type { /// /// Titles that are able to automatically determine that a user kills a teammate /// may send this feedback without user intervention. /// fair_play_kills_teammates = static_cast(XblReputationFeedbackType::FairPlayKillsTeammates), /// /// Titles that are able to automatically determine that a user is cheating /// may send this feedback without user intervention. /// fair_play_cheater = static_cast(XblReputationFeedbackType::FairPlayCheater), /// /// Titles that are able to automatically determine that a user has tampered with on-disk content /// may send this feedback without user intervention. /// fair_play_tampering = static_cast(XblReputationFeedbackType::FairPlayTampering), /// /// Titles that are able to automatically determine that a user quit a game early /// may send this feedback without user intervention. /// fair_play_quitter = static_cast(XblReputationFeedbackType::FairPlayQuitter), /// /// When a user is voted out of a game (kicked), titles /// may send this feedback without user intervention. /// fair_play_kicked = static_cast(XblReputationFeedbackType::FairPlayKicked), /// /// Titles that allow users to report inappropriate video communications /// may send this feedback. /// communications_inappropiate_video = static_cast(XblReputationFeedbackType::CommunicationsInappropriateVideo), /// /// Titles that allow users to report inappropriate voice communications /// may send this feedback. /// communications_abusive_voice = static_cast(XblReputationFeedbackType::CommunicationsAbusiveVoice), /// /// Titles that allow users to report inappropriate user generated content /// may send this feedback. /// inappropiate_user_generated_content = static_cast(XblReputationFeedbackType::InappropriateUserGeneratedContent), /// /// Titles that allow users to vote on a most valuable player at the end of a multiplayer session /// may send this feedback. /// positive_skilled_player = static_cast(XblReputationFeedbackType::PositiveSkilledPlayer), /// /// Titles that allow users to submit positive feedback on helpful fellow players /// may send this feedback. /// positive_helpful_player = static_cast(XblReputationFeedbackType::PositiveHelpfulPlayer), /// /// Titles that allow users to submit positive feedback on shared user generated content /// may send this feedback. /// positive_high_quality_user_generated_content = static_cast(XblReputationFeedbackType::PositiveHighQualityUserGeneratedContent), /// /// Titles that allow users to report phishing message may send this feedback. /// comms_phishing = static_cast(XblReputationFeedbackType::CommsPhishing), /// /// Titles that allow users to report communication based on a picture may send this feedback. /// comms_picture_message = static_cast(XblReputationFeedbackType::CommsPictureMessage), /// /// Titles that allow users to report spam messages may send this feedback. /// comms_spam = static_cast(XblReputationFeedbackType::CommsSpam), /// /// Titles that allow users to report text messages may send this feedback. /// comms_text_message = static_cast(XblReputationFeedbackType::CommsTextMessage), /// /// Titles that allow users to report voice messages may send this feedback. /// comms_voice_message = static_cast(XblReputationFeedbackType::CommsVoiceMessage), /// /// Titles that allow users to report voice messages may send this feedback. /// fair_play_console_ban_request = static_cast(XblReputationFeedbackType::FairPlayConsoleBanRequest), /// /// Titles that allow users to report if determine if a user stands idle on purpose in a game, usually round after round, may send this feedback. /// fair_play_idler = static_cast(XblReputationFeedbackType::FairPlayIdler), /// /// Titles that report a recommendation to ban a user from Xbox Live may send this feedback. /// fair_play_user_ban_request = static_cast(XblReputationFeedbackType::FairPlayUserBanRequest), /// /// Titles that allow users to report inappropriate gamer picture may send this feedback. /// user_content_gamerpic = static_cast(XblReputationFeedbackType::UserContentGamerpic), /// /// Titles that allow users to report inappropriate biography and other personal information may send this feedback. /// user_content_personalinfo = static_cast(XblReputationFeedbackType::UserContentPersonalInfo), /// /// Titles that allow users to report unsporting behavior may send this feedback. /// fair_play_unsporting = static_cast(XblReputationFeedbackType::FairPlayUnsporting), /// /// Titles that allow users to report leaderboard cheating may send this feedback. /// fair_play_leaderboard_cheater = static_cast(XblReputationFeedbackType::FairPlayLeaderboardCheater) }; enum class social_notification_type { /// /// unknown /// unknown = static_cast(XblSocialNotificationType::Unknown), /// /// User(s) were added. /// added = static_cast(XblSocialNotificationType::Added), /// /// User(s) data changed. /// changed = static_cast(XblSocialNotificationType::Changed), /// /// User(s) were removed. /// removed = static_cast(XblSocialNotificationType::Removed) }; class social_group_constants { public: /// /// Returns Favorites constant string /// static const string_t favorite() { return _T("Favorites"); } /// /// Returns People constant string /// static const string_t people() { return _T("People"); } }; /// /// Represents the relationship between the user and another Xbox user. /// class xbox_social_relationship { public: /// /// The person's Xbox user identifier. /// inline const string_t& xbox_user_id() const; /// /// Indicates whether the person is one that the user cares about more. /// Users can have a very large number of people in their people list, /// favorite people should be prioritized first in experiences and shown before others that are not favorites. /// inline bool is_favorite() const; /// /// Indicates whether the person is a friend (mutual follower) of the user. /// inline bool is_friend() const; /// /// Does not indicate a following/follower relation between caller user other users. /// Currently will return true if a person is a mutual follower of a user that /// requested information (this is dependent on the value of 'isFriend' field). /// inline bool is_following_caller() const; /// /// A collection of strings indicating which social networks this person has a relationship with. /// inline const std::vector& social_networks() const; /// /// Internal function /// inline xbox_social_relationship( const XblSocialRelationship& socialRelationship ); private: string_t m_xuid; bool m_isFavorite{ false }; bool m_isFollowingCaller{ false }; bool m_isFriend{ false }; std::vector m_socialNetworks; }; #if !XSAPI_NO_PPL /// /// Services that manage user relationship. /// class xbox_social_relationship_result { // Example: // { // "people": [ // { // "xuid": "2603643534573573", // "isFavorite": true, // "isFollowingCaller": true, // "socialNetworks": ["MyNetwork1", "MyNetwork2"] // }, // { // "xuid": "2603643534573572", // "isFavorite": true, // "isFollowingCaller": false, // "socialNetworks": ["MyNetwork1"] // }, // { // "xuid": "2603643534573577", // "isFavorite": false // "isFollowingCaller": false // }, // ], // "totalCount": 3 // } public: /// /// Collection of XboxSocialRelationship objects returned by a request. /// inline std::vector items() const; /// /// The total number of XboxSocialRelationship objects that can be requested. /// inline uint32_t total_count() const; /// /// Returns a boolean value that indicates if there are more pages of social relationships to retrieve. /// /// True if there are more pages, otherwise false. inline bool has_next() const; /// /// Returns an XboxSocialRelationshipResult object containing the next page. /// /// The maximum number of items the response can contain. Pass 0 to attempt /// retrieving all items. /// Returns an XboxSocialRelationshipResult object. /// Calls V1 GET /users/{ownerId}/people inline pplx::task> get_next( _In_ uint32_t maxItems ); /// /// Internal functions /// inline xbox_social_relationship_result() = default; inline xbox_social_relationship_result(XblSocialRelationshipResultHandle resultHandle, XblContextHandle xblContextHandle); inline xbox_social_relationship_result(const xbox_social_relationship_result& other); inline xbox_social_relationship_result& operator=(xbox_social_relationship_result other); inline ~xbox_social_relationship_result(); private: XblSocialRelationshipResultHandle m_resultHandle{ nullptr }; XblContextHandle m_xblContextHandle{ nullptr }; }; class social_relationship_change_event_args { public: /// /// The Xbox user ID for the user who's social graph changes are being listed for. /// inline const string_t& caller_xbox_user_id() const; /// /// The type of notification change. /// inline social_notification_type social_notification() const; /// /// The Xbox user ids who the event is for /// inline const std::vector& xbox_user_ids() const; /// /// Internal function /// inline social_relationship_change_event_args( const XblSocialRelationshipChangeEventArgs* args ); private: string_t m_callerXuid; social_notification_type m_notificationType{ social_notification_type::unknown }; std::vector m_xuids; }; class social_relationship_change_subscription : public xbox::services::real_time_activity::real_time_activity_subscription { public: /// /// The Xbox user ID. /// inline const string_t& xbox_user_id() const; /// /// Internal function /// inline social_relationship_change_subscription(_In_ XblRealTimeActivitySubscriptionHandle handle, _In_ uint64_t xuid); private: string_t m_xuid; friend class social_service; }; /// /// Services that manage user relationship. /// class social_service { public: /// /// Returns a XboxSocialRelationshipResult containing a the list of people that the user is connected to. /// Defaults to filtering to PersonView.All. /// Defaults to startIndex and maxItems of 0 to return entire list if possible. /// /// An XboxSocialRelationshipResult object. /// Calls V1 GET /users/{ownerId}/people?view={view}&startIndex={startIndex}&maxItems={maxItems} inline pplx::task> get_social_relationships(); /// /// Returns a xbox_social_relationship_result containing a the list of people that the user is connected to. /// /// Controls how the list is filtered. /// An xbox_social_relationship_result object. /// Calls V1 GET /users/{ownerId}/people?view={view}&startIndex={startIndex}&maxItems={maxItems} inline pplx::task> get_social_relationships( _In_ xbox_social_relationship_filter socialRelationshipFilter ); /// /// Returns a xbox_social_relationship_result containing a the list of people that the user is connected to. /// /// The Xbox User Id to get the social relationships for. /// An xbox_social_relationship_result object. /// Calls V1 GET /users/{ownerId}/people?view={view}&startIndex={startIndex}&maxItems={maxItems} inline pplx::task> get_social_relationships( _In_ const string_t& xboxUserId ); /// /// Returns a xbox_social_relationship_result containing a the list of people that the user is connected to. /// /// Controls how the list is filtered. /// Controls the starting index to return. /// Controls the number of xbox_social_relationship_result objects to get. 0 will return as many as possible /// An xbox_social_relationship_result object. /// Calls V1 GET /users/{ownerId}/people?view={view}&startIndex={startIndex}&maxItems={maxItems} inline pplx::task> get_social_relationships( _In_ xbox_social_relationship_filter socialRelationshipFilter, _In_ uint32_t startIndex, _In_ uint32_t maxItems ); /// /// Subscribes to the social service for people changed events /// DEPRECATED. Calling this API is no longer required and it will be removed in a future release. RTA subscription will be managed /// automatically by XSAPI as handlers are added and removed. /// /// The Xbox User ID of the player requesting the subscription. /// /// You can register an event handler for social relationship changes by calling set_social_changed_handler(). /// _XSAPICPP_DEPRECATED inline xbox_live_result> subscribe_to_social_relationship_change( _In_ const string_t& xboxUserId ); /// /// Unsubscribes a previously created social relationship change subscription. /// DEPRECATED. Calling this API is no longer required and it will be removed in a future release. RTA subscription will be managed /// automatically by XSAPI as handlers are added and removed. /// /// The subscription object to unsubscribe _XSAPICPP_DEPRECATED inline xbox_live_result unsubscribe_from_social_relationship_change( _In_ std::shared_ptr subscription ); /// /// Registers an event handler for social relationship change notifications. /// Event handlers receive social_relationship_change_event_args. /// /// The callback function that receives notifications. inline function_context add_social_relationship_changed_handler( _In_ std::function handler ); /// /// Removes a social relationship change handler /// /// The handler to remove. inline void remove_social_relationship_changed_handler( _In_ function_context context ); inline social_service(const social_service& other); inline social_service& operator=(social_service other); inline ~social_service(); private: inline social_service(XblContextHandle xblContextHandle); // ppl wrapper around XblSocialGetSocialRelationshipsAsync inline pplx::task> get_social_relationships( _In_ uint64_t xuid, _In_ XblSocialRelationshipFilter filter, _In_ size_t startIndex, _In_ size_t maxItems ); XblContextHandle m_xblContextHandle; uint64_t m_xuid{ 0 }; struct HandlerContext; friend xbox_live_context; }; /// ///Represents the parameters for submitting reputation feedback on a user /// class reputation_feedback_item { public: reputation_feedback_item() = default; /// /// Construct a reputation_feedback_item object /// /// The Xbox User ID of the user that reputation feedback is being submitted on. /// The reputation feedback type being submitted. /// The session reference of the multiplayer session directory session the user is sending feedback from. (Optional) /// User supplied text added to explain the reason for the feedback. (Optional) /// The Id of a resource that can be used as evidence for the feedback. Example: the Id of a video file. (Optional) inline reputation_feedback_item( _In_ const string_t& xboxUserId, _In_ reputation_feedback_type reputationFeedbackType, _In_ xbox::services::multiplayer::multiplayer_session_reference sessionRef = xbox::services::multiplayer::multiplayer_session_reference(), _In_ const string_t& reasonMessage = string_t(), _In_ const string_t& evidenceResourceId = string_t() ); /// /// The Xbox User ID of the user that reputation feedback is being submitted on. /// inline string_t xbox_user_id() const; /// /// The reputation feedback type being submitted. /// inline reputation_feedback_type feedback_type() const; /// /// The reference to the multiplayer session directory session the user is sending feedback from. /// inline const xbox::services::multiplayer::multiplayer_session_reference& session_reference() const; /// /// User supplied text added to explain the reason for the feedback. /// inline string_t reason_message() const; /// /// The Id of a resource that can be used as evidence for the feedback. Example: the Id of a video file. /// inline string_t evidence_resource_id() const; private: uint64_t m_xboxUserId; reputation_feedback_type m_reputationFeedbackType; xbox::services::multiplayer::multiplayer_session_reference m_sessionRef; std::string m_reasonMessage; std::string m_evidenceResourceId; friend class reputation_service; }; /// /// Manages the reputation service. /// class reputation_service { public: /// /// Submits reputation feedback on the specified user. /// /// The Xbox User ID of the user that reputation feedback is being submitted on. /// The reputation feedback type being submitted. /// The name of the multiplayer session directory session the user is sending feedback from. (Optional) /// User supplied text added to explain the reason for the feedback. (Optional) /// The Id of a resource that can be used as evidence for the feedback. Example: the Id of a video file. (Optional) /// The async object for notifying when the operation has been completed. /// Calls V100 POST /users/xuid({xuid})/feedback inline pplx::task> submit_reputation_feedback( _In_ const string_t& xboxUserId, _In_ reputation_feedback_type reputationFeedbackType, _In_ const string_t& sessionName = string_t(), _In_ const string_t& reasonMessage = string_t(), _In_ const string_t& evidenceResourceId = string_t() ); /// /// Submits batch reputation feedback on the specified users. /// /// A vector of reputation_feedback_item objects to submit reputation feedback on. /// The async object for notifying when the operation has been completed. /// Calls V101 POST /users/batchfeedback inline pplx::task> submit_batch_reputation_feedback( _In_ const std::vector& feedbackItems ); inline reputation_service(const reputation_service& other); inline reputation_service& operator=(reputation_service other); inline ~reputation_service(); private: inline reputation_service(XblContextHandle xblContextHandle); XblContextHandle m_xblContextHandle; friend xbox_live_context; }; #endif // !XSAPI_NO_PPL }}} #if !XSAPI_NO_PPL #include "impl/social.hpp" #endif ================================================ FILE: Include/xsapi-cpp/social_manager.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "xsapi-cpp/presence.h" #include "xsapi-c/social_manager_c.h" #include "xsapi-cpp/mem.h" #include "xsapi-cpp/types.h" namespace xbox { namespace services { namespace social { /// /// Contains classes and enumerations for more easily managing social /// scenarios. /// namespace manager { /// /// Detail level controls how much information is exposed in each xbox_live_social_graph_user /// Detail level can only be set on construction of social_manager /// enum class social_manager_extra_detail_level { /// Only get default PeopleHub information (presence, profile) no_extra_detail = static_cast(XblSocialManagerExtraDetailLevel::NoExtraDetail), /// Add extra detail for the title history for the users title_history_level = static_cast(XblSocialManagerExtraDetailLevel::TitleHistoryLevel), /// Add extra detail for the preferred color for the users preferred_color_level = static_cast(XblSocialManagerExtraDetailLevel::PreferredColorLevel) }; DEFINE_ENUM_FLAG_OPERATORS(social_manager_extra_detail_level); /// /// The filter level of information /// Title will only show users associated with a particular title /// enum class presence_filter { /// Unknown unknown = static_cast(XblPresenceFilter::Unknown), /// Is currently playing current title and is online title_online = static_cast(XblPresenceFilter::TitleOnline), /// Has played this title and is offline title_offline = static_cast(XblPresenceFilter::TitleOffline), /// Has played this title, is online but not currently playing this title title_online_outside_title = static_cast(XblPresenceFilter::TitleOnlineOutsideTitle), /// Everyone currently online all_online = static_cast(XblPresenceFilter::AllOnline), /// Everyone currently offline all_offline = static_cast(XblPresenceFilter::AllOffline), /// Everyone who has played or is playing the title all_title = static_cast(XblPresenceFilter::AllTitle), /// Everyone all = static_cast(XblPresenceFilter::All) }; /// /// The types of possible events /// enum class social_event_type { /// Users added to social graph users_added_to_social_graph = static_cast(XblSocialManagerEventType::UsersAddedToSocialGraph), /// Users removed from social graph users_removed_from_social_graph = static_cast(XblSocialManagerEventType::UsersRemovedFromSocialGraph), /// Users presence record has changed presence_changed = static_cast(XblSocialManagerEventType::PresenceChanged), /// Users profile information has changed profiles_changed = static_cast(XblSocialManagerEventType::ProfilesChanged), /// Relationship to users has changed social_relationships_changed = static_cast(XblSocialManagerEventType::SocialRelationshipsChanged), /// Social graph load complete from adding a local user local_user_added = static_cast(XblSocialManagerEventType::LocalUserAdded), /// /// Legacy. Formerly raised when a user's graph was destroyed, but is no longer raised. /// C++ interface will continue to raise this event (always on the next do_work call after removed_local_user) /// to maintain backward compatability. /// local_user_removed = static_cast(XblSocialManagerEventType::UnknownEvent) + 1, /// Xbox Social User Group load complete (will only trigger for views that take a list of users) social_user_group_loaded = static_cast(XblSocialManagerEventType::SocialUserGroupLoaded), /// Social user group updated social_user_group_updated = static_cast(XblSocialManagerEventType::SocialUserGroupUpdated), /// unknown. unknown = static_cast(XblSocialManagerEventType::UnknownEvent) }; /// /// Possible relationship types to filter by /// enum class relationship_filter { /// Friends of the user (user is following) friends = static_cast(XblRelationshipFilter::Friends), /// Favorites of the user favorite = static_cast(XblRelationshipFilter::Favorite) }; /// /// Identifies type of social user group created /// enum class social_user_group_type { /// Social user group based off of filters filter_type = static_cast(XblSocialUserGroupType::FilterType), /// Social user group based off of list of users user_list_type = static_cast(XblSocialUserGroupType::UserListType) }; /// /// Data about whether the user has played the title /// class title_history { public: /// /// Whether the user has played this title /// inline bool has_user_played() const; /// /// The last time the user had played /// inline utility::datetime last_time_user_played() const; /// /// Internal functions /// inline title_history(const XblTitleHistory& titleHistory); inline title_history(const title_history& other); inline title_history& operator=(title_history other); inline ~title_history() = default; private: std::shared_ptr m_owningPtr{ nullptr }; const XblTitleHistory* m_titleHistory; }; /// /// Preferred color for the user. Set via the shell. /// class preferred_color { public: /// /// Users primary color /// inline const char_t* primary_color() const; /// /// Users secondary color /// inline const char_t* secondary_color() const; /// /// Users tertiary color /// inline const char_t* tertiary_color() const; /// /// Does a comparison on if preferred colors are equal /// inline bool operator!=(const preferred_color& rhs) const; /// /// Internal function /// inline preferred_color(const XblPreferredColor& preferredColor); private: string_t m_primaryColor; string_t m_secondaryColor; string_t m_tertiaryColor; }; /// /// Social manager version of the presence title record /// Gives information about different titles presence information /// class social_manager_presence_title_record { public: /// /// The title ID. /// inline uint32_t title_id() const; /// /// The active state for the title. /// inline bool is_title_active() const; /// /// The formatted and localized presence string. /// inline const char_t* presence_text() const; /// /// The active state for the title. /// inline bool is_broadcasting() const; /// /// Device type /// inline xbox::services::presence::presence_device_type device_type() const; /// /// Whether or not this is the primary primary presence record /// inline bool is_primary() const; /// /// Internal functions /// inline social_manager_presence_title_record(const XblSocialManagerPresenceTitleRecord& titleRecord); inline social_manager_presence_title_record(const social_manager_presence_title_record& other); inline social_manager_presence_title_record& operator=(social_manager_presence_title_record other); inline ~social_manager_presence_title_record() = default; private: std::shared_ptr m_owningPtr{ nullptr }; const XblSocialManagerPresenceTitleRecord* m_titleRecord; string_t m_presenceText; }; /// /// Social manager presence record. Shows information on users current presence status and stores title records /// class social_manager_presence_record { public: /// /// The user's presence state. /// inline xbox::services::presence::user_presence_state user_state() const; /// /// Collection of presence title record objects returned by a request. /// inline const std::vector& presence_title_records() const; /// /// Returns whether the user is playing this title id /// inline bool is_user_playing_title(_In_ uint32_t titleId) const; /// /// Internal functions /// inline social_manager_presence_record(const XblSocialManagerPresenceRecord& presenceRecord); inline social_manager_presence_record(const social_manager_presence_record& other); inline social_manager_presence_record& operator=(social_manager_presence_record other); inline ~social_manager_presence_record() = default; private: std::shared_ptr m_owningPtr{ nullptr }; const XblSocialManagerPresenceRecord* m_presenceRecord; std::vector m_titleRecords; }; /// /// Xbox Social User that contains profile, presence, preferred color, and title history data /// class xbox_social_user { public: /// /// The xbox user id /// inline const char_t* xbox_user_id() const; /// /// Whether they are a favorite /// inline bool is_favorite() const; /// /// Whether the calling user is following them /// inline bool is_following_user() const; /// /// Whether they calling user is followed by this person /// inline bool is_followed_by_caller() const; /// /// The display name /// inline const char_t* display_name() const; /// /// The real name /// inline const char_t* real_name() const; /// /// The display pic uri /// inline const char_t* display_pic_url_raw() const; /// /// Whether to use the players avatar /// inline bool use_avatar() const; /// /// Players gamerscore /// inline const char_t* gamerscore() const; /// /// Players gamertag /// inline const char_t* gamertag() const; /// /// Modern gamertag for the player. Not guaranteed to be unique. /// inline const char_t* modern_gamertag() const; /// /// Suffix appended to modern gamertag to ensure uniqueness. May be empty in some cases. /// inline const char_t* modern_gamertag_suffix() const; /// /// Combined modern gamertag and suffix. Format will be "MGT#suffix". /// Guaranteed to be no more than 16 rendered characters. /// inline const char_t* unique_modern_gamertag() const; /// /// Users presence record /// inline const xbox::services::social::manager::social_manager_presence_record& presence_record() const; /// /// Title history for the user /// inline const xbox::services::social::manager::title_history& title_history() const; /// /// Preferred color for the user /// inline const preferred_color& preferred_color() const; /// /// Internal functions /// inline xbox_social_user(const XblSocialManagerUser& user); inline xbox_social_user(const xbox_social_user& other); inline xbox_social_user& operator=(xbox_social_user other); inline ~xbox_social_user() = default; private: std::shared_ptr m_owningPtr{ nullptr }; const XblSocialManagerUser* m_user; string_t m_gamerscore; string_t m_gamertag; string_t m_modernGamertag; string_t m_modernGamertagSuffix; string_t m_uniqueModernGamertag; string_t m_xboxUserId; string_t m_displayName; string_t m_realName; string_t m_displayPicUrlRaw; xbox::services::social::manager::title_history m_titleHistory; xbox::services::social::manager::preferred_color m_preferredColor; xbox::services::social::manager::social_manager_presence_record m_presenceRecord; }; /// /// Base class for social event args /// class social_event_args { public: social_event_args() {} virtual ~social_event_args() {} }; /// /// Contains an xbox user id for purposes of storing in stl data types /// class xbox_user_id_container { public: /// /// A users xbox user id /// inline const char_t* xbox_user_id() const; /// /// Internal function /// inline xbox_user_id_container(_In_ uint64_t xuid); private: string_t m_xboxUserId; }; /// /// An event that something in the social graph has changed /// class social_event { public: /// /// The user whose graph got changed /// inline xbox_live_user_t user() const; /// /// The type of event this is /// Tells the caller what to cast the event_args to /// inline social_event_type event_type() const; /// /// List of users this event affects /// inline std::vector users_affected() const; /// /// The social event args /// inline std::shared_ptr event_args() const; /// /// Error that occurred /// inline std::error_code err() const; /// /// Error message /// inline std::string err_message() const; /// /// Internal functions /// inline social_event(const XblSocialManagerEvent& event); inline social_event(const xbox_live_user_t& removedUser); private: XblSocialManagerEvent m_event{}; social_event_type m_eventType{ social_event_type::unknown }; std::shared_ptr m_args; }; /// /// A subset snapshot of the users social graph /// class xbox_social_user_group { public: /// /// Gets an up to date list of users from the social graph /// The returned value remains valid until the next call to do_work /// inline std::vector users() const; /// /// Returns copied group of users from social user group /// /// Vector of social users to populate /// An xbox_live_result representing the success of copying the users inline xbox_live_result get_copy_of_users( _Inout_ std::vector& socialUserVector ); /// /// Type of social user group /// inline social_user_group_type social_user_group_type() const; /// /// Users who are contained in this user group currently /// For list this is static, for filter this is dynamic and will change on do_work /// inline std::vector users_tracked_by_social_user_group() const; /// /// The local user who the user group is related to /// inline xbox_live_user_t local_user() const; /// /// Returns the presence filter used if group type is filter type /// inline presence_filter presence_filter_of_group() const; /// /// Returns the relationship filter used if group type is filter type /// inline relationship_filter relationship_filter_of_group() const; /// /// Returns users from xuids. Pointers become invalidated by next do_work /// inline std::vector get_users_from_xbox_user_ids(_In_ const std::vector& xboxUserIds); /// /// Internal function /// inline xbox_social_user_group(XblSocialManagerUserGroupHandle group); private: inline void PopulateUsers() const; XblSocialManagerUserGroupHandle m_group; mutable std::vector m_users; friend class social_manager; }; /// /// Social user group args for when a social user group loads /// class social_user_group_loaded_event_args : public social_event_args { public: /// /// The loaded social user group /// inline std::shared_ptr social_user_group() const; /// /// internal function /// inline social_user_group_loaded_event_args(std::shared_ptr group); private: std::shared_ptr m_group; }; /// /// Social Manager that handles core logic /// class social_manager { public: /// /// Gets the social_manager singleton instance /// inline static std::shared_ptr get_singleton_instance(); /// /// Create a social graph for the specified local user /// The result of a local user being added will be triggered through the local_user_added event in do_work /// /// Xbox Live User /// The level of verbosity that should be in the xbox_social_user /// An xbox_live_result to report any potential error inline xbox_live_result add_local_user( _In_ xbox_live_user_t user, _In_ social_manager_extra_detail_level extraDetailLevel ); /// /// Removes a social graph for the specified local user /// The result of a local user being added will be triggered through the local_user_removed event in do_work /// /// Xbox Live User /// An xbox_live_result to report any potential error inline xbox_live_result remove_local_user( _In_ xbox_live_user_t user ); /// /// Called whenever the title wants to update the social graph and get list of change events /// Must be called every frame for data to be up to date /// /// The list of what has changed in between social graph updates inline std::vector do_work(); /// /// Constructs a social Xbox Social User Group, which is a collection of users with social information /// The result of a user group being loaded will be triggered through the social_user_group_loaded event in do_work /// /// Xbox Live User /// The restriction of users based on their presence and title activity /// The restriction of users based on their relationship to the calling user /// An xbox_live_result of the created Xbox Social User Group inline xbox_live_result> create_social_user_group_from_filters( _In_ xbox_live_user_t user, _In_ presence_filter presenceDetailLevel, _In_ relationship_filter filter ); /// /// Constructs a social Xbox Social User Group, which is a collection of users with social information /// The result of a user group being loaded will be triggered through the social_user_group_loaded event in do_work /// /// Xbox Live User /// List of users to populate the Xbox Social User Group with. This is currently capped at 100 users total. /// An xbox_live_result of the created Xbox Social User Group inline xbox_live_result> create_social_user_group_from_list( _In_ xbox_live_user_t user, _In_ std::vector xboxUserIdList ); /// /// Destroys a created social Xbox Social User Group /// This will stop updating the Xbox Social User Group and remove tracking for any users the Xbox Social User Group holds /// /// The social Xbox Social User Group to destroy and stop tracking /// An xbox_live_result to report any potential error inline xbox_live_result destroy_social_user_group( _In_ std::shared_ptr socialUserGroup ); /// /// Returns all local users who have been added to the social manager /// inline std::vector local_users() const; /// /// Updates specified social user group to new group of users /// Does a diff to see which users have been added or removed from /// The result of a user group being updated will be triggered through the social_user_group_updated event in do_work /// /// The xbox social user group to add users to /// List of users to add to the xbox social user group. Total number of users not in social graph is limited at 100. /// An xbox_live_result representing the success of adding the users to the group inline xbox_live_result update_social_user_group( _In_ const std::shared_ptr& group, _In_ const std::vector& users ); /// /// Whether to enable social manager to poll every 30 seconds from the presence service /// /// Xbox Live User /// Whether or not polling should enabled /// An xbox_live_result representing the success enabling polling inline xbox_live_result set_rich_presence_polling_status( _In_ xbox_live_user_t user, _In_ bool shouldEnablePolling ); private: social_manager() = default; std::vector m_removedUsers; std::unordered_map> m_groups; friend class social_event; }; }}}} #include "impl/social_manager.hpp" ================================================ FILE: Include/xsapi-cpp/string_verify.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "xsapi-c/string_verify_c.h" namespace xbox { namespace services { class xbox_live_context; namespace system { /// Enumeration values that indicate the result code from string verification. /// These values are defined on the service side and should not be modified. /// enum class verify_string_result_code { /// No issues were found with the string. success = (int)XblVerifyStringResultCode::Success, /// The string contains offensive content. offensive = (int)XblVerifyStringResultCode::Offensive, /// The string is too long to verify. too_long = (int)XblVerifyStringResultCode::TooLong, /// An unknown error was encountered during string verification. unknown_error = (int) XblVerifyStringResultCode::UnknownError }; /// /// Contains information about the results of a string verification. /// class verify_string_result { public: /// /// The result code for the string verification. /// inline verify_string_result_code result_code() const; /// /// first_offending_substring() contains the first offending substring if the /// result code is verify_string_result_code::offensive. /// inline const string_t& first_offending_substring() const; /// /// Internal function /// inline verify_string_result(); /// /// Internal function /// inline verify_string_result( XblVerifyStringResultCode resultCode, const char* firstOffendingSubstring ); private: verify_string_result_code m_resultCode; string_t m_first_offending_substring; }; /// /// Provides methods to validate a string for use with Xbox live. /// class string_service { public: /// /// Verifies if a string contains acceptable text for use with Xbox Live. /// /// The string to verify. /// /// A verify_string_result object which indicates if the string contains unacceptable text. /// /// /// Returns a concurrency::task<T> object that represents the state of the asynchronous operation. /// /// Calls V2 GET /system/strings/validate /// inline pplx::task> verify_string(_In_ const string_t& stringToVerify); /// /// Verifies a collection of strings to see if each string contains acceptable text for use with Xbox Live. /// /// The collection of strings to verify. /// /// A collection of verify_string_result objects which indicate if the strings contain unacceptable text. /// /// /// Returns a concurrency::task<T> object that represents the state of the asynchronous operation. /// /// Calls V2 GET /system/strings/validate /// inline pplx::task>> verify_strings(_In_ const std::vector& stringsToVerify); inline string_service(const string_service& other); inline string_service& operator=(string_service other); inline ~string_service(); private: inline string_service(_In_ XblContextHandle contextHandle); XblContextHandle m_xblContext; friend xbox_live_context; }; }}} #include "impl/string_verify.hpp" ================================================ FILE: Include/xsapi-cpp/system.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "types.h" #include "errors.h" #include "xbox_live_context_settings.h" #include "xbox_live_app_config.h" #ifdef __OBJC__ #import #endif #if !HC_PLATFORM_IS_MICROSOFT && !XSAPI_NO_PPL #include "pplx/pplxtasks.h" #endif NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN class XboxLiveContextSettings; #if !defined(XSAPI_UNIT_TESTS) && !XSAPI_NO_PPL namespace events { class events_service; } #endif namespace multiplayer { namespace manager { class multiplayer_client_manager; }} NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN /// /// Configuration information for Xbox Live service objects. /// namespace system { class xbox_live_wns_event_args { public: /// /// Returns the xbox user id for the WNS event /// const string_t& xbox_user_id() const { return m_xbox_user_id; } /// /// Returns the notification type /// const string_t& notification_type() const { return m_notification_type; } /// /// Returns the full notification content /// const string_t& notification_content() const { return m_notification_content; } /// /// Internal function /// xbox_live_wns_event_args( _In_ string_t xbox_user_id, _In_ string_t notification_type, _In_ string_t notification_content ) : m_xbox_user_id(std::move(xbox_user_id)), m_notification_type(std::move(notification_type)), m_notification_content(std::move(notification_content)) {} private: string_t m_xbox_user_id; string_t m_notification_type; string_t m_notification_content; }; class xbox_live_services_settings : public std::enable_shared_from_this { public: /// /// Gets the singleton instance /// inline static std::shared_ptr get_singleton_instance(_In_ bool createIfRequired = true); /// /// Used by titles to register memory allocation hooks that are used by XSAPI when it /// needs to allocate a large block of memory such as SocialManager which uses a large block /// of memory to keep track of the friends list. /// /// The title's allocation function. Input is size of memory block that's being requested. Return is pointer to the allocated memory block /// The title's memory free function. Input is address of memory to free /// /// If titles choose not to provide their own allocation hooks, these system default allocators will be used instead. /// To unwire your hooks, call the same routine with nullptr passed in for both parameters. /// It is important to provide an implementation for both memAllocHandler and memFreeHandler if you hook them; /// hooking only one of them will be considered an error. /// inline static void set_memory_allocation_hooks( _In_ const std::function<_Ret_maybenull_ _Post_writable_byte_size_(dwSize) void*(_In_ size_t dwSize)>& memAllocHandler, _In_ const std::function& memFreeHandler ); /// /// Deprecated. XSAPI is using libHttpClient logging. A logging handler can be added using HCTraceSetClientCallback. /// _XSAPICPP_DEPRECATED inline function_context add_logging_handler(_In_ std::function handler); /// /// Deprecated. See above. /// _XSAPICPP_DEPRECATED inline void remove_logging_handler(_In_ function_context context); /// /// Indicates the level of debug messages to send to the debugger's Output window. /// inline xbox_services_diagnostics_trace_level diagnostics_trace_level() const; /// /// Sets the level of debug messages to send to the debugger's Output window. /// inline void set_diagnostics_trace_level(_In_ xbox_services_diagnostics_trace_level value); /// /// Deprecated. Registering WNS callbacks though XSAPI is no longer supported. /// _XSAPICPP_DEPRECATED inline function_context add_wns_handler(_In_ const std::function& handler); /// /// Deprecated. Registering WNS callbacks though XSAPI is no longer supported. /// _XSAPICPP_DEPRECATED inline void remove_wns_handler(_In_ function_context context); private: xbox_live_services_settings() = default; }; } // namespace system NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END #if !XSAPI_NO_PPL #include "impl/system.hpp" #endif ================================================ FILE: Include/xsapi-cpp/title_callable_ui.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #if !XSAPI_NO_PPL #include "xsapi-cpp/multiplayer.h" #endif // !XSAPI_NO_PPL namespace xbox { namespace services { namespace system { /// List of gaming privilege that a user can have. enum class gaming_privilege { /// The user can broadcast live gameplay. broadcast = 190, /// The user can view other user's friends list if this privilege is present. view_friends_list = 197, /// The user can upload recorded in-game videos to the cloud if this privilege is present. Viewing GameDVRs is subject to privacy controls. game_dvr = 198, /// Kinect recorded content can be uploaded to the cloud for the user and made accessible to anyone if this privilege is present. Viewing other user's Kinect content is subject to a privacy setting. share_kinect_content = 199, /// The user can join a party session if this privilege is present multiplayer_parties = 203, /// The user can participate in voice chat during parties and multiplayer game sessions if this privilege is present. Communicating with other users is subject to additional privacy permission checks communication_voice_ingame = 205, /// The user can use voice communication with Skype on Xbox One if this privilege is present communication_voice_skype = 206, /// The user can allocate a cloud compute cluster and manage a cloud compute cluster for a hosted game session if this privilege is present cloud_gaming_manage_session = 207, /// The user can join a cloud compute session if this privilege is present cloud_gaming_join_session = 208, /// The user can save games in cloud title storage if this privilege is present cloud_saved_games = 209, /// The user can share content with others if this privilege is present share_content = 211, /// The user can purchase, download and launch premium content available with the Xbox LIVE Gold subscription if this privilege is present premium_content = 214, /// The user can purchase and download premium subscription content and use premium subscription features when this privilege is present subscription_content = 219, /// The user is allowed to share progress information on social networks when this privilege is present social_network_sharing = 220, /// The user can access premium video services if this privilege is present premium_video = 224, /// The user can use video communication with Skype or other providers when this privilege is present. Communicating with other users is subject to additional privacy permission checks video_communications = 235, /// The user is authorized to purchase content when this privilege is present purchase_content = 245, /// The user is authorized to download and view online user created content when this privilege is present. user_created_content = 247, /// The user is authorized to view other user's profiles when this privilege is present. Viewing other user's profiles is subject to additional privacy checks profile_viewing = 249, /// The user can use asynchronous text messaging with anyone when this privilege is present. Extra privacy permissions checks are required to determine who the user is authorized to communicate with. Communicating with other users is subject to additional privacy permission checks communications = 252, /// The user can join a multiplayer sessions for a game when this privilege is present. multiplayer_sessions = 254, /// The user can follow other Xbox LIVE users and add Xbox LIVE friends when this privilege is present. add_friend = 255 }; #if !XSAPI_NO_PPL /// This class contains functions used for displaying stock UI during a game such as showing a people picker. class title_callable_ui { public: #if HC_PLATFORM != HC_PLATFORM_UWP /// /// Shows a picker UI that allows a person playing the game to select players /// from a presented list of other people. /// After the operation is complete, the list of selected Xbox User IDs is returned to the calling app. /// /// The prompt display text. /// A list of Xbox User IDs which the user can select from. /// A list of Xbox User IDs which will be pre-selected. /// The minimum number of people the user must select. /// The maximum number of people the user can select. /// /// Returns a pplx::task<T> object that represents the state of the asynchronous operation. /// The task completes when the UI is closed. /// result.payload() contains the list of users that were selected by the player. /// result.err() contains the error based on what happened in the case of an error. /// static pplx::task>> show_player_picker_ui( _In_ const string_t& promptDisplayText, _In_ const std::vector& xboxUserIds, _In_ const std::vector& preselectedXboxUserIds, _In_ uint32_t minSelectionCount, _In_ uint32_t maxSelectionCount ); /// /// Shows a picker UI populated from the selected user's friend list and suggested friend list. /// After selection, the user can send an invitation to play a game and/or party chat for a /// specified game session to the selected people. /// /// A reference to the multiplayer session to invite people to. /// The ID of the custom invite string that is displayed with /// the invite notification.The ID must match the ID that is assigned to the custom invite string /// in the title's multiplayer service configuration. The format of the parameter is "///{id}", /// where {id} is replaced with the ID of the custom string. For example, if the ID of the custom string /// "Play Capture the Flag" is 1, then you would set this parameter to "///1" in order to display the /// "Play Capture the Flag" custom string in the game invite. /// Pass an empty string if you don't want a custom string added to the invite. /// The custom activation context that is available to the invitee in the /// activation URI for an invite. The custom activation context string must be URL-safe and binary content /// should be encoded with URL-safe base64 encoding. The maximum length is 160 characters. /// /// Returns a pplx::task<T> object that represents the state of the asynchronous operation. /// The task completes when the UI is closed. /// result.err() contains the error based on what happened in the case of an error. /// static pplx::task> show_game_invite_ui( _In_ const xbox::services::multiplayer::multiplayer_session_reference& sessionReference, _In_ const string_t& invitationDisplayText, _In_ const string_t& contextStringId = string_t() ); /// /// Shows UI displaying the profile card for a specified user. /// /// The Xbox User ID to show information about. /// /// Returns a pplx::task<T> object that represents the state of the asynchronous operation. /// The task completes when the UI is closed. /// result.err() contains the error based on what happened in the case of an error. /// static pplx::task> show_profile_card_ui( _In_ const string_t& targetXboxUserId #if !HC_PLATFORM_IS_MICROSOFT , _In_ xbox_live_user_t user #endif ); /// /// Shows UI for adding or removing a specified person to or from the requesting user's friend list. /// /// The Xbox User ID to show information about. /// /// Returns a pplx::task<T> object that represents the state of the asynchronous operation. /// The task completes when the UI is closed. /// result.err() contains the error based on what happened in the case of an error. /// static pplx::task> show_change_friend_relationship_ui( _In_ const string_t& targetXboxUserId ); /// /// Shows UI presenting the requesting user's achievements for the specified title. /// /// The Xbox titleId to show information about. /// /// Returns a pplx::task<T> object that represents the state of the asynchronous operation. /// The task completes when the UI is closed. /// result.err() contains the error based on what happened in the case of an error. /// static pplx::task> show_title_achievements_ui( _In_ uint32_t titleId ); #endif #if defined(_APISET_TARGET_VERSION_WIN10_RS3) /// /// Shows UI displaying the friend finder app, so the user can get more friends /// /// /// Returns a pplx::task<T> object that represents the state of the asynchronous operation. /// The task completes when the UI is closed. /// result.err() contains the error based on what happened in the case of an error. /// static pplx::task> show_friend_finder_ui( ); /// /// Invokes the Xbox App to show full user profile for the target user /// /// The Xbox target xuid to show the profile for. /// /// result.err() contains the error based on what happened in the case of an error. /// static pplx::task> show_user_profile_ui(_In_ const string_t& targetXboxUserId); /// /// Shows UI displaying the title app for the calling application. /// /// /// result.err() contains the error based on what happened in the case of an error. /// static pplx::task> show_title_hub_ui( #if HC_PLATFORM == HC_PLATFORM_UWP _In_opt_ Windows::System::User^ user = nullptr #endif ); /// /// Shows UI displaying the user settings /// /// /// result.err() contains the error based on what happened in the case of an error. /// static pplx::task> show_user_settings_ui( ); /// /// Shows UI displaying a dialog to customize the user's profile /// /// /// result.err() contains the error based on what happened in the case of an error. /// static pplx::task> show_customize_user_profile_ui( ); #elif !HC_PLATFORM_IS_MICROSOFT /// /// Invokes the Xbox App to show full user profile for the target user /// /// The Xbox target xuid to show the profile for. /// /// result.err() contains the error based on what happened in the case of an error. /// static pplx::task> show_user_profile_ui(_In_ const string_t& targetXboxUserId); /// /// Invokes the Xbox App to show the title app for the calling application. /// /// /// result.err() contains the error based on what happened in the case of an error. /// static pplx::task> show_title_hub_ui(); /// /// Invokes the Xbox App to show the user settings /// /// /// result.err() contains the error based on what happened in the case of an error. /// static pplx::task> show_user_settings_ui(); #endif #if !HC_PLATFORM_IS_MICROSOFT /// /// Invokes the Xbox App to show add friends functionality. /// /// /// result.err() contains the error based on what happened in the case of an error. /// static pplx::task> show_add_friends_ui(); #endif private: }; #endif // !XSAPI_NO_PPL }}} ================================================ FILE: Include/xsapi-cpp/title_storage.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include #include "xsapi-c/title_storage_c.h" struct XblContext; namespace xbox { namespace services { class http_call; class xbox_live_context; }} namespace xbox { namespace services { /// /// Classes for handling title data in the cloud. /// namespace title_storage { class title_storage_service; /// Defines values used to indicate title storage type. enum class title_storage_type { /// /// Per-user data storage such as game state or game settings that can be only be accessed by Xbox One. /// User restrictions can be configured to public or owner only in the service configuration. /// trusted_platform_storage = static_cast(XblTitleStorageType::TrustedPlatformStorage), /// /// Global data storage. This storage type is only writable via title configuration sites or Xbox Live developer tools. /// Any platform may read from this storage type. Data could be rosters, maps, challenges, art resources, etc. /// global_storage = static_cast(XblTitleStorageType::GlobalStorage), /// /// Per-user data storage such as game state or game settings the can be accessed by Xbox One, Windows 10, and Windows Phone 10 devices /// User restrictions can be configured to public or owner only in the service configuration. /// universal = static_cast(XblTitleStorageType::Universal) }; /// Defines values used to indicate title storage blob type. enum class title_storage_blob_type { /// Unknown blob type. unknown = static_cast(XblTitleStorageBlobType::Unknown), /// Binary blob type. binary = static_cast(XblTitleStorageBlobType::Binary), /// JSON blob type. json = static_cast(XblTitleStorageBlobType::Json), /// Config blob type. config = static_cast(XblTitleStorageBlobType::Config) }; /// Defines values used to indicate the ETag match condition used when downloading, uploading or deleting title storage data. enum class title_storage_e_tag_match_condition { /// There is no match condition. not_used = static_cast(XblTitleStorageETagMatchCondition::NotUsed), /// Perform the request if the Etag value specified matches the service value. if_match = static_cast(XblTitleStorageETagMatchCondition::IfMatch), /// Perform the request if the Etag value specified does not match the service value. if_not_match = static_cast(XblTitleStorageETagMatchCondition::IfNotMatch) }; /// /// Returns the amount of storage space allocated and used. /// class title_storage_quota { public: /// /// The service configuration ID associated with the quota. /// inline const string_t& service_configuration_id() const; /// /// The TitleStorageType associated with the quota. /// inline title_storage_type storage_type() const; /// /// The Xbox User ID associated with the quota if StorageType is TrustedPlatformStorage or JsonStorage, otherwise null. /// inline string_t xbox_user_id() const; /// /// Number of bytes used in title storage of type StorageType. /// inline uint64_t used_bytes() const; /// /// Maximum number of bytes that can be used in title storage of type StorageType. /// Note that this is a soft limit and the used bytes may actually exceed this value. /// inline uint64_t quota_bytes() const; // Internal title_storage_quota() = default; inline title_storage_quota(string_t scid, title_storage_type storageType, uint64_t xuid, uint64_t usedByted, uint64_t quotaBytes); private: string_t m_serviceConfigurationId; title_storage_type m_storageType{}; uint64_t m_xboxUserId{ 0 }; uint64_t m_usedBytes{ 0 }; uint64_t m_quotaBytes{ 0 }; }; /// /// Metadata about a blob. /// class title_storage_blob_metadata { public: /// /// Initializes a new instance of the title_storage_blob_metadata class. /// /// The service configuration ID (SCID) of the title /// The TitleStorageType to get blob metadata objects for. Valid values are TrustedPlatormStorage, JsonStorage and GlobalStorage. /// The full path to to the blob. examples: "gameconfig.json" and "user/settings/playerconfiguration.json". /// The TitleStorageBlobType of this object. Valid values are Binary, Json and Config. /// The Xbox User ID of the title storage to enumerate. Ignored when dealing with GlobalStorage, so passing nullptr is acceptable in that case. (Optional) /// /// All other properties of this class are optional. To initialize optional properties, use the other constructors. /// ClientTimestamp.UniversalTime will be initialized to 0. Length is initialized to 0. /// title_storage_blob_metadata objects retrieved using TitleStorageService::GetBlobMetadataAsync will have current Length and ETag values. /// inline title_storage_blob_metadata( _In_ string_t serviceConfigurationId, _In_ title_storage_type storageType, _In_ string_t blobPath, _In_ title_storage_blob_type blobType, _In_ string_t xboxUserId ); /// /// Initializes a new instance of the title_storage_blob_metadata class including support for all optional properties except ClientTimestamp. /// /// The service configuration ID (SCID) of the title /// The TitleStorageType to get blob metadata objects for. Valid values are TrustedPlatormStorage, JsonStorage and GlobalStorage. /// The full path to to the blob. examples: "gameconfig.json" and "user/settings/playerconfiguration.json". /// The TitleStorageBlobType of this object. Valid values are Binary, Json and Config. /// The Xbox User ID of the title storage to enumerate. Ignored when dealing with GlobalStorage, so passing nullptr is acceptable in that case. (Optional) /// A display name suitable for displaying to the user. (Optional) /// An ETag value to be associated with this instance. It is used for upload, download and delete operations. (Optional) /// /// ClientTimestamp.UniversalTime will be initialized to 0. Length is initialized to 0. /// title_storage_blob_metadata objects retrieved using TitleStorageService::GetBlobMetadataAsync will have current Length and ETag values. /// inline title_storage_blob_metadata( _In_ string_t serviceConfigurationId, _In_ title_storage_type storageType, _In_ string_t blobPath, _In_ title_storage_blob_type blobType, _In_ string_t xboxUserId, _In_ string_t displayName, _In_ string_t eTag ); /// /// Initializes a new instance of the title_storage_blob_metadata class including support for all optional properties. /// /// The service configuration ID (SCID) of the title /// The TitleStorageType to get blob metadata objects for. Valid values are TrustedPlatormStorage, JsonStorage and GlobalStorage. /// The full path to to the blob. examples: "gameconfig.json" and "user/settings/playerconfiguration.json". /// The TitleStorageBlobType of this object. Valid values are Binary, Json and Config. /// The Xbox User ID of the title storage to enumerate. Ignored when dealing with GlobalStorage, so passing nullptr is acceptable in that case. (Optional) /// A display name suitable for displaying to the user. (Optional) /// An ETag value to be associated with this instance. It is used for upload, download and delete operations. (Optional) /// A client provided timestamp value to be associated with this instance. /// /// Length is initialized to 0. /// title_storage_blob_metadata objects retrieved using TitleStorageService::GetBlobMetadataAsync will have current Length and ETag values. /// inline title_storage_blob_metadata( _In_ string_t serviceConfigurationId, _In_ title_storage_type storageType, _In_ string_t blobPath, _In_ title_storage_blob_type blobType, _In_ string_t xboxUserId, _In_ string_t displayName, _In_ string_t eTag, _In_ utility::datetime clientTimestamp ); /// /// Blob path is a unique string that conforms to a SubPath\file format (example: "foo\bar\blob.txt"). /// inline string_t blob_path() const; /// /// Type of blob data. Possible values are: Binary, Json, and Config. /// inline title_storage_blob_type blob_type() const; /// /// Type of storage. /// inline title_storage_type storage_type() const; /// /// [optional] Friendly display name to show in app UI. /// inline string_t display_name() const; /// /// ETag for the file used in read and write requests. /// inline string_t e_tag() const; /// /// [optional] Timestamp assigned by the client. /// inline utility::datetime client_timestamp() const; /// /// [optional] Timestamp assigned by the client. /// inline void set_client_timestamp(_In_ utility::datetime value); /// /// Gets the number of bytes of the blob data. /// inline uint64_t length() const; /// /// The service configuration ID of the title /// inline string_t service_configuration_id() const; /// /// The Xbox User ID of the player this file belongs to. /// This value will be null for Global and Session files. /// inline string_t xbox_user_id() const; // Internal title_storage_blob_metadata() = default; inline title_storage_blob_metadata(XblTitleStorageBlobMetadata blobMetadata); private: XblTitleStorageBlobMetadata m_blobMetadata{}; friend title_storage_service; }; /// /// Metadata about blob data returned from the cloud. /// class title_storage_blob_metadata_result { public: /// /// Collection of title_storage_blob_metadata objects returned by a service request /// inline const std::vector& items() const; /// /// Returns an title_storage_blob_metadata_result object containing the next page of title_storage_blob_metadata objects /// /// The maximum number of items the result can contain. Pass 0 to attempt to retrieve all items. /// title_storage_blob_metadata_result object for the next page. inline pplx::task> get_next( _In_ uint32_t maxItems ) const; /// /// Indicates if there is additional data to retrieve from a get_next call /// inline bool has_next() const; // Internal title_storage_blob_metadata_result() = default; inline title_storage_blob_metadata_result(XblTitleStorageBlobMetadataResultHandle handle); inline title_storage_blob_metadata_result(_In_ const title_storage_blob_metadata_result& other); inline title_storage_blob_metadata_result& operator=(title_storage_blob_metadata_result other); inline ~title_storage_blob_metadata_result(); private: XblTitleStorageBlobMetadataResultHandle m_handle{ nullptr }; std::vector m_items; }; /// /// Blob data returned from the cloud. /// class title_storage_blob_result { public: /// /// The contents of the title storage blob. /// inline std::shared_ptr> const blob_buffer() const; /// /// Updated title_storage_blob_metadata object following an upload or download. /// inline const title_storage_blob_metadata& blob_metadata() const; // Internal title_storage_blob_result() = default; inline title_storage_blob_result(std::shared_ptr> blobBuffer, title_storage_blob_metadata blobMetadata); private: std::shared_ptr> m_blobBuffer; title_storage_blob_metadata m_blobMetadata; }; /// /// Services that manage title storage. /// class title_storage_service { public: /// /// Gets title storage quota information for the specified service configuration and storage type. /// For user storage types (TrustedPlatform and Json) the request will be made for the calling user's /// Xbox user Id. /// /// The service configuration ID (SCID) of the title /// The storage type to get quota information for. /// title_storage_quota object containing the quota information. /// Calls /// V1 GET trustedplatform/users/xuid({xuid})/scids/{scid} or /// V1 GET json/users/xuid({xuid})/scids/{scid} or /// V1 GET global/scids/{scid} /// inline pplx::task> get_quota( _In_ string_t serviceConfigurationId, _In_ title_storage_type storageType ); /// /// Gets a list of blob metadata objects under a given path for the specified service configuration, storage type and storage ID. /// /// The service configuration ID (SCID) of the title /// The storage type to get blob metadata objects for. /// The root path to enumerate. Results will be for blobs contained in this path and all subpaths. (Optional) /// The Xbox User ID of the title storage to enumerate. Ignored when enumerating GlobalStorage, so passing nullptr is acceptable. (Optional) /// The number of items to skip before returning results. (Optional) /// The maximum number of items to return. (Optional) /// title_storage_blob_metadata_result object containing the list of enumerated blob metadata objects. /// Calls /// V1 GET trustedplatform/users/xuid({xuid})/scids/{scid}/data/{path}?maxItems={maxItems}[skipItems={skipItems}] or /// V1 GET json/users/xuid({xuid})/scids/{scid}/data/{path}?maxItems={maxItems}[skipItems={skipItems}] or /// V1 GET global/scids/{scid}/data/{path}?maxItems={maxItems}[skipItems={skipItems}] /// inline pplx::task> get_blob_metadata( _In_ string_t serviceConfigurationId, _In_ title_storage_type storageType, _In_ string_t blobPath = string_t(), _In_ string_t xboxUserId = string_t(), _In_ uint32_t skipItems = 0, _In_ uint32_t maxItems = 0 ); /// /// Deletes a blob from title storage. /// /// The blob metadata for the title storage blob to delete. /// Specifies whether or not to have the delete operation check that the ETag matches before deleting the blob. /// Calls /// V1 DELETE trustedplatform/users/xuid({xuid})/scids/{scid}/data/{path},{type} or /// V1 DELETE json/users/xuid({xuid})/scids/{scid}/data/{path},{type} or /// V1 DELETE sessions/{sessionId}/scids/{scid}/data/{path},{type} /// inline pplx::task> delete_blob( _In_ const title_storage_blob_metadata& blobMetadata, _In_ bool deleteOnlyIfEtagMatches ); /// /// Downloads blob data from title storage. /// /// The blob metadata for the title storage blob to download. /// The client provided buffer to store the downloaded blob data in. This buffer needs to be large enough to store the blob being downloaded. /// If necessary, the length required for the buffer can be retrieved by getting the blob metadata. /// The ETag match condition used to determine if the blob should be downloaded. /// ConfigStorage filter string or JSONStorage json property name string to filter. (Optional) /// TitleStorageBlobResult object containing the client provided blob buffer and an updated title_storage_blob_metadata object. /// The metadata object will contain updated ETag and Length properties. /// /// This method will throw ERROR_INSUFFICIENT_BUFFER (0x8007007A) if the blobBuffer doesn't have enough capacity to hold the blob data. /// Calls V1 GET trustedplatform/users/xuid({xuid})/scids/{scid}/data/{path},{type} or /// V1 GET json/users/xuid({xuid})/scids/{scid}/data/{path},{type} or /// V1 GET global/scids/{scid}/data/{path},{type} or /// V1 GET sessions/{sessionId}/scids/{scid}/data/{path},{type} /// inline pplx::task> download_blob( _In_ title_storage_blob_metadata blobMetadata, _In_ std::shared_ptr> blobBuffer, _In_ title_storage_e_tag_match_condition etagMatchCondition, _In_ string_t selectQuery = string_t() ); /// /// Downloads blob data from title storage. /// /// The blob metadata for the title storage blob to download. /// The client provided buffer to store the downloaded blob data in. This buffer needs to be large enough to store the blob being downloaded. /// If necessary, the length required for the buffer can be retrieved by getting the blob metadata. /// The ETag match condition used to determine if the blob should be downloaded. /// ConfigStorage filter string or JSONStorage json property name string to filter. (Optional) /// The preferred download block size in bytes for binary blobs. /// TitleStorageBlobResult object containing the client provided blob buffer and an updated title_storage_blob_metadata object. /// The metadata object will contain updated ETag and Length properties. /// /// This method will throw ERROR_INSUFFICIENT_BUFFER (0x8007007A) if the blobBuffer doesn't have enough capacity to hold the blob data. /// Calls V1 GET trustedplatform/users/xuid({xuid})/scids/{scid}/data/{path},{type} or /// V1 GET json/users/xuid({xuid})/scids/{scid}/data/{path},{type} or /// V1 GET global/scids/{scid}/data/{path},{type} or /// V1 GET sessions/{sessionId}/scids/{scid}/data/{path},{type} /// inline pplx::task> download_blob( _In_ title_storage_blob_metadata blobMetadata, _In_ std::shared_ptr> blobBuffer, _In_ title_storage_e_tag_match_condition etagMatchCondition, _In_ string_t selectQuery, _In_ uint32_t preferredDownloadBlockSize ); /// /// Uploads blob data to title storage. /// /// Contains properties required to upload the buffer to title storage. Uploads require a service configuration Id, blob path, blob type and storage type at a minimum. /// The buffer containing the blob data to upload. This buffer must be available for the duration of the async operation. Clients should not modify the buffer while an upload is in progress. /// The ETag match condition used to determine if the blob data should be uploaded. /// The preferred upload block size in bytes for binary blobs. Binary blobs will be /// uploaded in multiple chunks of this size if they exceed it. Larger sizes are preferred by the service. /// If timeouts occur, the app should retry with a smaller size. Block size must be within the 1K to 4MB range. This method /// will use a default size if this parameter is not within the acceptable range. The current minimum size is 1024 bytes, /// maximum size is 4194304 bytes and the default size is 262144 bytes. /// /// title_storage_blob_metadata object with updated Etag and Length properties. /// /// V1 PUT json/users/xuid({xuid})/scids/{scid}/data/{path},{type} or /// V1 PUT global/scids/{scid}/data/{path},{type} or /// V1 PUT sessions/{sessionId}/scids/{scid}/data/{path},{type} /// inline pplx::task> upload_blob( _In_ title_storage_blob_metadata blobMetadata, _In_ std::shared_ptr> blobBuffer, _In_ title_storage_e_tag_match_condition etagMatchCondition, _In_ uint32_t preferredUploadBlockSize = XBL_TITLE_STORAGE_MIN_UPLOAD_BLOCK_SIZE ); inline title_storage_service(const title_storage_service& other); inline title_storage_service& operator=(title_storage_service other); inline ~title_storage_service(); private: inline title_storage_service(_In_ XblContextHandle contextHandle); XblContextHandle m_xblContext{ nullptr }; friend xbox_live_context; }; }}} #include "impl/title_storage.hpp" ================================================ FILE: Include/xsapi-cpp/types.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include #include #include #include "xsapi-c/types_c.h" #if HC_PLATFORM_IS_MICROSOFT #define _XSAPICPP_DEPRECATED __declspec(deprecated) #else #define _XSAPICPP_DEPRECATED __attribute__ ((deprecated)) #endif #if !HC_PLATFORM_IS_MICROSOFT || (defined(_MSC_VER) && (_MSC_VER >= 1900)) // VS2013 doesn't support default move constructor and assignment, so we implemented this. // However, a user defined move constructor and assignment will implicitly delete default copy // constructor and assignment in other compiler like clang. So we only define this in Win32 under VS2013 #define DEFAULT_MOVE_ENABLED #endif typedef void* function_context; #if HC_PLATFORM_IS_MICROSOFT typedef wchar_t char_t; typedef std::wstring string_t; typedef std::wstringstream stringstream_t; typedef std::wregex regex_t; typedef std::wsmatch smatch_t; #else typedef char char_t; typedef std::string string_t; typedef std::stringstream stringstream_t; typedef std::regex regex_t; typedef std::smatch smatch_t; #endif #if _MSC_VER <= 1800 typedef std::chrono::system_clock chrono_clock_t; #else typedef std::chrono::steady_clock chrono_clock_t; #endif // Forward declarations namespace xbox { namespace services { class xbox_live_context_settings; class JsonAllocator; namespace system { class xbox_live_user; } } } #if HC_PLATFORM != HC_PLATFORM_XDK // SSL client certificate context #if HC_PLATFORM_IS_MICROSOFT #include typedef PCCERT_CONTEXT cert_context; #endif #endif #if HC_PLATFORM == HC_PLATFORM_UWP typedef Windows::System::User^ user_creation_context; #else typedef void* user_creation_context; #endif typedef XblUserHandle xbox_live_user_t; #define NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN namespace xbox { namespace services { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END }} #define NAMESPACE_MICROSOFT_XBOX_SERVICES_BEGIN namespace Microsoft { namespace Xbox { namespace Services { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_END }}} #define NAMESPACE_MICROSOFT_XBOX_SERVICES_SYSTEM_CPP_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN namespace system { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_SYSTEM_CPP_END NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_SYSTEM_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_BEGIN namespace System { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_SYSTEM_END NAMESPACE_MICROSOFT_XBOX_SERVICES_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_SOCIAL_CPP_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN namespace social { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_SOCIAL_CPP_END NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_SOCIAL_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_BEGIN namespace Social { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_SOCIAL_END NAMESPACE_MICROSOFT_XBOX_SERVICES_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_ACHIEVEMENTS_CPP_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN namespace achievements { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_ACHIEVEMENTS_CPP_END NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_ACHIEVEMENTS_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_BEGIN namespace Achievements { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_ACHIEVEMENTS_END NAMESPACE_MICROSOFT_XBOX_SERVICES_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_ACHIEVEMENTS_MANAGER_CPP_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN namespace achievements { namespace manager { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_ACHIEVEMENTS_MANAGER_CPP_END NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END } } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_ACHIEVEMENTS_MANAGER_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_BEGIN namespace Achievements { namespace Manager { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_ACHIEVEMENTS_MANAGER_END NAMESPACE_MICROSOFT_XBOX_SERVICES_END } } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_LEADERBOARD_CPP_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN namespace leaderboard { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_LEADERBOARD_CPP_END NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_LEADERBOARD_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_BEGIN namespace Leaderboard { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_LEADERBOARD_END NAMESPACE_MICROSOFT_XBOX_SERVICES_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_USERSTATISTICS_CPP_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN namespace user_statistics { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_USERSTATISTICS_CPP_END NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_USERSTATISTICS_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_BEGIN namespace UserStatistics { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_USERSTATISTICS_END NAMESPACE_MICROSOFT_XBOX_SERVICES_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_CPP_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN namespace multiplayer { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_CPP_END NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_BEGIN namespace Multiplayer { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_END NAMESPACE_MICROSOFT_XBOX_SERVICES_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_MATCHMAKING_CPP_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN namespace matchmaking { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_MATCHMAKING_CPP_END NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_MATCHMAKING_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_BEGIN namespace Matchmaking { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_MATCHMAKING_END NAMESPACE_MICROSOFT_XBOX_SERVICES_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_MARKETPLACE_CPP_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN namespace marketplace { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_MARKETPLACE_CPP_END NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_MARKETPLACE_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_BEGIN namespace Marketplace { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_MARKETPLACE_END NAMESPACE_MICROSOFT_XBOX_SERVICES_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_PRIVACY_CPP_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN namespace privacy { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_PRIVACY_CPP_END NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_PRIVACY_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_BEGIN namespace Privacy { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_PRIVACY_END NAMESPACE_MICROSOFT_XBOX_SERVICES_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_RTA_CPP_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN namespace real_time_activity { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_RTA_CPP_END NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_RTA_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_BEGIN namespace RealTimeActivity { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_RTA_END NAMESPACE_MICROSOFT_XBOX_SERVICES_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_PRESENCE_CPP_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN namespace presence { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_PRESENCE_CPP_END NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_PRESENCE_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_BEGIN namespace Presence { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_PRESENCE_END NAMESPACE_MICROSOFT_XBOX_SERVICES_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_GAMESERVERPLATFORM_CPP_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN namespace game_server_platform { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_GAMESERVERPLATFORM_CPP_END NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_GAMESERVERPLATFORM_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_BEGIN namespace GameServerPlatform { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_GAMESERVERPLATFORM_END NAMESPACE_MICROSOFT_XBOX_SERVICES_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_TITLE_STORAGE_CPP_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN namespace title_storage { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_TITLE_STORAGE_CPP_END NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_TITLE_STORAGE_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_BEGIN namespace TitleStorage { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_TITLE_STORAGE_END NAMESPACE_MICROSOFT_XBOX_SERVICES_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_EVENTS_CPP_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN namespace events { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_EVENTS_CPP_END NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_EVENTS_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_BEGIN namespace Events { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_EVENTS_END NAMESPACE_MICROSOFT_XBOX_SERVICES_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_CONTEXTUAL_SEARCH_CPP_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN namespace contextual_search { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_CONTEXTUAL_SEARCH_CPP_END NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_CONTEXTUAL_SEARCH_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_BEGIN namespace ContextualSearch { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_CONTEXTUAL_SEARCH_END NAMESPACE_MICROSOFT_XBOX_SERVICES_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_ENTERTAINMENT_PROFILE_CPP_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN namespace entertainment_profile { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_ENTERTAINMENT_PROFILE_CPP_END NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_ENTERTAINMENT_PROFILE_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_BEGIN namespace EntertainmentProfile { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_ENTERTAINMENT_PROFILE_END NAMESPACE_MICROSOFT_XBOX_SERVICES_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_NOTIFICATION_CPP_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN namespace notification { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_NOTIFICATION_CPP_END NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_MANAGER_CPP_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN namespace multiplayer { namespace manager { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_MANAGER_CPP_END NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END } } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_MANAGER_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_BEGIN namespace Multiplayer { namespace Manager { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_MANAGER_END NAMESPACE_MICROSOFT_XBOX_SERVICES_END } } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_EXPERIMENTAL_CPP_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN namespace experimental { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_EXPERIMENTAL_CPP_END NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_EXPERIMENTAL_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_BEGIN namespace Experimental { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_EXPERIMENTAL_END NAMESPACE_MICROSOFT_XBOX_SERVICES_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_SOCIAL_MANAGER_CPP_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN namespace social { namespace manager { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_SOCIAL_MANAGER_CPP_END NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END } } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_SOCIAL_MANAGER_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_BEGIN namespace Social { namespace Manager { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_SOCIAL_MANAGER_END NAMESPACE_MICROSOFT_XBOX_SERVICES_END } } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_STAT_MANAGER_CPP_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN namespace stats { namespace manager { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_STAT_MANAGER_CPP_END NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END } } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_STATISTIC_MANAGER_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_BEGIN namespace Statistics { namespace Manager { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_STATISTIC_MANAGER_END NAMESPACE_MICROSOFT_XBOX_SERVICES_END } } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_PLAYER_STATE_CPP_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN namespace player_state { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_PLAYER_STATE_CPP_END NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_PLAYER_STATE_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_BEGIN namespace PlayerState { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_PLAYER_STATE_END NAMESPACE_MICROSOFT_XBOX_SERVICES_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_CLUBS_CPP_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN namespace clubs { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_CLUBS_CPP_END NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_CLUBS_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_BEGIN namespace Clubs { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_CLUBS_END NAMESPACE_MICROSOFT_XBOX_SERVICES_END } ================================================ FILE: Include/xsapi-cpp/user_statistics.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "xsapi-c/user_statistics_c.h" #include "xsapi-cpp/real_time_activity.h" namespace xbox { namespace services { class xbox_live_context; /// /// Contains classes and enumerations that let you retrieve /// player statistics from Xbox Live. /// namespace user_statistics { /// /// Contains information about a user statistic. /// class statistic { public: /// /// The name of the statistic. /// inline const string_t& statistic_name() const; /// /// The type of the statistic. /// inline const string_t& statistic_type() const; /// /// The value of the statistic. /// inline const string_t& value() const; /// /// Internal functions /// inline statistic(const XblStatistic& statistic); inline statistic(const string_t& name, const string_t& type, const string_t& value); private: string_t m_statName; string_t m_statType; string_t m_value; }; /// /// Contains statistical information from a service configuration. /// class service_configuration_statistic { public: /// /// The service configuration ID (SCID) associated with the leaderboard. /// inline const string_t& service_configuration_id() const; /// /// A collection of statistics used in leaderboards. /// inline const std::vector& statistics() const; /// /// Internal function /// inline service_configuration_statistic(const XblServiceConfigurationStatistic& serviceConfigurationStatistic); private: string_t m_serviceConfigurationId; std::vector m_statistics; }; /// /// Represents the results of a user statistic query. /// class user_statistics_result { public: /// /// The Xbox User ID for the user in a statistic. /// inline const string_t& xbox_user_id() const; /// /// A collection of statistics from a service configuration. /// inline const std::vector& service_configuration_statistics() const; /// /// Internal functions /// inline user_statistics_result() = default; inline user_statistics_result(const XblUserStatisticsResult& userStatisticsResult); private: string_t m_xboxUserId; std::vector m_serviceConfigurationStatistics; }; /// /// Contains requested statistics. /// class requested_statistics { public: /// /// Constructor for an RequestedStatistics object. /// /// The service configuration ID (SCID) of the title /// A collection of statistics. inline requested_statistics( _In_ string_t serviceConfigurationId, _In_ const std::vector& statistics ); /// /// Copy Constructor for an RequestedStatistics object. /// inline requested_statistics(_In_ const requested_statistics& other); /// /// The service configuration ID in use. /// inline const string_t& service_configuration_id() const; /// /// A collection of statistics. /// inline const std::vector& statistics() const; /// /// Internal function /// inline const XblRequestedStatistics& _requested_statistics() const; private: XblRequestedStatistics m_requestedStatistics; string_t m_scid; std::vector m_statistics; UTF8StringArrayRef m_statisticsC; }; /// /// Contains information about a change to a subscribed statistic. /// class statistic_change_event_args { public: /// /// The Xbox user ID used to create the subscription. /// inline const string_t& xbox_user_id() const; /// /// The service configuration ID used to create the subscription. /// inline const string_t& service_configuration_id() const; /// /// The statistic with an updated value. /// inline const statistic& latest_statistic() const; /// /// Internal function /// inline statistic_change_event_args(const XblStatisticChangeEventArgs& statisticEventArgs); private: string_t m_xboxUserId; string_t m_serviceConfigurationId; statistic m_statistic; }; /// /// Handles notification when the state of a statistic subscription changes. /// class statistic_change_subscription : public xbox::services::real_time_activity::real_time_activity_subscription { public: /// /// The Xbox user ID used to create the subscription. /// inline const string_t& xbox_user_id() const; /// /// The service config ID used to create the subscription. /// inline const string_t& service_configuration_id() const; /// /// The statistic the subscription is for. /// inline const xbox::services::user_statistics::statistic& statistic() const; /// /// Internal function /// inline statistic_change_subscription( _In_ string_t xboxUserId, _In_ string_t serviceConfigurationId, _In_ xbox::services::user_statistics::statistic newStat, _In_ XblRealTimeActivitySubscriptionHandle handle ); private: string_t m_xboxUserId; string_t m_serviceConfigurationId; xbox::services::user_statistics::statistic m_statistic; friend class user_statistics_service; }; /// /// Represents an endpoint that you can use to access the user statistic service. /// class user_statistics_service { public: /// /// Get a specified statistic for a specified user. /// /// The Xbox User ID of the player to get statistics for. /// The service configuration ID (SCID) of the title /// The name of the statistic to return. /// /// Returns a concurrency::task<T> object that represents the state of the asynchronous operation. /// The result of the asynchronous operation is the requested statistic. /// /// /// Calls V1 GET /users/xuid({xuid})/scids/{scid}/stats/{statname1} /// inline pplx::task> get_single_user_statistics( _In_ const string_t& xboxUserId, _In_ const string_t& serviceConfigurationId, _In_ const string_t& statisticName ); /// /// Get specified statistics for a single user. /// /// The Xbox User ID of the player to get statistics for. /// The service configuration ID (SCID) of the title /// A collection of statistic names to lookup. /// /// Returns a concurrency::task<T> object that represents the state of the asynchronous operation. /// The result is a user_statistics_result object that contains the requested statistics. /// Only statistics with values are returned. For example, if you ask for 3 statistic names and only 2 have values, /// only 2 statistics are returned by the service. /// /// Calls V1 GET /users/xuid({xuid})/scids/{scid}/stats/{statname1},...,{statnameN} inline pplx::task> get_single_user_statistics( _In_ const string_t& xboxUserId, _In_ const string_t& serviceConfigurationId, _In_ const std::vector& statisticNames ); /// /// Get statistics for multiple users. /// /// A list of the user Xbox user IDs to get stats for. /// The service configuration ID (SCID) of the title /// A collection of statistic names to lookup. /// /// Returns a concurrency::task<T> object that represents the state of the asynchronous operation. /// The result is a collection of user_statistics_result objects that contain the requested statistics. /// Only statistics with values are returned. For example, if you ask for 3 statistic names and only 2 have values, /// only 2 statistics are returned by the service. /// /// Calls V1 POST /batch inline pplx::task>> get_multiple_user_statistics( _In_ const std::vector& xboxUserIds, _In_ const string_t& serviceConfigurationId, _In_ std::vector& statisticNames ); /// /// Get statistics for users across different Service configurations. /// /// A list of the user Xbox user ID to get stats for. /// A list of the service config IDs and its associated array of statistics. /// /// Returns a concurrency::task<T> object that represents the state of the asynchronous operation. /// The result is a collection of user_statistics_result objects that contain the requested statistics. /// Only statistics with values are returned. For example, if you ask for 3 statistic names and only 2 have values, /// only 2 statistics are returned by the service. /// /// Calls V1 POST /batch inline pplx::task>> get_multiple_user_statistics_for_multiple_service_configurations( _In_ const std::vector& xboxUserIds, _In_ const std::vector& requestedServiceConfigurationStatisticsCollection ); /// /// Subscribes to statistic update notifications via the StatisticChanged event. /// /// The Xbox User ID of the player requesting the subscription. /// The service configuration ID (SCID) of the title /// The name of the statistic to subscribe to. /// A statistic_change_subscription object that contains the state of the subscription. /// You can register an event handler for statistic changes by calling set_statistic_changed_handler(). /// inline xbox_live_result> subscribe_to_statistic_change( _In_ const string_t& xboxUserId, _In_ const string_t& serviceConfigurationId, _In_ const string_t& statisticName ); /// /// Unsubscribes a previously created statistic change subscription. /// /// The subscription object to unsubscribe inline xbox_live_result unsubscribe_from_statistic_change( _In_ std::shared_ptr subscription ); /// /// Registers an event handler for statistic change notifications. /// Event handlers receive a statistic_change_event_args object. /// /// The callback function that receives notifications. /// /// A function_context object that can be used to unregister the event handler. /// inline function_context add_statistic_changed_handler(_In_ std::function handler); /// /// Unregisters an event handler for statistic change notifications. /// /// The function_context object that was returned when the event handler was registered. /// The callback function that receives notifications. inline void remove_statistic_changed_handler(_In_ function_context context); inline user_statistics_service(const user_statistics_service& other); inline user_statistics_service& operator=(user_statistics_service other); inline ~user_statistics_service(); private: inline user_statistics_service(_In_ XblContextHandle contextHandle); struct HandlerContext; XblContextHandle m_xblContext; friend xbox_live_context; }; }}} #include "impl/user_statistics.hpp" ================================================ FILE: Include/xsapi-cpp/xbox_live_app_config.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #ifdef __OBJC__ #import #endif NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN /// /// Represents the configuration of an Xbox Live application. /// class xbox_live_app_config { public: /// /// Gets the app config singleton. /// inline static std::shared_ptr get_app_config_singleton(); /// /// Returns the title id of the app. /// inline uint32_t title_id() const; /// /// Returns the service config id of the app. /// inline string_t scid() const; /// /// Returns the Xbox Live environment being used, it is empty before you sign in or using production. /// _XSAPICPP_DEPRECATED inline string_t environment() const; /// /// Returns the sandbox such as "XDKS.1", it is empty until you sign in. /// inline string_t sandbox() const; private: xbox_live_app_config() = default; }; NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END #include "impl/xbox_live_app_config.hpp" ================================================ FILE: Include/xsapi-cpp/xbox_live_context.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #if !XSAPI_NO_PPL #include "xsapi-cpp/system.h" #include "xsapi-cpp/multiplayer.h" #include "xsapi-cpp/title_storage.h" #include "xsapi-cpp/profile.h" #include "xsapi-cpp/privacy.h" #include "xsapi-cpp/leaderboard.h" #include "xsapi-cpp/social.h" #include "xsapi-cpp/presence.h" #include "xsapi-cpp/achievements.h" #include "xsapi-cpp/matchmaking.h" #include "xsapi-cpp/user_statistics.h" #include "xsapi-cpp/string_verify.h" #ifdef XSAPI_NOTIFICATION_SERVICE #include "xsapi-cpp/notification_service.h" #endif NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN /// /// Defines pointers to objects that access Xbox Live to create features for player /// interactions. /// /// Note: the XboxLiveContext is unique per instance. Changing state on one instance for a /// user does not affect a second instance of the context for the same user. Using multiple /// instances can therefore result in unexpected behavior. Titles should ensure to only use /// one instance of the XboxLiveContext per user. /// class xbox_live_context { public: /// /// Creates an xbox_live_context from a xbox_live_user /// inline xbox_live_context( _In_ XblUserHandle user ); /// /// Returns the associated system User. /// inline XblUserHandle user(); /// /// Returns a copy of the associated Xbox Live context handle. /// /// /// It is the caller's responsibility to close the returned handle. /// inline XblContextHandle handle(); /// /// Returns the current user's Xbox Live User ID. /// inline string_t xbox_live_user_id(); /// /// Returns an object containing settings that apply to all REST calls made such as retry and diagnostic settings. /// inline std::shared_ptr settings(); /// /// Returns an object containing Xbox Live app config such as title ID /// inline std::shared_ptr application_config(); /// /// A service for storing data in the cloud. /// inline title_storage::title_storage_service title_storage_service(); /// /// A service for managing user profiles. /// inline social::profile_service profile_service(); /// /// A service for managing privacy settings. /// inline privacy::privacy_service privacy_service(); #if !defined(XBOX_LIVE_CREATORS_SDK) /// /// A service for managing leaderboards. /// inline leaderboard::leaderboard_service leaderboard_service(); /// /// A service for managing social networking links. /// inline social::social_service social_service(); /// /// A service for managing reputation reports. /// inline social::reputation_service reputation_service(); /// /// A service for managing achievements. /// inline achievements::achievement_service achievement_service(); /// /// A service for managing user statistics. /// inline user_statistics::user_statistics_service user_statistics_service(); /// /// A service for managing multiplayer games. /// inline multiplayer::multiplayer_service multiplayer_service(); /// /// A service for managing matchmaking sessions. /// inline matchmaking::matchmaking_service matchmaking_service(); /// /// A service for managing real-time activity. /// inline std::shared_ptr real_time_activity_service(); /// /// A service used to check for offensive strings. /// inline system::string_service string_service(); /// /// A service for managing Rich Presence. /// inline presence::presence_service presence_service(); #ifdef XSAPI_NOTIFICATION_SERVICE /// /// A service for receiving notifications. /// inline std::shared_ptr notification_service(); #endif #ifdef XSAPI_EVENTS_SERVICE /// /// A service used to write in game events. /// inline events::events_service events_service(); #endif // !XDK_API && !XSAPI_UNIT_TESTS #endif // !defined(XBOX_LIVE_CREATORS_SDK) /// /// Internal function /// inline xbox_live_context(_In_ XblContextHandle xboxLiveContextHandle); inline ~xbox_live_context(); private: XblContextHandle m_handle = nullptr; #ifdef XSAPI_NOTIFICATION_SERVICE std::shared_ptr m_notificationService; #endif }; NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END #endif #if !XSAPI_NO_PPL #include "impl/xbox_live_context.hpp" #endif ================================================ FILE: Include/xsapi-cpp/xbox_live_context_settings.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include #include #include #include #include "xsapi-cpp/xbox_service_call_routed_event_args.h" #include "xsapi-c/xbox_live_context_c.h" #include "xsapi-c/xbox_live_context_settings_c.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN class http_call; /// /// Enumeration values that indicate the trace levels of debug output for service diagnostics. /// /// Setting the debug trace level to error or higher reports the last HRESULT, the current /// function, the source file, and the line number for many trace points in the Xbox live code. /// enum class xbox_services_diagnostics_trace_level { /// /// Output no tracing and debugging messages. /// off, /// /// Output error-handling messages. /// error, /// /// Output warnings and error-handling messages. /// warning, /// /// Output informational messages, warnings, and error-handling messages. /// info, /// /// Output all debugging and tracing messages. /// verbose }; /// /// Enum used with disable_asserts_for_xbox_live_throttling_in_dev_sandboxes() /// enum class xbox_live_context_throttle_setting { /// /// Passed to xboxLiveContext->settings()->disable_asserts_for_xbox_live_throttling_in_dev_sandboxes() /// to warn code reviewers that there's an outstanding Xbox Live calling pattern issue that needs to be addressed. /// this_code_needs_to_be_changed_to_avoid_throttling }; /// /// Enum used with disable_asserts_ APIs /// enum class xbox_live_context_recommended_setting { /// /// Passed to xboxLiveContext->settings()->disable_asserts_ APIs /// to warn code reviewers that there's an outstanding calling pattern issue that needs to be addressed. /// this_code_needs_to_be_changed_to_follow_best_practices }; /// /// Represents settings for an HTTP call. /// class xbox_live_context_settings { public: inline xbox_live_context_settings(XblContextHandle xblContextHandle); inline ~xbox_live_context_settings(); /// /// Registers for all service call notifications. Event handlers will receive xbox::services::xbox_service_call_routed_event_args /// /// The event handler function to call. /// /// A function_context object that can be used to unregister the event handler. /// inline function_context add_service_call_routed_handler(_In_ std::function handler); /// /// Unregisters from all service call notifications. /// /// The function_context object that was returned when the event handler was registered. inline void remove_service_call_routed_handler(_In_ function_context context); /// /// Gets the connect, send, and receive timeouts for HTTP socket operations of long calls such as Title Storage calls. /// Default is 5 minutes. Calls that take longer than this are aborted. /// inline std::chrono::seconds long_http_timeout() const; /// /// Sets the connect, send, and receive timeouts for HTTP socket operations of long calls such as Title Storage calls. /// Default is 5 minutes. Calls that take longer than this are aborted. /// Take care when setting this to smaller values as some calls like Title Storage may take a few minutes to complete. /// inline void set_long_http_timeout(_In_ std::chrono::seconds value); /// /// Gets the HTTP retry delay in seconds. /// /// Retries are delayed using an exponential back off. By default, it will delay 2 seconds then the /// next retry will delay 4 seconds, then 8 seconds, and so on up to a max of 1 min until either /// the call succeeds or the http_timeout_window is reached, at which point the call will fail. /// The delay is also jittered between the current and next delay to spread out service load. /// The default for http_timeout_window is 20 seconds and can be changed using set_http_timeout_window() /// /// If the service returns an HTTP error with a "Retry-After" header, then all future calls to that API /// will immediately fail with the original error without contacting the service until the "Retry-After" /// time has been reached. /// /// Idempotent service calls are retried when a network error occurs or the server responds with one of these HTTP status codes: /// 408 (Request Timeout) /// 429 (Too Many Requests) /// 500 (Internal Server Error) /// 502 (Bad Gateway) /// 503 (Service Unavailable) /// 504 (Gateway Timeout) /// inline std::chrono::seconds http_retry_delay() const; /// /// Sets the HTTP retry delay in seconds. The default and minimum delay is 2 seconds. /// /// Retries are delayed using an exponential back off. By default, it will delay 2 seconds then the /// next retry will delay 4 seconds, then 8 seconds, and so on up to a max of 1 min until either /// the call succeeds or the http_timeout_window is reached, at which point the call will fail. /// The delay is also jittered between the current and next delay to spread out service load. /// The default for http_timeout_window is 20 seconds and can be changed using set_http_timeout_window() /// /// If the service returns an HTTP error with a "Retry-After" header, then all future calls to that API /// will immediately fail with the original error without contacting the service until the "Retry-After" /// time has been reached. /// /// Idempotent service calls are retried when a network error occurs or the server responds with one of these HTTP status codes: /// 408 (Request Timeout) /// 429 (Too Many Requests) /// 500 (Internal Server Error) /// 502 (Bad Gateway) /// 503 (Service Unavailable) /// 504 (Gateway Timeout) /// inline void set_http_retry_delay(_In_ std::chrono::seconds value); /// /// Gets the HTTP timeout window in seconds. /// /// This controls how long to spend attempting to retry idempotent service calls before failing. /// The default is 20 seconds /// /// Idempotent service calls are retried when a network error occurs or the server responds with one of these HTTP status codes: /// 408 (Request Timeout) /// 429 (Too Many Requests) /// 500 (Internal Server Error) /// 502 (Bad Gateway) /// 503 (Service Unavailable) /// 504 (Gateway Timeout) /// inline std::chrono::seconds http_timeout_window() const; /// /// Sets the HTTP timeout window in seconds. /// /// This controls how long to spend attempting to retry idempotent service calls before failing. /// The default is 20 seconds. Set to 0 to turn off retry. /// /// Idempotent service calls are retried when a network error occurs or the server responds with one of these HTTP status codes: /// 408 (Request Timeout) /// 429 (Too Many Requests) /// 500 (Internal Server Error) /// 502 (Bad Gateway) /// 503 (Service Unavailable) /// 504 (Gateway Timeout) /// inline void set_http_timeout_window(_In_ std::chrono::seconds value); /// /// Gets the web socket timeout window in seconds. /// inline std::chrono::seconds websocket_timeout_window() const; /// /// Sets the web socket timeout window in seconds. /// Controls how long to spend attempting to retry establishing a websocket connection before failing. /// Default is 300 seconds. Set to 0 to turn off retry. /// inline void set_websocket_timeout_window(_In_ std::chrono::seconds value); /// /// Gets whether to use the dispatcher for event routing /// inline bool use_core_dispatcher_for_event_routing() const; /// /// Controls whether to use the CoreDispatcher from the User object to route events through. /// This is required to be true if using events with JavaScript. /// inline void set_use_core_dispatcher_for_event_routing(_In_ bool value); /// /// Disables asserts for Xbox Live throttling in dev sandboxes. /// The asserts will not fire in RETAIL sandbox, and this setting has no affect in RETAIL sandboxes. /// It is best practice to not call this API, and instead adjust the calling pattern but this is provided /// as a temporary way to get unblocked while in early stages of game development. /// inline void disable_asserts_for_xbox_live_throttling_in_dev_sandboxes( _In_ xbox_live_context_throttle_setting setting ); /// /// Disables asserts for having maximum number of websockets being activated /// i.e. MAXIMUM_WEBSOCKETS_ACTIVATIONS_ALLOWED_PER_USER (5) per user per title instance. /// It is best practice to not call this API, and instead adjust the calling pattern but this is provided /// as a temporary way to get unblocked while in early stages of game development. /// inline void disable_asserts_for_maximum_number_of_websockets_activated( _In_ xbox_live_context_recommended_setting setting ); /// /// Gets whether to use the xplatqos server for qos calls. /// inline bool use_crossplatform_qos_servers() const; /// /// Controls whether we use cross platform qos endpoints or not. /// In some case if you are shipping with TV_API enabled, you want to be able to choose. /// inline void set_use_crossplatform_qos_servers(_In_ bool value); public: #ifdef XSAPI_UNIT_TESTS XblContextHandle XboxLiveContextHandle() { return m_xblContextHandle; } #endif private: XblContextHandle m_xblContextHandle; struct HandlerContext; friend std::shared_ptr create_xbox_live_http_call( _In_ const std::shared_ptr& xboxLiveContextSettings, _In_ const string_t& httpMethod, _In_ const string_t& serverName, _In_ const web::uri& pathQueryFragment ); }; NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END #include "impl/xbox_live_context_settings.hpp" ================================================ FILE: Include/xsapi-cpp/xbox_service_call_routed_event_args.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "xsapi-cpp/http_call_request_message.h" #include "xsapi-c/xbox_live_context_settings_c.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN /// /// Contains information about a service call. /// class xbox_service_call_routed_event_args { public: /// /// Returns the XboxUserId of the User contacting the service /// inline string_t xbox_user_id() const; /// /// Returns the HTTP method used to contact the service. For example, "GET". /// inline string_t http_method() const; /// /// Returns the URI used to contact the service. /// inline string_t uri() const; /// /// Returns the request headers that were sent to the service. /// inline string_t request_headers() const; /// /// Returns the request body that was sent to the service. /// inline http_call_request_message request_body() const; /// /// Returns the number of responses in this session. /// inline uint32_t response_count() const; /// /// Returns the response headers returned by the service. /// inline string_t response_headers() const; /// /// Returns the response body returned by the service. /// inline string_t response_body() const; /// /// Returns the ETag returned by the service. /// inline string_t etag() const; /// /// Returns the authentication token returned by GetTokenAndSignatureAsync. /// inline string_t token() const; /// /// Returns the authentication signature returned by GetTokenAndSignatureAsync. /// inline string_t signature() const; /// /// Returns the HTTP status code. For example, 200. /// inline uint32_t http_status() const; /// /// Returns the a full response log formatted message of all the properties in xbox_service_call_routed_event_args. /// inline const string_t& full_response_formatted() const; /// /// Returns the time when the request was sent. /// inline chrono_clock_t::time_point request_time() const; /// /// Returns the time when the response was received. /// inline chrono_clock_t::time_point response_time() const; /// /// Returns the elapsed time span between sending a request and receiving a response. /// inline std::chrono::milliseconds elapsed_call_time() const; inline xbox_service_call_routed_event_args( _In_ const XblServiceCallRoutedArgs& internalArgs ); inline ~xbox_service_call_routed_event_args(); inline xbox_service_call_routed_event_args(const xbox_service_call_routed_event_args& other); inline xbox_service_call_routed_event_args& operator=(xbox_service_call_routed_event_args other); private: HCCallHandle m_callHandle{ nullptr }; long m_responseCount{ 0 }; string_t m_fullResponseFormatted; }; NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END #include "impl/xbox_service_call_routed_event_args.hpp" ================================================ FILE: LICENSE.md ================================================ MIT License Copyright (c) Microsoft Corporation All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: LINKTOSOURCE.md ================================================ ## How to link your project against source You might want to link against the XSAPI source if you want to debug an issue, or understand where an error code is coming from. Jump to the guide that matches the platform and API type you are using: - [How to link against the C GDK source](LINKTOSOURCE.md#how-to-link-against-the-xsapi-gdk-source) ### How to link against the XSAPI GDK source - In Visual Studio, choose "File->Add->Existing Project..." in Visual Studio to add the following projects to your application's solution. The project paths below are relative to the SDK source root. For Visual Studio 2017: ``` \Build\Microsoft.Xbox.Services.141.GDK.C\Microsoft.Xbox.Services.141.GDK.C.vcxproj \External\xal\External\libHttpClient\Build\libHttpClient.141.GDK.C\libHttpClient.141.GDK.C.vcxproj ``` For Visual Studio 2019: ``` \Build\Microsoft.Xbox.Services.142.GDK.C\Microsoft.Xbox.Services.142.GDK.C.vcxproj \External\xal\External\libHttpClient\Build\libHttpClient.142.GDK.C\libHttpClient.142.GDK.C.vcxproj ``` - Add the XSAPI props file to your project by clicking "View->Other Windows->Property Manager", right clicking on your project, selecting "Add Existing Property Sheet", then finally selecting the xsapi.staticlib.props file in the SDK source root. This will automatically update your projects XSAPI header include paths, add references to the relevant XSAPI projects, and prevent your project from linking with the installed GDK binaries. - Rebuild your Visual Studio solution. Note that in some cases the XSAPI project references won't be fully added until you reload your project after adding the XSAPI property sheet. ================================================ FILE: Microsoft.Xbox.Services.GDK.VS2017.sln ================================================ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.6.33829.357 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "GDK", "GDK", "{C9C47451-FCA6-4B7F-9BD0-20AD3B119737}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "APIExplorer.Shared", "Tests\APIExplorer\APIExplorer.Shared.vcxitems", "{CBC0BEC4-131D-4868-9345-71813557FB39}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{DC381015-B171-4DA5-B7FA-7CDE8196C6D3}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Xbox.Services.141.GDK.C", "Build\Microsoft.Xbox.Services.141.GDK.C\Microsoft.Xbox.Services.141.GDK.C.vcxproj", "{60139F62-BF37-4F11-BD93-5FBF4E92100C}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Xbox.Services.141.GDK.C.Thunks", "Build\Microsoft.Xbox.Services.141.GDK.C.Thunks\Microsoft.Xbox.Services.141.GDK.C.Thunks.vcxproj", "{E538394B-68CB-4597-87AD-7B6841CC1278}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DirectXTK12", "Tests\GDK\APIRunner.GDK\Kits\DirectXTK12\DirectXTK12_GDK_2017.vcxproj", "{052C4858-C76F-4CEA-8A1A-E8E5559E67C2}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "APIRunner.GDK.Src", "Tests\GDK\APIRunner.GDK\APIRunner.GDK.Src.vcxproj", "{46F31A54-4C74-41A8-B294-26B5689E51EF}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ThunksGenerator", "Build\Microsoft.Xbox.Services.141.GDK.C.Thunks\generator\ThunksGenerator\ThunksGenerator.csproj", "{21C651D1-61D7-46C5-BD23-128E40329AA5}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libHttpClient.GDK", "External\xal\External\libHttpClient\Build\libHttpClient.GDK\libHttpClient.GDK.vcxproj", "{A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{901F04C7-6EC2-4641-90E7-54D2F72DF570}" ProjectSection(SolutionItems) = preProject hc_settings.props = hc_settings.props External\xal\External\libHttpClient\Build\libHttpClient.GDK.props = External\xal\External\libHttpClient\Build\libHttpClient.GDK.props External\xal\External\libHttpClient\libHttpClient.props = External\xal\External\libHttpClient\libHttpClient.props External\xal\External\libHttpClient\platform_select.props = External\xal\External\libHttpClient\platform_select.props Build\xsapi.gdk.bwoi.props = Build\xsapi.gdk.bwoi.props Build\xsapi.gdk.props = Build\xsapi.gdk.props xsapi.paths.props = xsapi.paths.props xsapi.staticlib.props = xsapi.staticlib.props EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Xbox.Services.Common", "Build\Microsoft.Xbox.Services.Common\Microsoft.Xbox.Services.Common.vcxitems", "{CF3350E5-00A2-4647-A996-BAF7542B0327}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Xbox.Services.GDK", "Build\Microsoft.Xbox.Services.GDK\Microsoft.Xbox.Services.GDK.vcxitems", "{1E320494-894E-4FEB-90CA-DF4FE20499F4}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Common", "Common", "{81806275-2461-4CB7-935A-7EFEC3488A86}" EndProject Global GlobalSection(SharedMSBuildProjectFiles) = preSolution Build\Microsoft.Xbox.Services.GDK\Microsoft.Xbox.Services.GDK.vcxitems*{1e320494-894e-4feb-90ca-df4fe20499f4}*SharedItemsImports = 9 Tests\APIExplorer\APIExplorer.Shared.vcxitems*{46f31a54-4c74-41a8-b294-26b5689e51ef}*SharedItemsImports = 4 Build\Microsoft.Xbox.Services.Common\Microsoft.Xbox.Services.Common.vcxitems*{60139f62-bf37-4f11-bd93-5fbf4e92100c}*SharedItemsImports = 4 Build\Microsoft.Xbox.Services.GDK\Microsoft.Xbox.Services.GDK.vcxitems*{60139f62-bf37-4f11-bd93-5fbf4e92100c}*SharedItemsImports = 4 External\xal\External\libHttpClient\Build\libHttpClient.Common\libHttpClient.Common.vcxitems*{a5a6e02a-21ba-4d55-9fb9-7b24dedd3743}*SharedItemsImports = 4 External\xal\External\libHttpClient\Build\libHttpClient.GDK.Shared\libHttpClient.GDK.Shared.vcxitems*{a5a6e02a-21ba-4d55-9fb9-7b24dedd3743}*SharedItemsImports = 4 Tests\APIExplorer\APIExplorer.Shared.vcxitems*{cbc0bec4-131d-4868-9345-71813557fb39}*SharedItemsImports = 9 Build\Microsoft.Xbox.Services.Common\Microsoft.Xbox.Services.Common.vcxitems*{cf3350e5-00a2-4647-a996-baf7542b0327}*SharedItemsImports = 9 EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Debug|ARM = Debug|ARM Debug|ARM64 = Debug|ARM64 Debug|Gaming.Desktop.x64 = Debug|Gaming.Desktop.x64 Debug|Gaming.Xbox.Scarlett.x64 = Debug|Gaming.Xbox.Scarlett.x64 Debug|Gaming.Xbox.XboxOne.x64 = Debug|Gaming.Xbox.XboxOne.x64 Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU Release|ARM = Release|ARM Release|ARM64 = Release|ARM64 Release|Gaming.Desktop.x64 = Release|Gaming.Desktop.x64 Release|Gaming.Xbox.Scarlett.x64 = Release|Gaming.Xbox.Scarlett.x64 Release|Gaming.Xbox.XboxOne.x64 = Release|Gaming.Xbox.XboxOne.x64 Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Debug|Any CPU.ActiveCfg = Debug|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Debug|ARM.ActiveCfg = Debug|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Debug|ARM64.ActiveCfg = Debug|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Debug|Gaming.Desktop.x64.ActiveCfg = Debug|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Debug|Gaming.Desktop.x64.Build.0 = Debug|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Debug|Gaming.Xbox.Scarlett.x64.ActiveCfg = Debug|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Debug|Gaming.Xbox.Scarlett.x64.Build.0 = Debug|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Debug|Gaming.Xbox.XboxOne.x64.ActiveCfg = Debug|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Debug|Gaming.Xbox.XboxOne.x64.Build.0 = Debug|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Debug|x64.ActiveCfg = Debug|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Debug|x86.ActiveCfg = Debug|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Release|Any CPU.ActiveCfg = Release|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Release|ARM.ActiveCfg = Release|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Release|ARM64.ActiveCfg = Release|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Release|Gaming.Desktop.x64.ActiveCfg = Release|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Release|Gaming.Desktop.x64.Build.0 = Release|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Release|Gaming.Xbox.Scarlett.x64.ActiveCfg = Release|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Release|Gaming.Xbox.Scarlett.x64.Build.0 = Release|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Release|Gaming.Xbox.XboxOne.x64.ActiveCfg = Release|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Release|Gaming.Xbox.XboxOne.x64.Build.0 = Release|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Release|x64.ActiveCfg = Release|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Release|x86.ActiveCfg = Release|Gaming.Desktop.x64 {E538394B-68CB-4597-87AD-7B6841CC1278}.Debug|Any CPU.ActiveCfg = Debug|Gaming.Desktop.x64 {E538394B-68CB-4597-87AD-7B6841CC1278}.Debug|ARM.ActiveCfg = Debug|Gaming.Desktop.x64 {E538394B-68CB-4597-87AD-7B6841CC1278}.Debug|ARM64.ActiveCfg = Debug|Gaming.Desktop.x64 {E538394B-68CB-4597-87AD-7B6841CC1278}.Debug|Gaming.Desktop.x64.ActiveCfg = Debug|Gaming.Desktop.x64 {E538394B-68CB-4597-87AD-7B6841CC1278}.Debug|Gaming.Desktop.x64.Build.0 = Debug|Gaming.Desktop.x64 {E538394B-68CB-4597-87AD-7B6841CC1278}.Debug|Gaming.Xbox.Scarlett.x64.ActiveCfg = Debug|Gaming.Desktop.x64 {E538394B-68CB-4597-87AD-7B6841CC1278}.Debug|Gaming.Xbox.Scarlett.x64.Build.0 = Debug|Gaming.Desktop.x64 {E538394B-68CB-4597-87AD-7B6841CC1278}.Debug|Gaming.Xbox.XboxOne.x64.ActiveCfg = Debug|Gaming.Desktop.x64 {E538394B-68CB-4597-87AD-7B6841CC1278}.Debug|Gaming.Xbox.XboxOne.x64.Build.0 = Debug|Gaming.Desktop.x64 {E538394B-68CB-4597-87AD-7B6841CC1278}.Debug|x64.ActiveCfg = Debug|Gaming.Desktop.x64 {E538394B-68CB-4597-87AD-7B6841CC1278}.Debug|x86.ActiveCfg = Debug|Gaming.Desktop.x64 {E538394B-68CB-4597-87AD-7B6841CC1278}.Release|Any CPU.ActiveCfg = Release|Gaming.Desktop.x64 {E538394B-68CB-4597-87AD-7B6841CC1278}.Release|ARM.ActiveCfg = Release|Gaming.Desktop.x64 {E538394B-68CB-4597-87AD-7B6841CC1278}.Release|ARM64.ActiveCfg = Release|Gaming.Desktop.x64 {E538394B-68CB-4597-87AD-7B6841CC1278}.Release|Gaming.Desktop.x64.ActiveCfg = Release|Gaming.Desktop.x64 {E538394B-68CB-4597-87AD-7B6841CC1278}.Release|Gaming.Desktop.x64.Build.0 = Release|Gaming.Desktop.x64 {E538394B-68CB-4597-87AD-7B6841CC1278}.Release|Gaming.Xbox.Scarlett.x64.ActiveCfg = Release|Gaming.Desktop.x64 {E538394B-68CB-4597-87AD-7B6841CC1278}.Release|Gaming.Xbox.Scarlett.x64.Build.0 = Release|Gaming.Desktop.x64 {E538394B-68CB-4597-87AD-7B6841CC1278}.Release|Gaming.Xbox.XboxOne.x64.ActiveCfg = Release|Gaming.Desktop.x64 {E538394B-68CB-4597-87AD-7B6841CC1278}.Release|Gaming.Xbox.XboxOne.x64.Build.0 = Release|Gaming.Desktop.x64 {E538394B-68CB-4597-87AD-7B6841CC1278}.Release|x64.ActiveCfg = Release|Gaming.Desktop.x64 {E538394B-68CB-4597-87AD-7B6841CC1278}.Release|x86.ActiveCfg = Release|Gaming.Desktop.x64 {052C4858-C76F-4CEA-8A1A-E8E5559E67C2}.Debug|Any CPU.ActiveCfg = Debug|Gaming.Xbox.XboxOne.x64 {052C4858-C76F-4CEA-8A1A-E8E5559E67C2}.Debug|ARM.ActiveCfg = Debug|Gaming.Xbox.Scarlett.x64 {052C4858-C76F-4CEA-8A1A-E8E5559E67C2}.Debug|ARM64.ActiveCfg = Debug|Gaming.Xbox.Scarlett.x64 {052C4858-C76F-4CEA-8A1A-E8E5559E67C2}.Debug|Gaming.Desktop.x64.ActiveCfg = Debug|Gaming.Desktop.x64 {052C4858-C76F-4CEA-8A1A-E8E5559E67C2}.Debug|Gaming.Desktop.x64.Build.0 = Debug|Gaming.Desktop.x64 {052C4858-C76F-4CEA-8A1A-E8E5559E67C2}.Debug|Gaming.Xbox.Scarlett.x64.ActiveCfg = Debug|Gaming.Xbox.Scarlett.x64 {052C4858-C76F-4CEA-8A1A-E8E5559E67C2}.Debug|Gaming.Xbox.Scarlett.x64.Build.0 = Debug|Gaming.Xbox.Scarlett.x64 {052C4858-C76F-4CEA-8A1A-E8E5559E67C2}.Debug|Gaming.Xbox.XboxOne.x64.ActiveCfg = Debug|Gaming.Xbox.XboxOne.x64 {052C4858-C76F-4CEA-8A1A-E8E5559E67C2}.Debug|Gaming.Xbox.XboxOne.x64.Build.0 = Debug|Gaming.Xbox.XboxOne.x64 {052C4858-C76F-4CEA-8A1A-E8E5559E67C2}.Debug|x64.ActiveCfg = Debug|Gaming.Xbox.Scarlett.x64 {052C4858-C76F-4CEA-8A1A-E8E5559E67C2}.Debug|x86.ActiveCfg = Debug|Gaming.Xbox.Scarlett.x64 {052C4858-C76F-4CEA-8A1A-E8E5559E67C2}.Release|Any CPU.ActiveCfg = Release|Gaming.Xbox.XboxOne.x64 {052C4858-C76F-4CEA-8A1A-E8E5559E67C2}.Release|ARM.ActiveCfg = Release|Gaming.Xbox.Scarlett.x64 {052C4858-C76F-4CEA-8A1A-E8E5559E67C2}.Release|ARM64.ActiveCfg = Release|Gaming.Xbox.Scarlett.x64 {052C4858-C76F-4CEA-8A1A-E8E5559E67C2}.Release|Gaming.Desktop.x64.ActiveCfg = Release|Gaming.Desktop.x64 {052C4858-C76F-4CEA-8A1A-E8E5559E67C2}.Release|Gaming.Desktop.x64.Build.0 = Release|Gaming.Desktop.x64 {052C4858-C76F-4CEA-8A1A-E8E5559E67C2}.Release|Gaming.Xbox.Scarlett.x64.ActiveCfg = Release|Gaming.Xbox.Scarlett.x64 {052C4858-C76F-4CEA-8A1A-E8E5559E67C2}.Release|Gaming.Xbox.Scarlett.x64.Build.0 = Release|Gaming.Xbox.Scarlett.x64 {052C4858-C76F-4CEA-8A1A-E8E5559E67C2}.Release|Gaming.Xbox.XboxOne.x64.ActiveCfg = Release|Gaming.Xbox.XboxOne.x64 {052C4858-C76F-4CEA-8A1A-E8E5559E67C2}.Release|Gaming.Xbox.XboxOne.x64.Build.0 = Release|Gaming.Xbox.XboxOne.x64 {052C4858-C76F-4CEA-8A1A-E8E5559E67C2}.Release|x64.ActiveCfg = Release|Gaming.Xbox.Scarlett.x64 {052C4858-C76F-4CEA-8A1A-E8E5559E67C2}.Release|x86.ActiveCfg = Release|Gaming.Xbox.Scarlett.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Debug|Any CPU.ActiveCfg = Debug|Gaming.Desktop.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Debug|ARM.ActiveCfg = Debug|Gaming.Xbox.Scarlett.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Debug|ARM64.ActiveCfg = Debug|Gaming.Xbox.Scarlett.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Debug|Gaming.Desktop.x64.ActiveCfg = Debug|Gaming.Desktop.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Debug|Gaming.Desktop.x64.Build.0 = Debug|Gaming.Desktop.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Debug|Gaming.Desktop.x64.Deploy.0 = Debug|Gaming.Desktop.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Debug|Gaming.Xbox.Scarlett.x64.ActiveCfg = Debug|Gaming.Xbox.Scarlett.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Debug|Gaming.Xbox.Scarlett.x64.Build.0 = Debug|Gaming.Xbox.Scarlett.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Debug|Gaming.Xbox.Scarlett.x64.Deploy.0 = Debug|Gaming.Xbox.Scarlett.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Debug|Gaming.Xbox.XboxOne.x64.ActiveCfg = Debug|Gaming.Xbox.XboxOne.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Debug|Gaming.Xbox.XboxOne.x64.Build.0 = Debug|Gaming.Xbox.XboxOne.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Debug|Gaming.Xbox.XboxOne.x64.Deploy.0 = Debug|Gaming.Xbox.XboxOne.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Debug|x64.ActiveCfg = Debug|Gaming.Desktop.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Debug|x86.ActiveCfg = Debug|Gaming.Xbox.Scarlett.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Release|Any CPU.ActiveCfg = Release|Gaming.Desktop.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Release|ARM.ActiveCfg = Release|Gaming.Xbox.Scarlett.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Release|ARM64.ActiveCfg = Release|Gaming.Xbox.Scarlett.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Release|Gaming.Desktop.x64.ActiveCfg = Release|Gaming.Desktop.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Release|Gaming.Desktop.x64.Build.0 = Release|Gaming.Desktop.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Release|Gaming.Desktop.x64.Deploy.0 = Release|Gaming.Desktop.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Release|Gaming.Xbox.Scarlett.x64.ActiveCfg = Release|Gaming.Desktop.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Release|Gaming.Xbox.Scarlett.x64.Build.0 = Release|Gaming.Desktop.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Release|Gaming.Xbox.Scarlett.x64.Deploy.0 = Release|Gaming.Desktop.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Release|Gaming.Xbox.XboxOne.x64.ActiveCfg = Release|Gaming.Xbox.XboxOne.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Release|Gaming.Xbox.XboxOne.x64.Build.0 = Release|Gaming.Xbox.XboxOne.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Release|Gaming.Xbox.XboxOne.x64.Deploy.0 = Release|Gaming.Xbox.XboxOne.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Release|x64.ActiveCfg = Release|Gaming.Desktop.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Release|x86.ActiveCfg = Release|Gaming.Xbox.Scarlett.x64 {21C651D1-61D7-46C5-BD23-128E40329AA5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {21C651D1-61D7-46C5-BD23-128E40329AA5}.Debug|Any CPU.Build.0 = Debug|Any CPU {21C651D1-61D7-46C5-BD23-128E40329AA5}.Debug|ARM.ActiveCfg = Debug|Any CPU {21C651D1-61D7-46C5-BD23-128E40329AA5}.Debug|ARM.Build.0 = Debug|Any CPU {21C651D1-61D7-46C5-BD23-128E40329AA5}.Debug|ARM64.ActiveCfg = Debug|Any CPU {21C651D1-61D7-46C5-BD23-128E40329AA5}.Debug|ARM64.Build.0 = Debug|Any CPU {21C651D1-61D7-46C5-BD23-128E40329AA5}.Debug|Gaming.Desktop.x64.ActiveCfg = Debug|Any CPU {21C651D1-61D7-46C5-BD23-128E40329AA5}.Debug|Gaming.Xbox.Scarlett.x64.ActiveCfg = Debug|Any CPU {21C651D1-61D7-46C5-BD23-128E40329AA5}.Debug|Gaming.Xbox.Scarlett.x64.Build.0 = Debug|Any CPU {21C651D1-61D7-46C5-BD23-128E40329AA5}.Debug|Gaming.Xbox.XboxOne.x64.ActiveCfg = Debug|Any CPU {21C651D1-61D7-46C5-BD23-128E40329AA5}.Debug|Gaming.Xbox.XboxOne.x64.Build.0 = Debug|Any CPU {21C651D1-61D7-46C5-BD23-128E40329AA5}.Debug|x64.ActiveCfg = Debug|Any CPU {21C651D1-61D7-46C5-BD23-128E40329AA5}.Debug|x64.Build.0 = Debug|Any CPU {21C651D1-61D7-46C5-BD23-128E40329AA5}.Debug|x86.ActiveCfg = Debug|Any CPU {21C651D1-61D7-46C5-BD23-128E40329AA5}.Debug|x86.Build.0 = Debug|Any CPU {21C651D1-61D7-46C5-BD23-128E40329AA5}.Release|Any CPU.ActiveCfg = Release|Any CPU {21C651D1-61D7-46C5-BD23-128E40329AA5}.Release|Any CPU.Build.0 = Release|Any CPU {21C651D1-61D7-46C5-BD23-128E40329AA5}.Release|ARM.ActiveCfg = Release|Any CPU {21C651D1-61D7-46C5-BD23-128E40329AA5}.Release|ARM.Build.0 = Release|Any CPU {21C651D1-61D7-46C5-BD23-128E40329AA5}.Release|ARM64.ActiveCfg = Release|Any CPU {21C651D1-61D7-46C5-BD23-128E40329AA5}.Release|ARM64.Build.0 = Release|Any CPU {21C651D1-61D7-46C5-BD23-128E40329AA5}.Release|Gaming.Desktop.x64.ActiveCfg = Release|Any CPU {21C651D1-61D7-46C5-BD23-128E40329AA5}.Release|Gaming.Xbox.Scarlett.x64.ActiveCfg = Release|Any CPU {21C651D1-61D7-46C5-BD23-128E40329AA5}.Release|Gaming.Xbox.Scarlett.x64.Build.0 = Release|Any CPU {21C651D1-61D7-46C5-BD23-128E40329AA5}.Release|Gaming.Xbox.XboxOne.x64.ActiveCfg = Release|Any CPU {21C651D1-61D7-46C5-BD23-128E40329AA5}.Release|Gaming.Xbox.XboxOne.x64.Build.0 = Release|Any CPU {21C651D1-61D7-46C5-BD23-128E40329AA5}.Release|x64.ActiveCfg = Release|Any CPU {21C651D1-61D7-46C5-BD23-128E40329AA5}.Release|x64.Build.0 = Release|Any CPU {21C651D1-61D7-46C5-BD23-128E40329AA5}.Release|x86.ActiveCfg = Release|Any CPU {21C651D1-61D7-46C5-BD23-128E40329AA5}.Release|x86.Build.0 = Release|Any CPU {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Debug|Any CPU.ActiveCfg = Debug|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Debug|ARM.ActiveCfg = Debug|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Debug|ARM64.ActiveCfg = Debug|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Debug|Gaming.Desktop.x64.ActiveCfg = Debug|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Debug|Gaming.Desktop.x64.Build.0 = Debug|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Debug|Gaming.Xbox.Scarlett.x64.ActiveCfg = Debug|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Debug|Gaming.Xbox.XboxOne.x64.ActiveCfg = Debug|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Debug|x64.ActiveCfg = Debug|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Debug|x86.ActiveCfg = Debug|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Release|Any CPU.ActiveCfg = Release|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Release|ARM.ActiveCfg = Release|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Release|ARM64.ActiveCfg = Release|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Release|Gaming.Desktop.x64.ActiveCfg = Release|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Release|Gaming.Desktop.x64.Build.0 = Release|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Release|Gaming.Xbox.Scarlett.x64.ActiveCfg = Release|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Release|Gaming.Xbox.XboxOne.x64.ActiveCfg = Release|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Release|x64.ActiveCfg = Release|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Release|x86.ActiveCfg = Release|Gaming.Desktop.x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {CBC0BEC4-131D-4868-9345-71813557FB39} = {81806275-2461-4CB7-935A-7EFEC3488A86} {DC381015-B171-4DA5-B7FA-7CDE8196C6D3} = {C9C47451-FCA6-4B7F-9BD0-20AD3B119737} {60139F62-BF37-4F11-BD93-5FBF4E92100C} = {C9C47451-FCA6-4B7F-9BD0-20AD3B119737} {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743} = {C9C47451-FCA6-4B7F-9BD0-20AD3B119737} {E538394B-68CB-4597-87AD-7B6841CC1278} = {C9C47451-FCA6-4B7F-9BD0-20AD3B119737} {052C4858-C76F-4CEA-8A1A-E8E5559E67C2} = {DC381015-B171-4DA5-B7FA-7CDE8196C6D3} {46F31A54-4C74-41A8-B294-26B5689E51EF} = {DC381015-B171-4DA5-B7FA-7CDE8196C6D3} {21C651D1-61D7-46C5-BD23-128E40329AA5} = {C9C47451-FCA6-4B7F-9BD0-20AD3B119737} {CF3350E5-00A2-4647-A996-BAF7542B0327} = {81806275-2461-4CB7-935A-7EFEC3488A86} {1E320494-894E-4FEB-90CA-DF4FE20499F4} = {C9C47451-FCA6-4B7F-9BD0-20AD3B119737} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {622BB9B5-938F-41A2-BC8B-B7B3A64A73B5} EndGlobalSection EndGlobal ================================================ FILE: Microsoft.Xbox.Services.GDK.VS2019.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.6.33829.357 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "GDK", "GDK", "{165723E8-FF20-4427-A84B-68E05765C9AC}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Xbox.Services.142.GDK.C", "Build\Microsoft.Xbox.Services.142.GDK.C\Microsoft.Xbox.Services.142.GDK.C.vcxproj", "{60139F62-BF37-4F11-BD93-5FBF4E92100C}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libHttpClient.GDK", "External\xal\External\libHttpClient\Build\libHttpClient.GDK\libHttpClient.GDK.vcxproj", "{A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Xbox.Services.GDK", "Build\Microsoft.Xbox.Services.GDK\Microsoft.Xbox.Services.GDK.vcxitems", "{1E320494-894E-4FEB-90CA-DF4FE20499F4}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{68CC03E1-1625-450A-B3EF-AC356E5F5A69}" ProjectSection(SolutionItems) = preProject External\xal\External\libHttpClient\Build\libHttpClient.GDK.props = External\xal\External\libHttpClient\Build\libHttpClient.GDK.props External\xal\External\libHttpClient\Build\libHttpClient.paths.props = External\xal\External\libHttpClient\Build\libHttpClient.paths.props External\xal\External\libHttpClient\libHttpClient.props = External\xal\External\libHttpClient\libHttpClient.props External\xal\External\libHttpClient\platform_select.props = External\xal\External\libHttpClient\platform_select.props Build\xsapi.gdk.bwoi.props = Build\xsapi.gdk.bwoi.props Build\xsapi.gdk.props = Build\xsapi.gdk.props xsapi.paths.props = xsapi.paths.props xsapi.staticlib.props = xsapi.staticlib.props EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Common", "Common", "{8280F3FB-83FF-4351-A5F3-55E806F4D2D8}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Xbox.Services.Common", "Build\Microsoft.Xbox.Services.Common\Microsoft.Xbox.Services.Common.vcxitems", "{CF3350E5-00A2-4647-A996-BAF7542B0327}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Xbox.Services.GDK.C.Thunks", "Build\Microsoft.Xbox.Services.GDK.C.Thunks\Microsoft.Xbox.Services.GDK.C.Thunks.vcxproj", "{6AA78AA9-78A5-40C0-828F-5933AF847111}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ThunksGenerator", "Build\Microsoft.Xbox.Services.GDK.C.Thunks\generator\ThunksGenerator\ThunksGenerator.csproj", "{21C651D1-61D7-46C5-BD23-128E40329AA5}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Debug|Gaming.Desktop.x64 = Debug|Gaming.Desktop.x64 Debug|Gaming.Xbox.Scarlett.x64 = Debug|Gaming.Xbox.Scarlett.x64 Debug|Gaming.Xbox.XboxOne.x64 = Debug|Gaming.Xbox.XboxOne.x64 Release|Any CPU = Release|Any CPU Release|Gaming.Desktop.x64 = Release|Gaming.Desktop.x64 Release|Gaming.Xbox.Scarlett.x64 = Release|Gaming.Xbox.Scarlett.x64 Release|Gaming.Xbox.XboxOne.x64 = Release|Gaming.Xbox.XboxOne.x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Debug|Any CPU.ActiveCfg = Debug|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Debug|Any CPU.Build.0 = Debug|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Debug|Gaming.Desktop.x64.ActiveCfg = Debug|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Debug|Gaming.Desktop.x64.Build.0 = Debug|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Debug|Gaming.Xbox.Scarlett.x64.ActiveCfg = Debug|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Debug|Gaming.Xbox.Scarlett.x64.Build.0 = Debug|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Debug|Gaming.Xbox.XboxOne.x64.ActiveCfg = Debug|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Debug|Gaming.Xbox.XboxOne.x64.Build.0 = Debug|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Release|Any CPU.ActiveCfg = Release|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Release|Any CPU.Build.0 = Release|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Release|Gaming.Desktop.x64.ActiveCfg = Release|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Release|Gaming.Desktop.x64.Build.0 = Release|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Release|Gaming.Xbox.Scarlett.x64.ActiveCfg = Release|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Release|Gaming.Xbox.Scarlett.x64.Build.0 = Release|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Release|Gaming.Xbox.XboxOne.x64.ActiveCfg = Release|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Release|Gaming.Xbox.XboxOne.x64.Build.0 = Release|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Debug|Any CPU.ActiveCfg = Debug|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Debug|Any CPU.Build.0 = Debug|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Debug|Gaming.Desktop.x64.ActiveCfg = Debug|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Debug|Gaming.Desktop.x64.Build.0 = Debug|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Debug|Gaming.Xbox.Scarlett.x64.ActiveCfg = Debug|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Debug|Gaming.Xbox.XboxOne.x64.ActiveCfg = Debug|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Release|Any CPU.ActiveCfg = Release|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Release|Any CPU.Build.0 = Release|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Release|Gaming.Desktop.x64.ActiveCfg = Release|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Release|Gaming.Desktop.x64.Build.0 = Release|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Release|Gaming.Xbox.Scarlett.x64.ActiveCfg = Release|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Release|Gaming.Xbox.XboxOne.x64.ActiveCfg = Release|Gaming.Desktop.x64 {6AA78AA9-78A5-40C0-828F-5933AF847111}.Debug|Any CPU.ActiveCfg = Debug|Gaming.Desktop.x64 {6AA78AA9-78A5-40C0-828F-5933AF847111}.Debug|Any CPU.Build.0 = Debug|Gaming.Desktop.x64 {6AA78AA9-78A5-40C0-828F-5933AF847111}.Debug|Gaming.Desktop.x64.ActiveCfg = Debug|Gaming.Desktop.x64 {6AA78AA9-78A5-40C0-828F-5933AF847111}.Debug|Gaming.Desktop.x64.Build.0 = Debug|Gaming.Desktop.x64 {6AA78AA9-78A5-40C0-828F-5933AF847111}.Debug|Gaming.Xbox.Scarlett.x64.ActiveCfg = Debug|Gaming.Desktop.x64 {6AA78AA9-78A5-40C0-828F-5933AF847111}.Debug|Gaming.Xbox.Scarlett.x64.Build.0 = Debug|Gaming.Desktop.x64 {6AA78AA9-78A5-40C0-828F-5933AF847111}.Debug|Gaming.Xbox.XboxOne.x64.ActiveCfg = Debug|Gaming.Desktop.x64 {6AA78AA9-78A5-40C0-828F-5933AF847111}.Debug|Gaming.Xbox.XboxOne.x64.Build.0 = Debug|Gaming.Desktop.x64 {6AA78AA9-78A5-40C0-828F-5933AF847111}.Release|Any CPU.ActiveCfg = Release|Gaming.Desktop.x64 {6AA78AA9-78A5-40C0-828F-5933AF847111}.Release|Any CPU.Build.0 = Release|Gaming.Desktop.x64 {6AA78AA9-78A5-40C0-828F-5933AF847111}.Release|Gaming.Desktop.x64.ActiveCfg = Release|Gaming.Desktop.x64 {6AA78AA9-78A5-40C0-828F-5933AF847111}.Release|Gaming.Desktop.x64.Build.0 = Release|Gaming.Desktop.x64 {6AA78AA9-78A5-40C0-828F-5933AF847111}.Release|Gaming.Xbox.Scarlett.x64.ActiveCfg = Release|Gaming.Desktop.x64 {6AA78AA9-78A5-40C0-828F-5933AF847111}.Release|Gaming.Xbox.Scarlett.x64.Build.0 = Release|Gaming.Desktop.x64 {6AA78AA9-78A5-40C0-828F-5933AF847111}.Release|Gaming.Xbox.XboxOne.x64.ActiveCfg = Release|Gaming.Desktop.x64 {6AA78AA9-78A5-40C0-828F-5933AF847111}.Release|Gaming.Xbox.XboxOne.x64.Build.0 = Release|Gaming.Desktop.x64 {21C651D1-61D7-46C5-BD23-128E40329AA5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {21C651D1-61D7-46C5-BD23-128E40329AA5}.Debug|Any CPU.Build.0 = Debug|Any CPU {21C651D1-61D7-46C5-BD23-128E40329AA5}.Debug|Gaming.Desktop.x64.ActiveCfg = Debug|Any CPU {21C651D1-61D7-46C5-BD23-128E40329AA5}.Debug|Gaming.Desktop.x64.Build.0 = Debug|Any CPU {21C651D1-61D7-46C5-BD23-128E40329AA5}.Debug|Gaming.Xbox.Scarlett.x64.ActiveCfg = Debug|Any CPU {21C651D1-61D7-46C5-BD23-128E40329AA5}.Debug|Gaming.Xbox.Scarlett.x64.Build.0 = Debug|Any CPU {21C651D1-61D7-46C5-BD23-128E40329AA5}.Debug|Gaming.Xbox.XboxOne.x64.ActiveCfg = Debug|Any CPU {21C651D1-61D7-46C5-BD23-128E40329AA5}.Debug|Gaming.Xbox.XboxOne.x64.Build.0 = Debug|Any CPU {21C651D1-61D7-46C5-BD23-128E40329AA5}.Release|Any CPU.ActiveCfg = Release|Any CPU {21C651D1-61D7-46C5-BD23-128E40329AA5}.Release|Any CPU.Build.0 = Release|Any CPU {21C651D1-61D7-46C5-BD23-128E40329AA5}.Release|Gaming.Desktop.x64.ActiveCfg = Release|Any CPU {21C651D1-61D7-46C5-BD23-128E40329AA5}.Release|Gaming.Desktop.x64.Build.0 = Release|Any CPU {21C651D1-61D7-46C5-BD23-128E40329AA5}.Release|Gaming.Xbox.Scarlett.x64.ActiveCfg = Release|Any CPU {21C651D1-61D7-46C5-BD23-128E40329AA5}.Release|Gaming.Xbox.Scarlett.x64.Build.0 = Release|Any CPU {21C651D1-61D7-46C5-BD23-128E40329AA5}.Release|Gaming.Xbox.XboxOne.x64.ActiveCfg = Release|Any CPU {21C651D1-61D7-46C5-BD23-128E40329AA5}.Release|Gaming.Xbox.XboxOne.x64.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {60139F62-BF37-4F11-BD93-5FBF4E92100C} = {165723E8-FF20-4427-A84B-68E05765C9AC} {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743} = {165723E8-FF20-4427-A84B-68E05765C9AC} {1E320494-894E-4FEB-90CA-DF4FE20499F4} = {165723E8-FF20-4427-A84B-68E05765C9AC} {CF3350E5-00A2-4647-A996-BAF7542B0327} = {8280F3FB-83FF-4351-A5F3-55E806F4D2D8} {6AA78AA9-78A5-40C0-828F-5933AF847111} = {165723E8-FF20-4427-A84B-68E05765C9AC} {21C651D1-61D7-46C5-BD23-128E40329AA5} = {165723E8-FF20-4427-A84B-68E05765C9AC} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {AB434E05-34C8-4B45-BE86-E363CCF8DD76} EndGlobalSection GlobalSection(SharedMSBuildProjectFiles) = preSolution Build\Microsoft.Xbox.Services.GDK\Microsoft.Xbox.Services.GDK.vcxitems*{1e320494-894e-4feb-90ca-df4fe20499f4}*SharedItemsImports = 9 Build\Microsoft.Xbox.Services.Common\Microsoft.Xbox.Services.Common.vcxitems*{60139f62-bf37-4f11-bd93-5fbf4e92100c}*SharedItemsImports = 4 Build\Microsoft.Xbox.Services.GDK\Microsoft.Xbox.Services.GDK.vcxitems*{60139f62-bf37-4f11-bd93-5fbf4e92100c}*SharedItemsImports = 4 External\Xal\External\libHttpClient\Build\libHttpClient.Common\libHttpClient.Common.vcxitems*{a5a6e02a-21ba-4d55-9fb9-7b24dedd3743}*SharedItemsImports = 4 External\Xal\External\libHttpClient\Build\libHttpClient.GDK.Shared\libHttpClient.GDK.Shared.vcxitems*{a5a6e02a-21ba-4d55-9fb9-7b24dedd3743}*SharedItemsImports = 4 Build\Microsoft.Xbox.Services.Common\Microsoft.Xbox.Services.Common.vcxitems*{cf3350e5-00a2-4647-a996-baf7542b0327}*SharedItemsImports = 9 EndGlobalSection EndGlobal ================================================ FILE: Microsoft.Xbox.Services.GDK.VS2022.sln ================================================ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.6.33829.357 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "GDK", "GDK", "{C9C47451-FCA6-4B7F-9BD0-20AD3B119737}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "APIExplorer.Shared", "Tests\APIExplorer\APIExplorer.Shared.vcxitems", "{CBC0BEC4-131D-4868-9345-71813557FB39}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{DC381015-B171-4DA5-B7FA-7CDE8196C6D3}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Xbox.Services.142.GDK.C", "Build\Microsoft.Xbox.Services.142.GDK.C\Microsoft.Xbox.Services.142.GDK.C.vcxproj", "{60139F62-BF37-4F11-BD93-5FBF4E92100C}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DirectXTK12", "Tests\GDK\APIRunner.GDK\Kits\DirectXTK12\DirectXTK12_GDK_2017.vcxproj", "{052C4858-C76F-4CEA-8A1A-E8E5559E67C2}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "APIRunner.143.GDK.Src", "Tests\GDK\APIRunner.GDK\APIRunner.143.GDK.Src.vcxproj", "{46F31A54-4C74-41A8-B294-26B5689E51EF}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{E10FED00-36AB-4B26-8945-3E7D996C955C}" ProjectSection(SolutionItems) = preProject External\xal\External\libHttpClient\Build\libHttpClient.GDK.props = External\xal\External\libHttpClient\Build\libHttpClient.GDK.props External\xal\External\libHttpClient\libHttpClient.props = External\xal\External\libHttpClient\libHttpClient.props External\xal\External\libHttpClient\platform_select.props = External\xal\External\libHttpClient\platform_select.props Build\xsapi.gdk.bwoi.props = Build\xsapi.gdk.bwoi.props Build\xsapi.gdk.props = Build\xsapi.gdk.props xsapi.paths.props = xsapi.paths.props xsapi.staticlib.props = xsapi.staticlib.props EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libHttpClient.GDK", "External\xal\External\libHttpClient\Build\libHttpClient.GDK\libHttpClient.GDK.vcxproj", "{A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Xbox.Services.GDK", "Build\Microsoft.Xbox.Services.GDK\Microsoft.Xbox.Services.GDK.vcxitems", "{1E320494-894E-4FEB-90CA-DF4FE20499F4}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Xbox.Services.Common", "Build\Microsoft.Xbox.Services.Common\Microsoft.Xbox.Services.Common.vcxitems", "{CF3350E5-00A2-4647-A996-BAF7542B0327}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Common", "Common", "{E4213904-77D5-4847-B858-0938BF96064C}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Debug|ARM = Debug|ARM Debug|ARM64 = Debug|ARM64 Debug|Gaming.Desktop.x64 = Debug|Gaming.Desktop.x64 Debug|Gaming.Xbox.Scarlett.x64 = Debug|Gaming.Xbox.Scarlett.x64 Debug|Gaming.Xbox.XboxOne.x64 = Debug|Gaming.Xbox.XboxOne.x64 Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 Profile|Any CPU = Profile|Any CPU Profile|ARM = Profile|ARM Profile|ARM64 = Profile|ARM64 Profile|Gaming.Desktop.x64 = Profile|Gaming.Desktop.x64 Profile|Gaming.Xbox.Scarlett.x64 = Profile|Gaming.Xbox.Scarlett.x64 Profile|Gaming.Xbox.XboxOne.x64 = Profile|Gaming.Xbox.XboxOne.x64 Profile|x64 = Profile|x64 Profile|x86 = Profile|x86 Release|Any CPU = Release|Any CPU Release|ARM = Release|ARM Release|ARM64 = Release|ARM64 Release|Gaming.Desktop.x64 = Release|Gaming.Desktop.x64 Release|Gaming.Xbox.Scarlett.x64 = Release|Gaming.Xbox.Scarlett.x64 Release|Gaming.Xbox.XboxOne.x64 = Release|Gaming.Xbox.XboxOne.x64 Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Debug|Any CPU.ActiveCfg = Debug|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Debug|ARM.ActiveCfg = Debug|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Debug|ARM64.ActiveCfg = Debug|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Debug|Gaming.Desktop.x64.ActiveCfg = Debug|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Debug|Gaming.Desktop.x64.Build.0 = Debug|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Debug|Gaming.Xbox.Scarlett.x64.ActiveCfg = Debug|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Debug|Gaming.Xbox.Scarlett.x64.Build.0 = Debug|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Debug|Gaming.Xbox.XboxOne.x64.ActiveCfg = Debug|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Debug|Gaming.Xbox.XboxOne.x64.Build.0 = Debug|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Debug|x64.ActiveCfg = Debug|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Debug|x86.ActiveCfg = Debug|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Profile|Any CPU.ActiveCfg = Release|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Profile|Any CPU.Build.0 = Release|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Profile|ARM.ActiveCfg = Release|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Profile|ARM.Build.0 = Release|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Profile|ARM64.ActiveCfg = Release|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Profile|ARM64.Build.0 = Release|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Profile|Gaming.Desktop.x64.ActiveCfg = Release|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Profile|Gaming.Desktop.x64.Build.0 = Release|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Profile|Gaming.Xbox.Scarlett.x64.ActiveCfg = Release|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Profile|Gaming.Xbox.Scarlett.x64.Build.0 = Release|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Profile|Gaming.Xbox.XboxOne.x64.ActiveCfg = Release|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Profile|Gaming.Xbox.XboxOne.x64.Build.0 = Release|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Profile|x64.ActiveCfg = Release|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Profile|x64.Build.0 = Release|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Profile|x86.ActiveCfg = Release|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Profile|x86.Build.0 = Release|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Release|Any CPU.ActiveCfg = Release|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Release|ARM.ActiveCfg = Release|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Release|ARM64.ActiveCfg = Release|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Release|Gaming.Desktop.x64.ActiveCfg = Release|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Release|Gaming.Desktop.x64.Build.0 = Release|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Release|Gaming.Xbox.Scarlett.x64.ActiveCfg = Release|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Release|Gaming.Xbox.Scarlett.x64.Build.0 = Release|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Release|Gaming.Xbox.XboxOne.x64.ActiveCfg = Release|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Release|Gaming.Xbox.XboxOne.x64.Build.0 = Release|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Release|x64.ActiveCfg = Release|Gaming.Desktop.x64 {60139F62-BF37-4F11-BD93-5FBF4E92100C}.Release|x86.ActiveCfg = Release|Gaming.Desktop.x64 {052C4858-C76F-4CEA-8A1A-E8E5559E67C2}.Debug|Any CPU.ActiveCfg = Debug|Gaming.Xbox.XboxOne.x64 {052C4858-C76F-4CEA-8A1A-E8E5559E67C2}.Debug|ARM.ActiveCfg = Debug|Gaming.Xbox.Scarlett.x64 {052C4858-C76F-4CEA-8A1A-E8E5559E67C2}.Debug|ARM64.ActiveCfg = Debug|Gaming.Xbox.Scarlett.x64 {052C4858-C76F-4CEA-8A1A-E8E5559E67C2}.Debug|Gaming.Desktop.x64.ActiveCfg = Debug|Gaming.Desktop.x64 {052C4858-C76F-4CEA-8A1A-E8E5559E67C2}.Debug|Gaming.Desktop.x64.Build.0 = Debug|Gaming.Desktop.x64 {052C4858-C76F-4CEA-8A1A-E8E5559E67C2}.Debug|Gaming.Xbox.Scarlett.x64.ActiveCfg = Debug|Gaming.Xbox.Scarlett.x64 {052C4858-C76F-4CEA-8A1A-E8E5559E67C2}.Debug|Gaming.Xbox.Scarlett.x64.Build.0 = Debug|Gaming.Xbox.Scarlett.x64 {052C4858-C76F-4CEA-8A1A-E8E5559E67C2}.Debug|Gaming.Xbox.XboxOne.x64.ActiveCfg = Debug|Gaming.Xbox.XboxOne.x64 {052C4858-C76F-4CEA-8A1A-E8E5559E67C2}.Debug|Gaming.Xbox.XboxOne.x64.Build.0 = Debug|Gaming.Xbox.XboxOne.x64 {052C4858-C76F-4CEA-8A1A-E8E5559E67C2}.Debug|x64.ActiveCfg = Debug|Gaming.Xbox.Scarlett.x64 {052C4858-C76F-4CEA-8A1A-E8E5559E67C2}.Debug|x86.ActiveCfg = Debug|Gaming.Xbox.Scarlett.x64 {052C4858-C76F-4CEA-8A1A-E8E5559E67C2}.Profile|Any CPU.ActiveCfg = Profile|Gaming.Xbox.XboxOne.x64 {052C4858-C76F-4CEA-8A1A-E8E5559E67C2}.Profile|ARM.ActiveCfg = Profile|Gaming.Xbox.Scarlett.x64 {052C4858-C76F-4CEA-8A1A-E8E5559E67C2}.Profile|ARM64.ActiveCfg = Profile|Gaming.Xbox.Scarlett.x64 {052C4858-C76F-4CEA-8A1A-E8E5559E67C2}.Profile|Gaming.Desktop.x64.ActiveCfg = Profile|Gaming.Desktop.x64 {052C4858-C76F-4CEA-8A1A-E8E5559E67C2}.Profile|Gaming.Desktop.x64.Build.0 = Profile|Gaming.Desktop.x64 {052C4858-C76F-4CEA-8A1A-E8E5559E67C2}.Profile|Gaming.Xbox.Scarlett.x64.ActiveCfg = Profile|Gaming.Xbox.Scarlett.x64 {052C4858-C76F-4CEA-8A1A-E8E5559E67C2}.Profile|Gaming.Xbox.Scarlett.x64.Build.0 = Profile|Gaming.Xbox.Scarlett.x64 {052C4858-C76F-4CEA-8A1A-E8E5559E67C2}.Profile|Gaming.Xbox.XboxOne.x64.ActiveCfg = Profile|Gaming.Xbox.XboxOne.x64 {052C4858-C76F-4CEA-8A1A-E8E5559E67C2}.Profile|Gaming.Xbox.XboxOne.x64.Build.0 = Profile|Gaming.Xbox.XboxOne.x64 {052C4858-C76F-4CEA-8A1A-E8E5559E67C2}.Profile|x64.ActiveCfg = Profile|Gaming.Xbox.Scarlett.x64 {052C4858-C76F-4CEA-8A1A-E8E5559E67C2}.Profile|x86.ActiveCfg = Profile|Gaming.Xbox.Scarlett.x64 {052C4858-C76F-4CEA-8A1A-E8E5559E67C2}.Release|Any CPU.ActiveCfg = Release|Gaming.Xbox.XboxOne.x64 {052C4858-C76F-4CEA-8A1A-E8E5559E67C2}.Release|ARM.ActiveCfg = Release|Gaming.Xbox.Scarlett.x64 {052C4858-C76F-4CEA-8A1A-E8E5559E67C2}.Release|ARM64.ActiveCfg = Release|Gaming.Xbox.Scarlett.x64 {052C4858-C76F-4CEA-8A1A-E8E5559E67C2}.Release|Gaming.Desktop.x64.ActiveCfg = Release|Gaming.Desktop.x64 {052C4858-C76F-4CEA-8A1A-E8E5559E67C2}.Release|Gaming.Desktop.x64.Build.0 = Release|Gaming.Desktop.x64 {052C4858-C76F-4CEA-8A1A-E8E5559E67C2}.Release|Gaming.Xbox.Scarlett.x64.ActiveCfg = Release|Gaming.Xbox.Scarlett.x64 {052C4858-C76F-4CEA-8A1A-E8E5559E67C2}.Release|Gaming.Xbox.Scarlett.x64.Build.0 = Release|Gaming.Xbox.Scarlett.x64 {052C4858-C76F-4CEA-8A1A-E8E5559E67C2}.Release|Gaming.Xbox.XboxOne.x64.ActiveCfg = Release|Gaming.Xbox.XboxOne.x64 {052C4858-C76F-4CEA-8A1A-E8E5559E67C2}.Release|Gaming.Xbox.XboxOne.x64.Build.0 = Release|Gaming.Xbox.XboxOne.x64 {052C4858-C76F-4CEA-8A1A-E8E5559E67C2}.Release|x64.ActiveCfg = Release|Gaming.Xbox.Scarlett.x64 {052C4858-C76F-4CEA-8A1A-E8E5559E67C2}.Release|x86.ActiveCfg = Release|Gaming.Xbox.Scarlett.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Debug|Any CPU.ActiveCfg = Debug|Gaming.Xbox.XboxOne.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Debug|ARM.ActiveCfg = Debug|Gaming.Xbox.Scarlett.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Debug|ARM64.ActiveCfg = Debug|Gaming.Xbox.Scarlett.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Debug|Gaming.Desktop.x64.ActiveCfg = Debug|Gaming.Desktop.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Debug|Gaming.Desktop.x64.Build.0 = Debug|Gaming.Desktop.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Debug|Gaming.Desktop.x64.Deploy.0 = Debug|Gaming.Desktop.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Debug|Gaming.Xbox.Scarlett.x64.ActiveCfg = Debug|Gaming.Xbox.Scarlett.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Debug|Gaming.Xbox.Scarlett.x64.Build.0 = Debug|Gaming.Xbox.Scarlett.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Debug|Gaming.Xbox.Scarlett.x64.Deploy.0 = Debug|Gaming.Xbox.Scarlett.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Debug|Gaming.Xbox.XboxOne.x64.ActiveCfg = Debug|Gaming.Xbox.XboxOne.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Debug|Gaming.Xbox.XboxOne.x64.Build.0 = Debug|Gaming.Xbox.XboxOne.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Debug|Gaming.Xbox.XboxOne.x64.Deploy.0 = Debug|Gaming.Xbox.XboxOne.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Debug|x64.ActiveCfg = Debug|Gaming.Xbox.Scarlett.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Debug|x86.ActiveCfg = Debug|Gaming.Xbox.Scarlett.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Profile|Any CPU.ActiveCfg = Profile|Gaming.Xbox.XboxOne.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Profile|ARM.ActiveCfg = Profile|Gaming.Xbox.Scarlett.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Profile|ARM64.ActiveCfg = Profile|Gaming.Xbox.Scarlett.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Profile|Gaming.Desktop.x64.ActiveCfg = Profile|Gaming.Desktop.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Profile|Gaming.Desktop.x64.Build.0 = Profile|Gaming.Desktop.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Profile|Gaming.Desktop.x64.Deploy.0 = Profile|Gaming.Desktop.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Profile|Gaming.Xbox.Scarlett.x64.ActiveCfg = Profile|Gaming.Xbox.Scarlett.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Profile|Gaming.Xbox.Scarlett.x64.Build.0 = Profile|Gaming.Xbox.Scarlett.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Profile|Gaming.Xbox.Scarlett.x64.Deploy.0 = Profile|Gaming.Xbox.Scarlett.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Profile|Gaming.Xbox.XboxOne.x64.ActiveCfg = Profile|Gaming.Xbox.XboxOne.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Profile|Gaming.Xbox.XboxOne.x64.Build.0 = Profile|Gaming.Xbox.XboxOne.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Profile|Gaming.Xbox.XboxOne.x64.Deploy.0 = Profile|Gaming.Xbox.XboxOne.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Profile|x64.ActiveCfg = Profile|Gaming.Xbox.Scarlett.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Profile|x86.ActiveCfg = Profile|Gaming.Xbox.Scarlett.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Release|Any CPU.ActiveCfg = Release|Gaming.Xbox.XboxOne.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Release|ARM.ActiveCfg = Release|Gaming.Xbox.Scarlett.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Release|ARM64.ActiveCfg = Release|Gaming.Xbox.Scarlett.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Release|Gaming.Desktop.x64.ActiveCfg = Release|Gaming.Desktop.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Release|Gaming.Desktop.x64.Build.0 = Release|Gaming.Desktop.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Release|Gaming.Desktop.x64.Deploy.0 = Release|Gaming.Desktop.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Release|Gaming.Xbox.Scarlett.x64.ActiveCfg = Release|Gaming.Xbox.Scarlett.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Release|Gaming.Xbox.Scarlett.x64.Build.0 = Release|Gaming.Xbox.Scarlett.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Release|Gaming.Xbox.Scarlett.x64.Deploy.0 = Release|Gaming.Xbox.Scarlett.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Release|Gaming.Xbox.XboxOne.x64.ActiveCfg = Release|Gaming.Xbox.XboxOne.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Release|Gaming.Xbox.XboxOne.x64.Build.0 = Release|Gaming.Xbox.XboxOne.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Release|Gaming.Xbox.XboxOne.x64.Deploy.0 = Release|Gaming.Xbox.XboxOne.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Release|x64.ActiveCfg = Release|Gaming.Xbox.Scarlett.x64 {46F31A54-4C74-41A8-B294-26B5689E51EF}.Release|x86.ActiveCfg = Release|Gaming.Xbox.Scarlett.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Debug|Any CPU.ActiveCfg = Debug|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Debug|Any CPU.Build.0 = Debug|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Debug|ARM.ActiveCfg = Debug|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Debug|ARM.Build.0 = Debug|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Debug|ARM64.ActiveCfg = Debug|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Debug|ARM64.Build.0 = Debug|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Debug|Gaming.Desktop.x64.ActiveCfg = Debug|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Debug|Gaming.Desktop.x64.Build.0 = Debug|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Debug|Gaming.Xbox.Scarlett.x64.ActiveCfg = Debug|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Debug|Gaming.Xbox.Scarlett.x64.Build.0 = Debug|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Debug|Gaming.Xbox.XboxOne.x64.ActiveCfg = Debug|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Debug|Gaming.Xbox.XboxOne.x64.Build.0 = Debug|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Debug|x64.ActiveCfg = Debug|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Debug|x64.Build.0 = Debug|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Debug|x86.ActiveCfg = Debug|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Debug|x86.Build.0 = Debug|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Profile|Any CPU.ActiveCfg = Debug|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Profile|Any CPU.Build.0 = Debug|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Profile|ARM.ActiveCfg = Debug|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Profile|ARM.Build.0 = Debug|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Profile|ARM64.ActiveCfg = Debug|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Profile|ARM64.Build.0 = Debug|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Profile|Gaming.Desktop.x64.ActiveCfg = Debug|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Profile|Gaming.Desktop.x64.Build.0 = Debug|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Profile|Gaming.Xbox.Scarlett.x64.ActiveCfg = Debug|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Profile|Gaming.Xbox.Scarlett.x64.Build.0 = Debug|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Profile|Gaming.Xbox.XboxOne.x64.ActiveCfg = Debug|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Profile|Gaming.Xbox.XboxOne.x64.Build.0 = Debug|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Profile|x64.ActiveCfg = Debug|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Profile|x64.Build.0 = Debug|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Profile|x86.ActiveCfg = Debug|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Profile|x86.Build.0 = Debug|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Release|Any CPU.ActiveCfg = Release|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Release|Any CPU.Build.0 = Release|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Release|ARM.ActiveCfg = Release|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Release|ARM.Build.0 = Release|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Release|ARM64.ActiveCfg = Release|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Release|ARM64.Build.0 = Release|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Release|Gaming.Desktop.x64.ActiveCfg = Release|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Release|Gaming.Desktop.x64.Build.0 = Release|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Release|Gaming.Xbox.Scarlett.x64.ActiveCfg = Release|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Release|Gaming.Xbox.Scarlett.x64.Build.0 = Release|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Release|Gaming.Xbox.XboxOne.x64.ActiveCfg = Release|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Release|Gaming.Xbox.XboxOne.x64.Build.0 = Release|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Release|x64.ActiveCfg = Release|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Release|x64.Build.0 = Release|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Release|x86.ActiveCfg = Release|Gaming.Desktop.x64 {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743}.Release|x86.Build.0 = Release|Gaming.Desktop.x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {CBC0BEC4-131D-4868-9345-71813557FB39} = {E4213904-77D5-4847-B858-0938BF96064C} {DC381015-B171-4DA5-B7FA-7CDE8196C6D3} = {C9C47451-FCA6-4B7F-9BD0-20AD3B119737} {60139F62-BF37-4F11-BD93-5FBF4E92100C} = {C9C47451-FCA6-4B7F-9BD0-20AD3B119737} {052C4858-C76F-4CEA-8A1A-E8E5559E67C2} = {DC381015-B171-4DA5-B7FA-7CDE8196C6D3} {46F31A54-4C74-41A8-B294-26B5689E51EF} = {DC381015-B171-4DA5-B7FA-7CDE8196C6D3} {A5A6E02A-21BA-4D55-9FB9-7B24DEDD3743} = {C9C47451-FCA6-4B7F-9BD0-20AD3B119737} {1E320494-894E-4FEB-90CA-DF4FE20499F4} = {C9C47451-FCA6-4B7F-9BD0-20AD3B119737} {CF3350E5-00A2-4647-A996-BAF7542B0327} = {E4213904-77D5-4847-B858-0938BF96064C} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {622BB9B5-938F-41A2-BC8B-B7B3A64A73B5} EndGlobalSection GlobalSection(SharedMSBuildProjectFiles) = preSolution Build\Microsoft.Xbox.Services.GDK\Microsoft.Xbox.Services.GDK.vcxitems*{1e320494-894e-4feb-90ca-df4fe20499f4}*SharedItemsImports = 9 Tests\APIExplorer\APIExplorer.Shared.vcxitems*{46f31a54-4c74-41a8-b294-26b5689e51ef}*SharedItemsImports = 4 Build\Microsoft.Xbox.Services.Common\Microsoft.Xbox.Services.Common.vcxitems*{60139f62-bf37-4f11-bd93-5fbf4e92100c}*SharedItemsImports = 4 Build\Microsoft.Xbox.Services.GDK\Microsoft.Xbox.Services.GDK.vcxitems*{60139f62-bf37-4f11-bd93-5fbf4e92100c}*SharedItemsImports = 4 External\Xal\External\libHttpClient\Build\libHttpClient.Common\libHttpClient.Common.vcxitems*{a5a6e02a-21ba-4d55-9fb9-7b24dedd3743}*SharedItemsImports = 4 External\Xal\External\libHttpClient\Build\libHttpClient.GDK.Shared\libHttpClient.GDK.Shared.vcxitems*{a5a6e02a-21ba-4d55-9fb9-7b24dedd3743}*SharedItemsImports = 4 Tests\APIExplorer\APIExplorer.Shared.vcxitems*{cbc0bec4-131d-4868-9345-71813557fb39}*SharedItemsImports = 9 Build\Microsoft.Xbox.Services.Common\Microsoft.Xbox.Services.Common.vcxitems*{cf3350e5-00a2-4647-a996-baf7542b0327}*SharedItemsImports = 9 EndGlobalSection EndGlobal ================================================ FILE: NuGet.config ================================================ ================================================ FILE: README.md ================================================ ## Welcome! The Microsoft Xbox Live Service API (XSAPI) enables game developers to access Xbox Live. To get access to the Xbox Live service, you can join the Xbox Live Creators Program at https://aka.ms/xblcp, or apply to the ID@Xbox program at: http://www.xbox.com/en-us/Developers/id To learn more about these programs, please refer to the [developer program overview](https://docs.microsoft.com/en-us/windows/uwp/xbox-live/developer-program-overview). ## What's in the API: * Xbox Live Features - profile, social, presence, leaderboards, achievements, multiplayer, matchmaking, title storage * Xbox Live Authentication Library (XAL) public headers - Note that this repository does not contain full XAL source, it only contains XAL source files needed to support building with the Microsoft GDK. * Platforms - Microsoft GDK (targeting both PC and Console). Installing the Microsoft GDK is a prerequisite for building XSAPI. Additionally, source and projects for XDK and UWP platforms can be found at https://github.com/microsoft/xbox-live-api/tree/1807_xdk_qfe_preview * Support for Visual Studio 2017 and 2019 ## How to use the Xbox Live Services API (XSAPI) The best way to learn the API and see the best practices is to look at the Xbox Live samples that ship with the Microsoft GDK, and the [Xbox Live developer docs](https://docs.microsoft.com/en-us/windows/uwp/xbox-live/) ## How to clone repo This repo contains submodules. There are two ways to make sure you get submodules. When initially cloning, make sure you use the `--recursive` option. IE: git clone --recursive https://github.com/Microsoft/xbox-live-api.git If you already cloned the repo, you can initialize submodules with: git submodule sync git submodule update --init --recursive **Note that using GitHub's feature to "Download Zip" does not contain the submodules and will not properly build. Please clone recursively instead.** ## How to link your project against source You might want to link against the XSAPI source if you want to debug an issue, or understand where an error code is coming from. How to do this can be found at [How to link your project against source](LINKTOSOURCE.md) ## Contribute Back! Is there a feature missing that you'd like to see, or found a bug that you have a fix for? Or do you have an idea or just interest in helping out in building the library? Let us know and we'd love to work with you. For a good starting point on where we are headed and feature ideas, take a look at our [requested features and bugs](https://github.com/Microsoft/xbox-live-api/issues). Big or small we'd like to take your contributions back to help improve the Xbox Live Service API for everyone. ## Having Trouble? We'd love to get your review score, whether good or bad, but even more than that, we want to fix your problem. If you submit your issue as a Review, we won't be able to respond to your problem and ask any follow-up questions that may be necessary. The most efficient way to do that is to open a an issue in our [issue tracker](https://github.com/Microsoft/xbox-live-api/issues). The Xbox Live team will be engaged with the community and be continually improving our APIs, tools, and documentation based on the feedback received. ### Xbox Live GitHub projects * [Xbox Live Service API for C++](https://github.com/Microsoft/xbox-live-api) * [Xbox Live Samples (XDK/UWP)](https://github.com/Microsoft/xbox-live-samples) * [Xbox Live Resiliency Fiddler Plugin](https://github.com/Microsoft/xbox-live-resiliency-fiddler-plugin) * [Xbox Live Trace Analyzer](https://github.com/Microsoft/xbox-live-trace-analyzer) * [Xbox Live Developer Tools](https://github.com/Microsoft/xbox-live-developer-tools) * [libHttpClient](https://github.com/Microsoft/libHttpClient) This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. ================================================ FILE: SECURITY.md ================================================ ## Security Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)), please report it to us as described below. ## Reporting Security Issues **Please do not report security vulnerabilities through public GitHub issues.** Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report). If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc). You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc). Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) * Full paths of source file(s) related to the manifestation of the issue * The location of the affected source code (tag/branch/commit or direct URL) * Any special configuration required to reproduce the issue * Step-by-step instructions to reproduce the issue * Proof-of-concept or exploit code (if possible) * Impact of the issue, including how an attacker might exploit the issue This information will help us triage your report more quickly. If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://microsoft.com/msrc/bounty) page for more details about our active programs. ## Preferred Languages We prefer all communications to be in English. ## Policy Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd). ================================================ FILE: Source/Services/Achievements/Manager/achievements_manager_api.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "achievements_manager_internal.h" #include using namespace xbox::services; using namespace xbox::services::achievements::manager; NAMESPACE_MICROSOFT_XBOX_SERVICES_ACHIEVEMENTS_MANAGER_CPP_BEGIN template Ret ApiImpl(Ret&& fallbackReturnValue, TWork&& work) noexcept { auto state{ GlobalState::Get() }; if (!state) { return fallbackReturnValue; } assert(state->AchievementsManager()); return work(*state->AchievementsManager()); } template HRESULT ApiImpl(TWork&& work) noexcept { return ApiImpl(E_XBL_NOT_INITIALIZED, std::move(work)); } NAMESPACE_MICROSOFT_XBOX_SERVICES_ACHIEVEMENTS_MANAGER_CPP_END STDAPI XblAchievementsManagerResultGetAchievements( _In_ XblAchievementsManagerResultHandle resultHandle, _Out_ const XblAchievement** achievements, _Out_ uint64_t* achievementsCount ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(achievementsCount == nullptr || achievements == nullptr || resultHandle == nullptr); VERIFY_XBL_INITIALIZED(); *achievementsCount = resultHandle->Achievements().size(); *achievements = *achievementsCount > 0 ? resultHandle->Achievements().data() : nullptr; return S_OK; } CATCH_RETURN() STDAPI XblAchievementsManagerResultDuplicateHandle( _In_ XblAchievementsManagerResultHandle handle, _Out_ XblAchievementsManagerResultHandle* duplicatedHandle ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(handle == nullptr || duplicatedHandle == nullptr); handle->AddRef(); *duplicatedHandle = handle; return S_OK; } CATCH_RETURN() STDAPI_(void) XblAchievementsManagerResultCloseHandle( _In_ XblAchievementsManagerResultHandle handle ) XBL_NOEXCEPT try { if (handle) { handle->DecRef(); } } CATCH_RETURN_WITH(;) STDAPI XblAchievementsManagerAddLocalUser( _In_ XblUserHandle user, _In_opt_ XTaskQueueHandle queue ) XBL_NOEXCEPT try { return ApiImpl([&](AchievementsManager& achievementsManager) { RETURN_HR_INVALIDARGUMENT_IF(user == nullptr); auto wrapUserResult{ User::WrapHandle(user) }; RETURN_HR_IF_FAILED(wrapUserResult.Hresult()); return achievementsManager.AddLocalUser(wrapUserResult.ExtractPayload(), TaskQueue::DeriveWorkerQueue(queue)); }); } CATCH_RETURN() STDAPI XblAchievementsManagerRemoveLocalUser( _In_ XblUserHandle user ) XBL_NOEXCEPT try { return ApiImpl([&](AchievementsManager& achievementsManager) { RETURN_HR_INVALIDARGUMENT_IF_NULL(user); auto wrapUserResult{ User::WrapHandle(user) }; RETURN_HR_IF_FAILED(wrapUserResult.Hresult()); return achievementsManager.RemoveLocalUser(wrapUserResult.Payload()); }); } CATCH_RETURN() STDAPI XblAchievementsManagerIsUserInitialized( _In_ uint64_t xboxUserId ) XBL_NOEXCEPT try { return ApiImpl([&](AchievementsManager& achievementsManager) { RETURN_HR_INVALIDARGUMENT_IF(!achievementsManager.HasUser(xboxUserId)); if (!achievementsManager.IsUserInitialized(xboxUserId)) { return E_FAIL; } return S_OK; }); } CATCH_RETURN() STDAPI XblAchievementsManagerDoWork( _Outptr_result_maybenull_ const XblAchievementsManagerEvent** achievementsEvents, _Out_ size_t* achievementsEventsCount ) XBL_NOEXCEPT try { INIT_OUT_PTR_PARAM(achievementsEvents); return ApiImpl([&](AchievementsManager& achievementsManager) { RETURN_HR_INVALIDARGUMENT_IF(achievementsEvents == nullptr || achievementsEventsCount == nullptr); auto& events = achievementsManager.DoWork(); *achievementsEvents = nullptr; if (!events.empty()) { *achievementsEvents = &(*events.begin()); } *achievementsEventsCount = events.size(); return S_OK; } ); } CATCH_RETURN() STDAPI XblAchievementsManagerGetAchievement( _In_ uint64_t xboxUserId, _In_ const char* achievementId, _Outptr_result_maybenull_ XblAchievementsManagerResultHandle* achievementResult ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(achievementId == nullptr || achievementResult == nullptr); RETURN_HR_INVALIDARGUMENT_IF_EMPTY_STRING(achievementId); *achievementResult = nullptr; return ApiImpl([&](AchievementsManager& achievementsManager) { auto achievement = achievementsManager.GetAchievement(xboxUserId, achievementId); if (Failed(achievement)) { LOGS_ERROR << achievement.ErrorMessage(); return achievement.Hresult(); } std::shared_ptr resultHandle = MakeShared(achievement.ExtractPayload());//resultData); *achievementResult = resultHandle.get(); resultHandle->AddRef(); return S_OK; } ); } CATCH_RETURN() STDAPI XblAchievementsManagerGetAchievements( _In_ uint64_t xboxUserId, _In_ XblAchievementOrderBy sortField, _In_ XblAchievementsManagerSortOrder sortOrder, _Outptr_result_maybenull_ XblAchievementsManagerResultHandle* achievementsResult ) XBL_NOEXCEPT try { INIT_OUT_PTR_PARAM(achievementsResult); // Can't specify a sort order when just using default field to order on RETURN_HR_INVALIDARGUMENT_IF(sortField != XblAchievementOrderBy::UnlockTime && sortOrder != XblAchievementsManagerSortOrder::Unsorted); // Can't specify a sort field without specifying the order to sort on. RETURN_HR_INVALIDARGUMENT_IF(sortField == XblAchievementOrderBy::UnlockTime && sortOrder == XblAchievementsManagerSortOrder::Unsorted); bool areSortOptionsDefault = sortField != XblAchievementOrderBy::UnlockTime && sortOrder == XblAchievementsManagerSortOrder::Unsorted; if (!areSortOptionsDefault) { return XblAchievementsManagerGetAchievementsByState( xboxUserId, sortField, sortOrder, XblAchievementProgressState::Unknown, achievementsResult ); } return ApiImpl([&](AchievementsManager& achievementsManager) { RETURN_HR_INVALIDARGUMENT_IF(achievementsResult == nullptr); *achievementsResult = nullptr; auto achievements = achievementsManager.GetAchievements(xboxUserId); if (Failed(achievements)) { LOGS_ERROR << achievements.ErrorMessage(); return achievements.Hresult(); } if (achievements.Payload().second == 0) { return E_UNEXPECTED; } std::shared_ptr resultHandle = MakeShared(achievements.Payload().first, achievements.Payload().second, true); *achievementsResult = resultHandle.get(); resultHandle->AddRef(); return S_OK; } ); } CATCH_RETURN() STDAPI XblAchievementsManagerGetAchievementsByState( _In_ uint64_t xboxUserId, _In_ XblAchievementOrderBy sortField, _In_ XblAchievementsManagerSortOrder sortOrder, _In_ XblAchievementProgressState achievementState, _Outptr_result_maybenull_ XblAchievementsManagerResultHandle* achievementsResult ) XBL_NOEXCEPT try { INIT_OUT_PTR_PARAM(achievementsResult); // Can't specify a sort order when just using default field to order on RETURN_HR_INVALIDARGUMENT_IF(sortField != XblAchievementOrderBy::UnlockTime && sortOrder != XblAchievementsManagerSortOrder::Unsorted); // Can't specify a sort field without specifying the order to sort on. RETURN_HR_INVALIDARGUMENT_IF(sortField == XblAchievementOrderBy::UnlockTime && sortOrder == XblAchievementsManagerSortOrder::Unsorted); return ApiImpl([&](AchievementsManager& achievementsManager) { RETURN_HR_INVALIDARGUMENT_IF(achievementsResult == nullptr); // Return an invalid arg if one of sortField or sortOrder are set to a // non-default value while the other is set to a default value. RETURN_HR_INVALIDARGUMENT_IF((sortField == XblAchievementOrderBy::UnlockTime && sortOrder == XblAchievementsManagerSortOrder::Unsorted) || (sortOrder != XblAchievementsManagerSortOrder::Unsorted && sortField == XblAchievementOrderBy::DefaultOrder)); *achievementsResult = nullptr; AchievementsManagerSortFilterSettings sortOptions{ sortField, sortOrder, AchievementsManagerFilterType::All }; switch (achievementState) { case XblAchievementProgressState::Achieved: sortOptions.stateFilter = AchievementsManagerFilterType::Unlocked; break; case XblAchievementProgressState::InProgress: sortOptions.stateFilter = AchievementsManagerFilterType::InProgress; break; case XblAchievementProgressState::NotStarted: sortOptions.stateFilter = AchievementsManagerFilterType::NotStarted; break; default: sortOptions.stateFilter = AchievementsManagerFilterType::All; break; } auto achievements = achievementsManager.GetAchievements(xboxUserId, sortOptions); if (Failed(achievements)) { LOGS_ERROR << achievements.ErrorMessage(); return achievements.Hresult(); } // don't need to return an error for getting an empty vector, as it means there // were no elements that matched made it through the filter. std::shared_ptr resultHandle = MakeShared(achievements.Payload()); *achievementsResult = resultHandle.get(); resultHandle->AddRef(); return S_OK; } ); } CATCH_RETURN() STDAPI XblAchievementsManagerUpdateAchievement( _In_ uint64_t xboxUserId, _In_ const char* achievementId, _In_ uint8_t currentProgress ) XBL_NOEXCEPT try { return ApiImpl([&](AchievementsManager& achievementsManager) { RETURN_HR_INVALIDARGUMENT_IF_NULL(achievementId); RETURN_HR_INVALIDARGUMENT_IF_EMPTY_STRING(achievementId); currentProgress = static_cast(fmin(currentProgress, 100)); auto updateResult = achievementsManager.UpdateAchievement(xboxUserId, achievementId, currentProgress); if (Failed(updateResult)) { LOGS_ERROR << updateResult.ErrorMessage(); return updateResult.Hresult(); } return S_OK; } ); } CATCH_RETURN() ================================================ FILE: Source/Services/Achievements/Manager/achievements_manager_internal.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "achievements_manager_internal.h" #include "xbox_live_context_internal.h" #include "real_time_activity_manager.h" using namespace xbox::services; XblAchievementsManagerResult::XblAchievementsManagerResult(_In_ const XblAchievement & achievement) : m_achievements({ achievement }), m_achievementsData(nullptr), m_achievementsCount(), m_explicitCleanup(false) { } XblAchievementsManagerResult::XblAchievementsManagerResult(_In_ Vector& achievements, _In_ bool explicitCleanup) : m_achievements(std::move(achievements)), m_achievementsData(m_achievements.data()), m_achievementsCount(m_achievements.size()), m_explicitCleanup(explicitCleanup) { } XblAchievementsManagerResult::XblAchievementsManagerResult(_In_ XblAchievement* achievements, _In_ size_t achievementCount, _In_ bool explicitCleanup) : m_achievements(achievements, achievements + achievementCount), m_achievementsData(achievements), m_achievementsCount(achievementCount), m_explicitCleanup(explicitCleanup) { } XblAchievementsManagerResult::~XblAchievementsManagerResult() { for (auto& achievement : m_achievements) { achievements::manager::AchievementsManager::CleanUpAchievementCopyForResult(achievement); } if (m_explicitCleanup) { DeleteArray(m_achievementsData, m_achievementsCount); } m_achievementsData = nullptr; m_achievementsCount = 0; m_achievements.clear(); } const Vector& XblAchievementsManagerResult::Achievements() const { return m_achievements; } std::shared_ptr XblAchievementsManagerResult::GetSharedThis() { return shared_from_this(); } NAMESPACE_MICROSOFT_XBOX_SERVICES_ACHIEVEMENTS_MANAGER_CPP_BEGIN AchievementsManagerUser::AchievementsManagerUser( _In_ User&& localUser, _In_ const TaskQueue& queue ) noexcept : m_xuid{ localUser.Xuid() }, m_rtaManager{ GlobalState::Get()->RTAManager() }, m_queue{ queue.DeriveWorkerQueue() } { // Maintain legacy RTA activation count. m_rtaManager->Activate(localUser); m_xblContext = XblContext::Make(std::move(localUser)); } AchievementsManagerUser::~AchievementsManagerUser() { // Terminate any background work m_queue.Terminate(false); // Unregister rta handlers if (m_achievementProgressToken) { m_xblContext->AchievementsService()->RemoveAchievementProgressChangeHandler(m_achievementProgressToken); } if (m_rtaResyncToken) { m_rtaManager->RemoveResyncHandler(m_xblContext->User(), m_rtaResyncToken); } if (m_rtaConnectionToken) { m_rtaManager->RemoveStateChangedHandler(m_xblContext->User(), m_rtaConnectionToken); } m_rtaManager->Deactivate(m_xblContext->User()); for (auto& pair : m_userAchievements) { AchievementsManager::CleanUpDeepCopyAchievement(*pair.second); } DeleteArray(m_achievementCache, m_userAchievements.size()); } Result AchievementsManagerUser::Initialize( _In_ AsyncContext async ) { assert(!m_isInitialized); RETURN_HR_IF_FAILED(m_xblContext->Initialize(m_rtaManager)); m_xblContext->Settings()->SetHttpUserAgent(HttpCallAgent::AchievementsManager); std::weak_ptr weakThis{ shared_from_this() }; m_achievementProgressToken = m_xblContext->AchievementsService()->AddAchievementProgressChangeHandler( [weakThis](const XblAchievementProgressChangeEventArgs& args) { auto sharedThis{ weakThis.lock() }; if (sharedThis) { // convert to manager event for (uint32_t entryIndex = 0; entryIndex < args.entryCount; ++entryIndex) { // In rare cases, we may get a notification for an achievement that we don't have // cached. We'll log but otherwise ignore the RTA notification in those cases auto achievementId = args.updatedAchievementEntries[entryIndex].achievementId; if (sharedThis->m_userAchievements.find(achievementId) == sharedThis->m_userAchievements.end()) { LOGS_WARN << "Ignoring unexpected Achievement Progress RTA event for achievement not in the AchievementManager local cache."; continue; } XblAchievementsManagerEvent achievementEvent; achievementEvent.xboxUserId = sharedThis->m_xuid; achievementEvent.eventType = XblAchievementsManagerEventType::AchievementProgressUpdated; // Make a deep copy of the achievement entries here. Can't steal them with move, // as other handlers might have been added. achievementEvent.progressInfo = AchievementProgressChangeSubscription::DeepCopyProgressChangeEntry( args.updatedAchievementEntries[entryIndex] ); // Achievement progress notifications can potentially come in with no // targetProgressValue defined if it does not result in an achievement // being unlocked, so we need to copy the targetProgressValue from // the cached achievement. auto& progression = achievementEvent.progressInfo.progression; for (uint64_t i = 0; i < progression.requirementsCount; ++i) { auto& requirement = progression.requirements[i]; if (requirement.targetProgressValue == nullptr) { auto& cachedAchievement = sharedThis->m_userAchievements[achievementEvent.progressInfo.achievementId]; auto& cachedProgression = cachedAchievement->progression; auto matchedRequirement = std::find_if(cachedProgression.requirements, cachedProgression.requirements +cachedProgression.requirementsCount, [&requirement](const XblAchievementRequirement& cachedRequirement)->bool { return utils::str_icmp(requirement.id, cachedRequirement.id); }); if (matchedRequirement == (cachedProgression.requirements + cachedProgression.requirementsCount)) { char errorMsg[1024]; SPRINTF(errorMsg, 1024, "Could not find a requirement with ID %s for cached achievement %s. Couldn't populate event with targetProgressValue.", requirement.id, achievementEvent.progressInfo.achievementId); LOGS_ERROR << errorMsg; } requirement.targetProgressValue = Make(matchedRequirement->targetProgressValue); } } { std::lock_guard lock{ sharedThis->m_mutex }; sharedThis->m_eventsToProcess.push_back(achievementEvent); } } } } ); auto resyncCallback = [weakThis] (HRESULT hr) { auto sharedThis{ weakThis.lock() }; auto state{ GlobalState::Get() }; if (sharedThis && state) { std::lock_guard lock{ sharedThis->m_mutex }; sharedThis->m_isFetchingAchievements = false; if (FAILED(hr)) { LOGS_ERROR << "Fetching state of achievements for user with ID " << sharedThis->Xuid() << " for RTA Resync failed with error code " << hr; } else { // Needed if not already initialized, for instance if the user was added while offline sharedThis->m_isInitialized = true; } } }; // rta resync m_rtaResyncToken = m_rtaManager->AddResyncHandler(m_xblContext->User(), [weakThis, resyncCallback]() { if (auto sharedThis{ weakThis.lock() }) { std::lock_guard lock{ sharedThis->m_mutex }; sharedThis->m_isFetchingAchievements = true; sharedThis->FetchAchievements(AsyncContext { sharedThis->m_queue, resyncCallback }); } } ); m_rtaConnectionToken = m_rtaManager->AddStateChangedHandler(m_xblContext->User(), [weakThis, resyncCallback](XblRealTimeActivityConnectionState connectionState) { // Only take action if rta just connected. if (connectionState != XblRealTimeActivityConnectionState::Connected) { return; } if (auto sharedThis{ weakThis.lock() }) { std::lock_guard lock{ sharedThis->m_mutex }; // We only want to refetch state data if we're not in the middle of a fetch if (sharedThis->m_isFetchingAchievements) { return; } sharedThis->m_isFetchingAchievements = true; // If this function hasn't returned yet, this means that the // RTA connection dropped at some point (whether the network // connection dropped, or an issue with the RTA service, // etc.) and we've missed some data, meaning we need to // resync the cached state from the service. sharedThis->FetchAchievements(AsyncContext { sharedThis->m_queue, resyncCallback }); } } ); return FetchAchievements(AsyncContext { m_queue, [ async, weakThis ] (HRESULT hr) { auto sharedThis{ weakThis.lock() }; auto state{ GlobalState::Get() }; if (sharedThis && state) { { std::lock_guard lock{ sharedThis->m_mutex }; sharedThis->m_isFetchingAchievements = false; if (SUCCEEDED(hr)) { sharedThis->m_isInitialized = true; } } async.Complete(hr); } } }); } bool AchievementsManagerUser::IsInitialized() const { return m_isInitialized; } uint64_t AchievementsManagerUser::Xuid() const { return m_xuid; } Result AchievementsManagerUser::GetAchievement(_In_ const String & id) { if (m_userAchievements.find(id) == m_userAchievements.end()) { char errorMsg[1024]; SPRINTF(errorMsg, 1024, "Cannot find achievement with ID %s for user with ID %llu.", id.c_str(), static_cast(m_xuid)); return { E_BOUNDS, errorMsg }; } std::lock_guard lock{ m_mutex }; return AchievementsManager::CopyAchievementForResult(*m_userAchievements[id]); } XblAchievement* AchievementsManagerUser::GetAchievements() { std::lock_guard lock{ m_mutex }; return AchievementsManager::DeepCopyAchievements(m_achievementCache, m_userAchievements.size()); } Vector AchievementsManagerUser::GetAchievements( _In_ AchievementsManagerSortFilterSettings sortFilterSettings ) { Vector filteredAchievements; { std::lock_guard lock{ m_mutex }; for (const auto& pair : m_userAchievements) { const XblAchievement& achievement = *pair.second; bool isIncluded = true; // Does this achievement match the state requested? switch (sortFilterSettings.stateFilter) { case AchievementsManagerFilterType::Unlocked: isIncluded &= achievement.progressState == XblAchievementProgressState::Achieved; break; case AchievementsManagerFilterType::InProgress: isIncluded &= achievement.progressState == XblAchievementProgressState::InProgress; break; case AchievementsManagerFilterType::NotStarted: isIncluded &= achievement.progressState == XblAchievementProgressState::NotStarted; break; default: break; // Don't need to &= true here, it's just a wasted op. } if (isIncluded) { // Pushing back a shallow copy here, in case we need to truncate // the list before we return it. filteredAchievements.push_back(achievement); } } } // We only sort on UnlockTime, since we don't offer achievements from multiple // titles. This would need to change if we ever give more options for values to // sort on. if (sortFilterSettings.sortBy == XblAchievementOrderBy::UnlockTime) { // call sort function if (sortFilterSettings.sortOrder == XblAchievementsManagerSortOrder::Ascending) { std::sort(filteredAchievements.begin(), filteredAchievements.end(), [](const XblAchievement& a, const XblAchievement& b) -> bool { return a.progression.timeUnlocked < b.progression.timeUnlocked; } ); } else if (sortFilterSettings.sortOrder == XblAchievementsManagerSortOrder::Descending) { std::sort(filteredAchievements.begin(), filteredAchievements.end(), [](const XblAchievement& a, const XblAchievement& b) -> bool { return a.progression.timeUnlocked > b.progression.timeUnlocked; } ); } } return AchievementsManager::DeepCopyAchievements(filteredAchievements); } uint64_t AchievementsManagerUser::GetAchievementCount() const { return m_userAchievements.size(); } Result AchievementsManagerUser::CanUpdateAchievement(_In_ const String & achievementId, _In_ uint8_t progress) { // User added offline if still uninitialized, so must assume we can update for offline achievements if (!m_isInitialized) { return S_OK; } if (m_userAchievements.find(achievementId) == m_userAchievements.end()) { char errorMsg[1024]; SPRINTF(errorMsg, 1024, "Requested achievement with ID %s doesn't exist.", achievementId.c_str()); return { E_BOUNDS, errorMsg }; } if (m_userAchievements[achievementId]->progressState == XblAchievementProgressState::Achieved) { char errorMsg[1024]; SPRINTF(errorMsg, 1024, "Requested achievement with ID %s already achieved.", achievementId.c_str()); return{ E_UNEXPECTED, errorMsg }; } if (m_userAchievements[achievementId]->progression.requirementsCount > 1) { char errorMsg[1024]; SPRINTF(errorMsg, 1024, "Requested achievement with ID %s is an event based achievement and can't be updated through AchievementManager. Use the Stats API instead.", achievementId.c_str()); return { E_NOT_SUPPORTED, errorMsg }; } uint32_t targetValue = xbox::services::utils::internal_string_to_uint32(m_userAchievements[achievementId]->progression.requirements[0].targetProgressValue); if (targetValue != 100) { char errorMsg[1024]; SPRINTF(errorMsg, 1024, "Requested achievement with ID %s is an event based achievement and can't be updated through AchievementManager. Use the Stats API instead.", achievementId.c_str()); return { E_NOT_SUPPORTED, errorMsg }; } uint32_t currentValue = xbox::services::utils::internal_string_to_uint32(m_userAchievements[achievementId]->progression.requirements[0].currentProgressValue); if (currentValue >= progress) { char errorMsg[1024]; SPRINTF(errorMsg, 1024, "Can't update achievement with ID %s because the new progress value of %u is less than or equal to the achievement's current progress value of %u.", achievementId.c_str(), progress, currentValue ); return { E_INVALIDARG, errorMsg }; } return S_OK; } Result AchievementsManagerUser::UpdateAchievement(_In_ const String& achievementId, _In_ uint8_t percent) { return m_xblContext->AchievementsService()->UpdateAchievement( m_xuid, achievementId, percent, AsyncContext> { [ achievementId, percent ] (Result result) { if (FAILED(result.Hresult())) { LOGS_ERROR << "Updating achievement " << achievementId << " to " << percent << "% complete failed with error code " << result.Hresult(); } } }); } enum class ProgressValueType : uint32_t { NonNumeric = 0, UnsignedLong, SignedLong, FloatingPoint }; ProgressValueType IsNumber(const char* str) { char* p = nullptr; (void)strtoul(str, &p, 0); if (p == nullptr) { return ProgressValueType::UnsignedLong; } // If it couldn't be represented as an unsigned long, check to see if it can be signed next. (void)strtol(str, &p, 0); if (p == nullptr) { return ProgressValueType::SignedLong; } // If neither of those, then either it is a floating point number, or it is non-numeric. (void)strtod(str, &p); if (p == nullptr) { return ProgressValueType::FloatingPoint; } return ProgressValueType::NonNumeric; } bool TryParseToDouble(const char* str, double* out) { char* p = nullptr; double parsedValue = 0.0; parsedValue = strtod(str, &p); if (out) { *out = parsedValue; } return p == nullptr; } bool TryParseToUnsignedLong(const char* str, unsigned long* out) { char* p = nullptr; unsigned long parsedValue = 0; parsedValue = strtoul(str, &p, 0); if (out) { *out = parsedValue; } return p == nullptr; } bool TryParseToLong(const char* str, long* out) { char* p = nullptr; long parsedValue = 0; parsedValue = strtol(str, &p, 0); if (out) { *out = parsedValue; } return p == nullptr; } unsigned long ForceProgressValueToUnsignedLong(const char* progressValue, ProgressValueType valueType) { unsigned long forcedValue = 0; switch (valueType) { case ProgressValueType::UnsignedLong: TryParseToUnsignedLong(progressValue, &forcedValue); break; case ProgressValueType::SignedLong: { long parsedValue = 0; TryParseToLong(progressValue, &parsedValue); forcedValue = parsedValue > 0 ? parsedValue : 0; break; } case ProgressValueType::FloatingPoint: { double parsedValue = 0.0; TryParseToDouble(progressValue, &parsedValue); forcedValue = static_cast(parsedValue); break; } default: break; } return forcedValue; } bool ShouldUpdateProgress(const char* eventProgressValue, const char* cachedProgressValue) { ProgressValueType eventValueType = IsNumber(eventProgressValue); ProgressValueType cachedValueType = IsNumber(cachedProgressValue); // Always update if both values are non numeric, as we don't know how to do that comparison. // Maybe add a way for the title to add a conversion handler(s) in case they use non-numbers // to represent achievement progress? if (eventValueType == cachedValueType && eventValueType == ProgressValueType::NonNumeric) { LOGS_WARN << "Comparing progress values that are string typed. Cannot determine which string is considered greater by the title, and will always result in updating to cached value"; return true; } else if (eventValueType == ProgressValueType::NonNumeric || cachedValueType == ProgressValueType::NonNumeric) { // Only one of the two progress values is numeric, so we don't know how to do that // conversion. In this case we should log an error and not update, as this is // likely an unintended result. LOGS_ERROR << "Cannot compare numeric event progress value against cached non-numeric progress value. Progress for this requirement will not be updated."; return false; } else if (eventValueType != cachedValueType) { // Cached and event progress values are represented by different numeric types, so log // a warning and continue. Going to cast all values to unsigned long to continue. LOGS_WARN << "Comparing two different numeric types. Both values will be cast to unsigned long before comparison."; return ForceProgressValueToUnsignedLong(eventProgressValue, eventValueType) > ForceProgressValueToUnsignedLong(cachedProgressValue, cachedValueType); } // Value types are the same. bool shouldUpdate = false; switch (cachedValueType) { case ProgressValueType::UnsignedLong: { unsigned long eventValue, cachedValue; TryParseToUnsignedLong(cachedProgressValue, &cachedValue); TryParseToUnsignedLong(eventProgressValue, &eventValue); shouldUpdate = eventValue > cachedValue; break; } case ProgressValueType::SignedLong: { long eventValue, cachedValue; TryParseToLong(cachedProgressValue, &cachedValue); TryParseToLong(eventProgressValue, &eventValue); shouldUpdate = eventValue > cachedValue; break; } case ProgressValueType::FloatingPoint: { double eventValue, cachedValue; TryParseToDouble(cachedProgressValue, &cachedValue); TryParseToDouble(eventProgressValue, &eventValue); shouldUpdate = eventValue > cachedValue; break; } default: break; } return shouldUpdate; } Vector AchievementsManagerUser::ProcessEvents() { Vector eventsToApply; { std::lock_guard lock{ m_mutex }; eventsToApply = std::move(m_eventsToProcess); eventsToApply.insert(eventsToApply.end(), m_generatedEvents.begin(), m_generatedEvents.end()); m_generatedEvents.clear(); } // Using a regular for-loop here since we are modifying the contents of the // vector while iterating over it. Using a range-based for loop or using // iterators would result in the loop ending before the additional members // are iterated over. for (uint32_t i = 0; i < eventsToApply.size(); ++i) { const XblAchievementsManagerEvent& achievementEvent = eventsToApply[i]; switch (achievementEvent.eventType) { case XblAchievementsManagerEventType::AchievementProgressUpdated: { bool createUnlockEvent = false; const XblAchievementProgression& eventProgression = achievementEvent.progressInfo.progression; XblAchievementProgression& cachedProgression = m_userAchievements[achievementEvent.progressInfo.achievementId]->progression; // Explicitly not setting unlock time here, since if this is just getting achieved, we'll have an // unlock event to handle that. // Only set the state of the achievement if the event makes the state "InProgress", and the // achievement wasn't already completed. if (m_userAchievements[achievementEvent.progressInfo.achievementId]->progressState != XblAchievementProgressState::Achieved && achievementEvent.progressInfo.progressState == XblAchievementProgressState::InProgress) { m_userAchievements[achievementEvent.progressInfo.achievementId]->progressState = achievementEvent.progressInfo.progressState; } // Shortcut for if there is only one requirement in each to avoid the logic for the loop. if (cachedProgression.requirementsCount == 1) { // Check to make sure that the IDs are the same, just to be sure. if (!utils::str_icmp(cachedProgression.requirements[0].id, eventProgression.requirements[0].id)) { char errorMsg[1024]; SPRINTF(errorMsg, 1024, "Achievement event for achievement with ID %s has a requirement with ID %s that doesn't exist in the cached achievement.", achievementEvent.progressInfo.achievementId, eventProgression.requirements[0].id ); LOGS_ERROR << String(errorMsg); } // Perform comparison. This can change if service implementation simplifies getting cumulative progress values back. if(ShouldUpdateProgress(eventProgression.requirements[0].currentProgressValue, cachedProgression.requirements[0].currentProgressValue)) { Delete(cachedProgression.requirements[0].currentProgressValue); cachedProgression.requirements[0].currentProgressValue = Make(eventProgression.requirements[0].currentProgressValue); createUnlockEvent = (achievementEvent.progressInfo.progressState == XblAchievementProgressState::Achieved || (String)cachedProgression.requirements[0].currentProgressValue == (String)cachedProgression.requirements[0].targetProgressValue); } } else { bool allRequirementsComplete = true; Vector cachedRequirements(cachedProgression.requirements, cachedProgression.requirements + cachedProgression.requirementsCount); for (uint32_t requirementIndex = 0; requirementIndex < eventProgression.requirementsCount; ++requirementIndex) { // Find the cached requirement whose ID matches the requirement that was updated. auto requirement = std::find_if(cachedRequirements.begin(), cachedRequirements.end(), [&eventProgression, requirementIndex](const XblAchievementRequirement& requirement)->bool { return utils::str_icmp(requirement.id, eventProgression.requirements[requirementIndex].id) == 0; } ); if (requirement == cachedRequirements.end()) { char errorMsg[1024]; SPRINTF(errorMsg, 1024, "Achievement event for achievement with ID %s has a requirement with ID %s that doesn't exist in the cached achievement.", achievementEvent.progressInfo.achievementId, eventProgression.requirements[requirementIndex].id ); LOGS_ERROR << String(errorMsg); } else { // This check can change to simple eventProgress > cachedProgress check if service implementation simplifies // getting cumulative progress values back. if (ShouldUpdateProgress(eventProgression.requirements[requirementIndex].currentProgressValue, requirement->currentProgressValue)) { requirement->currentProgressValue = eventProgression.requirements[requirementIndex].currentProgressValue; } allRequirementsComplete &= (String)requirement->currentProgressValue == (String)requirement->targetProgressValue; } } createUnlockEvent = (allRequirementsComplete || achievementEvent.progressInfo.progressState == XblAchievementProgressState::Achieved); } if (createUnlockEvent) { XblAchievementsManagerEvent unlockEvent; unlockEvent.eventType = XblAchievementsManagerEventType::AchievementUnlocked; unlockEvent.xboxUserId = m_xuid; unlockEvent.progressInfo.progressState = XblAchievementProgressState::Achieved; unlockEvent.progressInfo.achievementId = Make(achievementEvent.progressInfo.achievementId); // Only populating the time unlocked as the actual progress info is irrelevent, since it just boils down to 100% // the title can grab that info if it needs it from the preceeding progress update event. unlockEvent.progressInfo.progression = XblAchievementProgression{ 0 }; unlockEvent.progressInfo.progression.timeUnlocked = achievementEvent.progressInfo.progression.timeUnlocked; eventsToApply.push_back(unlockEvent); } break; } case XblAchievementsManagerEventType::AchievementUnlocked: { auto achievementId = achievementEvent.progressInfo.achievementId; XblAchievement& cachedAchievement = *m_userAchievements[achievementId]; if (cachedAchievement.progression.requirementsCount != achievementEvent.progressInfo.progression.requirementsCount) { LOGS_ERROR << "Achievement event for achievement with ID " << achievementId << " has a different number of requirements than the cached version of the achievement."; } cachedAchievement.progressState = achievementEvent.progressInfo.progressState; cachedAchievement.progression.timeUnlocked = achievementEvent.progressInfo.progression.timeUnlocked; // For each reuqirement, update the current value to the target value. for (uint32_t requirementIndex = 0; requirementIndex < cachedAchievement.progression.requirementsCount; ++requirementIndex) { // Clean up expects currentProgressValue to be a dynamically allocated string, // so dynamically allocate this too so we don't crash trying to Delete a // statically allocated literal. Delete(cachedAchievement.progression.requirements[requirementIndex].currentProgressValue); cachedAchievement.progression.requirements[requirementIndex].currentProgressValue = Make("100"); } break; } default: break; } } return eventsToApply; } Vector GenerateEventFromAchievementDiff(uint64_t xuid, const XblAchievement& cached, const XblAchievement& updated) { Vector generatedEvents; // Check each requirement on the achievement, and if there is a difference in the progress value, // then generate a progress event. Vector updatedRequirements; uint64_t requirementCount = updated.progression.requirementsCount; for (uint64_t requirementIndex = 0; requirementIndex < requirementCount; ++requirementIndex) { Vector cachedRequirements(cached.progression.requirements, cached.progression.requirements + cached.progression.requirementsCount); auto& updatedRequirement = updated.progression.requirements[requirementIndex]; // Find the cached requirement whose ID matches the requirement that was updated. auto cachedRequirement = std::find_if(cachedRequirements.begin(), cachedRequirements.end(), [&updatedRequirement](const XblAchievementRequirement& requirement)->bool { return utils::str_icmp(requirement.id, updatedRequirement.id) == 0; } ); if (cachedRequirement == cachedRequirements.end()) { char errorMsg[1024]; SPRINTF(errorMsg, 1024, "Achievement from achievement event with ID %s has a requirement with ID %s that doesn't exist in the cached achievement.", updated.id, updatedRequirement.id ); LOGS_ERROR << String(errorMsg); } else { // Compare the progress values. if (utils::internal_string_to_uint32(cachedRequirement->currentProgressValue) < utils::internal_string_to_uint32(updatedRequirement.currentProgressValue)) { updatedRequirements.push_back(updatedRequirement); } } } if (updatedRequirements.size() > 0) { // check to see if we have an event for this change already. XblAchievementsManagerEvent event; event.xboxUserId = xuid; event.eventType = XblAchievementsManagerEventType::AchievementProgressUpdated; XblAchievementProgressChangeEntry& progressEntry = event.progressInfo; progressEntry.progressState = updated.progressState; progressEntry.achievementId = Make(updated.id); XblAchievementProgression& progress = progressEntry.progression; // Move the contents of the vector into the progression struct. progress.requirementsCount = updatedRequirements.size(); progress.requirements = MakeArray(progress.requirementsCount); for (uint64_t i = 0; i < progress.requirementsCount; ++i) { std::swap(progress.requirements[i].id, updatedRequirements[i].id); std::swap(progress.requirements[i].currentProgressValue, updatedRequirements[i].currentProgressValue); std::swap(progress.requirements[i].targetProgressValue, updatedRequirements[i].targetProgressValue); } progress.timeUnlocked = updated.progression.timeUnlocked; generatedEvents.push_back(event); } return generatedEvents; } HRESULT AchievementsManagerUser::FetchAchievements(_In_ AsyncContext async) { constexpr uint32_t achievementsPerFetch = 100; return m_xblContext->AchievementsService()->GetAchievementsForTitle( m_xuid, AppConfig::Instance()->TitleId(), XblAchievementType::All, false, XblAchievementOrderBy::DefaultOrder, 0, achievementsPerFetch, AsyncContext>>{ async.Queue(), [ =, weakThis = std::weak_ptr{ shared_from_this() } ] (Result> result) { auto sharedThis{ weakThis.lock() }; if (sharedThis) { sharedThis->HandleAchievementsResults(result, achievementsPerFetch, async); } } }); } HRESULT AchievementsManagerUser::HandleAchievementsResults( _In_ Result> result, _In_ uint32_t achievementsPerFetch, _In_ AsyncContext async, _In_ Vector fetchedAchievements ) { if (Succeeded(result)) { { std::lock_guard lock{ m_mutex }; for (const XblAchievement& achievement : result.Payload()->Achievements()) { // If this is being called after the user has already been initialized, then // something happened and needed to do an RTA resync. So compare against the // existing achievement, and generate the proper event from it. if (m_isInitialized) { // In rare cases we'll get back a notification that isn't in our local cache. If that happens // we'll simply ignore the new achievement until the title restarts or the user is removed // and re-added to achievements manager if (m_userAchievements.find(achievement.id) == m_userAchievements.end()) { LOGS_WARN << "Fetch achievements returned new achievement that wasn't present in local achievement cache."; continue; } // We only want to have this function generate unlock events if we're expecting // RTA notifications. Otherwise it will be produced from the progress change // event. auto generatedEvents = GenerateEventFromAchievementDiff(m_xuid, *m_userAchievements[achievement.id], achievement); if (generatedEvents.size() > 0) { for (auto generatedEvent : generatedEvents) { // Check to see if there is already an event for this change. Only add this event // to the generated list if it is completely unique. auto foundEvent = std::find_if(m_eventsToProcess.begin(), m_eventsToProcess.end(), [&generatedEvent](const XblAchievementsManagerEvent& elem)->bool { if (generatedEvent.eventType != elem.eventType) { return false; } if (generatedEvent.progressInfo.achievementId != elem.progressInfo.achievementId) { return false; } if (generatedEvent.progressInfo.progressState != elem.progressInfo.progressState) { return false; } auto& cachedProgression = elem.progressInfo.progression; auto& eventProgression = generatedEvent.progressInfo.progression; if (eventProgression.timeUnlocked != cachedProgression.timeUnlocked) { return false; } if (eventProgression.requirementsCount != cachedProgression.requirementsCount) { return false; } for (uint32_t i = 0; i < eventProgression.requirementsCount; ++i) { auto& eventRequirement = eventProgression.requirements[i]; auto& cachedRequirement = cachedProgression.requirements[i]; // If the requirement IDs are different between events for the same achievement, then this is a // unique progress update. If the target progress values are different, then they are also unique, // however it likely means that there was a modification to the requirement on the achievement service // (which is unlikely to happen with a released game). if (((String)eventRequirement.id != (String)cachedRequirement.id || (String)eventRequirement.targetProgressValue != (String)cachedRequirement.targetProgressValue)) { return false; } // Only recognize this as a unique event if the events current progress is greater than the caches. if (!ShouldUpdateProgress(eventRequirement.currentProgressValue, cachedRequirement.currentProgressValue)) { return false; } } return true; } ); // Event is unique, so push it onto the list of events to process. if (foundEvent == m_eventsToProcess.end()) { m_generatedEvents.push_back(generatedEvent); } } } continue; } fetchedAchievements.push_back(AchievementsManager::DeepCopyAchievement(achievement)); } } if (result.Payload()->HasNext()) { result.Payload()->GetNext( achievementsPerFetch, AsyncContext>>{ async.Queue(), [ async, achievementsPerFetch, weakThis = std::weak_ptr(shared_from_this()), fetchedAchievements ] (Result> result) { auto sharedThis{ weakThis.lock() }; if (sharedThis) { sharedThis->HandleAchievementsResults(result, achievementsPerFetch, async, fetchedAchievements); } } }); } else { if (!m_isInitialized) { size_t achievementCount = fetchedAchievements.size(); m_achievementCache = MakeArray(achievementCount); for (size_t index = 0; index < achievementCount; ++index) { m_achievementCache[index] = fetchedAchievements[index]; m_userAchievements[m_achievementCache[index].id] = m_achievementCache + index; } } async.Complete(result.Hresult()); } } else { LOGS_ERROR << "Failed to fetch current state of achievements for user " << m_xuid << " with error code " << result.Hresult(); async.Complete(result.Hresult()); } return S_OK; } ////////////////////////////////////////////////////////// XblAchievement AchievementsManager::CopyAchievementForResult(const XblAchievement& other) { // Most values that are dynamically allocated when creating the // original achievement struct should not change between frames, // so it is okay if we do shallow copies for those values. XblAchievement copy { other.id, other.serviceConfigurationId, other.name, other.titleAssociations, // title associations other.titleAssociationsCount, other.progressState, other.progression, // need to do a deep copy of this other.mediaAssets, // media assets other.mediaAssetsCount, other.platformsAvailableOn, // platforms available on other.platformsAvailableOnCount, other.isSecret, other.unlockedDescription, other.lockedDescription, other.productId, other.type, other.participationType, other.available, other.rewards, // rewards other.rewardsCount, other.estimatedUnlockTime, other.deepLink, other.isRevoked }; // Need to do a deep copy of progression so that, if progress values change // while the title is still using a result from a prior frame, we do not // invalidate the memory that the requirement is using. copy.progression.requirements = MakeArray(copy.progression.requirementsCount); XblAchievementRequirement* sourceRequirements = other.progression.requirements; for (uint64_t i = 0; i < copy.progression.requirementsCount; ++i) { copy.progression.requirements[i] = XblAchievementRequirement{ sourceRequirements[i].id, Make(sourceRequirements[i].currentProgressValue), sourceRequirements[i].targetProgressValue }; } return copy; } HRESULT AchievementsManager::CleanUpAchievementCopyForResult(XblAchievement& achievement) { for (uint64_t i = 0; i < achievement.progression.requirementsCount; ++i) { XblAchievementRequirement& requirement = achievement.progression.requirements[i]; Delete(requirement.currentProgressValue); } DeleteArray(achievement.progression.requirements, achievement.progression.requirementsCount); return S_OK; } XblAchievement AchievementsManager::DeepCopyAchievement(_In_ const XblAchievement & other) { XblAchievement copy { Make(other.id), Make(other.serviceConfigurationId), Make(other.name), nullptr, // title associations other.titleAssociationsCount, other.progressState, other.progression, // need to do a deep copy of this nullptr, // media assets other.mediaAssetsCount, nullptr, // platforms available on other.platformsAvailableOnCount, other.isSecret, Make(other.unlockedDescription), Make(other.lockedDescription), Make(other.productId), other.type, other.participationType, other.available, nullptr, // rewards other.rewardsCount, other.estimatedUnlockTime, Make(other.deepLink), other.isRevoked }; // titleAssociations XblAchievementTitleAssociation* associations = MakeArray(copy.titleAssociationsCount); for (uint64_t i = 0; i < copy.titleAssociationsCount; ++i) { associations[i] = XblAchievementTitleAssociation{ Make(other.titleAssociations[i].name), other.titleAssociations[i].titleId }; } copy.titleAssociations = associations; // deep copy progression copy.progression.requirements = MakeArray(copy.progression.requirementsCount); XblAchievementRequirement* sourceRequirements = other.progression.requirements; for (uint64_t i = 0; i < copy.progression.requirementsCount; ++i) { copy.progression.requirements[i] = XblAchievementRequirement{ Make(sourceRequirements[i].id), Make(sourceRequirements[i].currentProgressValue), Make(sourceRequirements[i].targetProgressValue) }; } auto CloneMediaAsset = [](const XblAchievementMediaAsset& arg)->XblAchievementMediaAsset { return XblAchievementMediaAsset{ arg.name ? Make(arg.name) : nullptr, arg.mediaAssetType, arg.url ? Make(arg.url) : nullptr }; }; // media assets XblAchievementMediaAsset* assets = MakeArray(copy.mediaAssetsCount); for (uint64_t i = 0; i < copy.mediaAssetsCount; ++i) { assets[i] = CloneMediaAsset(other.mediaAssets[i]); } copy.mediaAssets = assets; // platforms Vector platforms(other.platformsAvailableOn, other.platformsAvailableOn + other.platformsAvailableOnCount); copy.platformsAvailableOn = const_cast(MakeArray(platforms)); // rewards XblAchievementReward* rewards = MakeArray(copy.rewardsCount); for (uint64_t i = 0; i < copy.rewardsCount; ++i) { rewards[i] = XblAchievementReward{ Make(other.rewards[i].name), Make(other.rewards[i].description), Make(other.rewards[i].value), other.rewards[i].rewardType, Make(other.rewards[i].valueType), nullptr }; if (other.rewards[i].mediaAsset != nullptr) { rewards[i].mediaAsset = Make( CloneMediaAsset(*other.rewards[i].mediaAsset) ); } } copy.rewards = rewards; return copy; } XblAchievement* AchievementsManager::DeepCopyAchievements(XblAchievement* cache, size_t size) { XblAchievement* copiedArray = MakeArray(cache, size); memcpy(copiedArray, cache, sizeof(XblAchievement)*size); for (size_t index = 0; index < size; ++index) { // Need to do a deep copy of progression so that, if progress values change // while the title is still using a result from a prior frame, we do not // invalidate the memory that the requirement is using. copiedArray[index].progression.requirements = MakeArray(copiedArray[index].progression.requirementsCount); XblAchievementRequirement* sourceRequirements = cache[index].progression.requirements; for (uint64_t i = 0; i < copiedArray[index].progression.requirementsCount; ++i) { copiedArray[index].progression.requirements[i] = XblAchievementRequirement{ sourceRequirements[i].id, Make(sourceRequirements[i].currentProgressValue), sourceRequirements[i].targetProgressValue }; } } return copiedArray; } Vector AchievementsManager::DeepCopyAchievements(const Vector& achievements) { Vector achievementsCopy; for (const auto& source : achievements) { achievementsCopy.push_back(CopyAchievementForResult(source)); } return achievementsCopy; } HRESULT AchievementsManager::CleanUpDeepCopyAchievement(_In_ XblAchievement& achievement) { Delete(achievement.id); Delete(achievement.serviceConfigurationId); Delete(achievement.name); for (uint64_t i = 0; i < achievement.titleAssociationsCount; ++i) { Delete(achievement.titleAssociations[i].name); } DeleteArray(achievement.titleAssociations, achievement.titleAssociationsCount); for (uint64_t i = 0; i < achievement.progression.requirementsCount; ++i) { XblAchievementRequirement& requirement = achievement.progression.requirements[i]; Delete(requirement.id); Delete(requirement.currentProgressValue); Delete(requirement.targetProgressValue); } DeleteArray(achievement.progression.requirements, achievement.progression.requirementsCount); for (uint64_t i = 0; i < achievement.mediaAssetsCount; ++i) { XblAchievementMediaAsset& asset = achievement.mediaAssets[i]; if (asset.name) { Delete(asset.name); } if (asset.url) { Delete(asset.url); } } DeleteArray(achievement.mediaAssets, achievement.mediaAssetsCount); DeleteArray(achievement.platformsAvailableOn, achievement.platformsAvailableOnCount); Delete(achievement.unlockedDescription); Delete(achievement.lockedDescription); Delete(achievement.productId); for (uint64_t i = 0; i < achievement.rewardsCount; ++i) { XblAchievementReward& reward = achievement.rewards[i]; Delete(reward.name); Delete(reward.description); Delete(reward.value); Delete(reward.valueType); if (reward.mediaAsset != nullptr) { if (reward.mediaAsset->name) { Delete(reward.mediaAsset->name); } if (reward.mediaAsset->url) { Delete(reward.mediaAsset->url); } Delete(reward.mediaAsset); } } DeleteArray(achievement.rewards, achievement.rewardsCount); Delete(achievement.deepLink); return S_OK; } HRESULT AchievementsManager::AddLocalUser( User&& user, TaskQueue && queue ) noexcept { std::lock_guard lock{ m_mutex }; auto xuid{ user.Xuid() }; if (m_localUsers.find(xuid) != m_localUsers.end()) { LOGS_ERROR << "User " << xuid << " already added to AchievementsManager"; return E_UNEXPECTED; } auto localUser = MakeShared(std::move(user), queue); Result result = localUser->Initialize(AsyncContext{ [ sharedThis{ shared_from_this() }, weakUser = std::weak_ptr{ localUser->shared_from_this() } ] (HRESULT hr) { auto user{ weakUser.lock() }; if (FAILED(hr)) { LOGS_WARN << "Failed to initialize local user online with ID " << user->Xuid() << " with error code " << hr; } std::lock_guard lock{ sharedThis->m_mutex }; if (user) { XblAchievementsManagerEvent userAddedEvent{ {}, user->Xuid(), XblAchievementsManagerEventType::LocalUserInitialStateSynced }; sharedThis->m_pendingEvents.push_back(userAddedEvent); } } }); if (Succeeded(result)) { m_localUsers[xuid] = localUser; } return result.Hresult(); } HRESULT AchievementsManager::RemoveLocalUser( _In_ const User & user ) noexcept { std::lock_guard lock{ m_mutex }; auto userIter{ m_localUsers.find(user.Xuid()) }; if (userIter == m_localUsers.end()) { LOGS_ERROR << "User " << user.Xuid() << " was not added to AchievementsManager"; return E_BOUNDS; } m_localUsers.erase(userIter); m_localUsers.erase(user.Xuid()); return S_OK; } const Vector& AchievementsManager::DoWork() XBL_NOEXCEPT { std::lock_guard lock{ m_mutex }; // Clean up the allocations made when doing the copy of the progress entry // for the event. for (auto& event : m_publishedEvents) { AchievementProgressChangeSubscription::CleanUpProgressChangeEntry(event.progressInfo); } m_publishedEvents = std::move(m_pendingEvents); for (auto& pair : m_localUsers) { auto& user{ pair.second }; auto achievementEvents = user->ProcessEvents(); m_publishedEvents.insert(m_publishedEvents.end(), achievementEvents.begin(), achievementEvents.end()); } m_pendingEvents.clear(); return m_publishedEvents; } Result AchievementsManager::GetAchievement( _In_ uint64_t xuid, _In_ const String achievementId ) { if (m_localUsers.find(xuid) == m_localUsers.end()) { char errorMsg[1024]; SPRINTF(errorMsg, 1024, "User with ID %llu not yet registered with AchievementsManager.", static_cast(xuid)); return { E_BOUNDS, errorMsg}; } if (!m_localUsers[xuid]->IsInitialized()) { char errorMsg[1024]; SPRINTF(errorMsg, 1024, "User with ID %llu not yet initialized.", static_cast(xuid)); return { E_UNEXPECTED, errorMsg}; } return m_localUsers[xuid]->GetAchievement(achievementId); } Result> AchievementsManager::GetAchievements(_In_ uint64_t xuid) { if (m_localUsers.find(xuid) == m_localUsers.end()) { char errorMsg[1024]; SPRINTF(errorMsg, 1024, "User with ID %llu not yet registered with AchievementsManager.", static_cast(xuid)); return { E_BOUNDS, errorMsg }; } if (!m_localUsers[xuid]->IsInitialized()) { char errorMsg[1024]; SPRINTF(errorMsg, 1024, "User with ID %llu not yet initialized.", static_cast(xuid)); return { E_UNEXPECTED, errorMsg }; } return std::pair({ m_localUsers[xuid]->GetAchievements() , m_localUsers[xuid]->GetAchievementCount() }); } Result> AchievementsManager::GetAchievements( _In_ uint64_t xuid, _In_ AchievementsManagerSortFilterSettings requestConfig ) { if (m_localUsers.find(xuid) == m_localUsers.end()) { char errorMsg[1024]; SPRINTF(errorMsg, 1024, "User with ID %llu not yet registered with AchievementsManager.", static_cast(xuid)); return { E_BOUNDS, errorMsg }; } if (!m_localUsers[xuid]->IsInitialized()) { char errorMsg[1024]; SPRINTF(errorMsg, 1024, "User with ID %llu not yet initialized.", static_cast(xuid)); return { E_UNEXPECTED, errorMsg }; } return m_localUsers[xuid]->GetAchievements(requestConfig); } Result AchievementsManager::UpdateAchievement( _In_ uint64_t xuid, _In_ const String achievementId, _In_ uint8_t progress ) { if (m_localUsers.find(xuid) == m_localUsers.end()) { char errorMsg[1024]; SPRINTF(errorMsg, 1024, "User with ID %llu not yet registered with AchievementsManager.", static_cast(xuid)); return { E_BOUNDS, errorMsg }; } std::shared_ptr localUser = m_localUsers[xuid]; auto isAchievementUpdateable = localUser->CanUpdateAchievement(achievementId, progress); if (Failed(isAchievementUpdateable)) { return isAchievementUpdateable; } return localUser->UpdateAchievement(achievementId, progress); } bool AchievementsManager::HasUser(_In_ uint64_t xuid) const { return m_localUsers.find(xuid) != m_localUsers.end(); } bool AchievementsManager::IsUserInitialized(_In_ uint64_t xuid) { return m_localUsers[xuid]->IsInitialized(); } uint64_t AchievementsManager::GetUserAchievementCount(_In_ uint64_t xuid) { return m_localUsers[xuid]->GetAchievementCount(); } NAMESPACE_MICROSOFT_XBOX_SERVICES_ACHIEVEMENTS_MANAGER_CPP_END ================================================ FILE: Source/Services/Achievements/Manager/achievements_manager_internal.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "xsapi-c/achievements_manager_c.h" #include "achievements_internal.h" namespace xbox { namespace services { namespace achievements { namespace manager { class AchievementsManager; } } } } struct XblAchievementsManagerResult : public xbox::services::RefCounter, public std::enable_shared_from_this { public: XblAchievementsManagerResult(_In_ const XblAchievement& achievement); XblAchievementsManagerResult(_In_ Vector& achievements, _In_ bool explicitCleanup = false); XblAchievementsManagerResult(_In_ XblAchievement* achievements, _In_ size_t achievementCount, _In_ bool explicitCleanup = false); virtual ~XblAchievementsManagerResult(); const Vector& Achievements() const; protected: // RefCounter std::shared_ptr GetSharedThis() override; private: XblAchievementsManagerResult(const XblAchievementsManagerResult& other) = delete; XblAchievementsManagerResult& operator=(XblAchievementsManagerResult other) = delete; Vector m_achievements; XblAchievement* m_achievementsData; size_t m_achievementsCount; bool m_explicitCleanup; }; /// /// Enumeration values that indicate the lock state of an achievement. /// /// enum class AchievementsManagerFilterType : uint32_t { /// /// Specifies both locked and unlocked achievements to be included. /// All = 0, /// /// Specifies that unlocked achievements should be included. /// Unlocked = 1, /// /// Specifies that achievements with no progress should be included. /// NotStarted = 2, /// /// Specifies that achievements currently in progress should be included. /// InProgress = 3, }; /// /// Collection of options used to customise the subset of achievements /// to return back to the caller. /// struct AchievementsManagerSortFilterSettings { /// /// The field to sort the list of achievements on. TitleId will behave /// the same as DefaultOrder, as AchievementsManager only handles one title /// at a time. /// XblAchievementOrderBy sortBy; /// /// The direction by which to sort the list of achievements. /// XblAchievementsManagerSortOrder sortOrder; /// /// The achievement state to include in the list of achievements. /// AchievementsManagerFilterType stateFilter; } ; NAMESPACE_MICROSOFT_XBOX_SERVICES_ACHIEVEMENTS_MANAGER_CPP_BEGIN class AchievementsManagerUser : public std::enable_shared_from_this { public: AchievementsManagerUser( _In_ User&& localUser, _In_ const TaskQueue& queue ) noexcept; virtual ~AchievementsManagerUser(); Result Initialize( _In_ AsyncContext async ); bool IsInitialized() const; uint64_t Xuid() const; uint64_t GetAchievementCount() const; Vector ProcessEvents(); Result GetAchievement( _In_ const String& id ); XblAchievement* GetAchievements(); Vector GetAchievements( _In_ AchievementsManagerSortFilterSettings sortFilterSettings ); Result CanUpdateAchievement( _In_ const String& achievementId, _In_ uint8_t percent ); Result UpdateAchievement( _In_ const String& achievementId, _In_ uint8_t percent ); private: HRESULT FetchAchievements( _In_ AsyncContext async ); HRESULT HandleAchievementsResults( _In_ Result> result, _In_ uint32_t achievementsPerFetch, _In_ AsyncContext async, _In_ Vector fetchedAchievements = Vector() ); std::mutex m_mutex; bool m_isInitialized = false; bool m_isFetchingAchievements = true; Map m_userAchievements; XblAchievement* m_achievementCache{ nullptr }; uint64_t m_xuid{ 0 }; Vector m_eventsToProcess; Vector m_generatedEvents; std::shared_ptr m_xblContext; XblFunctionContext m_achievementProgressToken{ 0 }; XblFunctionContext m_rtaResyncToken{ 0 }; XblFunctionContext m_rtaConnectionToken{ 0 }; std::shared_ptr m_rtaManager; TaskQueue m_queue; }; class AchievementsManager : public std::enable_shared_from_this { public: AchievementsManager() = default; // In this case, The object returned from this function is mostly a shallow // copy of the argument, as most values in an XblAchievement don't change // during gameplay. The ones that do will be deep copied. Should only // be called static XblAchievement CopyAchievementForResult(const XblAchievement & other); static HRESULT CleanUpAchievementCopyForResult(XblAchievement & achievement); // Deep copy makes dynamic allocations so that the object can be completely // detatched from the original object. Requires parts of the object to be // manually deallocated. static XblAchievement DeepCopyAchievement(_In_ const XblAchievement& other); static Vector DeepCopyAchievements(const Vector& achievements); static XblAchievement* DeepCopyAchievements(XblAchievement* cache, size_t size); // Helper function that aids in freeing the dynamically allocated memory used // when creating a deep copy of an achievement. static HRESULT CleanUpDeepCopyAchievement(_In_ XblAchievement& achievement); HRESULT AddLocalUser( _In_ User&& user, _In_ TaskQueue&& queue ) noexcept; HRESULT RemoveLocalUser( _In_ const User& user ) noexcept; const Vector& DoWork() XBL_NOEXCEPT; Result GetAchievement( _In_ uint64_t xuid, _In_ const String achievementId ); Result> GetAchievements( _In_ uint64_t xuid ); Result> GetAchievements( _In_ uint64_t xuid, _In_ AchievementsManagerSortFilterSettings requestConfig ); Result UpdateAchievement( _In_ uint64_t xuid, _In_ const String achievementId, _In_ uint8_t progress ); bool HasUser( _In_ uint64_t xuid ) const; bool IsUserInitialized( _In_ uint64_t xuid ); uint64_t GetUserAchievementCount( _In_ uint64_t xuid ); private: std::mutex m_mutex; Vector m_publishedEvents; Vector m_pendingEvents; Map> m_localUsers; }; NAMESPACE_MICROSOFT_XBOX_SERVICES_ACHIEVEMENTS_MANAGER_CPP_END ================================================ FILE: Source/Services/Achievements/achievement_service_internal.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "achievements_internal.h" #include "xbox_live_context_internal.h" #include "xsapi-c/errors_c.h" #include "real_time_activity_manager.h" #if HC_PLATFORM == HC_PLATFORM_XDK #pragma pack(push, 16) #include "EtwPlus.h" #endif NAMESPACE_MICROSOFT_XBOX_SERVICES_ACHIEVEMENTS_CPP_BEGIN #if HC_PLATFORM == HC_PLATFORM_XDK EXTERN_C __declspec(selectany) ETX_FIELD_DESCRIPTOR XSAPI_Update_Achievement_Fields[5] = { { EtxFieldType_UnicodeString,0 }, { EtxFieldType_UnicodeString,0 }, { EtxFieldType_GUID,0 }, { EtxFieldType_UnicodeString,0 }, { EtxFieldType_UInt32,0 } }; EXTERN_C __declspec(selectany) ETX_EVENT_DESCRIPTOR XSAPI_Update_Achievement_Events[1] = { { { 1, 1, 0, 0, 0, 0, 0x0 }, "AchievementUpdate", "0.7.IGAU-1.0", XSAPI_Update_Achievement_Fields, 5, 0, EtxEventEnabledState_Undefined, EtxEventEnabledState_ProviderDefault, EtxPopulationSample_Undefined, EtxPopulationSample_UseProviderPopulationSample, EtxEventLatency_Undefined, EtxEventLatency_ProviderDefault, EtxEventPriority_Undefined, EtxEventPriority_ProviderDefault } }; EXTERN_C __declspec(selectany) REGHANDLE XSAPI_Update_Achievement_Handle = (REGHANDLE)0; EXTERN_C __declspec(selectany) ETX_PROVIDER_DESCRIPTOR XSAPI_Update_Achievement_Provider = { "", { 0 }, 1, (ETX_EVENT_DESCRIPTOR*)&XSAPI_Update_Achievement_Events, 0, EtxProviderEnabledState_Undefined, EtxProviderEnabledState_OnByDefault, 0, 100, EtxProviderLatency_Undefined, EtxProviderLatency_RealTime, EtxProviderPriority_Undefined, EtxProviderPriority_Critical }; #endif AchievementsService::AchievementsService( _In_ User&& user, _In_ std::shared_ptr xboxLiveContextSettings, _In_ std::shared_ptr appConfig, _In_ std::weak_ptr xboxLiveContextImpl, _In_ std::shared_ptr rtaManager ) : m_user{ std::move(user) }, m_xboxLiveContextSettings{ std::move(xboxLiveContextSettings) }, m_appConfig{ std::move(appConfig) }, m_rtaManager{ std::move(rtaManager) }, m_xboxLiveContextImpl{ std::move(xboxLiveContextImpl) } { } HRESULT AchievementsService::UpdateAchievement( _In_ uint64_t xboxUserId, _In_ const String& achievementId, _In_ uint32_t percentComplete, _In_ AsyncContext> async ) const noexcept { return UpdateAchievement( xboxUserId, m_appConfig->TitleId(), m_appConfig->Scid(), achievementId, percentComplete, std::move(async) ); } HRESULT AchievementsService::UpdateAchievement( _In_ uint64_t xboxUserId, _In_ uint32_t titleId, _In_ const String& scid, _In_ const String& achievementId, _In_ uint32_t percentComplete, _In_ AsyncContext> async ) const noexcept { RETURN_HR_INVALIDARGUMENT_IF(titleId == 0); RETURN_HR_INVALIDARGUMENT_IF(scid.empty()); RETURN_HR_INVALIDARGUMENT_IF(achievementId.empty()); RETURN_HR_INVALIDARGUMENT_IF(percentComplete > 100); // Achievements service is doing case sensitive comparison on scid and always expects it to be lower case String lowercaseScid = utils::ToLower(scid); #if HC_PLATFORM == HC_PLATFORM_XDK { auto state = GlobalState::Get(); if (!state) { return E_XBL_NOT_INITIALIZED; } // Register ETX provider if it hasn't been registered yet if (XSAPI_Update_Achievement_Handle == 0) { string_t wScid = utils::string_t_from_internal_string(lowercaseScid); std::error_code errC = utils::guid_from_string(wScid, const_cast(&XSAPI_Update_Achievement_Provider.Guid), false); if (errC) { return utils::convert_xbox_live_error_code_to_hresult(errC); } XSAPI_Update_Achievement_Provider.Name = state->AchievementsProviderName().data(); ULONG errorCode = EtxRegister(&XSAPI_Update_Achievement_Provider, &XSAPI_Update_Achievement_Handle); RETURN_HR_IF_FAILED(HRESULT_FROM_WIN32(errorCode)); } } #endif Stringstream subPath; subPath << ("/users/xuid(") << xboxUserId << (")/achievements/") << lowercaseScid << ("/update"); Result userResult = m_user.Copy(); RETURN_HR_IF_FAILED(userResult.Hresult()); auto httpCall = MakeShared(userResult.ExtractPayload()); HRESULT hr = httpCall->Init( m_xboxLiveContextSettings, "POST", XblHttpCall::BuildUrl("achievements", subPath.str()), xbox_live_api::update_achievement ); RETURN_HR_IF_FAILED(hr); RETURN_HR_IF_FAILED(httpCall->SetXblServiceContractVersion(2)); JsonDocument request(rapidjson::kObjectType); JsonDocument::AllocatorType& allocator = request.GetAllocator(); JsonValue achievementJson(rapidjson::kObjectType); achievementJson.AddMember("id", JsonValue (achievementId.c_str(), allocator).Move(), allocator); achievementJson.AddMember("percentComplete", percentComplete, allocator); JsonValue achievementsJson = JsonValue(rapidjson::kArrayType); achievementsJson.PushBack(achievementJson, allocator); request.AddMember("action", "progressUpdate", allocator); request.AddMember("serviceConfigId", JsonValue(lowercaseScid.c_str(), allocator).Move(), allocator); request.AddMember("titleId", titleId, allocator); request.AddMember("userId", JsonValue(utils::uint64_to_internal_string(xboxUserId).c_str(), allocator).Move(), allocator); request.AddMember("achievements", achievementsJson, allocator); RETURN_HR_IF_FAILED(httpCall->SetRequestBody(JsonUtils::SerializeJson(request))); hr = httpCall->Perform(AsyncContext{ async.Queue().DeriveWorkerQueue(), [ async, achievementId, percentComplete, xboxLiveContext{ m_xboxLiveContextImpl.lock() } ] (HttpResult httpResult) { bool shouldWriteOffline{ false }; HRESULT hrHttp{ S_OK }; // Errors from the task include auth errors when getting the token and sig HRESULT hrTask = httpResult.Hresult(); auto xblConditionTask = XblGetErrorCondition(hrTask); if (xblConditionTask == XblErrorCondition::Network || xblConditionTask == XblErrorCondition::Auth || xblConditionTask == XblErrorCondition::GenericError) { shouldWriteOffline = true; } if (SUCCEEDED(hrTask) && httpResult.Payload()) { // Errors from the http stack include network errors hrHttp = httpResult.Payload()->Result(); auto xblConditionHttp = XblGetErrorCondition(hrHttp); // The HTTP status code auto statusCode = httpResult.Payload()->HttpStatus(); if (xblConditionHttp == XblErrorCondition::Network || xblConditionHttp == XblErrorCondition::Auth || xblConditionHttp == XblErrorCondition::Http429TooManyRequests || xblConditionHttp == XblErrorCondition::GenericError || (statusCode >= 500 && statusCode < 600)) { shouldWriteOffline = true; } } // If any error or HTTP status code falls into one of these known buckets, // then write the achievement unlock event so it can be sent when the device is back online // even if the game isn't running if (shouldWriteOffline && xboxLiveContext) { async.Complete(WriteOfflineUpdateAchievement( xboxLiveContext, achievementId, percentComplete )); } else { async.Complete(FAILED(hrTask) ? hrTask : hrHttp); } } }); return hr; } #if HC_PLATFORM == HC_PLATFORM_XDK ULONG AchievementsService::EventWriteAchievementUpdate( _In_ PCWSTR userId, _In_ PCWSTR achievementId, _In_ const uint32_t percentComplete ) { if (achievementId == nullptr) return ERROR_BAD_ARGUMENTS; if (userId == nullptr) return ERROR_BAD_ARGUMENTS; static const uint32_t EventWriteAchievementUpdate_ArgCount = 5; static const uint32_t EventWriteAchievementUpdate_ScratchSize = 64; EVENT_DATA_DESCRIPTOR eventData[EventWriteAchievementUpdate_ArgCount] = { 0 }; UINT8 scratch[EventWriteAchievementUpdate_ScratchSize] = { 0 }; EtxFillCommonFields_v7(&eventData[0], scratch, EventWriteAchievementUpdate_ScratchSize); EventDataDescCreate(&eventData[1], userId, (ULONG)((wcslen(userId) + 1) * sizeof(WCHAR))); EventDataDescCreate(&eventData[2], &GlobalState::Get()->AchievementsSessionId(), sizeof(GUID)); EventDataDescCreate(&eventData[3], achievementId, (ULONG)((wcslen(achievementId) + 1) * sizeof(WCHAR))); EventDataDescCreate(&eventData[4], &percentComplete, sizeof(percentComplete)); return EtxEventWrite( &XSAPI_Update_Achievement_Events[0], &XSAPI_Update_Achievement_Provider, XSAPI_Update_Achievement_Handle, EventWriteAchievementUpdate_ArgCount, eventData ); } HRESULT AchievementsService::WriteOfflineUpdateAchievement( _In_ std::shared_ptr xboxLiveContextImpl, _In_ const String& achievementId, _In_ uint32_t percentComplete ) { ULONG errorCode = EventWriteAchievementUpdate( utils::uint64_to_string_t(xboxLiveContextImpl->Xuid()).c_str(), convert::utf8_to_utf16(achievementId).c_str(), percentComplete ); HRESULT hr = HRESULT_FROM_WIN32(errorCode); return hr; } #else HRESULT AchievementsService::WriteOfflineUpdateAchievement( _In_ std::shared_ptr xboxLiveContextImpl, _In_ const String& achievementId, _In_ uint32_t percentComplete ) { JsonDocument properties(rapidjson::kObjectType); properties.AddMember("AchievementId", JsonValue(achievementId.c_str(), properties.GetAllocator()).Move(), properties.GetAllocator()); properties.AddMember("PercentComplete", percentComplete, properties.GetAllocator()); auto propertiesStrUtf8 = JsonUtils::SerializeJson(properties); #ifdef XSAPI_EVENTS_SERVICE HRESULT hr = xboxLiveContextImpl->EventsService()->WriteInGameEvent("AchievementUpdate", propertiesStrUtf8.c_str(), nullptr); #else // aka XSAPI_UNIT_TESTS HRESULT hr = E_FAIL; UNREFERENCED_PARAMETER(propertiesStrUtf8); #endif return hr; } #endif HRESULT AchievementsService::GetAchievementsForTitle( _In_ uint64_t xboxUserId, _In_ uint32_t titleId, _In_ XblAchievementType type, _In_ bool unlockedOnly, _In_ XblAchievementOrderBy orderBy, _In_ uint32_t skipItems, _In_ uint32_t maxItems, _In_ AsyncContext>> async ) const noexcept { return GetAchievements( xboxUserId, Vector{ titleId }, type, unlockedOnly, orderBy, skipItems, maxItems, String{}, std::move(async) ); } HRESULT AchievementsService::GetAchievement( _In_ uint64_t xboxUserId, _In_ const String& serviceConfigurationId, _In_ const String& achievementId, _In_ AsyncContext>> async ) const noexcept { RETURN_HR_INVALIDARGUMENT_IF(serviceConfigurationId.empty()); RETURN_HR_INVALIDARGUMENT_IF(achievementId.empty()); Stringstream subPath; subPath << ("/users/xuid(") << xboxUserId << (")/achievements/") << utils::ToLower(serviceConfigurationId) << ("/") << achievementId; Result userResult = m_user.Copy(); RETURN_HR_IF_FAILED(userResult.Hresult()); auto httpCall = MakeShared(userResult.ExtractPayload()); HRESULT hr = httpCall->Init( m_xboxLiveContextSettings, "GET", XblHttpCall::BuildUrl("achievements", subPath.str()), xbox_live_api::get_achievement ); RETURN_HR_IF_FAILED(hr); RETURN_HR_IF_FAILED(httpCall->SetXblServiceContractVersion(2)); hr = httpCall->Perform(AsyncContext{ async.Queue().DeriveWorkerQueue(), [ async, service{ shared_from_this() } ] (HttpResult httpResult) { HRESULT hr = httpResult.Hresult(); if (FAILED(hr)) { return async.Complete(hr); } hr = httpResult.Payload()->Result(); if (FAILED(hr)) { return async.Complete(hr); } auto result = XblAchievementsResult::Deserialize(httpResult.Payload()->GetResponseBodyJson(), service); if (Succeeded(result)) { auto& achievements = result.Payload()->Achievements(); if (achievements.size() == 1) { async.Complete({ result }); } else if (achievements.size() > 1) { LOG_DEBUG("get_achievement:Return payload was larger than expected"); async.Complete({ E_UNEXPECTED }); } else { LOG_DEBUG("get_achievement:The achievement is not found"); async.Complete({ E_UNEXPECTED }); } } else { async.Complete({ result.Hresult() }); } } }); return hr; } HRESULT AchievementsService::GetAchievements( _In_ uint64_t xboxUserId, _In_ const Vector& titleIds, _In_ XblAchievementType type, _In_ bool unlockedOnly, _In_ XblAchievementOrderBy orderBy, _In_ uint32_t skipItems, _In_ uint32_t maxItems, _In_ const String& continuationToken, _In_ AsyncContext>> async ) const noexcept { RETURN_HR_INVALIDARGUMENT_IF(type == XblAchievementType::Unknown); auto subPath = GetAchievementsSubpath( xboxUserId, titleIds, type, unlockedOnly, orderBy, skipItems, maxItems, continuationToken ); Result userResult = m_user.Copy(); RETURN_HR_IF_FAILED(userResult.Hresult()); auto httpCall = MakeShared(userResult.ExtractPayload()); HRESULT hr = httpCall->Init( m_xboxLiveContextSettings, "GET", XblHttpCall::BuildUrl("achievements", subPath), xbox_live_api::get_achievement ); RETURN_HR_IF_FAILED(hr); RETURN_HR_IF_FAILED(httpCall->SetXblServiceContractVersion(2)); hr = httpCall->Perform(AsyncContext{ async.Queue().DeriveWorkerQueue(), [ service{ shared_from_this() }, async, xboxUserId, titleIds, type, unlockedOnly, orderBy ] (HttpResult httpResult) { HRESULT hr = httpResult.Hresult(); if (FAILED(hr)) { return async.Complete(hr); } hr = httpResult.Payload()->Result(); if (FAILED(hr)) { return async.Complete(hr); } if (!httpResult.Payload()->GetResponseBodyJson().IsNull()) { auto result = XblAchievementsResult::Deserialize(httpResult.Payload()->GetResponseBodyJson(), service); if (Succeeded(result)) { result.Payload()->SetNextPageQueryParameters( xboxUserId, titleIds, static_cast(type), unlockedOnly, static_cast(orderBy) ); } async.Complete(result); } } }); return hr; } XblFunctionContext AchievementsService::AddAchievementProgressChangeHandler( _In_ AchievementProgressChangeHandler handler ) noexcept { std::lock_guard lock{ m_lock }; if (!m_achievementProgressChangeSubscription) { m_achievementProgressChangeSubscription = MakeShared(m_user.Xuid(), m_appConfig->Scid()); m_rtaManager->AddSubscription(m_user, m_achievementProgressChangeSubscription); } return m_achievementProgressChangeSubscription->AddHandler(std::move(handler)); } void AchievementsService::RemoveAchievementProgressChangeHandler( _In_ XblFunctionContext token ) noexcept { std::lock_guard lock{ m_lock }; if (m_achievementProgressChangeSubscription) { if (m_achievementProgressChangeSubscription->RemoveHandler(token) == 0) { m_rtaManager->RemoveSubscription(m_user, m_achievementProgressChangeSubscription); m_achievementProgressChangeSubscription.reset(); } } } String AchievementsService::GetAchievementsSubpath( _In_ uint64_t xboxUserId, _In_ const Vector& titleIds, _In_ XblAchievementType type, _In_ bool unlockedOnly, _In_ XblAchievementOrderBy orderBy, _In_ uint32_t skipItems, _In_ uint32_t maxItems, _In_ const String& continuationToken ) { xbox::services::uri_builder subPathBuilder; Stringstream path; path << ("/users/xuid("); path << xboxUserId; path << (")/achievements"); subPathBuilder.append_path(path.str()); Stringstream titleQuery; auto &last = titleIds.back(); for (const auto& titleId : titleIds) { titleQuery << titleId; if (&titleId != &last) { titleQuery << (","); } } subPathBuilder.append_query("titleId", titleQuery.str()); if (type != XblAchievementType::All) { subPathBuilder.append_query("types", EnumName(type)); } if (unlockedOnly) { subPathBuilder.append_query("unlockedOnly=true"); } switch (orderBy) { case XblAchievementOrderBy::TitleId: { subPathBuilder.append_query("orderBy", "title"); break; } case XblAchievementOrderBy::UnlockTime: { subPathBuilder.append_query("orderBy", "unlocktime"); break; } default: break; } utils::append_paging_info( subPathBuilder, skipItems, maxItems, continuationToken ); return subPathBuilder.to_string(); } void AchievementsService::CleanupAchievement(XblAchievement& a) { Delete(a.id); Delete(a.serviceConfigurationId); Delete(a.name); for (size_t i = 0; i < a.titleAssociationsCount; ++i) { Delete(a.titleAssociations[i].name); } DeleteArray(a.titleAssociations, a.titleAssociationsCount); for (size_t i = 0; i < a.mediaAssetsCount; ++i) { Delete(a.mediaAssets[i].name); Delete(a.mediaAssets[i].url); } DeleteArray(a.mediaAssets, a.mediaAssetsCount); DeleteArray(a.platformsAvailableOn, a.platformsAvailableOnCount); Delete(a.unlockedDescription); Delete(a.lockedDescription); Delete(a.productId); for (size_t i = 0; i < a.rewardsCount; ++i) { Delete(a.rewards[i].name); Delete(a.rewards[i].description); Delete(a.rewards[i].value); Delete(a.rewards[i].valueType); if (a.rewards[i].mediaAsset) { Delete(a.rewards[i].mediaAsset->name); Delete(a.rewards[i].mediaAsset->url); } } DeleteArray(a.rewards, a.rewardsCount); Delete(a.deepLink); ZeroMemory(&a, sizeof(XblAchievement)); } XblAchievementProgressState ProgressStateFromString( _In_ const xsapi_internal_string& value ) { if (utils::str_icmp_internal(value, "Achieved") == 0) { return XblAchievementProgressState::Achieved; } else if (utils::str_icmp_internal(value, "NotStarted") == 0) { return XblAchievementProgressState::NotStarted; } else if (utils::str_icmp_internal(value, "InProgress") == 0) { return XblAchievementProgressState::InProgress; } return XblAchievementProgressState::Unknown; } XblAchievementType AchievementTypeFromString( _In_ const xsapi_internal_string& value ) { if (utils::str_icmp_internal(value, "Persistent") == 0) { return XblAchievementType::Persistent; } else if (utils::str_icmp_internal(value, "Challenge") == 0) { return XblAchievementType::Challenge; } return XblAchievementType::Unknown; } XblAchievementParticipationType ParticipationTypeFromString( _In_ const xsapi_internal_string& value ) { if (utils::str_icmp_internal(value, "Individual") == 0) { return XblAchievementParticipationType::Individual; } else if (utils::str_icmp_internal(value, "Group") == 0) { return XblAchievementParticipationType::Group; } return XblAchievementParticipationType::Unknown; } XblAchievementMediaAssetType MediaAssetTypeFromString( _In_ const xsapi_internal_string& value ) { if (utils::str_icmp_internal(value, "Icon") == 0) { return XblAchievementMediaAssetType::Icon; } else if (utils::str_icmp_internal(value, "Art") == 0) { return XblAchievementMediaAssetType::Art; } return XblAchievementMediaAssetType::Unknown; } XblAchievementRewardType RewardTypeFromString( _In_ const xsapi_internal_string& value ) { if (utils::str_icmp_internal(value, "Gamerscore") == 0) { return XblAchievementRewardType::Gamerscore; } else if (utils::str_icmp_internal(value, "InApp") == 0) { return XblAchievementRewardType::InApp; } else if (utils::str_icmp_internal(value, "Art") == 0) { return XblAchievementRewardType::Art; } return XblAchievementRewardType::Unknown; } Result AchievementsService::DeserializeTitleAssociation( const JsonValue& json ) { XblAchievementTitleAssociation titleAssociation{}; if (json.IsNull()) { return Result{ titleAssociation }; } HRESULT errc = S_OK; xsapi_internal_string name; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "name", name, true)); titleAssociation.name = Make(name); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonInt(json, "id", titleAssociation.titleId, true)); return Result{ titleAssociation, errc }; } Result AchievementsService::DeserializeRequirement( const JsonValue& json ) { XblAchievementRequirement requirement{}; if (json.IsNull()) { return Result{ requirement }; } xsapi_internal_string id, current, target; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "id", id, true)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "current", current, true)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "target", target, true)); requirement.id = Make(id); requirement.currentProgressValue = Make(current); requirement.targetProgressValue = Make(target); return Result{ requirement, S_OK }; } Result AchievementsService::DeserializeProgression( const JsonValue& json ) { XblAchievementProgression progression{}; if (json.IsNull()) { return Result{ progression }; } HRESULT errc = S_OK; xsapi_internal_vector requirementsVector; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonVector( DeserializeRequirement, json, "requirements", requirementsVector, true )); progression.requirements = MakeArray(requirementsVector); progression.requirementsCount = requirementsVector.size(); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonTimeT(json, "timeUnlocked", progression.timeUnlocked, true)); return Result{ progression, xbox::services::legacy::ConvertHr(errc) }; } Result AchievementsService::DeserializeTimeWindow( const JsonValue& json ) { XblAchievementTimeWindow timeWindow{}; if (json.IsNull()) { return Result{ timeWindow }; } HRESULT errc = S_OK; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonTimeT(json, "startDate", timeWindow.startDate, true)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonTimeT(json, "endDate", timeWindow.endDate, true)); return Result{ timeWindow, xbox::services::legacy::ConvertHr(errc) }; } Result AchievementsService::DeserializeMediaAsset( const JsonValue& json ) { XblAchievementMediaAsset mediaAsset{}; if (json.IsNull()) { return Result{ mediaAsset }; } xsapi_internal_string name, type, url; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "name", name, true)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "type", type, true)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "url", url, true)); mediaAsset.name = Make(name); mediaAsset.mediaAssetType = MediaAssetTypeFromString(type); mediaAsset.url = Make(url); return Result{ mediaAsset, S_OK }; } Result AchievementsService::DeserializeReward( const JsonValue& json ) { XblAchievementReward reward{}; if (json.IsNull()) { return Result{ reward }; } HRESULT errc = S_OK; xsapi_internal_string name, description, value, rewardType, valueType; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "name", name, true)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "description", description, true)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "value", value, true)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "type", rewardType, true)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "valueType", valueType, true)); reward.name = Make(name); reward.description = Make(description); reward.value = Make(value); reward.rewardType = RewardTypeFromString(rewardType); reward.valueType = Make(valueType); if (json.IsObject() && json.HasMember("mediaAsset")) { auto mediaAssetResult = DeserializeMediaAsset(json["mediaAsset"]); reward.mediaAsset = Make(mediaAssetResult.ExtractPayload()); if (Failed(mediaAssetResult) || FAILED(errc)) { return Result{ reward, E_FAIL }; } return Result{ reward }; } else { reward.mediaAsset = Make(XblAchievementMediaAsset()); return Result{ reward, E_FAIL }; } } Result AchievementsService::DeserializeAchievement( const JsonValue& json ) { XblAchievement a{}; if (json.IsNull()) { return Result{ a }; } HRESULT errCode = S_OK; xsapi_internal_string id, serviceConfigId, name; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "id", id, true)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "serviceConfigId", serviceConfigId, true)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "name", name, true)); a.id = Make(id); a.serviceConfigurationId = Make(serviceConfigId); a.name = Make(name); xsapi_internal_vector titleAssociationVector; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonVector( DeserializeTitleAssociation, json, "titleAssociations", titleAssociationVector, true )); a.titleAssociations = MakeArray(titleAssociationVector); a.titleAssociationsCount = titleAssociationVector.size(); xsapi_internal_string progressState; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "progressState", progressState)); a.progressState = ProgressStateFromString(progressState); xsapi_internal_vector mediaAssetsVector; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonVector( DeserializeMediaAsset, json, "mediaAssets", mediaAssetsVector, true )); a.mediaAssets = MakeArray(mediaAssetsVector); a.mediaAssetsCount = mediaAssetsVector.size(); xsapi_internal_vector platformsVector; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonVector(JsonUtils::JsonStringExtractor, json, "platforms", platformsVector, true)); a.platformsAvailableOn = const_cast(MakeArray(platformsVector)); a.platformsAvailableOnCount = platformsVector.size(); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonBool(json, "isSecret", a.isSecret)); xsapi_internal_string description, lockedDescription, productId; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "description", description, true)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "lockedDescription", lockedDescription, true)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "productId", productId, true)); a.unlockedDescription = Make(description); a.lockedDescription = Make(lockedDescription); a.productId = Make(productId); xsapi_internal_string achievementType, participationType; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "achievementType", achievementType, true)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "participationType", participationType, true)); a.type = AchievementTypeFromString(achievementType); a.participationType = ParticipationTypeFromString(participationType); Result timeWindowResult{ XblAchievementTimeWindow() }; if (json.IsObject() && json.HasMember("timeWindow")) { timeWindowResult = DeserializeTimeWindow(json["timeWindow"]); } else { //required return WEB_E_INVALID_JSON_STRING; } a.available = timeWindowResult.ExtractPayload(); xsapi_internal_vector rewardsVector; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonVector( DeserializeReward, json, "rewards", rewardsVector, true )); a.rewards = MakeArray(rewardsVector); a.rewardsCount = rewardsVector.size(); std::chrono::seconds seconds{}; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonStringTimespanInSeconds(json, "estimatedTime", seconds, true)); a.estimatedUnlockTime = seconds.count(); xsapi_internal_string deeplink; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "deeplink", deeplink, true)); a.deepLink = Make(deeplink); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonBool(json, "isRevoked", a.isRevoked, true)); Result progressionResult{ XblAchievementProgression() }; if (json.IsObject() && json.HasMember("progression")) { progressionResult = DeserializeProgression(json["progression"]); } else { //required return WEB_E_INVALID_JSON_STRING; } a.progression = progressionResult.ExtractPayload(); if (Failed(progressionResult) || Failed(timeWindowResult) || FAILED(errCode)) { CleanupAchievement(a); return Result{ E_FAIL }; } return Result{ a }; } #if HC_PLATFORM == HC_PLATFORM_XDK #pragma pack(pop) #endif NAMESPACE_MICROSOFT_XBOX_SERVICES_ACHIEVEMENTS_CPP_END ================================================ FILE: Source/Services/Achievements/achievements_api.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "xsapi-c/achievements_c.h" #include "Achievements/achievements_internal.h" #include "xbox_live_app_config_internal.h" #include "xbox_live_context_internal.h" using namespace xbox::services; using namespace xbox::services::achievements; using namespace xbox::services::system; STDAPI XblAchievementsGetAchievementsForTitleIdAsync( _In_ XblContextHandle xboxLiveContext, _In_ uint64_t xboxUserId, _In_ uint32_t titleId, _In_ XblAchievementType type, _In_ bool unlockedOnly, _In_ XblAchievementOrderBy orderBy, _In_ uint32_t skipItems, _In_ uint32_t maxItems, _In_ XAsyncBlock* async ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(xboxLiveContext == nullptr || async == nullptr); VERIFY_XBL_INITIALIZED(); return RunAsync(async, __FUNCTION__, [ xblContext{ xboxLiveContext->shared_from_this() }, xboxUserId, titleId, type, unlockedOnly, orderBy, skipItems, maxItems, achievmentsResult{ std::shared_ptr() } ] (XAsyncOp op, const XAsyncProviderData* data) mutable { switch (op) { case XAsyncOp::DoWork: { RETURN_HR_IF_FAILED(xblContext->AchievementsService()->GetAchievementsForTitle( xboxUserId, titleId, type, unlockedOnly, orderBy, skipItems, maxItems, AsyncContext>>{ data->async->queue, [ &achievmentsResult, async{ data->async } ] (Result> result) { if (Succeeded(result)) { achievmentsResult = result.ExtractPayload(); } XAsyncComplete(async, result.Hresult(), sizeof(XblAchievementsResultHandle)); } })); return E_PENDING; } case XAsyncOp::GetResult: { auto resultHandle = static_cast(data->buffer); *resultHandle = achievmentsResult.get(); achievmentsResult->AddRef(); return S_OK; } default: { return S_OK; } } }); } CATCH_RETURN() STDAPI XblAchievementsGetAchievementsForTitleIdResult( _In_ XAsyncBlock* async, _Out_ XblAchievementsResultHandle* result ) XBL_NOEXCEPT try { return XAsyncGetResult(async, nullptr, sizeof(XblAchievementsResultHandle), result, nullptr); } CATCH_RETURN() STDAPI XblAchievementsResultGetAchievements( _In_ XblAchievementsResultHandle result, _Out_ const XblAchievement** achievements, _Out_ size_t* achievementsCount ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(achievementsCount == nullptr || achievements == nullptr || result == nullptr); VERIFY_XBL_INITIALIZED(); *achievementsCount = result->Achievements().size(); *achievements = result->Achievements().data(); return S_OK; } CATCH_RETURN() STDAPI XblAchievementsResultHasNext( _In_ XblAchievementsResultHandle result, _Out_ bool* hasNext ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(result == nullptr || hasNext == nullptr); VERIFY_XBL_INITIALIZED(); *hasNext = result->HasNext(); return S_OK; } CATCH_RETURN() STDAPI XblAchievementsResultGetNextAsync( _In_ XblAchievementsResultHandle result, _In_ uint32_t maxItems, _In_ XAsyncBlock* async ) XBL_NOEXCEPT try { VERIFY_XBL_INITIALIZED(); return RunAsync(async, __FUNCTION__, [ inputResult{ result->shared_from_this() }, maxItems, outputResult{ std::shared_ptr() } ] (XAsyncOp op, const XAsyncProviderData* data) mutable { switch (op) { case XAsyncOp::DoWork: { RETURN_HR_IF_FAILED(inputResult->GetNext( maxItems, AsyncContext>>{ data->async->queue, [ &outputResult, async{ data->async } ] (Result> result) { if (Succeeded(result)) { outputResult = result.ExtractPayload(); } XAsyncComplete(async, result.Hresult(), sizeof(XblAchievementsResultHandle)); } })); return E_PENDING; } case XAsyncOp::GetResult: { auto resultHandle = static_cast(data->buffer); *resultHandle = outputResult.get(); outputResult->AddRef(); return S_OK; } default: { return S_OK; } } }); } CATCH_RETURN() STDAPI XblAchievementsResultGetNextResult( _In_ XAsyncBlock* async, _Out_ XblAchievementsResultHandle* result ) XBL_NOEXCEPT try { return XAsyncGetResult(async, nullptr, sizeof(XblAchievementsResultHandle), result, nullptr); } CATCH_RETURN() STDAPI XblAchievementsUpdateAchievementAsyncInternal( _In_ XblContextHandle xboxLiveContextHandle, _In_ uint64_t xboxUserId, _In_ uint32_t titleId, _In_opt_z_ const char* serviceConfigurationId, _In_z_ const char* achievementId, _In_ uint32_t percentComplete, _In_ XAsyncBlock* async ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(xboxLiveContextHandle); RETURN_HR_INVALIDARGUMENT_IF_NULL(async); RETURN_HR_INVALIDARGUMENT_IF_NULL(achievementId); VERIFY_XBL_INITIALIZED(); return RunAsync(async, __FUNCTION__, [ xboxLiveContext{ xboxLiveContextHandle->shared_from_this() }, xboxUserId, titleId, scid = String{ serviceConfigurationId ? serviceConfigurationId : "" }, achievementId = String{ achievementId }, percentComplete ] (XAsyncOp op, const XAsyncProviderData* data) { switch (op) { case XAsyncOp::DoWork: { if (titleId == 0) { RETURN_HR_IF_FAILED(xboxLiveContext->AchievementsService()->UpdateAchievement( xboxUserId, achievementId, percentComplete, data->async )); } else { RETURN_HR_IF_FAILED(xboxLiveContext->AchievementsService()->UpdateAchievement( xboxUserId, titleId, scid, achievementId, percentComplete, data->async )); } return E_PENDING; } default: { return S_OK; } } }); } CATCH_RETURN() STDAPI XblAchievementsUpdateAchievementAsync( _In_ XblContextHandle xboxLiveContext, _In_ uint64_t xboxUserId, _In_z_ const char* achievementId, _In_ uint32_t percentComplete, _In_ XAsyncBlock* async ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(achievementId); return XblAchievementsUpdateAchievementAsyncInternal( xboxLiveContext, xboxUserId, AppConfig::Instance()->TitleId(), AppConfig::Instance()->Scid().c_str(), achievementId, percentComplete, async ); } CATCH_RETURN() STDAPI XblAchievementsUpdateAchievementForTitleIdAsync( _In_ XblContextHandle xboxLiveContext, _In_ uint64_t xboxUserId, _In_ const uint32_t titleId, _In_z_ const char* serviceConfigurationId, _In_z_ const char* achievementId, _In_ uint32_t percentComplete, _In_ XAsyncBlock* async ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(serviceConfigurationId); RETURN_HR_INVALIDARGUMENT_IF_NULL(achievementId); return XblAchievementsUpdateAchievementAsyncInternal(xboxLiveContext, xboxUserId, titleId, serviceConfigurationId, achievementId, percentComplete, async); } CATCH_RETURN() STDAPI XblAchievementsGetAchievementAsync( _In_ XblContextHandle xboxLiveContextHandle, _In_ uint64_t xboxUserId, _In_z_ const char* serviceConfigurationId, _In_z_ const char* achievementId, _In_ XAsyncBlock* async ) XBL_NOEXCEPT try { VERIFY_XBL_INITIALIZED(); RETURN_HR_INVALIDARGUMENT_IF_NULL(xboxLiveContextHandle); RETURN_HR_INVALIDARGUMENT_IF_NULL(serviceConfigurationId); RETURN_HR_INVALIDARGUMENT_IF_NULL(achievementId); RETURN_HR_INVALIDARGUMENT_IF_NULL(async); return RunAsync(async, __FUNCTION__, [ xboxLiveContext{ xboxLiveContextHandle->shared_from_this() }, xboxUserId, scid = String{ serviceConfigurationId }, achievementId = String{ achievementId }, achievementResult = std::shared_ptr{ nullptr } ] (XAsyncOp op, const XAsyncProviderData* data) mutable { switch (op) { case XAsyncOp::DoWork: { RETURN_HR_IF_FAILED(xboxLiveContext->AchievementsService()->GetAchievement( xboxUserId, scid, achievementId, AsyncContext>>{ data->async->queue, [ &achievementResult, async{ data->async } ] (Result> result) { if (Succeeded(result)) { achievementResult = result.ExtractPayload(); } XAsyncComplete(async, result.Hresult(), sizeof(XblAchievementsResultHandle)); } })); return E_PENDING; } case XAsyncOp::GetResult: { auto resultHandle = static_cast(data->buffer); *resultHandle = achievementResult.get(); achievementResult->AddRef(); return S_OK; } default: { return S_OK; } } }); } CATCH_RETURN() STDAPI XblAchievementsGetAchievementResult( _In_ XAsyncBlock* async, _Out_ XblAchievementsResultHandle* result ) XBL_NOEXCEPT try { return XAsyncGetResult(async, nullptr, sizeof(XblAchievementsResultHandle), result, nullptr); } CATCH_RETURN() STDAPI XblAchievementsResultDuplicateHandle( _In_ XblAchievementsResultHandle handle, _Out_ XblAchievementsResultHandle* duplicatedHandle ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(handle == nullptr || duplicatedHandle == nullptr); handle->AddRef(); *duplicatedHandle = handle; return S_OK; } CATCH_RETURN() STDAPI_(void) XblAchievementsResultCloseHandle( _In_ XblAchievementsResultHandle handle ) XBL_NOEXCEPT try { if (handle) { handle->DecRef(); } } CATCH_RETURN_WITH(;) STDAPI_(XblFunctionContext) XblAchievementsAddAchievementProgressChangeHandler( _In_ XblContextHandle xblContextHandle, _In_ XblAchievementsProgressChangeHandler handler, _In_opt_ void* handlerContext ) XBL_NOEXCEPT try { if (xblContextHandle == nullptr || handler == nullptr) { return XblFunctionContext{ 0 }; } return xblContextHandle->AchievementsService()->AddAchievementProgressChangeHandler( [ handler, handlerContext ] (const XblAchievementProgressChangeEventArgs& args) { try { handler(&args, handlerContext); } catch (...) { LOGS_ERROR << __FUNCTION__ << ": exception in client handler!"; } }); } CATCH_RETURN() STDAPI XblAchievementsRemoveAchievementProgressChangeHandler( _In_ XblContextHandle xboxLiveContext, _In_ XblFunctionContext functionContext ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(xboxLiveContext); xboxLiveContext->AchievementsService()->RemoveAchievementProgressChangeHandler(functionContext); return S_OK; } CATCH_RETURN() ================================================ FILE: Source/Services/Achievements/achievements_internal.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "xsapi-c/achievements_c.h" #include "real_time_activity_subscription.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_ACHIEVEMENTS_CPP_BEGIN typedef Callback AchievementProgressChangeHandler; class AchievementProgressChangeSubscription : public real_time_activity::Subscription { public: AchievementProgressChangeSubscription( _In_ uint64_t xuid, _In_ const xbox::services::String& scid ) noexcept; XblFunctionContext AddHandler(AchievementProgressChangeHandler handler) noexcept; size_t RemoveHandler(XblFunctionContext token) noexcept; static XblAchievementProgressChangeEntry DeepCopyProgressChangeEntry (XblAchievementProgressChangeEntry& source); static void CleanUpProgressChangeEntry(XblAchievementProgressChangeEntry& entry); protected: void OnEvent(const JsonValue& data) noexcept override; private: Map m_handlers; XblFunctionContext m_nextHandlerToken{ 1 }; mutable std::mutex m_lock; uint64_t m_userId; }; class AchievementsService : public std::enable_shared_from_this { public: AchievementsService( _In_ User&& user, _In_ std::shared_ptr xboxLiveContextSettings, _In_ std::shared_ptr appConfig, _In_ std::weak_ptr<::XblContext> xboxLiveContextImpl, _In_ std::shared_ptr rtaManager ); HRESULT UpdateAchievement( _In_ uint64_t xboxUserId, _In_ const String& achievementId, _In_ uint32_t percentComplete, _In_ AsyncContext> async ) const noexcept; HRESULT UpdateAchievement( _In_ uint64_t xboxUserId, _In_ uint32_t titleId, _In_ const String& serviceConfigurationId, _In_ const String& achievementId, _In_ uint32_t percentComplete, _In_ AsyncContext> async ) const noexcept; HRESULT GetAchievementsForTitle( _In_ uint64_t xboxUserId, _In_ uint32_t titleId, _In_ XblAchievementType type, _In_ bool unlockedOnly, _In_ XblAchievementOrderBy orderBy, _In_ uint32_t skipItems, _In_ uint32_t maxItems, _In_ AsyncContext>> async ) const noexcept; HRESULT GetAchievement( _In_ uint64_t xboxUserId, _In_ const String& serviceConfigurationId, _In_ const String& achievementId, _In_ AsyncContext>> async ) const noexcept; HRESULT GetAchievements( _In_ uint64_t xboxUserId, _In_ const Vector& titleIds, _In_ XblAchievementType type, _In_ bool unlockedOnly, _In_ XblAchievementOrderBy orderBy, _In_ uint32_t skipItems, _In_ uint32_t maxItems, _In_ const String& continuationToken, _In_ AsyncContext>> callback ) const noexcept; XblFunctionContext AddAchievementProgressChangeHandler( _In_ AchievementProgressChangeHandler handler ) noexcept; void RemoveAchievementProgressChangeHandler( _In_ XblFunctionContext token ) noexcept; // Since there is not a destructor for XblAchievement objects, use this helper to free any dynamically // allocated memory associated with an XblAchievement and zero it out. static void CleanupAchievement(XblAchievement& achievement); static Result DeserializeAchievement(const JsonValue& json); private: // Achievements endpoints static String GetAchievementsSubpath( _In_ uint64_t xboxUserId, _In_ const Vector& titleIds, _In_ XblAchievementType type, _In_ bool unlockedOnly, _In_ XblAchievementOrderBy orderBy, _In_ uint32_t skipItems, _In_ uint32_t maxItems, _In_ const String& continuationToken ); User m_user; std::shared_ptr m_xboxLiveContextSettings; std::shared_ptr m_appConfig; std::shared_ptr m_rtaManager; std::weak_ptr m_xboxLiveContextImpl; std::shared_ptr m_achievementProgressChangeSubscription; mutable std::mutex m_lock; #if HC_PLATFORM == HC_PLATFORM_XDK static HRESULT WriteOfflineUpdateAchievement( _In_ std::shared_ptr xboxLiveContextImpl, _In_ const String& achievementId, _In_ uint32_t percentComplete ); static ULONG EventWriteAchievementUpdate( _In_ PCWSTR userId, _In_ PCWSTR achievementId, _In_ const uint32_t percentComplete ); #else static HRESULT WriteOfflineUpdateAchievement( _In_ std::shared_ptr xboxLiveContextImpl, _In_ const String& achievementId, _In_ uint32_t percentComplete ); #endif // Deserialization helpers static Result DeserializeTitleAssociation(const JsonValue& json); static Result DeserializeRequirement(const JsonValue& json); static Result DeserializeProgression(const JsonValue& json); static Result DeserializeTimeWindow(const JsonValue& json); static Result DeserializeMediaAsset(const JsonValue& json); static Result DeserializeReward(const JsonValue& json); friend struct XblContext; friend class achievements_result_internal; }; NAMESPACE_MICROSOFT_XBOX_SERVICES_ACHIEVEMENTS_CPP_END struct XblAchievementsResult : public xbox::services::RefCounter, public std::enable_shared_from_this { public: XblAchievementsResult(_In_ std::shared_ptr service); virtual ~XblAchievementsResult(); static xbox::services::Result> Deserialize( _In_ const JsonDocument& json, _In_ std::shared_ptr service); const Vector& Achievements() const; bool HasNext() const; HRESULT GetNext( _In_ uint32_t maxItems, _In_ AsyncContext>> async ) const noexcept; void SetNextPageQueryParameters( _In_ uint64_t xuid, _In_ const xsapi_internal_vector& titleIds, _In_ XblAchievementType type, _In_ bool unlockedOnly, _In_ XblAchievementOrderBy orderBy ); protected: // RefCounter std::shared_ptr GetSharedThis() override; private: XblAchievementsResult(const XblAchievementsResult& other) = delete; XblAchievementsResult& operator=(XblAchievementsResult other) = delete; xsapi_internal_vector m_achievements; // Info needed for GetNext query xsapi_internal_string m_continuationToken; uint64_t m_xuid{ 0 }; xsapi_internal_vector m_titleIds; XblAchievementType m_achievementType{ XblAchievementType::All }; bool m_unlockedOnly{ false }; XblAchievementOrderBy m_orderBy{ XblAchievementOrderBy::DefaultOrder }; std::weak_ptr m_serviceWeakPointer; }; ================================================ FILE: Source/Services/Achievements/achievements_result.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "achievements_internal.h" #include "xsapi_utils.h" using namespace xbox::services; using namespace xbox::services::achievements; XblAchievementsResult::~XblAchievementsResult() { for (auto& achievement : m_achievements) { AchievementsService::CleanupAchievement(achievement); } } Result> XblAchievementsResult::Deserialize( _In_ const JsonDocument& json, _In_ std::shared_ptr service) { if (json.IsNull()) { return WEB_E_INVALID_JSON_STRING; } auto achievementResult = MakeShared(service); HRESULT errCode = S_OK; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonVector( AchievementsService::DeserializeAchievement, json, "achievements", achievementResult->m_achievements, true )); if (json.IsObject() && json.HasMember("pagingInfo")) { const JsonValue& pageInfoJson = json["pagingInfo"]; if (!pageInfoJson.IsNull()) { RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(pageInfoJson, "continuationToken", achievementResult->m_continuationToken, true)); } } if (FAILED(errCode)) { return Result>{ errCode }; } return Result>{ achievementResult }; } XblAchievementsResult::XblAchievementsResult( _In_ std::shared_ptr service ) : m_serviceWeakPointer{ service } { } const xsapi_internal_vector& XblAchievementsResult::Achievements() const { return m_achievements; } std::shared_ptr XblAchievementsResult::GetSharedThis() { return shared_from_this(); } bool XblAchievementsResult::HasNext() const { return !m_continuationToken.empty(); } HRESULT XblAchievementsResult::GetNext( _In_ uint32_t maxItems, _In_ AsyncContext>> async ) const noexcept { auto service{ m_serviceWeakPointer.lock() }; if (m_continuationToken.empty() || service == nullptr) { return E_UNEXPECTED; } return service->GetAchievements( m_xuid, m_titleIds, m_achievementType, m_unlockedOnly, m_orderBy, 0, // use continuationToken, ignore skipItems. maxItems, m_continuationToken, std::move(async) ); } void XblAchievementsResult::SetNextPageQueryParameters( _In_ uint64_t xuid, _In_ const xsapi_internal_vector& titleIds, _In_ XblAchievementType type, _In_ bool unlockedOnly, _In_ XblAchievementOrderBy orderBy ) { m_xuid = xuid; m_titleIds = titleIds; m_achievementType = type; m_unlockedOnly = unlockedOnly; m_orderBy = orderBy; } ================================================ FILE: Source/Services/Achievements/achievements_subscription.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "achievements_internal.h" #include "xsapi_utils.h" #include "xsapi-c/errors_c.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_ACHIEVEMENTS_CPP_BEGIN AchievementProgressChangeSubscription::AchievementProgressChangeSubscription( _In_ uint64_t xuid, _In_ const xbox::services::String& scid ) noexcept : m_userId(xuid) { Stringstream uri; uri << "https://achievements.xboxlive.com/users/xuid(" << m_userId << ")/achievements/" << utils::ToLower(scid); m_resourceUri = uri.str(); } XblFunctionContext AchievementProgressChangeSubscription::AddHandler( AchievementProgressChangeHandler handler ) noexcept { std::lock_guard lock{ m_lock }; m_handlers[m_nextHandlerToken] = std::move(handler); return m_nextHandlerToken++; } size_t AchievementProgressChangeSubscription::RemoveHandler( XblFunctionContext token ) noexcept { std::lock_guard lock{ m_lock }; m_handlers.erase(token); return m_handlers.size(); } XblAchievementProgressChangeEntry AchievementProgressChangeSubscription::DeepCopyProgressChangeEntry(XblAchievementProgressChangeEntry & source) { XblAchievementProgressChangeEntry entry; entry.achievementId = Make(source.achievementId); entry.progressState = source.progressState; entry.progression.requirementsCount = source.progression.requirementsCount; entry.progression.timeUnlocked = source.progression.timeUnlocked; entry.progression.requirements = MakeArray(source.progression.requirementsCount); XblAchievementRequirement*& eventRequirements = entry.progression.requirements; for (uint64_t i = 0; i < entry.progression.requirementsCount; ++i) { eventRequirements[i].id = Make(source.progression.requirements[i].id); eventRequirements[i].currentProgressValue = Make(source.progression.requirements[i].currentProgressValue); eventRequirements[i].targetProgressValue = Make(source.progression.requirements[i].targetProgressValue); } return entry; } void AchievementProgressChangeSubscription::CleanUpProgressChangeEntry(XblAchievementProgressChangeEntry & entry) { Delete(entry.achievementId); auto& progression = entry.progression; for (uint64_t i = 0; i < progression.requirementsCount; ++i) { Delete(progression.requirements[i].id); Delete(progression.requirements[i].currentProgressValue); Delete(progression.requirements[i].targetProgressValue); } DeleteArray(progression.requirements, progression.requirementsCount); } void AchievementProgressChangeSubscription::OnEvent( const JsonValue& data ) noexcept { // Payload format at http://xboxwiki/wiki/RTA%3AEVENT#Achievements is currently out of // date. Below is the payload format discovered while debugging. // [, , // { // "progression": [ // { // "id" : "1", // "progressState" : "InProgress", // "timeUnlocked" : "2013-01-17T03:19:00.3087016Z", // "requirements" : [ // { // "id":"12345678-1234-1234-1234-123456789012", // "current" : "1", // "target" : "100", // "operationType" : "sum", // "valueType" : "Integer", // "ruleParticipationType" : "Individual" // } // ] // } // ], // "serviceConfigId" : "87654321-4321-4321-4321-210987654321" // } HRESULT hr(S_OK); xbox::services::Vector progressEntries; auto deserializeRequirement = [](const JsonValue& json) -> Result { XblAchievementRequirement requirement{}; if (json.IsNull()) { return Result{ requirement }; } xbox::services::String id, current, target; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "id", id, true)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "current", current, true)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "target", target, false)); requirement.id = Make(id); requirement.currentProgressValue = Make(current); requirement.targetProgressValue = Make(target); return Result{ requirement, S_OK }; }; auto deserializeProgressEntry = [&deserializeRequirement](const JsonValue& json) -> Result { XblAchievementProgressChangeEntry progressEntry{}; XblAchievementProgression progression{}; if (json.IsNull()) { return Result{ progressEntry }; } xbox::services::String achievementId, progressState; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "id", achievementId, true)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "progressState", progressState, true)); xbox::services::Vector requirementsVector; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonVector( deserializeRequirement, json, "requirements", requirementsVector, true )); progression.requirements = MakeArray(requirementsVector); progression.requirementsCount = requirementsVector.size(); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonTimeT(json, "timeUnlocked", progression.timeUnlocked, true)); progressEntry.progression = progression; progressEntry.achievementId = Make(achievementId); progressEntry.progressState = EnumValue(progressState.data()); return Result{progressEntry, S_OK}; }; hr = JsonUtils::ExtractJsonVector( deserializeProgressEntry, data, "progression", progressEntries, true ); if (!SUCCEEDED(hr)) { LOGS_ERROR << __FUNCTION__ << ": Ignoring malformed payload"; return; } XblAchievementProgressChangeEventArgs args{ progressEntries.data(), progressEntries.size() }; if (SUCCEEDED(hr)) { std::unique_lock lock{ m_lock }; auto handlers{ m_handlers }; lock.unlock(); for (auto& handler : handlers) { handler.second(args); } } else { LOGS_DEBUG << __FUNCTION__ << ": Ignoring malformed event"; } // clean up allocations for each of the entries. for (auto& entry : progressEntries) { Delete(entry.achievementId); auto& progression = entry.progression; for (uint64_t i = 0; i < progression.requirementsCount; ++i) { Delete(progression.requirements[i].id); Delete(progression.requirements[i].currentProgressValue); Delete(progression.requirements[i].targetProgressValue); } DeleteArray(progression.requirements, progression.requirementsCount); } } NAMESPACE_MICROSOFT_XBOX_SERVICES_ACHIEVEMENTS_CPP_END ================================================ FILE: Source/Services/Common/Cpp/pch.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" ================================================ FILE: Source/Services/Common/Cpp/pch.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "pch_common.h" ================================================ FILE: Source/Services/Common/Unix/pch.cpp ================================================ #include "pch.h" #ifndef _LINK_WITH_CPPRESTSDK #include "cpprestsdk_impl.h" #endif ================================================ FILE: Source/Services/Common/Unix/pch.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch_common.h" ================================================ FILE: Source/Services/Common/iOS/pch.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #ifndef _LINK_WITH_CPPRESTSDK #include "cpprestsdk_impl.h" #endif ================================================ FILE: Source/Services/Common/iOS/pch.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #define _LIBCPP_ENABLE_CXX17_REMOVED_AUTO_PTR #include "Unix/pch.h" #define XSAPI_I 1 ================================================ FILE: Source/Services/Common/pch_common.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "httpClient/config.h" #if HC_PLATFORM == HC_PLATFORM_ANDROID || HC_PLATFORM_IS_APPLE #pragma clang diagnostic ignored "-Woverloaded-virtual" #pragma clang diagnostic ignored "-Wc++14-extensions" #pragma clang diagnostic ignored "-Wreorder" #endif #if HC_PLATFORM_IS_MICROSOFT #pragma warning(disable : 4355 4628 4266 4555 4263 4264 4868 4062 4826 4244 4654) #pragma warning(disable : 4619 4616 4350 4351 4472 4640 5038 4471) #pragma warning(disable : 4061 4265 4365 4571 4623 4625 4626 4668 4710 4711 4746 4774 4820 4987 4996 5026 5027 5031 5032 5039 5037 5045) #pragma warning(disable: 4503) // C4503: decorated name length exceeded, name was truncated #pragma warning(disable: 4242) #pragma warning(disable: 4634) // C4634: Discarding XML document comment for invalid target. #pragma warning(disable: 26812) // enum instead of enum class #if _DEBUG && defined(XSAPI_UNIT_TESTS) #define _CRTDBG_MAP_ALLOC #include #include #endif // If you wish to build your application for a previous Windows platform, include WinSDKVer.h and // set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. #include // Windows #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers #endif #include #else // HC_PLATFORM_IS_MICROSOFT #ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS #endif #include #endif // HC_PLATFORM_IS_MICROSOFT #if HC_PLATFORM == HC_PLATFORM_ANDROID || HC_PLATFORM_IS_APPLE #define _NOEXCEPT noexcept #endif // STL includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if HC_PLATFORM_IS_MICROSOFT #include #endif #include "httpClient/pal.h" #include "httpClient/httpClient.h" #include "XAsync.h" #include "XAsyncProvider.h" #include "XTaskQueue.h" #include "xsapi-c/types_c.h" #include "internal_types.h" #include "xsapi-c/pal.h" #include "internal_mem.h" #include #include "http_headers.h" #if HC_PLATFORM != HC_PLATFORM_GDK #include "cpprest/http_msg.h" #endif #include "http_utils.h" #if HC_PLATFORM == HC_PLATFORM_GDK #include "HookedUri/uri.h" #endif #undef max // needed for the version of RapidJson we're using to avoid warnings #undef min #define RAPIDJSON_NAMESPACE xbox::services::rapidjson #define RAPIDJSON_NAMESPACE_BEGIN namespace xbox { namespace services { namespace rapidjson { #define RAPIDJSON_NAMESPACE_END } } } #include "rapidjson/document.h" #include "rapidjson/stringbuffer.h" #include "rapidjson/writer.h" #include "async_helpers.h" #include "xsapi_utils.h" #include "xsapi_json_utils.h" #include "public_utils_legacy.h" #include "ref_counter.h" #include "internal_errors.h" #include "Logger/log.h" #include "xbox_live_app_config_internal.h" #include "user.h" #include "http_call_wrapper_internal.h" #include "global_state.h" #include "enum_traits.h" #include "xsapi-c/xbox_live_context_c.h" #include "shared_macros.h" #ifndef ARRAYSIZE #define ARRAYSIZE(x) sizeof(x) / sizeof(x[0]) #endif #if HC_PLATFORM_IS_MICROSOFT #define SPRINTF(buffer, size, format, ...) sprintf_s(buffer, size, format, __VA_ARGS__) #else #define SPRINTF(buffer, size, format, ...) snprintf(buffer, size, format, __VA_ARGS__) #endif #if HC_PLATFORM_IS_PLAYSTATION #include "xsapi_ps.h" #endif ================================================ FILE: Source/Services/Common/xbox_live_context.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "xbox_live_context_internal.h" #include "presence_internal.h" #include "Achievements/achievements_internal.h" #include "profile_internal.h" #include "social_internal.h" #include "string_service_internal.h" #include "multiplayer_internal.h" #include "xbox_live_app_config_internal.h" #include "xbox_live_context_settings_internal.h" #include "title_storage_internal.h" #ifdef XSAPI_NOTIFICATION_SERVICE #include "notification_internal.h" #endif using namespace xbox::services; using namespace xbox::services::system; /*static*/ std::shared_ptr XblContext::Make( _In_ xbox::services::User&& user ) noexcept { auto xblContext = std::shared_ptr( new (Alloc(sizeof(XblContext))) XblContext{ std::move(user) }, Deleter(), Allocator() ); return xblContext; } const xbox::services::User& XblContext::User() const noexcept { return m_user; } XblContext::~XblContext() { if (m_userChangeEventToken) { User::UnregisterChangeEventHandle(m_userChangeEventToken); } } std::shared_ptr XblContext::GetSharedThis() { return shared_from_this(); } HRESULT XblContext::Initialize( std::shared_ptr rtaManager ) { m_xboxLiveContextSettings = MakeShared(); std::weak_ptr thisWeakPtr = shared_from_this(); TaskQueue globalQueue; { auto state = GlobalState::Get(); if (state) { globalQueue = state->Queue(); } } { Result userResult = m_user.Copy(); RETURN_HR_IF_FAILED(userResult.Hresult()); m_achievementService = MakeShared(userResult.ExtractPayload(), m_xboxLiveContextSettings, AppConfig::Instance(), thisWeakPtr, rtaManager); } { Result userResult = m_user.Copy(); RETURN_HR_IF_FAILED(userResult.Hresult()); m_profileService = MakeShared(userResult.ExtractPayload(), m_xboxLiveContextSettings, AppConfig::Instance()); } { Result userResult = m_user.Copy(); RETURN_HR_IF_FAILED(userResult.Hresult()); m_reputationServiceImpl = MakeShared(userResult.ExtractPayload(), m_xboxLiveContextSettings); } { Result userResult = m_user.Copy(); RETURN_HR_IF_FAILED(userResult.Hresult()); m_presenceService = MakeShared(userResult.ExtractPayload(), globalQueue, m_xboxLiveContextSettings, rtaManager); } { Result userResult = m_user.Copy(); RETURN_HR_IF_FAILED(userResult.Hresult()); m_socialService = MakeShared(userResult.ExtractPayload(), m_xboxLiveContextSettings, rtaManager); } { Result userResult = m_user.Copy(); RETURN_HR_IF_FAILED(userResult.Hresult()); m_stringService = MakeShared(userResult.ExtractPayload(), m_xboxLiveContextSettings); } { Result userResult = m_user.Copy(); RETURN_HR_IF_FAILED(userResult.Hresult()); m_multiplayerService = MakeShared(userResult.ExtractPayload(), m_xboxLiveContextSettings, AppConfig::Instance(), rtaManager); } { Result userResult = m_user.Copy(); RETURN_HR_IF_FAILED(userResult.Hresult()); m_privacyService = MakeShared(userResult.ExtractPayload(), m_xboxLiveContextSettings); } { Result userResult = m_user.Copy(); RETURN_HR_IF_FAILED(userResult.Hresult()); m_titleManagedStatisticsService = MakeShared(userResult.ExtractPayload(), m_xboxLiveContextSettings); } { Result userResult = m_user.Copy(); RETURN_HR_IF_FAILED(userResult.Hresult()); m_userStatisticsService = MakeShared(userResult.ExtractPayload(), globalQueue, m_xboxLiveContextSettings, rtaManager); } { Result userResult = m_user.Copy(); RETURN_HR_IF_FAILED(userResult.Hresult()); m_leaderboardService = MakeShared(userResult.ExtractPayload(), m_xboxLiveContextSettings, AppConfig::Instance()); } { Result userResult = m_user.Copy(); RETURN_HR_IF_FAILED(userResult.Hresult()); m_matchmakingService = MakeShared(userResult.ExtractPayload(), m_xboxLiveContextSettings); } { Result userResult = m_user.Copy(); RETURN_HR_IF_FAILED(userResult.Hresult()); m_titleStorageService = MakeShared(userResult.ExtractPayload(), m_xboxLiveContextSettings); } { Result userResult = m_user.Copy(); RETURN_HR_IF_FAILED(userResult.Hresult()); m_multiplayerActivityService = MakeShared(userResult.ExtractPayload(), globalQueue, m_xboxLiveContextSettings); } { Result userResult = m_user.Copy(); RETURN_HR_IF_FAILED(userResult.Hresult()); #ifdef XSAPI_EVENTS_SERVICE m_eventsService = MakeShared( userResult.ExtractPayload() #ifdef XSAPI_INTERNAL_EVENTS_SERVICE , globalQueue #endif ); RETURN_HR_IF_FAILED(m_eventsService->Initialize()); #endif } { Result userResult = m_user.Copy(); RETURN_HR_IF_FAILED(userResult.Hresult()); #ifdef XSAPI_NOTIFICATION_SERVICE #if !defined(XSAPI_UNIT_TESTS) && (HC_PLATFORM == HC_PLATFORM_WIN32 || HC_PLATFORM_IS_EXTERNAL) m_notificationService = MakeShared(userResult.ExtractPayload(), globalQueue, m_xboxLiveContextSettings, rtaManager); RETURN_HR_IF_FAILED(m_notificationService->Initialize()); #elif HC_PLATFORM == HC_PLATFORM_ANDROID || HC_PLATFORM == HC_PLATFORM_IOS m_notificationService = MakeShared(userResult.ExtractPayload(), m_xboxLiveContextSettings); #endif #endif } #ifdef XSAPI_NOTIFICATION_SERVICE auto userChangedRegistrationResult = User::RegisterChangeEventHandler( [ thisWeakPtr, queue{ globalQueue.DeriveWorkerQueue() } ] (UserLocalId localId, UserChangeType changeType) { auto sharedThis{ thisWeakPtr.lock() }; if (sharedThis) { if (sharedThis->m_user.LocalId() == localId.value && changeType == XalUserChange_SignedOut) { sharedThis->NotificationService()->UnregisterFromNotificationService(AsyncContext{ queue }); } } }); RETURN_HR_IF_FAILED(userChangedRegistrationResult.Hresult()); m_userChangeEventToken = userChangedRegistrationResult.Payload(); #endif return S_OK; } uint64_t XblContext::Xuid() const { return m_user.Xuid(); } std::shared_ptr XblContext::ProfileService() { return m_profileService; } std::shared_ptr XblContext::SocialService() { return m_socialService; } std::shared_ptr XblContext::ReputationService() { return m_reputationServiceImpl; } std::shared_ptr XblContext::AchievementsService() { return m_achievementService; } std::shared_ptr XblContext::MultiplayerService() { return m_multiplayerService; } std::shared_ptr XblContext::StringService() { return m_stringService; } std::shared_ptr XblContext::MatchmakingService() { return m_matchmakingService; } std::shared_ptr XblContext::PrivacyService() { return m_privacyService; } std::shared_ptr XblContext::TitleManagedStatisticsService() { return m_titleManagedStatisticsService; } std::shared_ptr XblContext::UserStatisticsService() { return m_userStatisticsService; } std::shared_ptr XblContext::LeaderboardService() { return m_leaderboardService; } std::shared_ptr XblContext::TitleStorageService() { return m_titleStorageService; } #ifdef XSAPI_EVENTS_SERVICE std::shared_ptr XblContext::EventsService() { return m_eventsService; } #endif std::shared_ptr XblContext::PresenceService() { return m_presenceService; } #ifdef XSAPI_NOTIFICATION_SERVICE std::shared_ptr XblContext::NotificationService() { return m_notificationService; } #endif std::shared_ptr XblContext::MultiplayerActivityService() noexcept { return m_multiplayerActivityService; } std::shared_ptr XblContext::Settings() { return m_xboxLiveContextSettings; } ================================================ FILE: Source/Services/Common/xbox_live_context_api.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "xbox_live_context_internal.h" #include "xbox_live_app_config_internal.h" using namespace xbox::services; STDAPI XblContextCreateHandle( _In_ XblUserHandle user, _Out_ XblContextHandle* context ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(user == nullptr || context == nullptr); auto globalState{ GlobalState::Get() }; if (!globalState) { return E_XBL_NOT_INITIALIZED; } auto wrapUserResult{ User::WrapHandle(user) }; RETURN_HR_IF_FAILED(wrapUserResult.Hresult()); auto xboxLiveContext = XblContext::Make(wrapUserResult.ExtractPayload()); HRESULT hr = xboxLiveContext->Initialize(globalState->RTAManager()); if (SUCCEEDED(hr)) { xboxLiveContext->AddRef(); *context = xboxLiveContext.get(); } return hr; } CATCH_RETURN() STDAPI XblContextDuplicateHandle( _In_ XblContextHandle handle, _Out_ XblContextHandle* duplicatedHandle ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(handle == nullptr || duplicatedHandle == nullptr); handle->AddRef(); *duplicatedHandle = handle; return S_OK; } CATCH_RETURN() STDAPI_(void) XblContextCloseHandle( _In_ XblContextHandle handle ) XBL_NOEXCEPT try { if (handle) { handle->DecRef(); } } CATCH_RETURN_WITH(;) STDAPI XblContextGetUser( _In_ XblContextHandle context, _Out_ XblUserHandle* user ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(user == nullptr || context == nullptr); return XalUserDuplicateHandle(context->User().Handle(), user); } CATCH_RETURN() STDAPI XblContextGetXboxUserId( _In_ XblContextHandle context, _Out_ uint64_t* xuid ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(context == nullptr || xuid == nullptr); *xuid = context->Xuid(); return S_OK; } CATCH_RETURN() void XblSetApiType( _In_ XblApiType apiType ) XBL_NOEXCEPT try { auto state = GlobalState::Get(); if (state) { state->ApiType = apiType; } } CATCH_RETURN_WITH(;) ================================================ FILE: Source/Services/Common/xbox_live_context_internal.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include #include "achievements_internal.h" #include "multiplayer_internal.h" #include "matchmaking_internal.h" #include "privacy_service_internal.h" #include "user_statistics_internal.h" #include "title_managed_statistics_internal.h" #include "leaderboard_internal.h" #include "events_service.h" #include "presence_internal.h" #include "profile_internal.h" #include "title_storage_internal.h" #include "string_service_internal.h" #include "social_internal.h" #include "multiplayer_activity_internal.h" #ifdef XSAPI_NOTIFICATION_SERVICE #include "notification_internal.h" #endif NAMESPACE_MICROSOFT_XBOX_SERVICES_MATCHMAKING_CPP_BEGIN namespace legacy { class matchmaking_service; } NAMESPACE_MICROSOFT_XBOX_SERVICES_MATCHMAKING_CPP_END struct XblContext : public std::enable_shared_from_this, public xbox::services::RefCounter { public: static std::shared_ptr Make( _In_ xbox::services::User&& user ) noexcept; ~XblContext(); const xbox::services::User& User() const noexcept; uint64_t Xuid() const; /// /// A service for managing user profiles. /// std::shared_ptr ProfileService(); /// /// A service for managing social networking links. /// std::shared_ptr SocialService(); /// /// A service for managing reputation reports. /// std::shared_ptr ReputationService(); /// /// A service for managing achievements. /// std::shared_ptr AchievementsService(); /// /// A service for managing multiplayer games. /// std::shared_ptr MultiplayerService(); /// /// A service for managing Matchmaking Requests. /// std::shared_ptr MatchmakingService(); /// /// A service for managing privacy settings. /// std::shared_ptr PrivacyService(); /// /// A service for verifying strings. /// std::shared_ptr StringService(); /// /// A service for managing document statistics. /// std::shared_ptr TitleManagedStatisticsService(); /// /// A service for managing user statistics. /// std::shared_ptr UserStatisticsService(); /// /// A service for managing leaderboards. /// std::shared_ptr LeaderboardService(); /// /// A service for managing Rich Presence. /// std::shared_ptr PresenceService(); #ifdef XSAPI_NOTIFICATION_SERVICE /// /// A service used for delivering notifications. /// std::shared_ptr NotificationService(); #endif /// /// A service for storing data in the cloud. /// std::shared_ptr TitleStorageService(); /// /// Service for tracking multiplayer activity, recent players, and sending multiplayer invites. /// std::shared_ptr MultiplayerActivityService() noexcept; #ifdef XSAPI_EVENTS_SERVICE /// /// A service used to write in game events. /// std::shared_ptr EventsService(); #endif std::shared_ptr ApplicationConfig() const; /// /// Returns an object containing settings that apply to all REST calls made such as retry and diagnostic settings. /// std::shared_ptr Settings(); HRESULT Initialize(std::shared_ptr rtaManager); protected: std::shared_ptr GetSharedThis() override; XblContext(_In_ xbox::services::User&& user) noexcept : m_user{ std::move(user) } {} private: std::shared_ptr m_xboxLiveContextSettings; std::shared_ptr m_achievementService; std::shared_ptr m_profileService; std::shared_ptr m_reputationServiceImpl; std::shared_ptr m_socialService; std::shared_ptr m_stringService; std::shared_ptr m_multiplayerService; std::shared_ptr m_matchmakingService; std::shared_ptr m_privacyService; std::shared_ptr m_titleManagedStatisticsService; std::shared_ptr m_userStatisticsService; std::shared_ptr m_leaderboardService; std::shared_ptr m_titleStorageService; std::shared_ptr m_multiplayerActivityService; std::shared_ptr m_presenceService; #if !defined(XSAPI_UNIT_TESTS) && (HC_PLATFORM == HC_PLATFORM_WIN32 || HC_PLATFORM_IS_EXTERNAL) std::shared_ptr m_notificationService; #elif HC_PLATFORM == HC_PLATFORM_UWP std::shared_ptr m_notificationService; #elif HC_PLATFORM == HC_PLATFORM_ANDROID || HC_PLATFORM == HC_PLATFORM_IOS std::shared_ptr m_notificationService; #elif defined(XSAPI_NOTIFICATION_SERVICE) std::shared_ptr m_notificationService; #endif #ifdef XSAPI_EVENTS_SERVICE std::shared_ptr m_eventsService; #endif uint64_t m_userChangeEventToken{ 0 }; uint64_t m_xuid{ 0 }; xbox::services::User m_user; }; ================================================ FILE: Source/Services/Common/xbox_live_context_settings.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "xbox_live_context_internal.h" #include "xbox_live_app_config_internal.h" #if HC_PLATFORM == HC_PLATFORM_ANDROID #include #endif using namespace xbox::services; NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN #if __cplusplus_winrt Windows::UI::Core::CoreDispatcher^ XboxLiveContextSettings::_s_dispatcher; #endif uint32_t XboxLiveContextSettings::LongHttpTimeout() const { return m_longHttpTimeoutInSeconds; } void XboxLiveContextSettings::SetLongHttpTimeout(_In_ uint32_t timeoutInSeconds) { m_longHttpTimeoutInSeconds = timeoutInSeconds; } uint32_t XboxLiveContextSettings::HttpRetryDelay() const { return m_httpRetryDelayInSeconds; } void XboxLiveContextSettings::SetHttpRetryDelay(_In_ uint32_t delayInSeconds) { m_httpRetryDelayInSeconds = __max(delayInSeconds, MIN_RETRY_DELAY_SECONDS); } uint32_t XboxLiveContextSettings::HttpTimeoutWindow() const { return m_httpTimeoutWindowInSeconds; } void XboxLiveContextSettings::SetHttpTimeoutWindow(_In_ uint32_t timeoutWindowInSeconds) { m_httpTimeoutWindowInSeconds = timeoutWindowInSeconds; } uint32_t XboxLiveContextSettings::WebsocketTimeoutWindow() const { return m_websocketTimeoutWindowInSeconds; } void XboxLiveContextSettings::SetWebsocketTimeoutWindow(_In_ uint32_t timeoutInSeconds) { m_websocketTimeoutWindowInSeconds = timeoutInSeconds; } HttpCallAgent XboxLiveContextSettings::HttpUserAgent() const { return m_userAgent; } void XboxLiveContextSettings::SetHttpUserAgent(_In_ HttpCallAgent userAgent) { m_userAgent = userAgent; } #if XSAPI_WINRT bool XboxLiveContextSettings::UseCoreDispatcherForEventRouting() const { return m_useCoreDispatcherForEventRouting; } void XboxLiveContextSettings::SetUseCoreDispatcherForEventRouting(_In_ bool value) { m_useCoreDispatcherForEventRouting = value; } #endif bool XboxLiveContextSettings::UseCrossplatformQosServers() const { return m_useXplatQosServer; } void XboxLiveContextSettings::SetUseCrossplatformQosServers(_In_ bool value) { m_useXplatQosServer = value; } NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END STDAPI XblContextSettingsGetLongHttpTimeout( _In_ XblContextHandle context, _Out_ uint32_t* timeoutInSeconds ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(context == nullptr || timeoutInSeconds == nullptr); *timeoutInSeconds = context->Settings()->LongHttpTimeout(); return S_OK; } CATCH_RETURN() STDAPI XblContextSettingsSetLongHttpTimeout( _In_ XblContextHandle context, _In_ uint32_t timeoutInSeconds ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(context == nullptr); context->Settings()->SetLongHttpTimeout(timeoutInSeconds); return S_OK; } CATCH_RETURN() STDAPI XblContextSettingsGetHttpRetryDelay( _In_ XblContextHandle context, _Out_ uint32_t* delayInSeconds ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(context == nullptr || delayInSeconds == nullptr); *delayInSeconds = context->Settings()->HttpRetryDelay(); return S_OK; } CATCH_RETURN() STDAPI XblContextSettingsSetHttpRetryDelay( _In_ XblContextHandle context, _In_ uint32_t delayInSeconds ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(context == nullptr); context->Settings()->SetHttpRetryDelay(delayInSeconds); return S_OK; } CATCH_RETURN() STDAPI XblContextSettingsGetHttpTimeoutWindow( _In_ XblContextHandle context, _Out_ uint32_t* timeoutWindowInSeconds ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(context == nullptr || timeoutWindowInSeconds == nullptr); *timeoutWindowInSeconds = context->Settings()->HttpTimeoutWindow(); return S_OK; } CATCH_RETURN() STDAPI XblContextSettingsSetHttpTimeoutWindow( _In_ XblContextHandle context, _In_ uint32_t timeoutWindowInSeconds ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(context == nullptr); context->Settings()->SetHttpTimeoutWindow(timeoutWindowInSeconds); return S_OK; } CATCH_RETURN() STDAPI XblContextSettingsGetWebsocketTimeoutWindow( _In_ XblContextHandle context, _Out_ uint32_t* timeoutWindowInSeconds ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(context == nullptr || timeoutWindowInSeconds == nullptr); *timeoutWindowInSeconds = context->Settings()->WebsocketTimeoutWindow(); return S_OK; } CATCH_RETURN() STDAPI XblContextSettingsSetWebsocketTimeoutWindow( _In_ XblContextHandle context, _In_ uint32_t timeoutWindowInSeconds ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(context == nullptr); context->Settings()->SetWebsocketTimeoutWindow(timeoutWindowInSeconds); return S_OK; } CATCH_RETURN() #if XSAPI_WINRT STDAPI XblContextSettingsGetUseCoreDispatcherForEventRouting( _In_ XblContextHandle context, _Out_ bool* useDispatcher ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(context == nullptr || useDispatcher == nullptr); *useDispatcher = context->Settings()->UseCoreDispatcherForEventRouting(); return S_OK; } CATCH_RETURN() STDAPI XblContextSettingsSetUseCoreDispatcherForEventRouting( _In_ XblContextHandle context, _In_ bool useDispatcher ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(context == nullptr); context->Settings()->SetUseCoreDispatcherForEventRouting(useDispatcher); return S_OK; } CATCH_RETURN() #endif STDAPI XblContextSettingsGetUseCrossPlatformQosServers( _In_ XblContextHandle context, _Out_ bool* value ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(context == nullptr || value == nullptr); *value = context->Settings()->UseCrossplatformQosServers(); return S_OK; } CATCH_RETURN() STDAPI XblContextSettingsSetUseCrossPlatformQosServers( _In_ XblContextHandle context, _In_ bool value ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(context); context->Settings()->SetUseCrossplatformQosServers(value); return S_OK; } CATCH_RETURN() ================================================ FILE: Source/Services/Common/xbox_live_context_settings_internal.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include #include #include "xsapi-c/xbox_live_context_settings_c.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN #define DEFAULT_HTTP_TIMEOUT_SECONDS (30) #ifdef XSAPI_UNIT_TESTS #define MIN_HTTP_TIMEOUT_SECONDS (0) // speed up unit tests #else #define MIN_HTTP_TIMEOUT_SECONDS (5) #endif #define MIN_HTTP_TIMEOUT_MILLISECONDS (MIN_HTTP_TIMEOUT_SECONDS * 1000) #define DEFAULT_LONG_HTTP_TIMEOUT_SECONDS (5 * 60) #ifdef XSAPI_UNIT_TESTS #define DEFAULT_WEBSOCKET_TIMEOUT_SECONDS (1) #else #define DEFAULT_WEBSOCKET_TIMEOUT_SECONDS (60) #endif #define MAXIMUM_WEBSOCKETS_ACTIVATIONS_ALLOWED_PER_USER (5) #define DEFAULT_HTTP_RETRY_WINDOW_SECONDS (20) #define DEFAULT_RETRY_DELAY_SECONDS (2) #define MIN_RETRY_DELAY_SECONDS (2) enum class HttpCallAgent : uint32_t { Title, MultiplayerManager, SocialManager, MultiplayerActivity, AchievementsManager }; class XboxLiveContextSettings { public: XboxLiveContextSettings() = default; uint32_t LongHttpTimeout() const; void SetLongHttpTimeout(_In_ uint32_t timeoutInSeconds); uint32_t HttpRetryDelay() const; void SetHttpRetryDelay(_In_ uint32_t delayInSeconds); uint32_t HttpTimeoutWindow() const; void SetHttpTimeoutWindow(_In_ uint32_t timeoutWindowInSeconds); uint32_t WebsocketTimeoutWindow() const; void SetWebsocketTimeoutWindow(_In_ uint32_t timeoutInSeconds); HttpCallAgent HttpUserAgent() const; void SetHttpUserAgent(_In_ HttpCallAgent userAgent); #if XSAPI_WINRT // WinRT only bool UseCoreDispatcherForEventRouting() const; void SetUseCoreDispatcherForEventRouting(_In_ bool value); #endif bool UseCrossplatformQosServers() const; void SetUseCrossplatformQosServers(_In_ bool value); public: #if __cplusplus_winrt static Windows::UI::Core::CoreDispatcher^ _s_dispatcher; #endif private: uint32_t m_httpTimeoutInSeconds{ DEFAULT_HTTP_TIMEOUT_SECONDS }; uint32_t m_longHttpTimeoutInSeconds{ DEFAULT_LONG_HTTP_TIMEOUT_SECONDS }; uint32_t m_httpRetryDelayInSeconds{ DEFAULT_RETRY_DELAY_SECONDS }; uint32_t m_httpTimeoutWindowInSeconds{ DEFAULT_HTTP_RETRY_WINDOW_SECONDS }; uint32_t m_websocketTimeoutWindowInSeconds{ DEFAULT_WEBSOCKET_TIMEOUT_SECONDS }; HttpCallAgent m_userAgent{ HttpCallAgent::Title }; #if XSAPI_WINRT bool m_useCoreDispatcherForEventRouting{ false }; #endif bool m_useXplatQosServer{ HC_PLATFORM == HC_PLATFORM_XDK }; }; NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END ================================================ FILE: Source/Services/Common/xbox_live_global_api.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "xsapi-c/errors_c.h" using namespace xbox::services; using namespace xbox::services::system; #ifndef MAKE_HTTP_HRESULT #define MAKE_HTTP_HRESULT(code) MAKE_HRESULT(1, 0x019, code) #endif STDAPI XblMemSetFunctions( _In_opt_ XblMemAllocFunction memAllocFunc, _In_opt_ XblMemFreeFunction memFreeFunc ) XBL_NOEXCEPT try { if (GlobalState::Get()) { return E_XBL_ALREADY_INITIALIZED; } if (memAllocFunc && memFreeFunc) { g_pMemAllocHook = memAllocFunc; g_pMemFreeHook = memFreeFunc; } else if (!memAllocFunc && !memFreeFunc) { g_pMemAllocHook = DefaultAlloc; g_pMemFreeHook = DefaultFree; } else { // Require that the hooks be set together return E_INVALIDARG; } // Try to set memory hooks for libHttpClient as well. If it is already initialized (either by Xal or by the client), // there is nothing we can do. We can't log an error either because GlobalState has not yet been set up. auto hr = HCMemSetFunctions(memAllocFunc, memFreeFunc); if (FAILED(hr) && hr != E_HC_ALREADY_INITIALISED) { return hr; } return S_OK; } CATCH_RETURN() STDAPI XblMemGetFunctions( _Out_ XblMemAllocFunction* memAllocFunc, _Out_ XblMemFreeFunction* memFreeFunc ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(memAllocFunc == nullptr || memFreeFunc == nullptr); *memAllocFunc = g_pMemAllocHook; *memFreeFunc = g_pMemFreeHook; return S_OK; } CATCH_RETURN() STDAPI XblInitialize( _In_ const XblInitArgs* args ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(args); HRESULT hr = GlobalState::Create(args); LOGS_DEBUG << __FUNCTION__; return hr; } CATCH_RETURN() STDAPI XblCleanupAsync( XAsyncBlock* async ) XBL_NOEXCEPT try { LOGS_DEBUG << __FUNCTION__; return GlobalState::CleanupAsync(async); } CATCH_RETURN() STDAPI_(XTaskQueueHandle) XblGetAsyncQueue() XBL_NOEXCEPT try { XTaskQueueHandle duplicatedHandle{ nullptr }; auto state = GlobalState::Get(); if (state && state->Queue().GetHandle()) { XTaskQueueDuplicateHandle(state->Queue().GetHandle(), &duplicatedHandle); } return duplicatedHandle; } CATCH_RETURN_WITH(nullptr) STDAPI XblGetScid( _Out_ const char** scid ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(scid); VERIFY_XBL_INITIALIZED(); *scid = AppConfig::Instance()->Scid().data(); return S_OK; } CATCH_RETURN(); STDAPI_(XblErrorCondition) XblGetErrorCondition( _In_ HRESULT inHr ) XBL_NOEXCEPT try { if (SUCCEEDED(inHr)) { return XblErrorCondition::NoError; } else if (HRESULT_FACILITY(inHr) == FACILITY_HTTP) { switch (inHr) { case HTTP_E_STATUS_NOT_MODIFIED: return XblErrorCondition::Http304NotModified; case HTTP_E_STATUS_NOT_FOUND: return XblErrorCondition::Http404NotFound; case HTTP_E_STATUS_PRECOND_FAILED: return XblErrorCondition::Http412PreconditionFailed; case MAKE_HTTP_HRESULT(429): return XblErrorCondition::Http429TooManyRequests; case HTTP_E_STATUS_REQUEST_TIMEOUT: case HTTP_E_STATUS_SERVICE_UNAVAIL: case HTTP_E_STATUS_GATEWAY_TIMEOUT: return XblErrorCondition::HttpServiceTimeout; default: return XblErrorCondition::HttpGeneric; } } else if (HRESULT_FACILITY(inHr) == FACILITY_INTERNET) { return XblErrorCondition::Network; } else if ((inHr >= (unsigned)xbl_error_code::AM_E_XASD_UNEXPECTED && inHr <= (unsigned)xbl_error_code::AM_E_XTITLE_TIMEOUT) || (inHr >= (unsigned)xbl_error_code::XO_E_DEVMODE_NOT_AUTHORIZED && inHr <= (unsigned)xbl_error_code::XO_E_CONTENT_NOT_AUTHORIZED)) { return XblErrorCondition::Auth; } else { switch (inHr) { case ONL_E_ACTION_REQUIRED: case E_XBL_AUTH_UNKNOWN_ERROR: case E_XBL_AUTH_RUNTIME_ERROR: case E_XBL_AUTH_NO_TOKEN: case __HRESULT_FROM_WIN32(ERROR_NO_SUCH_USER): case __HRESULT_FROM_WIN32(ERROR_CANCELLED): return XblErrorCondition::Auth; case E_BOUNDS: return XblErrorCondition::GenericOutOfRange; default: return XblErrorCondition::GenericError; } } } CATCH_RETURN_WITH(XblErrorCondition::GenericError) STDAPI_(void) XblDisableAssertsForXboxLiveThrottlingInDevSandboxes( _In_ XblConfigSetting setting ) XBL_NOEXCEPT try { UNREFERENCED_PARAMETER(setting); AppConfig::Instance()->DisableAssertsForXboxLiveThrottlingInDevSandboxes(); } CATCH_RETURN_WITH(;) STDAPI XblSetOverrideConfiguration( _In_ const char* overrideScid, _In_ uint32_t overrideTitleId ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(overrideScid); VERIFY_XBL_INITIALIZED(); AppConfig::Instance()->SetOverrideScid(overrideScid); AppConfig::Instance()->SetOverrideTitleId(overrideTitleId); return S_OK; } CATCH_RETURN() STDAPI XblSetOverrideLocale( _In_ char const* overrideLocale ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(overrideLocale); VERIFY_XBL_INITIALIZED(); auto state{ GlobalState::Get() }; if (state) { state->OverrideLocale(overrideLocale); return S_OK; } else { return E_FAIL; } } CATCH_RETURN() STDAPI_(XblFunctionContext) XblAddServiceCallRoutedHandler( _In_ XblCallRoutedHandler handler, _In_opt_ void* context ) XBL_NOEXCEPT try { auto state{ GlobalState::Get() }; if (state && handler) { return state->AddServiceCallRoutedHandler(handler, context); } else { return XblFunctionContext{ 0 }; } } CATCH_RETURN() STDAPI_(void) XblRemoveServiceCallRoutedHandler( _In_ XblFunctionContext token ) XBL_NOEXCEPT try { auto state{ GlobalState::Get() }; if (state) { state->RemoveServiceCallRoutedHandler(token); } } CATCH_RETURN_WITH(;) ================================================ FILE: Source/Services/Events/Android/events_service_android.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "events_service_xsapi.h" #include "a/java_interop.h" #include "a/jni_utils.h" #include NAMESPACE_MICROSOFT_XBOX_SERVICES_EVENTS_CPP_BEGIN void EventsService::InitializeTenantSettings() { m_tenantSettings = MakeShared(cll::AndroidPartA(java_interop::get_java_interop_singleton()->GetJniEnv(), nullptr, IKey())); } NAMESPACE_MICROSOFT_XBOX_SERVICES_EVENTS_CPP_END ================================================ FILE: Source/Services/Events/Generic/events_service_generic.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include #include "events_service_xsapi.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_EVENTS_CPP_BEGIN void EventsService::InitializeTenantSettings() { auto appConfig = AppConfig::Instance(); // .c_str() to force conversion from xsapi_internal_string const& to std::string&& m_tenantSettings = MakeShared(cll::GenericPartA{ IKey().c_str(), appConfig->AppId().c_str(), appConfig->AppVer().c_str(), appConfig->OsName().c_str(), appConfig->OsLocale().c_str(), appConfig->OsVersion().c_str(), appConfig->DeviceClass().c_str(), appConfig->DeviceId().c_str() }); } NAMESPACE_MICROSOFT_XBOX_SERVICES_EVENTS_CPP_END ================================================ FILE: Source/Services/Events/Windows/events_service_windows.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "events_service_xsapi.h" #include NAMESPACE_MICROSOFT_XBOX_SERVICES_EVENTS_CPP_BEGIN void EventsService::InitializeTenantSettings() { m_tenantSettings = MakeShared(cll::Windows7PartA(IKey())); } NAMESPACE_MICROSOFT_XBOX_SERVICES_EVENTS_CPP_END ================================================ FILE: Source/Services/Events/event.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "events_service_xsapi.h" #include "xbox_live_app_config_internal.h" #include "cll/BasicJsonWriter.h" #include "cll/ConversionHelpers.h" using namespace xbox::services; NAMESPACE_MICROSOFT_XBOX_SERVICES_EVENTS_CPP_BEGIN #define EVENT_NAME_PREFIX "Microsoft.XboxLive.T" Event::Event( _In_ uint64_t xuid, _In_ const xsapi_internal_string& eventName, _In_ const JsonValue& dimensions, _In_ const JsonValue& measurements, _In_ xbox::services::datetime timestamp ) : m_xuid{ xuid }, m_eventName{ eventName }, m_fullEventName{ EVENT_NAME_PREFIX + std::to_string(AppConfig::Instance()->TitleId()) + "." + m_eventName.data() }, m_timestamp{ std::move(timestamp) } { JsonUtils::CopyFrom(m_dimensions, dimensions); JsonUtils::CopyFrom(m_measurements, measurements); bool state; cll::BasicJsonWriter::StartObject(m_data, state); cll::BasicJsonWriter::WriteField(m_data, state, "baseType", "Microsoft.XboxLive.InGame"); cll::BasicJsonWriter::StartStruct(m_data, state, "baseData"); cll::BasicJsonWriter::WriteField(m_data, state, "name", eventName.data()); cll::BasicJsonWriter::WriteField(m_data, state, "serviceConfigId", AppConfig::Instance()->Scid().data()); cll::BasicJsonWriter::WriteField(m_data, state, "titleId", std::to_string(AppConfig::Instance()->TitleId())); cll::BasicJsonWriter::WriteField(m_data, state, "userId", utils::uint64_to_internal_string(xuid).data()); if (dimensions.IsObject()) { cll::BasicJsonWriter::StartStruct(m_data, state, "properties"); for (const auto& pair : dimensions.GetObject()) { #if _WIN32 cll::BasicJsonWriter::WriteSerializedStruct(m_data, state, pair.name.GetString(), JsonUtils::SerializeJson(pair.value).c_str()); #else cll::BasicJsonWriter::WriteSerializedStruct(m_data, state, pair.name.GetString(), JsonUtils::SerializeJson(pair.value).c_str()); #endif } cll::BasicJsonWriter::EndStruct(m_data, state); // properties } if (measurements.IsObject()) { cll::BasicJsonWriter::StartStruct(m_data, state, "measurements"); for (const auto& pair : measurements.GetObject()) { #if _WIN32 cll::BasicJsonWriter::WriteSerializedStruct(m_data, state, pair.name.GetString(), JsonUtils::SerializeJson(pair.value).c_str()); #else cll::BasicJsonWriter::WriteSerializedStruct(m_data, state, pair.name.GetString(), JsonUtils::SerializeJson(pair.value).c_str()); #endif } cll::BasicJsonWriter::EndStruct(m_data, state); // measurements } cll::BasicJsonWriter::EndStruct(m_data, state); // baseData cll::BasicJsonWriter::EndObject(m_data, state); } Event::Event(const Event& other) { m_xuid = other.m_xuid; m_eventName = other.m_eventName; m_fullEventName = other.m_fullEventName; JsonUtils::CopyFrom(m_dimensions, other.m_dimensions); JsonUtils::CopyFrom(m_measurements, other.m_measurements); m_timestamp = other.m_timestamp; m_data = other.m_data; } Result Event::Deserialize( _In_ const xsapi_internal_string& inputData ) { // tab-separated (0x0A) // xuid | event name | timestamp | dimensions json | measurements json auto parts = utils::string_split_internal(inputData, '\t'); if (parts.size() < 5) { return Result{ Event{}, E_FAIL }; } auto xuid = utils::internal_string_to_uint64(parts[0]); auto& eventName = parts[1]; auto timestamp = xbox::services::datetime::from_string(parts[2], xbox::services::datetime::ISO_8601); JsonDocument dimensionsJson; dimensionsJson.Parse(parts[3].data()); JsonDocument measurementsJson; measurementsJson.Parse(parts[4].data()); if (dimensionsJson.IsNull() || measurementsJson.IsNull()) { return Result{ Event{}, E_FAIL }; } return Result(Event{ xuid, eventName, dimensionsJson, measurementsJson, timestamp }); } const std::string& Event::Data() const { return m_data; } const std::string& Event::FullEventName() const { return m_fullEventName; } const xbox::services::datetime& Event::Timestamp() const { return m_timestamp; } std::string Event::SerializeFieldValue( _In_ const JsonValue& value ) { xsapi_internal_string valueStr; if (value.IsString()) { valueStr = value.GetString(); } else { valueStr = JsonUtils::SerializeJson(value); } return valueStr.data(); } xsapi_internal_string Event::Serialize() const { // serialization format // tab-separated (0x0A) // xuid | event name | timestamp | dimensions json | measurements json char seperator{ '\t' }; xsapi_internal_stringstream ss; ss << m_xuid << seperator; ss << m_eventName << seperator; ss << m_timestamp.to_string(xbox::services::datetime::ISO_8601) << seperator; ss << JsonUtils::SerializeJson(m_dimensions) << seperator; ss << JsonUtils::SerializeJson(m_measurements); return ss.str(); } NAMESPACE_MICROSOFT_XBOX_SERVICES_EVENTS_CPP_END ================================================ FILE: Source/Services/Events/event_queue.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "events_service_xsapi.h" #include "local_storage.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_EVENTS_CPP_BEGIN EventQueue::EventQueue( User&& user, std::shared_ptr tenantSettings ) : m_user{ std::move(user) }, m_tenantSettings{ tenantSettings } { auto state{ GlobalState::Get() }; assert(state); m_localStorage = state->LocalStorage(); Stringstream ss; ss << m_filenamePrefix << m_user.Xuid() << ".dir"; m_directoryFilename = ss.str(); } void EventQueue::Initialize() { // Initialize in memory directory. Treat as write through cache, don't read from directory file on disk after this. m_localStorage->ReadAsync(m_user, m_directoryFilename, [ weakThis = std::weak_ptr{ shared_from_this() } ] (Result> result) { auto sharedThis{ weakThis.lock() }; if (sharedThis && Succeeded(result)) { auto& bytes = result.Payload(); auto fileMetadata = utils::string_split_internal(xsapi_internal_string{ bytes.begin(), bytes.end() }, '\n'); std::lock_guard lock{ sharedThis->m_mutex }; for (auto& metadata : fileMetadata) { xsapi_internal_stringstream ss{ metadata }; xsapi_internal_string filename; uint64_t fileSize{ 0 }; ss >> filename >> fileSize; sharedThis->m_fileMetadata[filename] = fileSize; sharedThis->m_totalFilesSize += fileSize; } sharedThis->Populate(); } }); } void EventQueue::Cleanup() { // Doing this in a cleanup method rather than the destructor so that we can ensure queue lifetime // during the asynchronous flush. If we are already in the destructor, we can't take a shared reference // to the queue. std::lock_guard lock{ m_mutex }; // Make sure the failed payload gets flushed as well if (m_failedPayload) { m_queue.push_back(std::move(m_failedPayload)); } Flush(); } HRESULT EventQueue::AddEvent(Event&& event) { return AddEvents(xsapi_internal_vector{ 1, std::move(event) }); } HRESULT EventQueue::AddEvents(xsapi_internal_vector&& events) { std::lock_guard lock{ m_mutex }; std::shared_ptr payload; if (m_queue.empty()) { auto copyUserResult = m_user.Copy(); RETURN_HR_IF_FAILED(copyUserResult.Hresult()); payload = MakeShared(copyUserResult.ExtractPayload(), m_tenantSettings); m_queue.push_back(payload); } else { payload = m_queue.back(); } for (auto& event : events) { auto hr = payload->AddEvent(event); if (FAILED(hr)) { auto copyUserResult = m_user.Copy(); RETURN_HR_IF_FAILED(copyUserResult.Hresult()); payload = MakeShared(copyUserResult.ExtractPayload(), m_tenantSettings); m_queue.push_back(payload); hr = payload->AddEvent(event); RETURN_HR_IF_FAILED(hr); } } if (m_mode == Mode::Offline) { return Flush(); } return S_OK; } std::shared_ptr EventQueue::GetNextPayload(size_t minimumEventCount) { std::lock_guard guard{ m_mutex }; std::shared_ptr nextPayload{ nullptr }; if (m_failedPayload) { std::swap(m_failedPayload, nextPayload); } else if (!m_queue.empty() && m_queue.front()->EventCount() >= minimumEventCount) { nextPayload = m_queue.front(); m_queue.pop_front(); } return nextPayload; } void EventQueue::SetMode(Mode mode) { std::lock_guard lock{ m_mutex }; if (m_mode == mode) { return; } m_mode = mode; switch (mode) { case Mode::Offline: { assert(m_failedPayload); Flush(); break; } case Mode::Normal: { Populate(); break; } } } void EventQueue::RequeueFailedPayload(std::shared_ptr failedPayload) { std::lock_guard lock{ m_mutex }; m_failedPayload = std::move(failedPayload); } HRESULT EventQueue::Populate() { // Ensure that m_mutex is locked when calling this helper function for (auto& metadata : m_fileMetadata) { HRESULT hr = m_localStorage->ReadAsync( m_user, metadata.first, [ metadata = std::pair{ metadata }, weakThis = std::weak_ptr{ shared_from_this() } ] (Result> readResult) { auto sharedThis{ weakThis.lock() }; if (sharedThis && Succeeded(readResult) && sharedThis->m_mode == Mode::Normal) { auto& bytes = readResult.Payload(); auto serializedEvents = utils::string_split_internal(xsapi_internal_string{ bytes.begin(), bytes.end() }, '\n'); for (const auto& serializedEvent : serializedEvents) { auto deserializationResult = Event::Deserialize(serializedEvent); if (Succeeded(deserializationResult)) { sharedThis->AddEvent(deserializationResult.ExtractPayload()); } } { std::lock_guard lock{ sharedThis->m_mutex }; sharedThis->m_totalFilesSize -= metadata.second; sharedThis->m_fileMetadata.erase(metadata.first); sharedThis->WriteDirectoryFile(); } auto clearAsyncHR = sharedThis->m_localStorage->ClearAsync(sharedThis->m_user, metadata.first, nullptr); if (FAILED(clearAsyncHR)) { // Log failure but don't let when failure prevent populating the rest of the files. LOGS_WARN << "Failed to read events file due to user being available. HR = " << clearAsyncHR; } } }); if (FAILED(hr)) { // Log failure but don't let when failure prevent populating the rest of the files. LOGS_WARN << "Failed to read events file " << metadata.first << " that appeared in " << m_directoryFilename; } } return S_OK; } HRESULT EventQueue::Flush() { // Ensure that m_mutex is locked when calling this helper function if (m_flushInProgress) { return S_OK; } // If there is an existing non-full file, continue writing to that std::pair fileMetadata{ "", 0 }; if (!m_fileMetadata.empty() && m_fileMetadata.rbegin()->second < m_maxFileSize) { fileMetadata = *m_fileMetadata.rbegin(); } xsapi_internal_vector flushData; auto targetDataSize = static_cast(m_maxFileSize - fileMetadata.second); flushData.reserve(targetDataSize + static_cast(m_tenantSettings->getMaxEventSizeInBytes())); xbox::services::datetime earliestTimestamp{ xbox::services::datetime::utc_now() }; for (auto iter = m_queue.begin(); iter != m_queue.end(); iter = m_queue.erase(iter)) { auto payload{ *iter }; auto eventsRemainingInPayload = payload->ExtractEventsAndSerialize(flushData, targetDataSize, earliestTimestamp); if (eventsRemainingInPayload) { // The only reason events can be remaining after ExtractEventsAndSerialize is if the flushData was full. // Break early so the partial payload doesn't get erased from queue. break; } } if (flushData.empty()) { return S_OK; } // Construct filename if we don't already have one if (fileMetadata.first.empty()) { Stringstream ss; ss << m_filenamePrefix << std::hex << std::uppercase << earliestTimestamp.to_interval() << ".json"; fileMetadata.first = ss.str(); assert(m_fileMetadata.empty() || fileMetadata.first > m_fileMetadata.rend()->first); } m_flushInProgress = true; // To avoid client event data from being lost, we never want to cleanup before // we finish flushing unuploaded events auto holdCleanup{ GlobalState::Get() }; assert(holdCleanup); HRESULT hr = m_localStorage->WriteAsync( m_user, XblLocalStorageWriteMode::Append, fileMetadata.first, std::move(flushData), [ sharedThis{ shared_from_this() }, filename{ fileMetadata.first }, holdCleanup ] (Result result) { std::lock_guard lock{ sharedThis->m_mutex }; assert(sharedThis->m_flushInProgress); sharedThis->m_flushInProgress = false; if (Succeeded(result)) { auto previousFileSize{ sharedThis->m_fileMetadata[filename] }; auto newFileSize{ result.ExtractPayload() }; sharedThis->m_totalFilesSize += (newFileSize - previousFileSize); sharedThis->m_fileMetadata[filename] = newFileSize; while (sharedThis->m_totalFilesSize > sharedThis->m_storageAllotment) { LOGS_INFO << "Offline events files have exceeded the configured storage allotment."; LOGS_INFO << "The oldest events file will be deleted and offline event data will be permanently lost."; auto metadataToDelete{ sharedThis->m_fileMetadata.begin() }; sharedThis->m_totalFilesSize -= metadataToDelete->second; auto clearAsyncHR = sharedThis->m_localStorage->ClearAsync(sharedThis->m_user, metadataToDelete->first, nullptr); if (SUCCEEDED(clearAsyncHR)) { sharedThis->m_fileMetadata.erase(metadataToDelete); } else { LOGS_INFO << "The user is currently unavailable. Failed to clear oldest event."; return; //todo: What's the proper handling here? } } sharedThis->WriteDirectoryFile(); sharedThis->Flush(); } }); return hr; } HRESULT EventQueue::WriteDirectoryFile() { // Ensure that m_mutex is locked when calling this helper function xsapi_internal_vector data; for (auto& metadata : m_fileMetadata) { data.insert(data.end(), metadata.first.begin(), metadata.first.end()); data.push_back('\t'); auto sizeString{ utils::uint64_to_internal_string(metadata.second) }; data.insert(data.end(), sizeString.begin(), sizeString.end()); data.push_back('\n'); } auto holdCleanup{ GlobalState::Get() }; assert(holdCleanup); return m_localStorage->WriteAsync( m_user, XblLocalStorageWriteMode::Truncate, m_directoryFilename, std::move(data), [ holdCleanup ] (Result result) { if (Failed(result)) { LOGS_ERROR << "Failed to write events directory file. Offline event data may be lost!"; } }); } HRESULT EventQueue::SetMaxFileSize(uint64_t fileSizeInBytes) { if (fileSizeInBytes < 1024) { LOGS_ERROR << "Max file size must be at least 1kb"; return E_INVALIDARG; } m_maxFileSize = fileSizeInBytes; return S_OK; } HRESULT EventQueue::SetStorageAllotment(uint64_t storageAllotmentInBytes) { if (storageAllotmentInBytes < m_maxFileSize) { LOGS_ERROR << "Storage allotment must be greater than the maximum file size."; return E_INVALIDARG; } m_storageAllotment = storageAllotmentInBytes; return S_OK; } uint64_t EventQueue::m_maxFileSize{ 128000 }; // default file size of 128k uint64_t EventQueue::m_storageAllotment{ m_maxFileSize * 150 }; // default storage allotment of 150 files (~20MB) NAMESPACE_MICROSOFT_XBOX_SERVICES_EVENTS_CPP_END ================================================ FILE: Source/Services/Events/event_upload_payload.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "events_service_xsapi.h" #include "cll/ExceptionCodes.h" #define DEVICE_TICKET_ID "1" #define USER_TICKET_ID "2" using namespace xbox::services::legacy; NAMESPACE_MICROSOFT_XBOX_SERVICES_EVENTS_CPP_BEGIN EventUploadPayload::EventUploadPayload( _In_ User&& user, _In_ std::shared_ptr tenantSettings ) : m_user{ std::move(user) }, m_tenantSettings{ tenantSettings } { } HRESULT EventUploadPayload::AddEvent(_In_ const Event& event) { if (m_events.size() >= static_cast(m_tenantSettings->getMaxEventsPerPost())) { LOGS_DEBUG << "Cannot add more events to payload."; return E_FAIL; } m_events.push_back(event); return S_OK; } size_t EventUploadPayload::EventCount() const { return m_events.size(); } HRESULT EventUploadPayload::GetRequestData( _In_ AsyncContext> async ) { if (!m_requestData.requestBody.empty()) { async.Complete(m_requestData); return S_OK; } return CreateTicketData({ async.Queue(), [ sharedThis{ shared_from_this() }, async ] (Result> result) { if (Succeeded(result)) { cll::CllUploadRequestData cllRequestData; for (auto& event : sharedThis->m_events) { cll::CllEvent cllEvent; sharedThis->m_tenantSettings->populateEvent( cllEvent, EventsService::IKey(), event.FullEventName(), event.Data(), event.Timestamp().to_string(xbox::services::datetime::date_format::ISO_8601).c_str(), cll::LatencyNormal, cll::PersistenceUnspecified, cll::SensitivityUnspecified, cll::SampleRate_Unspecified, result.Payload(), cll::CorrelationVector{} ); sharedThis->m_tenantSettings->addEventToRequest(cllEvent, cllRequestData); } sharedThis->m_requestData.requestBody = cllRequestData.getRequestBody().data(); for (const auto& header : cllRequestData.getHeaders()) { sharedThis->m_requestData.headers[header.first.data()] = header.second.data(); } } async.Complete(Result{ sharedThis->m_requestData, result.Hresult() }); } }); } size_t EventUploadPayload::ExtractEventsAndSerialize( _Inout_ xsapi_internal_vector& serializedData, _In_ size_t targetDataSize, _Inout_ xbox::services::datetime& timestamp ) { for (auto iter = m_events.begin(); iter != m_events.end() && serializedData.size() < targetDataSize; iter = m_events.erase(iter)) { auto& event{ *iter }; auto serializedEvent = event.Serialize(); serializedData.insert(serializedData.end(), serializedEvent.begin(), serializedEvent.end()); serializedData.push_back('\n'); if (event.Timestamp() < timestamp) { timestamp = event.Timestamp(); } } return m_events.size(); } HRESULT EventUploadPayload::CreateTicketData( _In_ AsyncContext>> async ) { m_user.GetTokenAndSignature("POST", "https://vortex-win.data.microsoft.com", {}, nullptr, 0, false, { async.Queue(), [ async, sharedThis{ shared_from_this() } ] (Result result) { if (Failed(result)) { return async.Complete(result.Hresult()); } cll::TicketData deviceTicketData { cll::TicketType::TicketTypeXauthDevice, DEVICE_TICKET_ID, result.Payload().token.data() }; sharedThis->m_user.GetTokenAndSignature("POST", "https://vortex-events.xboxlive.com", {}, nullptr, 0, false, { async.Queue(), [ async, deviceTicketData ] (Result result) { if (Failed(result)) { return async.Complete(result.Hresult()); } cll::TicketData userTicketData{ cll::TicketType::TicketTypeXauthUser, USER_TICKET_ID, result.Payload().token.data() }; return async.Complete(std::vector{ deviceTicketData, userTicketData }); } }); } }); return S_OK; } NAMESPACE_MICROSOFT_XBOX_SERVICES_EVENTS_CPP_END ================================================ FILE: Source/Services/Events/events_service.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once NAMESPACE_MICROSOFT_XBOX_SERVICES_EVENTS_CPP_BEGIN class IEventsService { public: virtual HRESULT WriteInGameEvent( _In_z_ const char* eventName, _In_opt_z_ const char* dimensions, _In_opt_z_ const char* measurements ) = 0; virtual HRESULT Initialize() = 0; virtual ~IEventsService() = default; }; NAMESPACE_MICROSOFT_XBOX_SERVICES_EVENTS_CPP_END #ifdef XSAPI_EVENTS_SERVICE #ifdef XSAPI_GRTS_EVENTS_SERVICE #include "events_service_gdk.h" #elif defined(XSAPI_WRL_EVENTS_SERVICE) #include "events_service_etw.h" #elif defined(XSAPI_INTERNAL_EVENTS_SERVICE) #include "events_service_xsapi.h" #endif #endif ================================================ FILE: Source/Services/Events/events_service_api.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "xsapi-c/events_c.h" #include "xbox_live_context_internal.h" using namespace xbox::services; STDAPI XblEventsWriteInGameEvent( _In_ XblContextHandle xboxLiveContext, _In_z_ const char* eventName, _In_opt_z_ const char* dimensions, _In_opt_z_ const char* measurements ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(xboxLiveContext == nullptr || eventName == nullptr); VERIFY_XBL_INITIALIZED(); return xboxLiveContext->EventsService()->WriteInGameEvent(eventName, dimensions, measurements); } CATCH_RETURN() #ifdef XSAPI_INTERNAL_EVENTS_SERVICE STDAPI XblEventsSetStorageAllotment( uint64_t storageAllotmentInBytes ) XBL_NOEXCEPT try { return events::EventQueue::SetStorageAllotment(storageAllotmentInBytes); } CATCH_RETURN() STDAPI XblEventsSetMaxFileSize( uint64_t maxFileSizeInByes ) XBL_NOEXCEPT try { return events::EventQueue::SetMaxFileSize(maxFileSizeInByes); } CATCH_RETURN() #endif // !XSAPI_ETW_EVENTS_SERVICE ================================================ FILE: Source/Services/Events/events_service_etw.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #ifdef XSAPI_WRL_EVENTS_SERVICE #include "events_service_etw.h" #include #include #include using namespace xbox::services::system; using namespace Microsoft::WRL; using namespace Microsoft::WRL::Wrappers; using namespace ABI::Windows::Foundation; using namespace ABI::Windows::Foundation::Diagnostics; DEFINE_GUID(XSAPI_TELEMETRY_GROUP, 0x53b78fc6, 0xe359, 0x453e, 0x89, 0xfe, 0xa5, 0xf4, 0xe5, 0xff, 0x4a, 0xf3); // {5E9EDC93-F04F-47EC-8DCB-0CF8F3442BDC} DEFINE_GUID(GUID_LOGGING_CHANNEL, 0x5e9edc93, 0xf04f, 0x47ec, 0x8d, 0xcb, 0xc, 0xf8, 0xf3, 0x44, 0x2b, 0xdc); #define XBOX_LIVE_LOGGING_OPTIONS 0x0000800000000000 #define XBOX_LIVE_LOGGING_TAGS 0x00B00000 NAMESPACE_MICROSOFT_XBOX_SERVICES_EVENTS_CPP_BEGIN EventsService::EventsService(_In_ User&& user) : m_user{ std::move(user) } { m_playSession = utils::create_guid(true); } EventsService::~EventsService() { #if HC_PLATFORM != HC_PLATFORM_UWP if (SUCCEEDED(m_hrCoIncrementMTAUsage)) { CoDecrementMTAUsage(m_mtaUsageCookie); } #endif } HRESULT EventsService::Initialize( ) { #if HC_PLATFORM != HC_PLATFORM_UWP m_hrCoIncrementMTAUsage = CoIncrementMTAUsage(&m_mtaUsageCookie); if (FAILED(m_hrCoIncrementMTAUsage)) { LOG_DEBUG("CoIncrementMTAUsage failed with during EventsService::Initialize."); return m_hrCoIncrementMTAUsage; } #endif ComPtr loggingChannelOptionsFactory; HRESULT hr = GetActivationFactory(HStringReference(RuntimeClass_Windows_Foundation_Diagnostics_LoggingChannelOptions).Get(), &loggingChannelOptionsFactory); if (FAILED(hr)) { LOG_DEBUG("GetActivationFactory for ILoggingChannelOptionsFactory failed during EventsService::Initialize."); return hr; } ComPtr loggingChannelOptions; hr = loggingChannelOptionsFactory->Create(XSAPI_TELEMETRY_GROUP, &loggingChannelOptions); if (FAILED(hr)) { LOG_DEBUG("ILoggingChannelOptions::Create failed during EventsService::Initialize."); return hr; } ComPtr loggingChannelFactory; hr = GetActivationFactory(HStringReference(RuntimeClass_Windows_Foundation_Diagnostics_LoggingChannel).Get(), &loggingChannelFactory); if (FAILED(hr)) { LOG_DEBUG("GetActivationFactory for ILoggingChannelFactory2 failed during EventsService::Initialize."); return hr; } xsapi_internal_wstringstream ss; ss << L"Microsoft.XboxLive.T" << AppConfig::Instance()->TitleId(); HString channelName; channelName.Set(ss.str().data()); hr = loggingChannelFactory->CreateWithOptionsAndId(channelName.Get(), loggingChannelOptions.Get(), GUID_LOGGING_CHANNEL, &m_loggingChannel); if (FAILED(hr)) { LOG_DEBUG("ILoggingChannelFactory2::CreateWithOptionsAndId failed during EventsService::Initialize."); return hr; } ComPtr loggingOptionsFactory; hr = GetActivationFactory(HStringReference(RuntimeClass_Windows_Foundation_Diagnostics_LoggingOptions).Get(), &loggingOptionsFactory); if (FAILED(hr)) { LOG_DEBUG("GetActivationFactory for ILoggingOptionsFactory failed during EventsService::Initialize."); return hr; } hr = loggingOptionsFactory->CreateWithKeywords(XBOX_LIVE_LOGGING_OPTIONS, &m_loggingOptions); if (FAILED(hr)) { LOG_DEBUG("ILoggingOptionsFactory::CreateWithKeywords failed during EventsService::Initialize."); return hr; } m_loggingOptions->put_Tags(XBOX_LIVE_LOGGING_TAGS); return S_OK; } HRESULT EventsService::WriteInGameEvent( _In_z_ const char* eventName, _In_opt_z_ const char* dimensions, _In_opt_z_ const char* measurements ) { JsonDocument dimensionsJson; JsonDocument measurementsJson; std::regex regex("[A-Za-z]+[A-Za-z0-9_]*"); bool matchFound = std::regex_match(eventName, regex); if (!matchFound) { LOG_DEBUG("Invalid event name"); return E_INVALIDARG; } if (dimensions) { dimensionsJson.Parse(dimensions); } if (measurements) { measurementsJson.Parse(measurements); } if(dimensionsJson.HasParseError() || measurementsJson.HasParseError()) { LOG_DEBUG("Unable to parse json string"); return E_INVALIDARG; } return WriteInGameEventHelper(eventName, dimensionsJson, measurementsJson); } HRESULT EventsService::WriteInGameEventHelper( _In_ const xsapi_internal_string& eventName, _In_ const JsonValue& dimensions, _In_ const JsonValue& measurements ) { try { auto fields = CreateLoggingFields(eventName, dimensions, measurements); //m_loggingChannel->loge ComPtr loggingTarget; m_loggingChannel->QueryInterface(__uuidof(ILoggingTarget), &loggingTarget); loggingTarget->LogEventWithFieldsAndOptions(utils::HStringFromUtf8(eventName.data()).Get(), fields.Get(), LoggingLevel_Critical, m_loggingOptions.Get()); } catch (const std::exception&) { return utils::convert_exception_to_hresult(); } catch (...) { return E_FAIL; } return S_OK; } ComPtr EventsService::CreateLoggingFields( _In_ const xsapi_internal_string& eventName, _In_ const JsonValue& dimensions, _In_ const JsonValue& measurements ) { THROW_CPP_INVALIDARGUMENT_IF_STRING_EMPTY(eventName); THROW_CPP_INVALIDARGUMENT_IF(!(dimensions.IsObject() || dimensions.IsNull())); THROW_CPP_INVALIDARGUMENT_IF(!(measurements.IsObject() || measurements.IsNull())); ComPtr fields; RoActivateInstance(HStringReference(RuntimeClass_Windows_Foundation_Diagnostics_LoggingFields).Get(), &fields); fields->BeginStruct(HStringReference(L"PartB_Microsoft.XboxLive.InGame").Get()); fields->AddString(HStringReference(L"name").Get(), utils::HStringFromUtf8(eventName.data()).Get()); fields->AddString(HStringReference(L"serviceConfigId").Get(), utils::HStringFromUtf8(AppConfig::Instance()->Scid().data()).Get()); fields->AddString(HStringReference(L"playerSessionId").Get(), utils::HStringFromUtf8(m_playSession.data()).Get()); fields->AddUInt32(HStringReference(L"titleId").Get(), AppConfig::Instance()->TitleId()); fields->AddString(HStringReference(L"userId").Get(), utils::HStringFromUtf8(utils::uint64_to_internal_string(m_user.Xuid()).data()).Get()); fields->AddUInt16(HStringReference(L"ver").Get(), 1); if (dimensions.IsObject()) { // Add properties fields->BeginStruct(HStringReference(L"properties").Get()); for (const auto& jsonPair : dimensions.GetObject()) { std::pair pair; pair.first = jsonPair.name.GetString(); JsonUtils::CopyFrom(pair.second, jsonPair.value); AddValuePair(fields, pair); } fields->EndStruct(); } if (measurements.IsObject()) { // Add measurements fields->BeginStruct(HStringReference(L"measurements").Get()); for (const auto& jsonPair : measurements.GetObject()) { std::pair pair; pair.first = jsonPair.name.GetString(); JsonUtils::CopyFrom(pair.second, jsonPair.value); AddValuePair(fields, pair); } fields->EndStruct(); } fields->EndStruct(); return fields; } void EventsService::AddValuePair( _Inout_ Microsoft::WRL::ComPtr fields, _In_ const std::pair& pair ) { // check property name. const auto& name = pair.first; THROW_CPP_INVALIDARGUMENT_IF_STRING_EMPTY(name); std::regex regex("[A-Za-z]+[A-Za-z0-9]*"); bool matchFound = std::regex_match(name, regex); if (!matchFound) { throw std::invalid_argument("Invalid properties or measurements name"); } auto s = utility::conversions::utf8_to_utf16(name.data()); HStringReference propertyName{ s.c_str() }; const auto& value = pair.second; switch (value.GetType()) { case rapidjson::Type::kNumberType: // if value can fit into int64, add as int64 if (value.IsInt64()) { fields->AddInt64(propertyName.Get(), value.GetInt64()); } else if (value.IsUint64()) { fields->AddUInt64(propertyName.Get(), value.GetUint64()); } else { fields->AddDouble(propertyName.Get(), value.GetDouble()); } break; case rapidjson::Type::kTrueType: case rapidjson::Type::kFalseType: fields->AddBoolean(propertyName.Get(), value.GetBool()); break; case rapidjson::Type::kNullType: fields->AddEmpty(propertyName.Get()); break; case rapidjson::Type::kStringType: fields->AddString(propertyName.Get(), HStringReference(utils::string_t_from_internal_string(value.GetString()).data()).Get()); break; default: throw std::invalid_argument("Logging property type not supported"); } } NAMESPACE_MICROSOFT_XBOX_SERVICES_EVENTS_CPP_END #endif ================================================ FILE: Source/Services/Events/events_service_etw.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #ifdef XSAPI_WRL_EVENTS_SERVICE #include #include #include "events_service.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_EVENTS_CPP_BEGIN class EventsService : public IEventsService, public std::enable_shared_from_this { public: EventsService(_In_ User&& user); ~EventsService(); HRESULT Initialize(); HRESULT WriteInGameEvent( _In_z_ const char* eventName, _In_opt_z_ const char* dimensions, _In_opt_z_ const char* measurements ); private: HRESULT WriteInGameEventHelper( _In_ const xsapi_internal_string& eventName, _In_ const JsonValue& dimensions, _In_ const JsonValue& measurement ); CO_MTA_USAGE_COOKIE m_mtaUsageCookie = nullptr; #if HC_PLATFORM != HC_PLATFORM_UWP HRESULT m_hrCoIncrementMTAUsage = E_FAIL; #endif User m_user; xsapi_internal_string m_playSession; xsapi_internal_string m_scid; Microsoft::WRL::ComPtr m_loggingChannel; Microsoft::WRL::ComPtr m_loggingOptions; void AddValuePair( _Inout_ Microsoft::WRL::ComPtr fields, _In_ const std::pair& pair ); Microsoft::WRL::ComPtr CreateLoggingFields( _In_ const xsapi_internal_string& eventName, _In_ const JsonValue& properties, _In_ const JsonValue& measurement ); }; NAMESPACE_MICROSOFT_XBOX_SERVICES_EVENTS_CPP_END #endif ================================================ FILE: Source/Services/Events/events_service_gdk.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "events_service_gdk.h" #if XSAPI_BUILD_WITH_1910_GRTS #include "XGameEvent.h" #include "XGameRuntimeFeature.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_EVENTS_CPP_BEGIN EventsService::EventsService( User&& user ) : m_user{ std::move(user) }, m_playSession{ utils::create_guid(true) } { } EventsService::~EventsService() { } HRESULT EventsService::Initialize() { return S_OK; } HRESULT EventsService::WriteInGameEvent( _In_z_ const char* eventName, _In_opt_z_ const char* dimensions, _In_opt_z_ const char* measurements ) { if (XGameRuntimeIsFeatureAvailable(XGameRuntimeFeature::XGameEvent)) { const char* scid{ nullptr }; HRESULT hr = XblGetScid(&scid); if (FAILED(hr)) { return hr; } String lowercaseScid = utils::ToLower(scid); return XGameEventWrite( m_user.Handle(), lowercaseScid.c_str(), m_playSession.c_str(), eventName, dimensions, measurements); } else { return E_NOTIMPL; } } NAMESPACE_MICROSOFT_XBOX_SERVICES_EVENTS_CPP_END #endif ================================================ FILE: Source/Services/Events/events_service_gdk.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "events_service.h" #if XSAPI_BUILD_WITH_1910_GRTS NAMESPACE_MICROSOFT_XBOX_SERVICES_EVENTS_CPP_BEGIN class EventsService : public IEventsService, public std::enable_shared_from_this { public: EventsService(_In_ User&& user); ~EventsService(); HRESULT Initialize(); HRESULT WriteInGameEvent( _In_z_ const char* eventName, _In_opt_z_ const char* dimensions, _In_opt_z_ const char* measurements ); private: User m_user; xsapi_internal_string m_playSession; }; NAMESPACE_MICROSOFT_XBOX_SERVICES_EVENTS_CPP_END #endif XSAPI_BUILD_WITH_1910_GRTS ================================================ FILE: Source/Services/Events/events_service_xsapi.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "xsapi-c/events_c.h" #include "events_service_xsapi.h" #include "xbox_live_context_internal.h" #include using namespace xbox::services; NAMESPACE_MICROSOFT_XBOX_SERVICES_EVENTS_CPP_BEGIN EventsService::EventsService( _In_ User user, _In_ const TaskQueue& queue ) : m_user{ std::move(user) }, m_queue{ queue.DeriveWorkerQueue() } { InitializeTenantSettings(); } EventsService::~EventsService() { assert(m_eventQueue); m_eventQueue->Cleanup(); } HRESULT EventsService::Initialize() { auto copyUserResult = m_user.Copy(); RETURN_HR_IF_FAILED(copyUserResult.Hresult()); m_eventQueue = MakeShared(copyUserResult.ExtractPayload(), m_tenantSettings); m_eventQueue->Initialize(); return ScheduleUpload(); } HRESULT EventsService::WriteInGameEvent( _In_z_ const char* eventName, _In_opt_z_ const char* dimensions, _In_opt_z_ const char* measurements ) { JsonDocument dimensionsJson; JsonDocument measurementsJson; std::regex regex("[A-Za-z]+[A-Za-z0-9_]*"); bool matchFound = std::regex_match(eventName, regex); if (!matchFound) { LOG_DEBUG("Invalid event name"); return E_INVALIDARG; } if (dimensions) { dimensionsJson.Parse(dimensions); } if (measurements) { measurementsJson.Parse(measurements); } if(dimensionsJson.HasParseError() || measurementsJson.HasParseError()) { LOG_DEBUG("Unable to parse json string"); return E_INVALIDARG; } return WriteInGameEventHelper(eventName, dimensionsJson, measurementsJson); } HRESULT EventsService::WriteInGameEventHelper( _In_ const xsapi_internal_string& eventName, _In_ const JsonValue& dimensions, _In_ const JsonValue& measurements ) { return m_eventQueue->AddEvent(Event{ m_user.Xuid(), eventName, dimensions, measurements }); } const std::string& EventsService::IKey() { static std::string iKey; if (iKey.empty()) { std::stringstream defaultIKey; defaultIKey << "P-XBL-T" << AppConfig::Instance()->TitleId(); iKey = defaultIKey.str(); } return iKey; } HRESULT EventsService::ScheduleUpload() noexcept { auto hr = m_queue.RunWork([ weakThis = std::weak_ptr{ shared_from_this() } ] { auto state{ GlobalState::Get() }; auto sharedThis{ weakThis.lock() }; // Don't schedule another upload if XblCleanup has been called. Otherwise, // ensure lifetime of EventsService & GlobalState until the upload is complete // to make sure no client data is lost. if (sharedThis && state) { sharedThis->UploadAsync(AsyncContext<>{ [ sharedThis, state ] { // Schedule the next upload sharedThis->ScheduleUpload(); } }); } }, __min(std::pow(2, m_numRetryAttempts), 600) * m_minimumUploadIntervalInMs ); if (FAILED(hr)) { // Not much we can do if RunWork fails so just log the error and return LOGS_ERROR << __FUNCTION__ << " failed with HRESULT " << hr; } return hr; } void EventsService::UploadAsync( AsyncContext<> async ) noexcept { // If more than m_maximumUploadIntervalInMs has passed since the last upload, begin an upload // with whatever events are pending. Otherwise, wait until we have at least m_payloadMinimumEventCount // events and return immediately. uint64_t timeSinceLastUpload = std::chrono::duration_cast (chrono_clock_t::now() - m_lastUploadAttempt).count(); size_t payloadMinumumEventCount = timeSinceLastUpload > m_maximumUploadIntervalInMs ? 1 : m_payloadMinimumEventCount; std::shared_ptr payload = m_eventQueue->GetNextPayload(payloadMinumumEventCount); if (!payload) { return async.Complete(); } HRESULT hr = UploadEventPayload(payload, AsyncContext{ TaskQueue(), [ sharedThis{ shared_from_this() }, payload, async ] (HRESULT hr) { // Super naive implementation for now - on every failed upload switch to offline mode, on every successful upload // switch to normal mode. This will incur a lot of disk I/O so this needs to be tuned long term. switch (hr) { //Intentional fallthrough here for certain failure http statuses. //Failures that shouldn't be retried are functionally the same as successes, //in that we want to reset the backoff process and not requeue the payload. case HTTP_E_STATUS_BAD_REQUEST: //Retrying a 400 likely won't resolve the issue. Drop the request. //TODO: [natiskan] Write telemetry to keep track of failed 400s. case S_OK: sharedThis->m_numRetryAttempts = 0; sharedThis->m_eventQueue->SetMode(Mode::Normal); break; default: sharedThis->OnFailedPayload(payload); sharedThis->m_eventQueue->SetMode(Mode::Offline); break; } async.Complete(); } }); if (FAILED(hr)) { OnFailedPayload(payload); return async.Complete(); } } HRESULT EventsService::UploadEventPayload( std::shared_ptr payload, AsyncContext async ) { m_lastUploadAttempt = chrono_clock_t::now(); return payload->GetRequestData({ async.Queue(), [ weakThis = std::weak_ptr{ shared_from_this() }, async ] (Result result) { auto sharedThis{ weakThis.lock() }; if (sharedThis && Succeeded(result)) { Result userResult = sharedThis->m_user.Copy(); if (FAILED(userResult.Hresult())) { return async.Complete(userResult.Hresult()); } auto httpCall = MakeShared(userResult.ExtractPayload()); auto hr = httpCall->Init(MakeShared(), "POST", sharedThis->m_eventUploadUrl, xbox_live_api::events_upload); if (FAILED(hr)) { return async.Complete(hr); } // Don't allow retries. We want to fail as fast as possible and the payload will // be retried by the EventsService later anyways. httpCall->SetRetryAllowed(false); //Explicitly allow retry of 401s with an updated token, as this can only retry once at most httpCall->SetAuthRetryAllowed(true); httpCall->SetTimeout(sharedThis->m_uploadTimeoutInSeconds); for (auto& header : result.Payload().headers) { httpCall->SetHeader(header.first, header.second); } httpCall->SetRequestBody(result.Payload().requestBody); httpCall->Perform(AsyncContext{ async.Queue().GetHandle(), [ async ] (HttpResult result) { HRESULT hr = result.Hresult(); if (SUCCEEDED(hr)) { hr = result.Payload()->Result(); if (FAILED(hr)) { HC_TRACE_INFORMATION(XSAPI, "Event upload failed with HTTP status %u", result.Payload()->HttpStatus()); } } async.Complete(hr); } }); } } }); } void xbox::services::events::EventsService::OnFailedPayload(std::shared_ptr failedPayload) { m_eventQueue->RequeueFailedPayload(failedPayload); m_numRetryAttempts++; } NAMESPACE_MICROSOFT_XBOX_SERVICES_EVENTS_CPP_END ================================================ FILE: Source/Services/Events/events_service_xsapi.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include #include #include "events_service.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_EVENTS_CPP_BEGIN class Event { public: Event( _In_ uint64_t xuid, _In_ const xsapi_internal_string& eventName, _In_ const JsonValue& dimensions, _In_ const JsonValue& measurements, _In_ xbox::services::datetime timestamp = xbox::services::datetime::utc_now() ); Event(const Event& other); static Result Deserialize( _In_ const xsapi_internal_string& inputData ); const std::string& Data() const; const std::string& FullEventName() const; const xbox::services::datetime& Timestamp() const; xsapi_internal_string Serialize() const; private: Event() = default; std::string SerializeFieldValue( _In_ const JsonValue& value ); uint64_t m_xuid{ 0 }; xsapi_internal_string m_eventName; std::string m_fullEventName; JsonDocument m_dimensions; JsonDocument m_measurements; xbox::services::datetime m_timestamp; std::string m_data; }; class EventUploadPayload : public std::enable_shared_from_this { public: EventUploadPayload( _In_ User&& user, _In_ std::shared_ptr tenantSettings ); HRESULT AddEvent(_In_ const Event& event); size_t EventCount() const; struct RequestData { xsapi_internal_http_headers headers; xsapi_internal_string requestBody; }; HRESULT GetRequestData(_In_ AsyncContext> async); size_t ExtractEventsAndSerialize( _Inout_ xsapi_internal_vector& serializedData, _In_ size_t targetDataSize, _Inout_ xbox::services::datetime& timestamp ); private: HRESULT CreateTicketData( _In_ AsyncContext>> async ); User m_user; std::shared_ptr m_tenantSettings; cll::CllUploadRequestData m_cllRequestData; RequestData m_requestData; xsapi_internal_list m_events; }; enum class Mode { Normal, Offline }; // Class to manage pending events class EventQueue : public std::enable_shared_from_this { public: EventQueue( User&& user, std::shared_ptr tenantSettings ); void Initialize(); void Cleanup(); HRESULT AddEvent(Event&& event); HRESULT AddEvents(xsapi_internal_vector&& events); std::shared_ptr GetNextPayload(size_t minimumEventCount = 1); void SetMode(Mode mode); void RequeueFailedPayload(std::shared_ptr failedPayload); static HRESULT SetMaxFileSize(uint64_t fileSizeInBytes); static HRESULT SetStorageAllotment(uint64_t storageAllotmentInBytes); private: HRESULT WriteDirectoryFile(); HRESULT Populate(); HRESULT Flush(); std::mutex m_mutex; std::atomic m_mode{ Mode::Normal }; User m_user; std::shared_ptr m_tenantSettings; xsapi_internal_string const m_filenamePrefix{ "XblEvents" }; xsapi_internal_string m_directoryFilename; xsapi_internal_list> m_queue; std::shared_ptr m_failedPayload; // flush metadata Map m_fileMetadata; uint64_t m_totalFilesSize{ 0 }; bool m_flushInProgress{ false }; std::shared_ptr m_localStorage; static uint64_t m_maxFileSize; static uint64_t m_storageAllotment; }; class EventsService : public IEventsService, public std::enable_shared_from_this { public: EventsService( _In_ User user, _In_ const TaskQueue& queue ); ~EventsService(); HRESULT Initialize(); HRESULT WriteInGameEvent( _In_z_ const char* eventName, _In_opt_z_ const char* dimensions, _In_opt_z_ const char* measurements ); static const std::string& IKey(); private: HRESULT WriteInGameEventHelper( _In_ const xsapi_internal_string& eventName, _In_ const JsonValue& dimensions, _In_ const JsonValue& measurement ); User m_user; TaskQueue m_queue; void InitializeTenantSettings(); HRESULT ScheduleUpload() noexcept; void UploadAsync(AsyncContext<> async) noexcept; HRESULT UploadEventPayload( std::shared_ptr payload, AsyncContext async ); void OnFailedPayload(std::shared_ptr failedPayload); uint64_t m_minimumUploadIntervalInMs{ 1000 }; uint64_t m_maximumUploadIntervalInMs{ 5000 }; size_t m_payloadMinimumEventCount{ 1 }; uint32_t m_numRetryAttempts{ 0 }; xsapi_internal_string m_eventUploadUrl{ "https://vortex.data.microsoft.com/collect/v1" }; uint32_t m_uploadTimeoutInSeconds{ 5 }; chrono_clock_t::time_point m_lastUploadAttempt{ chrono_clock_t::now() }; std::shared_ptr m_eventQueue; std::shared_ptr m_tenantSettings; }; NAMESPACE_MICROSOFT_XBOX_SERVICES_EVENTS_CPP_END ================================================ FILE: Source/Services/Events/iOS/events_service_ios.mm ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "events_service_xsapi.h" #include NAMESPACE_MICROSOFT_XBOX_SERVICES_EVENTS_CPP_BEGIN void EventsService::InitializeTenantSettings() { m_tenantSettings = MakeShared(cll::iOSPartA(IKey())); } NAMESPACE_MICROSOFT_XBOX_SERVICES_EVENTS_CPP_END ================================================ FILE: Source/Services/Leaderboard/leaderboard_column.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "shared_macros.h" #include "leaderboard_internal.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_LEADERBOARD_CPP_BEGIN LeaderboardColumn::LeaderboardColumn() { m_statName = xsapi_internal_string(); m_statType = legacy::leaderboard_stat_type::stat_other; } LeaderboardColumn::LeaderboardColumn( _In_ xsapi_internal_string statName, _In_ legacy::leaderboard_stat_type stat_type ) : m_statName(std::move(statName)), m_statType(std::move(stat_type)) { } const xsapi_internal_string& LeaderboardColumn::StatName() const { return m_statName; } legacy::leaderboard_stat_type LeaderboardColumn::StatType() const { return m_statType; } /* static */ xbox::services::Result LeaderboardColumn::Deserialize( _In_ const JsonValue& json ) { xsapi_internal_string statName; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "statName", statName, true)); xsapi_internal_string statTypeStr; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "type", statTypeStr, true)); legacy::leaderboard_stat_type statType = legacy::leaderboard_stat_type::stat_other; if (utils::str_icmp_internal(statTypeStr, "Integer") == 0) { statType = legacy::leaderboard_stat_type::stat_uint64; } else if (utils::str_icmp_internal(statTypeStr, "Double") == 0) { statType = legacy::leaderboard_stat_type::stat_double; } else if (utils::str_icmp_internal(statTypeStr, "String") == 0) { statType = legacy::leaderboard_stat_type::stat_string; } return LeaderboardColumn( std::move(statName), statType ); } size_t LeaderboardColumn::SizeOf() { size_t size = sizeof(XblLeaderboardColumn); size += m_statName.size() + 1; size = static_cast((size + XBL_ALIGN_SIZE - 1) / XBL_ALIGN_SIZE) * XBL_ALIGN_SIZE; return size; } char* LeaderboardColumn::Serialize(XblLeaderboardColumn* column, char* buffer) { utils::strcpy(buffer, m_statName.size() + 1, m_statName.c_str()); column->statName = static_cast(buffer); size_t s = m_statName.size() + 1; if ((s % XBL_ALIGN_SIZE) != 0) { s = static_cast((s + XBL_ALIGN_SIZE - 1) / XBL_ALIGN_SIZE) * XBL_ALIGN_SIZE; } buffer += s; XblLeaderboardStatType statType = XblLeaderboardStatType::Other; if (m_statType == legacy::leaderboard_stat_type::stat_boolean) statType = XblLeaderboardStatType::Boolean; else if (m_statType == legacy::leaderboard_stat_type::stat_double) statType = XblLeaderboardStatType::Double; else if (m_statType == legacy::leaderboard_stat_type::stat_string) statType = XblLeaderboardStatType::String; else if (m_statType == legacy::leaderboard_stat_type::stat_uint64) statType = XblLeaderboardStatType::Uint64; column->statType = statType; return buffer; } NAMESPACE_MICROSOFT_XBOX_SERVICES_LEADERBOARD_CPP_END ================================================ FILE: Source/Services/Leaderboard/leaderboard_internal.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "xsapi-c/leaderboard_c.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_LEADERBOARD_CPP_BEGIN class LeaderboardResult; namespace legacy { /// Enumerates the data type of a leaderboard statistic. enum class leaderboard_stat_type { /// Unsigned 64 bit integer. stat_uint64, /// Boolean. stat_boolean, /// Double. stat_double, /// String. stat_string, /// Unknown. stat_other }; } struct LeaderboardGlobalQuery { xsapi_internal_string scid; xsapi_internal_string name; xsapi_internal_string xuid; xsapi_internal_string socialGroup; xsapi_internal_vector columns; bool isTitleManaged{ false }; }; struct LeaderboardSocialQuery { xsapi_internal_string xuid; xsapi_internal_string scid; xsapi_internal_string statName; xsapi_internal_string socialGroup; xsapi_internal_string sortOrder; bool isTitleManaged{ false }; }; class LeaderboardColumn { public: LeaderboardColumn(); LeaderboardColumn( _In_ xsapi_internal_string statName, _In_ legacy::leaderboard_stat_type stat_type ); static Result Deserialize( _In_ const JsonValue& json ); size_t SizeOf(); char* Serialize(XblLeaderboardColumn* column, char* buffer); const xsapi_internal_string& StatName() const; legacy::leaderboard_stat_type StatType() const; private: xsapi_internal_string m_statName; legacy::leaderboard_stat_type m_statType; }; class LeaderboardRow { public: LeaderboardRow(); LeaderboardRow( _In_ xsapi_internal_string gamertag, _In_ xsapi_internal_string modernGamertag, _In_ xsapi_internal_string modernGamertagSuffix, _In_ xsapi_internal_string uniqueModernGamertag, _In_ uint64_t xboxUserId, _In_ double percentile, _In_ uint32_t rank, _In_ uint32_t globalRank, _In_ xsapi_internal_vector columnValues, _In_ xsapi_internal_string metadata ); LeaderboardRow(const LeaderboardRow& other); LeaderboardRow& operator =(const LeaderboardRow& other); static Result Deserialize( _In_ const JsonValue& json ); size_t SizeOf(); char* Serialize(XblLeaderboardRow* row, char* buffer); const xsapi_internal_string& Gamertag() const; const xsapi_internal_string& ModernGamertag() const; const xsapi_internal_string& ModernGamertagSuffix() const; const xsapi_internal_string& UniqueGamertagSuffix() const; uint64_t XboxUserId() const; double Percentile() const; uint32_t Rank() const; uint32_t GlobalRank() const; const xsapi_internal_vector& ColumnValues() const; private: xsapi_internal_string m_gamertag; xsapi_internal_string m_modernGamertag; xsapi_internal_string m_modernGamertagSuffix; xsapi_internal_string m_uniqueModernGamertag; uint64_t m_xuid{ 0 }; double m_percentile{ 0.0 }; uint32_t m_rank{ 0 }; uint32_t m_globalRank{ 0 }; xsapi_internal_vector m_columnValues; xsapi_internal_vector m_columnValuesC; JsonDocument m_metadata; friend LeaderboardResult; }; class LeaderboardResult { public: LeaderboardResult() = default; static Result Deserialize(_In_ const JsonValue& json); size_t SizeOfQuery(); char* SerializeQuery(XblLeaderboardQuery* query, char* buffer); size_t SizeOf(); char* Serialize(char* buffer); XblSocialGroupType ParseSocialGroup(xsapi_internal_string socialGroupStr); uint32_t TotalRowCount() const; const xsapi_internal_vector& Columns() const; const xsapi_internal_vector& Rows() const; bool HasNext() const; void SetNextQuery(std::shared_ptr query); void SetNextQuery(std::shared_ptr query); void ParseAdditionalColumns(const xsapi_internal_vector& additionalColumnNames); private: LeaderboardResult( _In_ uint32_t totalRowCount, _In_ String continuationToken, _In_ Vector columns, _In_ Vector rows ); uint32_t m_totalRowCount{}; String m_continuationToken; Vector m_columns; Vector m_rows; std::shared_ptr m_globalQuery; xsapi_internal_vector m_additionalColumnleaderboardNamesC; std::shared_ptr m_socialQuery; }; class LeaderboardService : public std::enable_shared_from_this { public: LeaderboardService( _In_ User&& user, _In_ std::shared_ptr xboxLiveContextSettings, _In_ std::shared_ptr appConfig ); HRESULT GetLeaderboardForSocialGroup( _In_ const xsapi_internal_string& xuid, _In_ const xsapi_internal_string& scid, _In_ const xsapi_internal_string& statName, _In_ const xsapi_internal_string& socialGroup, _In_ uint32_t skipToRank, _In_ const xsapi_internal_string& skipToXuid, _In_ const xsapi_internal_string& sortOrder, _In_ uint32_t maxItems, _In_ const xsapi_internal_string& continuationToken, _In_ bool isTitleManaged, _In_ XAsyncBlock* async ); HRESULT GetLeaderboard( _In_ const xsapi_internal_string& scid, _In_ const xsapi_internal_string& name, _In_ uint32_t skipToRank, _In_ const xsapi_internal_string& skipToXuid, _In_ const xsapi_internal_string& xuid, _In_ const xsapi_internal_string& socialGroup, _In_ uint32_t maxItems, _In_ const xsapi_internal_string& continuationToken, _In_ const xsapi_internal_vector& additionalColumnNames, _In_ bool isTitleManaged, _In_ XAsyncBlock* async ); private: User m_user; std::shared_ptr m_xboxLiveContextSettings; std::shared_ptr m_appConfig; friend LeaderboardResult; }; NAMESPACE_MICROSOFT_XBOX_SERVICES_LEADERBOARD_CPP_END ================================================ FILE: Source/Services/Leaderboard/leaderboard_result.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "leaderboard_internal.h" #include "social_internal.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_LEADERBOARD_CPP_BEGIN LeaderboardResult::LeaderboardResult( _In_ uint32_t totalRowCount, _In_ String continuationToken, _In_ Vector columns, _In_ Vector rows ) : m_totalRowCount{ totalRowCount }, m_continuationToken{ std::move(continuationToken) }, m_columns{ std::move(columns) }, m_rows{ std::move(rows) } { } uint32_t LeaderboardResult::TotalRowCount() const { return m_totalRowCount; } const xsapi_internal_vector& LeaderboardResult::Columns() const { return m_columns; } const xsapi_internal_vector& LeaderboardResult::Rows() const { return m_rows; } void LeaderboardResult::SetNextQuery(std::shared_ptr query) { m_globalQuery = std::move(query); } void LeaderboardResult::SetNextQuery(std::shared_ptr query) { m_socialQuery = std::move(query); } void LeaderboardResult::ParseAdditionalColumns(const xsapi_internal_vector& additionalColumnNames) { xsapi_internal_vector columns; if (m_columns.size() == 0) { return; } columns.push_back(m_columns[0]); std::unordered_map stats; for (auto& row : m_rows) { for (uint32_t i = 0; i < additionalColumnNames.size(); ++i) { const xsapi_internal_string& columnName = additionalColumnNames[i]; auto stat = stats.find(columnName); if (row.m_metadata.IsObject() && row.m_metadata.HasMember(columnName.data())) { const JsonValue& val = row.m_metadata[columnName.c_str()]; if (stat == stats.end() || stat->second == legacy::leaderboard_stat_type::stat_other) { if (val.IsBool()) { stats[columnName] = legacy::leaderboard_stat_type::stat_boolean; } else if (val.IsNumber()) { stats[columnName] = legacy::leaderboard_stat_type::stat_double; } else if (val.IsString()) { stats[columnName] = legacy::leaderboard_stat_type::stat_string; } else { stats[columnName] = legacy::leaderboard_stat_type::stat_other; } } auto columnValues = JsonUtils::SerializeJson(val); if (i >= row.m_columnValues.size() - 1) { row.m_columnValues.push_back(columnValues); } else { row.m_columnValues[i] = columnValues; } } } } for (const auto& columnName : additionalColumnNames) { columns.push_back(LeaderboardColumn(columnName, stats[columnName])); } m_columns = columns; } bool LeaderboardResult::HasNext() const { return !m_continuationToken.empty(); } Result LeaderboardResult::Deserialize(_In_ const JsonValue& json) { if (!json.IsObject()) { return WEB_E_INVALID_JSON_STRING; } // Paging info String continuationToken; if (json.HasMember("pagingInfo")) { const JsonValue& pagingInfo = json["pagingInfo"]; if (!pagingInfo.IsNull()) { RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(pagingInfo, "continuationToken", continuationToken, false)); } } else { return WEB_E_INVALID_JSON_STRING; } // Leaderboard metadata uint32_t totalCount{ 0 }; Vector columns; if (json.HasMember("leaderboardInfo")) { const auto& leaderboardInfoJson{ json["leaderboardInfo"] }; if (!leaderboardInfoJson.IsObject()) { return WEB_E_INVALID_JSON_STRING; } RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonInt(leaderboardInfoJson, "totalCount", totalCount)); if (leaderboardInfoJson.HasMember("columnDefinition")) { // This response schema is used by Global event based stat backed leaderboard queries auto columnResult = LeaderboardColumn::Deserialize(leaderboardInfoJson["columnDefinition"]); RETURN_HR_IF_FAILED(columnResult.Hresult()); columns.push_back(columnResult.ExtractPayload()); } else if (leaderboardInfoJson.HasMember("columns") && leaderboardInfoJson["columns"].IsArray()) { // These response schema is used by all other leaderboard queries for (auto& columnJson : leaderboardInfoJson["columns"].GetArray()) { auto columnResult = LeaderboardColumn::Deserialize(columnJson); RETURN_HR_IF_FAILED(columnResult.Hresult()); columns.push_back(columnResult.ExtractPayload()); } } else { return WEB_E_INVALID_JSON_STRING; } } else { return WEB_E_INVALID_JSON_STRING; } Vector rows; if (json.HasMember("userList") && json["userList"].IsArray()) { const auto& jsonRows = json["userList"].GetArray(); for (const auto& row : jsonRows) { auto rowResult = LeaderboardRow::Deserialize(row); RETURN_HR_IF_FAILED(rowResult.Hresult()); rows.push_back(rowResult.ExtractPayload()); } } else if (json.HasMember("leaderboard")) { } else { return WEB_E_INVALID_JSON_STRING; } return LeaderboardResult{ totalCount, continuationToken, std::move(columns), std::move(rows) }; } size_t LeaderboardResult::SizeOfQuery() { size_t size = m_continuationToken.size() + 1; if (m_globalQuery) { size += m_globalQuery->name.size() + 1; size += sizeof(char*) * m_globalQuery->columns.size(); for (auto column : m_globalQuery->columns) { size += column.size() + 1; } } else if (m_socialQuery) { size += m_socialQuery->statName.size() + 1; } return size; } XblSocialGroupType LeaderboardResult::ParseSocialGroup(xsapi_internal_string socialGroupStr) { XblSocialGroupType socialGroup = XblSocialGroupType::None; if (utils::str_icmp_internal(socialGroupStr, xbox::services::social::legacy::social_group_constants::people()) == 0) { socialGroup = XblSocialGroupType::People; } else if (utils::str_icmp_internal(socialGroupStr, xbox::services::social::legacy::social_group_constants::favorite()) == 0) { socialGroup = XblSocialGroupType::Favorites; } return socialGroup; } char* LeaderboardResult::SerializeQuery(XblLeaderboardQuery* query, char* buffer) { utils::strcpy(buffer, m_continuationToken.size() + 1, m_continuationToken.c_str()); query->continuationToken = static_cast(buffer); buffer += m_continuationToken.size() + 1; if (m_globalQuery) { utils::strcpy(query->scid, m_globalQuery->scid.size() + 1, m_globalQuery->scid.c_str()); query->socialGroup = ParseSocialGroup(m_globalQuery->socialGroup); query->xboxUserId = m_globalQuery->xuid.empty() ? 0 : utils::internal_string_to_uint64(m_globalQuery->xuid); utils::strcpy(buffer, m_globalQuery->name.size() + 1, m_globalQuery->name.c_str()); if (m_globalQuery->isTitleManaged) { query->statName = static_cast(buffer); } else { query->leaderboardName = static_cast(buffer); } buffer += m_globalQuery->name.size() + 1; m_additionalColumnleaderboardNamesC.resize(m_globalQuery->columns.size()); query->additionalColumnleaderboardNamesCount = m_globalQuery->columns.size(); query->additionalColumnleaderboardNames = reinterpret_cast(buffer); buffer += sizeof(char*) * m_globalQuery->columns.size(); for (size_t i = 0; i < m_globalQuery->columns.size(); i++) { m_additionalColumnleaderboardNamesC[i] = m_globalQuery->columns[i].c_str(); utils::strcpy(buffer, m_globalQuery->columns[i].size() + 1, m_additionalColumnleaderboardNamesC[i]); query->additionalColumnleaderboardNames[i] = static_cast(buffer); buffer += m_globalQuery->columns[i].size() + 1; } query->queryType = m_globalQuery->isTitleManaged ? XblLeaderboardQueryType::TitleManagedStatBackedGlobal : XblLeaderboardQueryType::UserStatBacked; } else if (m_socialQuery) { utils::strcpy(query->scid, m_socialQuery->scid.size() + 1, m_socialQuery->scid.c_str()); query->socialGroup = ParseSocialGroup(m_socialQuery->socialGroup); query->xboxUserId = m_socialQuery->xuid.empty() ? 0 : utils::internal_string_to_uint64(m_socialQuery->xuid); XblLeaderboardSortOrder sortOrder = XblLeaderboardSortOrder::Descending; if (utils::str_icmp_internal(m_socialQuery->sortOrder, "ascending") == 0) sortOrder = XblLeaderboardSortOrder::Ascending; query->order = sortOrder; utils::strcpy(buffer, m_socialQuery->statName.size() + 1, m_socialQuery->statName.c_str()); query->statName = static_cast(buffer); buffer += m_socialQuery->statName.size() + 1; query->queryType = m_socialQuery->isTitleManaged ? XblLeaderboardQueryType::TitleManagedStatBackedSocial : XblLeaderboardQueryType::UserStatBacked; } return buffer; } size_t LeaderboardResult::SizeOf() { size_t size = sizeof(XblLeaderboardResult); for (auto column : m_columns) { size += column.SizeOf(); } for (auto row : m_rows) { size += row.SizeOf(); } size += SizeOfQuery(); return size; } char* LeaderboardResult::Serialize(char* buffer) { XblLeaderboardResult* result = reinterpret_cast(buffer); buffer += sizeof(XblLeaderboardResult); result->hasNext = !m_continuationToken.empty(); result->totalRowCount = m_totalRowCount; result->columnsCount = m_columns.size(); result->columns = reinterpret_cast(buffer); buffer += sizeof(XblLeaderboardColumn) * m_columns.size(); for (size_t i = 0; i < m_columns.size(); i++) { buffer = m_columns[i].Serialize(&result->columns[i], buffer); } result->rowsCount = m_rows.size(); result->rows = reinterpret_cast(buffer); buffer += sizeof(XblLeaderboardRow) * m_rows.size(); for (size_t i = 0; i < m_rows.size(); i++) { buffer = m_rows[i].Serialize(&result->rows[i], buffer); } return SerializeQuery(&result->nextQuery, buffer); } NAMESPACE_MICROSOFT_XBOX_SERVICES_LEADERBOARD_CPP_END ================================================ FILE: Source/Services/Leaderboard/leaderboard_row.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "shared_macros.h" #include "leaderboard_internal.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_LEADERBOARD_CPP_BEGIN LeaderboardRow::LeaderboardRow() { } LeaderboardRow::LeaderboardRow( _In_ xsapi_internal_string gamertag, _In_ xsapi_internal_string modernGamertag, _In_ xsapi_internal_string modernGamertagSuffix, _In_ xsapi_internal_string uniqueModernGamertag, _In_ uint64_t xboxUserId, _In_ double percentile, _In_ uint32_t rank, _In_ uint32_t globalRank, _In_ xsapi_internal_vector columnValues, _In_ xsapi_internal_string metadata ) : m_gamertag{ std::move(gamertag) }, m_modernGamertag{ std::move(modernGamertag) }, m_modernGamertagSuffix{ std::move(modernGamertagSuffix) }, m_uniqueModernGamertag{ std::move(uniqueModernGamertag) }, m_xuid{ xboxUserId }, m_percentile{ percentile }, m_rank{ rank }, m_globalRank{ globalRank }, m_columnValues(std::move(columnValues)) { if(!metadata.empty()) { m_metadata.Parse(metadata.c_str()); } } LeaderboardRow::LeaderboardRow(const LeaderboardRow& other) { m_gamertag = other.m_gamertag; m_modernGamertag = other.m_modernGamertag; m_modernGamertagSuffix = other.m_modernGamertagSuffix; m_uniqueModernGamertag = other.m_uniqueModernGamertag; m_xuid = other.m_xuid; m_percentile = other.m_percentile; m_rank = other.m_rank; m_globalRank = other.m_globalRank; m_columnValues = other.m_columnValues; m_columnValuesC = other.m_columnValuesC; JsonUtils::CopyFrom(m_metadata, other.m_metadata); } LeaderboardRow& LeaderboardRow::operator =(const LeaderboardRow& other) { m_gamertag = other.m_gamertag; m_modernGamertag = other.m_modernGamertag; m_modernGamertagSuffix = other.m_modernGamertagSuffix; m_uniqueModernGamertag = other.m_uniqueModernGamertag; m_xuid = other.m_xuid; m_percentile = other.m_percentile; m_rank = other.m_rank; m_globalRank = other.m_globalRank; m_columnValues = other.m_columnValues; m_columnValuesC = other.m_columnValuesC; JsonUtils::CopyFrom(m_metadata, other.m_metadata); return *this; } const xsapi_internal_string& LeaderboardRow::Gamertag() const { return m_gamertag; } const xsapi_internal_string& LeaderboardRow::ModernGamertag() const { return m_modernGamertag; } const xsapi_internal_string& LeaderboardRow::ModernGamertagSuffix() const { return m_modernGamertagSuffix; } const xsapi_internal_string& LeaderboardRow::UniqueGamertagSuffix() const { return m_uniqueModernGamertag; } uint64_t LeaderboardRow::XboxUserId() const { return m_xuid; } double LeaderboardRow::Percentile() const { return m_percentile; } uint32_t LeaderboardRow::Rank() const { return m_rank; } uint32_t LeaderboardRow::GlobalRank() const { return m_globalRank; } const xsapi_internal_vector& LeaderboardRow::ColumnValues() const { return m_columnValues; } /* static */ Result LeaderboardRow::Deserialize( _In_ const JsonValue& json ) { xsapi_internal_string gamertag; xsapi_internal_string modernGamertag; xsapi_internal_string modernGamertagSuffix; xsapi_internal_string uniqueModernGamertag; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "gamertag", gamertag, true)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "moderngamertag", modernGamertag, true)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "moderngamertagsuffix", modernGamertagSuffix, true)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "uniquemoderngamertag", uniqueModernGamertag, true)); uint64_t xuid = 0; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonXuid(json, "xuid", xuid, true)); double percentile = 0.0; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonDouble(json, "percentile", percentile, true)); int rank = 0; int globalRank = 0; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonInt(json, "rank", rank, true)); if (json.IsObject() && json.HasMember("globalrank") && !json["globalrank"].IsNull()) { RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonInt(json, "globalrank", globalRank, false)); } xsapi_internal_vector values; if (json.IsObject() && json.HasMember("value") && !json["value"].IsNull()) { xsapi_internal_string value; JsonUtils::ExtractJsonString(json, "value", value, true); values.push_back(value); } else { RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonVector(JsonUtils::JsonStringExtractor, json, "values", values, true)); } xsapi_internal_string metadata; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "valuemetadata", metadata, false)); return LeaderboardRow( gamertag, modernGamertag, modernGamertagSuffix, uniqueModernGamertag, xuid, percentile, rank, globalRank, values, metadata ); } size_t LeaderboardRow::SizeOf() { // size must be rounded up to support word aligned data because of the arbitrary length string packing // we won't actually use the extra space, but this will report that it is how much is needed size_t size = sizeof(XblLeaderboardRow); size += sizeof(char*) * m_columnValues.size(); for (auto columnVal : m_columnValues) { size += columnVal.size() + 1; } size = static_cast((size + XBL_ALIGN_SIZE - 1) / XBL_ALIGN_SIZE) * XBL_ALIGN_SIZE; return size; } char* LeaderboardRow::Serialize(XblLeaderboardRow* row, char* buffer) { // the function needs to return the final position of the buffer; however // we have to ensure that the buffer is left word-aligned so that the next // data can be read on proper boundaries. row->percentile = m_percentile; row->rank = m_rank; row->globalRank = m_globalRank; row->xboxUserId = m_xuid; utils::strcpy(row->gamertag, sizeof(XblLeaderboardRow::gamertag), m_gamertag.c_str()); utils::strcpy(row->modernGamertag, sizeof(XblLeaderboardRow::modernGamertag), m_modernGamertag.data()); utils::strcpy(row->modernGamertagSuffix, sizeof(XblLeaderboardRow::modernGamertagSuffix), m_modernGamertagSuffix.data()); utils::strcpy(row->uniqueModernGamertag, sizeof(XblLeaderboardRow::uniqueModernGamertag), m_uniqueModernGamertag.data()); m_columnValuesC.resize(m_columnValues.size()); row->columnValuesCount = m_columnValues.size(); row->columnValues = reinterpret_cast(buffer); size_t s1 = sizeof(char*) * m_columnValues.size(); buffer += s1; size_t s2 = 0; for (size_t i = 0; i < m_columnValues.size(); i++) { m_columnValuesC[i] = m_columnValues[i].c_str(); utils::strcpy(buffer, m_columnValues[i].size() + 1, m_columnValuesC[i]); row->columnValues[i] = static_cast(buffer); s2 += m_columnValues[i].size() + 1; buffer += m_columnValues[i].size() + 1; } size_t s = s1 + s2; if ((s % XBL_ALIGN_SIZE) != 0) { // calculate how much padding is needed s = (static_cast((s + XBL_ALIGN_SIZE - 1) / XBL_ALIGN_SIZE) * XBL_ALIGN_SIZE) - s; buffer += s; } return buffer; } NAMESPACE_MICROSOFT_XBOX_SERVICES_LEADERBOARD_CPP_END ================================================ FILE: Source/Services/Leaderboard/leaderboard_service.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "xbox_live_context_internal.h" #include "leaderboard_internal.h" using namespace xbox::services; using namespace xbox::services::system; using namespace xbox::services::legacy; NAMESPACE_MICROSOFT_XBOX_SERVICES_LEADERBOARD_CPP_BEGIN // We need to use a different XBL contract versions depending on // 1) whether the leaderboard is backed event based stats vs. title managed stats // 2) whether it is a social leaderboard or a global leaderboard // Using set header manually instead of SetContractVersion since leaderboard service is using contract versions that are // not integer values. constexpr const char* g_titleMangedStatsGlobalLeaderboardVersion{ "4.1" }; constexpr const char* g_titleMangedStatsSocialLeaderboardVersion{ "2.1" }; constexpr const char* g_eventBasedStatsGlobalLeaderboardVersion{ "3.1" }; constexpr const char* g_eventBasedStatsSocialLeaderboardVersion{ "1.1" }; LeaderboardService::LeaderboardService( _In_ User&& user, _In_ std::shared_ptr xboxLiveContextSettings, _In_ std::shared_ptr appConfig ) : m_user{ std::move(user) }, m_xboxLiveContextSettings(std::move(xboxLiveContextSettings)), m_appConfig(std::move(appConfig)) { } xbl_result CreateLeaderboardUrl( _In_ const xsapi_internal_string& scid, _In_ const xsapi_internal_string& name, _In_ uint32_t skipToRank, _In_ const xsapi_internal_string& skipToXuid, _In_ uint32_t maxItems, _In_ const xsapi_internal_string& continuationToken, _In_ bool metadata, _In_ bool isTitleManaged, _In_ const xsapi_internal_string& xuid = xsapi_internal_string(), _In_ const xsapi_internal_string& socialGroup = xsapi_internal_string() ) { if (scid.empty()) return xbl_result(xbl_error_code::invalid_argument, "scid is required for getting leaderboards"); if (name.empty()) return xbl_result(xbl_error_code::invalid_argument, "name is required for getting leaderboards"); xbox::services::uri_builder builder; xsapi_internal_stringstream path; path << "/scids/"; path << xbox::services::uri::encode_uri(scid, xbox::services::uri::components::path); if (isTitleManaged) { path << "/leaderboards/stat("; path << xbox::services::uri::encode_uri(name, xbox::services::uri::components::path); path << ")"; } else { path << "/leaderboards/"; path << xbox::services::uri::encode_uri(name, xbox::services::uri::components::path); } builder.set_path(path.str()); if (metadata) { builder.append_query("include", "valuemetadata"); } if (!xuid.empty()) { builder.append_query("xuid", xuid); } if (maxItems > 0) { builder.append_query("maxItems", maxItems); } if (!skipToXuid.empty()) { if (skipToRank > 0) { return xbl_result(xbl_error_code::invalid_argument, "Cannot skip to XUID and rank"); } builder.append_query("skipToUser", skipToXuid); } else { if (!continuationToken.empty()) { builder.append_query("continuationToken", continuationToken); } else if (skipToRank > 0) { builder.append_query("skipToRank", skipToRank); } } if (!socialGroup.empty()) { builder.append_query("view", "People"); builder.append_query("viewTarget", socialGroup); } return xbl_result(builder.to_string()); } HRESULT LeaderboardService::GetLeaderboard( _In_ const xsapi_internal_string& scid, _In_ const xsapi_internal_string& name, _In_ uint32_t skipToRank, _In_ const xsapi_internal_string& skipToXuid, _In_ const xsapi_internal_string& xuid, _In_ const xsapi_internal_string& socialGroup, _In_ uint32_t maxItems, _In_ const xsapi_internal_string& continuationToken, _In_ const xsapi_internal_vector& additionalColumnNames, _In_ bool isTitleManaged, _In_ XAsyncBlock* async ) { RETURN_HR_INVALIDARGUMENT_IF_EMPTY_STRING(scid); RETURN_HR_INVALIDARGUMENT_IF_EMPTY_STRING(name); xbl_result url = CreateLeaderboardUrl( scid, name, skipToRank, skipToXuid, maxItems, continuationToken, additionalColumnNames.size() != 0, isTitleManaged, xuid, socialGroup ); if (url.err()) return utils::convert_xbox_live_error_code_to_hresult(url.err()); std::shared_ptr query = MakeShared(); query->scid = scid; query->name = name; query->xuid = xuid; query->socialGroup = socialGroup; query->columns = additionalColumnNames; query->isTitleManaged = isTitleManaged; return RunAsync(async, __FUNCTION__, [ result = LeaderboardResult{}, sharedThis{ shared_from_this() }, url, query, additionalColumnNames ] (_In_ XAsyncOp op, _In_ const XAsyncProviderData* data) mutable { if (op == XAsyncOp::DoWork) { Result userResult = sharedThis->m_user.Copy(); RETURN_HR_IF_FAILED(userResult.Hresult()); auto httpCall = MakeShared(userResult.ExtractPayload()); HRESULT hr = httpCall->Init( sharedThis->m_xboxLiveContextSettings, "GET", XblHttpCall::BuildUrl("leaderboards", url.payload()), xbox_live_api::get_leaderboard_internal ); RETURN_HR_IF_FAILED(hr); // Using set header directly instead of SetContractVersion since leaderboard service is using contract versions that are // not integer values. if (query->isTitleManaged) { RETURN_HR_IF_FAILED(httpCall->SetHeader(CONTRACT_VERSION_HEADER, g_titleMangedStatsGlobalLeaderboardVersion)); } else { RETURN_HR_IF_FAILED(httpCall->SetHeader(CONTRACT_VERSION_HEADER, g_eventBasedStatsGlobalLeaderboardVersion)); } hr = httpCall->Perform(AsyncContext{ TaskQueue::DeriveWorkerQueue(data->async->queue), [ &result, data, additionalColumnNames, query ] (HttpResult httpResult) { HRESULT hr = httpResult.Hresult(); if (SUCCEEDED(hr)) { hr = httpResult.Payload()->Result(); if (SUCCEEDED(hr)) { auto xblResult = LeaderboardResult::Deserialize( httpResult.Payload()->GetResponseBodyJson() ); result = xblResult.Payload(); result.SetNextQuery(query); if (additionalColumnNames.size() > 0) { result.ParseAdditionalColumns(additionalColumnNames); } hr = xblResult.Hresult(); } } XAsyncComplete(data->async, hr, result.SizeOf()); }}); return SUCCEEDED(hr) ? E_PENDING : hr; } else if (op == XAsyncOp::GetResult) { char* buffer = static_cast(data->buffer); ZeroMemory(buffer, data->bufferSize); buffer = result.Serialize(buffer); XSAPI_ASSERT(static_cast(buffer) == static_cast(static_cast(data->buffer) + result.SizeOf())); } return S_OK; }); } xbl_result CreateLeaderboardForSocialGroupUrl( _In_ const xsapi_internal_string& xuid, _In_ const xsapi_internal_string& scid, _In_ const xsapi_internal_string& statName, _In_ const xsapi_internal_string& socialGroup, _In_ uint32_t skipToRank, _In_ const xsapi_internal_string& skipToXuid, _In_ const xsapi_internal_string& sortOrder, _In_ uint32_t maxItems, _In_ const xsapi_internal_string& continuationToken ) { if (xuid.empty()) return xbl_result(xbl_error_code::invalid_argument, "xuid is required for getting leaderboard for social group"); if (scid.empty()) return xbl_result(xbl_error_code::invalid_argument, "scid is required for getting leaderboard for social group"); if (statName.empty()) return xbl_result(xbl_error_code::invalid_argument, "statName is required for getting leaderboard for social group"); if (socialGroup.empty()) return xbl_result(xbl_error_code::invalid_argument, "socialGroup is required for getting leaderboard for social group"); // "/users/xuid({xuid})/scids/{scid}/stats/{statname}/people/{group}" xbox::services::uri_builder builder; xsapi_internal_stringstream path; path << "/users/xuid("; path << xbox::services::uri::encode_uri(xuid, xbox::services::uri::components::path); path << ")/scids/"; path << xbox::services::uri::encode_uri(scid, xbox::services::uri::components::path); path << "/stats/"; path << xbox::services::uri::encode_uri(statName, xbox::services::uri::components::path); path << "/people/"; path << xbox::services::uri::encode_uri(socialGroup, xbox::services::uri::components::path); builder.set_path(path.str()); if (!sortOrder.empty()) { builder.append_query("sort", sortOrder); } if (maxItems > 0) { builder.append_query("maxItems", maxItems); } if (!skipToXuid.empty()) { builder.append_query("skipToUser", skipToXuid); } else { if (!continuationToken.empty()) { builder.append_query("continuationToken", continuationToken); } else if (skipToRank > 0) { builder.append_query("skipToRank", skipToRank); } } return xbl_result(builder.to_string()); } HRESULT LeaderboardService::GetLeaderboardForSocialGroup( _In_ const xsapi_internal_string& xuid, _In_ const xsapi_internal_string& scid, _In_ const xsapi_internal_string& statName, _In_ const xsapi_internal_string& socialGroup, _In_ uint32_t skipToRank, _In_ const xsapi_internal_string& skipToXuid, _In_ const xsapi_internal_string& sortOrder, _In_ uint32_t maxItems, _In_ const xsapi_internal_string& continuationToken, _In_ bool isTitleManaged, _In_ XAsyncBlock* async ) { RETURN_HR_INVALIDARGUMENT_IF_EMPTY_STRING(xuid); RETURN_HR_INVALIDARGUMENT_IF_EMPTY_STRING(scid); RETURN_HR_INVALIDARGUMENT_IF_EMPTY_STRING(statName); // To align with People moniker support, we are mapping "People" to "all" until the // leaderboard service can align with naming conventions. xsapi_internal_string group = socialGroup; if (utils::str_icmp_internal(socialGroup, "People") == 0) { group = "all"; } xbl_result url = CreateLeaderboardForSocialGroupUrl( xuid, scid, statName, group, skipToRank, skipToXuid, sortOrder, maxItems, continuationToken); if (url.err()) return utils::convert_xbox_live_error_code_to_hresult(url.err()); auto query = MakeShared(); query->xuid = xuid; query->scid = scid; query->statName = statName; query->socialGroup = group; query->sortOrder = sortOrder; query->isTitleManaged = isTitleManaged; return RunAsync(async, __FUNCTION__, [ result = LeaderboardResult{}, sharedThis{ shared_from_this() }, url, query ] (_In_ XAsyncOp op, _In_ const XAsyncProviderData* data) mutable { if (op == XAsyncOp::DoWork) { Result userResult = sharedThis->m_user.Copy(); RETURN_HR_IF_FAILED(userResult.Hresult()); auto httpCall = MakeShared(userResult.ExtractPayload()); HRESULT hr = httpCall->Init( sharedThis->m_xboxLiveContextSettings, "GET", XblHttpCall::BuildUrl("leaderboards", url.payload()), xbox_live_api::get_leaderboard_for_social_group_internal ); RETURN_HR_IF_FAILED(hr); if (query->isTitleManaged) { RETURN_HR_IF_FAILED(httpCall->SetHeader(CONTRACT_VERSION_HEADER, g_titleMangedStatsSocialLeaderboardVersion)); } else { RETURN_HR_IF_FAILED(httpCall->SetHeader(CONTRACT_VERSION_HEADER, g_eventBasedStatsSocialLeaderboardVersion)); } hr = httpCall->Perform(AsyncContext{ TaskQueue::DeriveWorkerQueue(data->async->queue), [ &result, data, query ] (HttpResult httpResult) { HRESULT hr = httpResult.Hresult(); if (SUCCEEDED(hr)) { hr = httpResult.Payload()->Result(); if (SUCCEEDED(hr)) { auto xblResult = LeaderboardResult::Deserialize( httpResult.Payload()->GetResponseBodyJson() ); result = xblResult.Payload(); result.SetNextQuery(query); hr = xblResult.Hresult(); } } XAsyncComplete(data->async, hr, result.SizeOf()); }}); return SUCCEEDED(hr) ? E_PENDING : hr; } else if (op == XAsyncOp::GetResult) { char* buffer = static_cast(data->buffer); ZeroMemory(buffer, data->bufferSize); buffer = result.Serialize(buffer); XSAPI_ASSERT(static_cast(buffer) == static_cast(static_cast(data->buffer) + data->bufferSize)); } return S_OK; }); } NAMESPACE_MICROSOFT_XBOX_SERVICES_LEADERBOARD_CPP_END STDAPI XblLeaderboardGetLeaderboardAsync( _In_ XblContextHandle xboxLiveContext, _In_ XblLeaderboardQuery leaderboardQuery, _In_ XAsyncBlock* async ) XBL_NOEXCEPT try { VERIFY_XBL_INITIALIZED(); RETURN_HR_INVALIDARGUMENT_IF_NULL(xboxLiveContext); if (leaderboardQuery.queryType != XblLeaderboardQueryType::UserStatBacked) { // For title managed stat backed leaderboards, the statistic name must be specified RETURN_HR_INVALIDARGUMENT_IF_NULL(leaderboardQuery.statName); } else { // For event stat backed leaderboards, the query must specify a leaderboard name OR a stat name RETURN_HR_INVALIDARGUMENT_IF(leaderboardQuery.leaderboardName == nullptr && leaderboardQuery.statName == nullptr); } xsapi_internal_string xuid = utils::uint64_to_internal_string(leaderboardQuery.xboxUserId); if (leaderboardQuery.xboxUserId == 0) xuid = ""; xsapi_internal_string skipToXuid = utils::uint64_to_internal_string(leaderboardQuery.skipToXboxUserId); if (leaderboardQuery.skipToXboxUserId == 0) skipToXuid = ""; xsapi_internal_string continuationToken(""); if (leaderboardQuery.continuationToken) continuationToken = leaderboardQuery.continuationToken; xsapi_internal_string socialGroup(""); if (leaderboardQuery.socialGroup == XblSocialGroupType::People) { socialGroup = xbox::services::social::legacy::social_group_constants::people(); } else if (leaderboardQuery.socialGroup == XblSocialGroupType::Favorites) { socialGroup = xbox::services::social::legacy::social_group_constants::favorite(); } if ((leaderboardQuery.queryType == XblLeaderboardQueryType::UserStatBacked && leaderboardQuery.leaderboardName) || leaderboardQuery.queryType == XblLeaderboardQueryType::TitleManagedStatBackedGlobal) { // Depending on the type of query, we either use the leaderboard name or the stat name String name{}; switch (leaderboardQuery.queryType) { case XblLeaderboardQueryType::UserStatBacked: { name = leaderboardQuery.leaderboardName; break; } case XblLeaderboardQueryType::TitleManagedStatBackedGlobal: { name = leaderboardQuery.statName; break; } default: break; } return xboxLiveContext->LeaderboardService()->GetLeaderboard( leaderboardQuery.scid, name, leaderboardQuery.skipResultToRank, skipToXuid, xuid, socialGroup, leaderboardQuery.maxItems, continuationToken, utils::string_array_to_internal_string_vector(leaderboardQuery.additionalColumnleaderboardNames, leaderboardQuery.additionalColumnleaderboardNamesCount), leaderboardQuery.queryType != XblLeaderboardQueryType::UserStatBacked, async ); } else { if (socialGroup.empty()) { socialGroup = xbox::services::social::legacy::social_group_constants::people(); } xsapi_internal_string statName(""); if (leaderboardQuery.statName) statName = leaderboardQuery.statName; xsapi_internal_string sortOrder(""); if (leaderboardQuery.order == XblLeaderboardSortOrder::Ascending) { sortOrder = "ascending"; } if (leaderboardQuery.order == XblLeaderboardSortOrder::Descending) { sortOrder = "descending"; } return xboxLiveContext->LeaderboardService()->GetLeaderboardForSocialGroup( xuid, leaderboardQuery.scid, statName, socialGroup, leaderboardQuery.skipResultToRank, skipToXuid, sortOrder, leaderboardQuery.maxItems, continuationToken, leaderboardQuery.queryType != XblLeaderboardQueryType::UserStatBacked, async ); } } CATCH_RETURN() STDAPI XblLeaderboardGetLeaderboardResultSize( _In_ XAsyncBlock* async, _Out_ size_t* resultSizeInBytes ) XBL_NOEXCEPT try { return XAsyncGetResultSize(async, resultSizeInBytes); } CATCH_RETURN() STDAPI XblLeaderboardGetLeaderboardResult( _In_ XAsyncBlock* async, _In_ size_t bufferSize, _Out_writes_bytes_to_(bufferSize, *bufferUsed) void* buffer, _Outptr_ XblLeaderboardResult** ptrToBuffer, _Out_opt_ size_t* bufferUsed ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(ptrToBuffer); auto hr = XAsyncGetResult(async, nullptr, bufferSize, buffer, bufferUsed); if (SUCCEEDED(hr)) { *ptrToBuffer = static_cast(buffer); } return hr; } CATCH_RETURN() STDAPI XblLeaderboardResultGetNextAsync( _In_ XblContextHandle xboxLiveContext, _In_ XblLeaderboardResult* leaderboardResult, _In_ uint32_t maxItems, _In_ XAsyncBlock* async ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(xboxLiveContext); RETURN_HR_IF(leaderboardResult->nextQuery.continuationToken == nullptr, E_BOUNDS); leaderboardResult->nextQuery.maxItems = maxItems; return XblLeaderboardGetLeaderboardAsync( xboxLiveContext, leaderboardResult->nextQuery, async ); } CATCH_RETURN() STDAPI XblLeaderboardResultGetNextResultSize( _In_ XAsyncBlock* async, _Out_ size_t* resultSizeInBytes ) XBL_NOEXCEPT try { return XAsyncGetResultSize(async, resultSizeInBytes); } CATCH_RETURN() STDAPI XblLeaderboardResultGetNextResult( _In_ XAsyncBlock* async, _In_ size_t bufferSize, _Out_writes_bytes_to_(bufferSize, *bufferUsed) void* buffer, _Outptr_ XblLeaderboardResult** ptrToBuffer, _Out_opt_ size_t* bufferUsed ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(ptrToBuffer); VERIFY_XBL_INITIALIZED(); auto hr = XAsyncGetResult(async, nullptr, bufferSize, buffer, bufferUsed); if (SUCCEEDED(hr)) { *ptrToBuffer = static_cast(buffer); } return hr; } CATCH_RETURN() ================================================ FILE: Source/Services/Matchmaking/hopper_statistics_response.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "matchmaking_internal.h" using namespace xbox::services::multiplayer; NAMESPACE_MICROSOFT_XBOX_SERVICES_MATCHMAKING_CPP_BEGIN HopperStatisticsResponse::HopperStatisticsResponse() {} HopperStatisticsResponse::HopperStatisticsResponse( _In_ xsapi_internal_string hopperName, _In_ std::chrono::seconds estimatedWaitTime, _In_ uint32_t playersWaitingToMatch ) : m_hopperName(hopperName), m_estimatedWaitTime(estimatedWaitTime), m_playersWaitingToMatch(playersWaitingToMatch) { } const xsapi_internal_string& HopperStatisticsResponse::HopperName() const { return m_hopperName; } const std::chrono::seconds& HopperStatisticsResponse::EstimatedWaitTime() const { return m_estimatedWaitTime; } uint32_t HopperStatisticsResponse::PlayersWaitingToMatch() const { return m_playersWaitingToMatch; } NAMESPACE_MICROSOFT_XBOX_SERVICES_MATCHMAKING_CPP_END ================================================ FILE: Source/Services/Matchmaking/match_ticket_details_response.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "matchmaking_internal.h" using namespace xbox::services::multiplayer; NAMESPACE_MICROSOFT_XBOX_SERVICES_MATCHMAKING_CPP_BEGIN const XblTicketStatus MatchTicketDetailsResponse::MatchStatus() const { return m_matchStatus; } const std::chrono::seconds& MatchTicketDetailsResponse::EstimatedWaitTime() const { return m_estimatedWaitTime; } const XblPreserveSessionMode MatchTicketDetailsResponse::PreserveSession() const { return m_preserveSession; } const XblMultiplayerSessionReference MatchTicketDetailsResponse::TicketSession() const { return m_ticketSession; } const XblMultiplayerSessionReference MatchTicketDetailsResponse::TargetSession() const { return m_targetSession; } const JsonValue& MatchTicketDetailsResponse::TicketAttributes() const { return m_ticketAttributes; } MatchTicketDetailsResponse::MatchTicketDetailsResponse() { } MatchTicketDetailsResponse::MatchTicketDetailsResponse( _In_ XblTicketStatus matchStatus, _In_ std::chrono::seconds estimatedWaitTime, _In_ XblPreserveSessionMode preserveSession, _In_ XblMultiplayerSessionReference ticketSession, _In_ XblMultiplayerSessionReference targetSession, _In_ const JsonValue& ticketAttributes ) : m_matchStatus(matchStatus), m_estimatedWaitTime(estimatedWaitTime), m_preserveSession(preserveSession), m_ticketSession(ticketSession), m_targetSession(targetSession) { JsonUtils::CopyFrom(m_ticketAttributes, ticketAttributes); } MatchTicketDetailsResponse::MatchTicketDetailsResponse(const MatchTicketDetailsResponse& other) : m_matchStatus(other.m_matchStatus), m_estimatedWaitTime(other.m_estimatedWaitTime), m_preserveSession(other.m_preserveSession), m_ticketSession(other.m_ticketSession), m_targetSession(other.m_targetSession) { JsonUtils::CopyFrom(m_ticketAttributes, other.m_ticketAttributes); } MatchTicketDetailsResponse& MatchTicketDetailsResponse::operator =(const MatchTicketDetailsResponse& other) { if (this != &other) { m_matchStatus = other.m_matchStatus; m_estimatedWaitTime = other.m_estimatedWaitTime; m_preserveSession = other.m_preserveSession; m_ticketSession = other.m_ticketSession; m_targetSession = other.m_targetSession; JsonUtils::CopyFrom(m_ticketAttributes, other.m_ticketAttributes); } return *this; } NAMESPACE_MICROSOFT_XBOX_SERVICES_MATCHMAKING_CPP_END ================================================ FILE: Source/Services/Matchmaking/matchmaking_internal.h ================================================ #pragma once #include "xsapi-c/matchmaking_c.h" #include "multiplayer_internal.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_MATCHMAKING_CPP_BEGIN class MatchmakingService : public std::enable_shared_from_this { public: MatchmakingService( _In_ User&& user, _In_ std::shared_ptr contextSettings ); HRESULT CreateMatchTicket( _In_ const XblMultiplayerSessionReference& ticketSessionReference, _In_ const xsapi_internal_string& matchmakingServiceConfigurationId, _In_ const xsapi_internal_string& hopperName, _In_ const std::chrono::seconds& ticketTimeout, _In_ XblPreserveSessionMode preserveSession, _In_ JsonValue& ticketAttributesJson, _In_ XAsyncBlock* async ); HRESULT DeleteMatchTicketAsync( _In_ const xsapi_internal_string& serviceConfigurationId, _In_ const xsapi_internal_string& hopperName, _In_ const xsapi_internal_string& ticketId, _In_ XAsyncBlock* async ); HRESULT GetMatchTicketDetailsAsync( _In_ const xsapi_internal_string& serviceConfigurationId, _In_ const xsapi_internal_string& hopperName, _In_ const xsapi_internal_string& ticketId, _In_ XAsyncBlock* async ); HRESULT GetHopperStatistics( _In_ const xsapi_internal_string& serviceConfigurationId, _In_ const xsapi_internal_string& hopperName, _In_ XAsyncBlock* async ); private: static xsapi_internal_string GetMatchmakingSubPath( _In_ const xsapi_internal_string& serviceConfigId, _In_ const xsapi_internal_string& hopperName, _In_ const xsapi_internal_string& ticketId ); static xsapi_internal_string GetHopperSubPath( _In_ const xsapi_internal_string& serviceConfigId, _In_ const xsapi_internal_string& hopperName ); static xsapi_internal_string ConvertPreserveSessionModeToString( _In_ XblPreserveSessionMode preserve_session ); User m_user; std::shared_ptr m_contextSettings; }; /// /// Represents a server response to a hopper statistics request. /// class HopperStatisticsResponse { public: HopperStatisticsResponse(); HopperStatisticsResponse( _In_ xsapi_internal_string hopperName, _In_ std::chrono::seconds estimatedWaitTime, _In_ uint32_t playersWaitingToMatch ); /// /// Name of the hopper in which a match was requested. /// const xsapi_internal_string& HopperName() const; /// /// Estimated wait time for a match request to be matched with other players. /// const std::chrono::seconds& EstimatedWaitTime() const; /// /// The number of players in the hopper waiting to be matched. /// uint32_t PlayersWaitingToMatch() const; private: /// /// Name of the hopper in which a match was requested. /// xsapi_internal_string m_hopperName; /// /// Estimated wait time for a match request to be matched with other players. /// std::chrono::seconds m_estimatedWaitTime{}; /// /// The number of players in the hopper waiting to be matched. /// uint32_t m_playersWaitingToMatch{ 0 }; }; /// /// Represents a server response to a request for match ticket details. /// class MatchTicketDetailsResponse { public: MatchTicketDetailsResponse(); MatchTicketDetailsResponse ( _In_ XblTicketStatus matchStatus, _In_ std::chrono::seconds estimatedWaitTime, _In_ XblPreserveSessionMode preserveSession, _In_ XblMultiplayerSessionReference ticketSession, _In_ XblMultiplayerSessionReference targetSession, _In_ const JsonValue& ticketAttributes ); MatchTicketDetailsResponse(const MatchTicketDetailsResponse& other); MatchTicketDetailsResponse& operator =(const MatchTicketDetailsResponse& other); /// /// Status of a match request. /// const XblTicketStatus MatchStatus() const; /// /// Estimated wait time for a match request to be matched with other players. /// const std::chrono::seconds& EstimatedWaitTime() const; /// /// An enum value to specify whether the match should preserve the session on which the match has been requested. /// const XblPreserveSessionMode PreserveSession() const; /// /// The session on which the match was requested. /// const XblMultiplayerSessionReference TicketSession() const; /// /// The session on which a match request has been found. /// const XblMultiplayerSessionReference TargetSession() const; /// /// The attributes of a match request. /// const JsonValue& TicketAttributes() const; static XblTicketStatus ConvertStringToTicketStatus(_In_ const xsapi_internal_string& value); static XblPreserveSessionMode ConvertStringToPreserveSessionMode(_In_ const xsapi_internal_string& value); private: /// /// Status of a match request. /// XblTicketStatus m_matchStatus{ XblTicketStatus::Unknown }; /// /// Estimated wait time for a match request to be matched with other players. /// std::chrono::seconds m_estimatedWaitTime{}; /// /// An enum value to specify whether the match should preserve the session on which the match has been requested. /// XblPreserveSessionMode m_preserveSession{ XblPreserveSessionMode::Unknown }; /// /// The session on which the match was requested. /// XblMultiplayerSessionReference m_ticketSession{}; /// /// The session on which a match request has been found. /// XblMultiplayerSessionReference m_targetSession{}; /// /// The attributes of a match request. /// JsonDocument m_ticketAttributes; }; NAMESPACE_MICROSOFT_XBOX_SERVICES_MATCHMAKING_CPP_END ================================================ FILE: Source/Services/Matchmaking/matchmaking_service.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "shared_macros.h" #include "matchmaking_internal.h" #include "xbox_live_app_config_internal.h" #include "xbox_live_context_internal.h" using namespace xbox::services; NAMESPACE_MICROSOFT_XBOX_SERVICES_MATCHMAKING_CPP_BEGIN MatchmakingService::MatchmakingService( _In_ User&& user, _In_ std::shared_ptr contextSettings ) : m_user{ std::move(user) }, m_contextSettings(contextSettings) { } /*static*/ XblTicketStatus MatchTicketDetailsResponse::ConvertStringToTicketStatus(_In_ const xsapi_internal_string& value) { XblTicketStatus ticketStatus = XblTicketStatus::Unknown; if (!value.empty()) { if (utils::str_icmp_internal(value, "expired") == 0) { ticketStatus = XblTicketStatus::Expired; } else if (utils::str_icmp_internal(value, "searching") == 0) { ticketStatus = XblTicketStatus::Searching; } else if (utils::str_icmp_internal(value, "found") == 0) { ticketStatus = XblTicketStatus::Found; } else if (utils::str_icmp_internal(value, "canceled") == 0) { ticketStatus = XblTicketStatus::Canceled; } } return ticketStatus; } /*static*/ XblPreserveSessionMode MatchTicketDetailsResponse::ConvertStringToPreserveSessionMode(_In_ const xsapi_internal_string& value) { XblPreserveSessionMode preserveSessionMode = XblPreserveSessionMode::Unknown; if (!value.empty()) { if (utils::str_icmp_internal(value, "always") == 0) { preserveSessionMode = XblPreserveSessionMode::Always; } else if (utils::str_icmp_internal(value, "never") == 0) { preserveSessionMode = XblPreserveSessionMode::Never; } } return preserveSessionMode; } xsapi_internal_string MatchmakingService::GetMatchmakingSubPath( _In_ const xsapi_internal_string& serviceConfigId, _In_ const xsapi_internal_string& hopperName, _In_ const xsapi_internal_string& ticketId ) { xsapi_internal_stringstream ss; ss << "/serviceconfigs/"; ss << serviceConfigId; ss << "/hoppers/"; ss << hopperName; if (!ticketId.empty()) { ss << "/tickets/"; ss << ticketId; } return ss.str(); } xsapi_internal_string MatchmakingService::GetHopperSubPath( _In_ const xsapi_internal_string& serviceConfigId, _In_ const xsapi_internal_string& hopperName ) { xsapi_internal_stringstream ss; ss << "/serviceconfigs/"; ss << serviceConfigId; ss << "/hoppers/"; ss << hopperName; ss << "/stats"; return ss.str(); } xsapi_internal_string MatchmakingService::ConvertPreserveSessionModeToString( _In_ XblPreserveSessionMode preserve_session ) { xsapi_internal_string retval("unknown"); if (preserve_session == XblPreserveSessionMode::Always) { retval = "always"; } else if (preserve_session == XblPreserveSessionMode::Never) { retval = "never"; } return retval; } /*static*/ HRESULT DeserializeHopperStatisticsResponseResult( _In_ const JsonValue& json, _Out_ HopperStatisticsResponse& result ) { HRESULT errc = S_OK; if (json.IsNull()) { result = HopperStatisticsResponse(); } else { xsapi_internal_string name; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "name", name, true)); int waitTime = 0; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonInt(json, "waitTime", waitTime)); int population = 0; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonInt(json, "population", population, false)); result = HopperStatisticsResponse( name, std::chrono::seconds(waitTime), population ); } if (FAILED(errc)) { return WEB_E_INVALID_JSON_STRING; } return S_OK; } /*static*/ HRESULT DeserializeCreateMatchTicketResponseResult( _In_ const JsonValue& json, _Out_ XblCreateMatchTicketResponse& result ) { HRESULT errc = S_OK; if (json.IsNull()) { result = XblCreateMatchTicketResponse(); memset(&result, 0, sizeof(XblCreateMatchTicketResponse)); } else { xsapi_internal_string ticketId; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "ticketId", ticketId, true)); memset(&result, 0, sizeof(XblCreateMatchTicketResponse)); utils::strcpy(result.matchTicketId, ticketId.size() + 1, ticketId.c_str()); int waitTime = 0; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonInt(json, "waitTime", waitTime)); result.estimatedWaitTime = waitTime; } if (FAILED(errc)) { return WEB_E_INVALID_JSON_STRING; } return S_OK; } /*static*/ HRESULT DeserializeMatchTicketDetailsResponse( _In_ const JsonValue& json, _Out_ MatchTicketDetailsResponse& result ) { HRESULT errc = S_OK; if (json.IsNull() || !json.IsObject()) { result = MatchTicketDetailsResponse(); } else { XblMultiplayerSessionReference ticketSession{}; XblMultiplayerSessionReference targetSession{}; if (json.HasMember("ticketSessionRef")) { ticketSession = xbox::services::multiplayer::Serializers::DeserializeSessionReference(json["ticketSessionRef"]).Payload(); } if (json.HasMember("targetSessionRef")) { targetSession = xbox::services::multiplayer::Serializers::DeserializeSessionReference(json["targetSessionRef"]).Payload(); } xsapi_internal_string ticketStatus, preserveSession; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "ticketStatus", ticketStatus, true)); int waitTime = 0; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonInt(json, "waitTime", waitTime)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "preserveSession", preserveSession, false)); if (json.HasMember("ticketAttributes")) { result = MatchTicketDetailsResponse( MatchTicketDetailsResponse::ConvertStringToTicketStatus(ticketStatus), std::chrono::seconds(waitTime), MatchTicketDetailsResponse::ConvertStringToPreserveSessionMode(preserveSession), ticketSession, targetSession, json["ticketAttributes"] ); } else { result = MatchTicketDetailsResponse( MatchTicketDetailsResponse::ConvertStringToTicketStatus(ticketStatus), std::chrono::seconds(waitTime), MatchTicketDetailsResponse::ConvertStringToPreserveSessionMode(preserveSession), ticketSession, targetSession, JsonValue() ); } } if (FAILED(errc)) { return WEB_E_INVALID_JSON_STRING; } return S_OK; } HRESULT MatchmakingService::CreateMatchTicket( _In_ const XblMultiplayerSessionReference& ticketSessionReference, _In_ const xsapi_internal_string& matchmakingServiceConfigurationId, _In_ const xsapi_internal_string& hopperName, _In_ const std::chrono::seconds& ticketTimeout, _In_ XblPreserveSessionMode preserveSession, _In_ JsonValue& ticketAttributesJson, _In_ XAsyncBlock* async ) { RETURN_HR_INVALIDARGUMENT_IF_NULL(async); XblCreateMatchTicketResponse result; xsapi_internal_string subPath = GetMatchmakingSubPath( matchmakingServiceConfigurationId, hopperName, xsapi_internal_string() ); JsonDocument request(rapidjson::kObjectType); JsonDocument::AllocatorType& allocator = request.GetAllocator(); request.AddMember("giveUpDuration", JsonValue(static_cast(ticketTimeout.count())).Move(), allocator); request.AddMember("preserveSession", JsonValue(ConvertPreserveSessionModeToString(preserveSession).c_str(), allocator).Move(), allocator); XblMultiplayerSessionReference _ticketSessionReference = XblMultiplayerSessionReferenceCreate( ticketSessionReference.Scid, ticketSessionReference.SessionTemplateName, ticketSessionReference.SessionName); JsonValue ticketSessonRefJsonObj(rapidjson::kObjectType); multiplayer::Serializers::SerializeSessionReference(_ticketSessionReference, ticketSessonRefJsonObj, allocator); request.AddMember("ticketSessionRef", ticketSessonRefJsonObj, allocator); if (!ticketAttributesJson.IsNull()) { request.AddMember("ticketAttributes", ticketAttributesJson, allocator); } xsapi_internal_string requestString = JsonUtils::SerializeJson(request); return RunAsync(async, __FUNCTION__, [ result, sharedThis{ shared_from_this() }, subPath, requestString ] (XAsyncOp op, const XAsyncProviderData* data) mutable { if (op == XAsyncOp::DoWork) { Result userResult = sharedThis->m_user.Copy(); RETURN_HR_IF_FAILED(userResult.Hresult()); auto httpCall = MakeShared(userResult.ExtractPayload()); HRESULT hr = httpCall->Init( sharedThis->m_contextSettings, "POST", XblHttpCall::BuildUrl("smartmatch", subPath), xbox_live_api::create_match_ticket ); RETURN_HR_IF_FAILED(hr); RETURN_HR_IF_FAILED(httpCall->SetRetryAllowed(false)); RETURN_HR_IF_FAILED(httpCall->SetXblServiceContractVersion(103)); RETURN_HR_IF_FAILED(httpCall->SetRequestBody(requestString)); hr = httpCall->Perform(AsyncContext{ TaskQueue::DeriveWorkerQueue(data->async->queue), [&result, data](HttpResult httpResult) { HRESULT hr = httpResult.Hresult(); if (SUCCEEDED(hr)) { hr = httpResult.Payload()->Result(); if (SUCCEEDED(hr)) { hr = DeserializeCreateMatchTicketResponseResult(httpResult.Payload()->GetResponseBodyJson(), result); } } XAsyncComplete(data->async, hr, sizeof(XblCreateMatchTicketResponse)); }}); return SUCCEEDED(hr) ? E_PENDING : hr; } else if (op == XAsyncOp::GetResult) { auto resultPtr = static_cast(data->buffer); *resultPtr = result; } return S_OK; }); } HRESULT MatchmakingService::DeleteMatchTicketAsync( _In_ const xsapi_internal_string& serviceConfigurationId, _In_ const xsapi_internal_string& hopperName, _In_ const xsapi_internal_string& ticketId, _In_ XAsyncBlock* async ) { RETURN_HR_INVALIDARGUMENT_IF_NULL(async); xsapi_internal_string subPath = GetMatchmakingSubPath( serviceConfigurationId, hopperName, ticketId ); return RunAsync(async, __FUNCTION__, [ sharedThis{ shared_from_this() }, subPath ] (XAsyncOp op, const XAsyncProviderData* data) mutable { if (op == XAsyncOp::DoWork) { Result userResult = sharedThis->m_user.Copy(); RETURN_HR_IF_FAILED(userResult.Hresult()); auto httpCall = MakeShared(userResult.ExtractPayload()); HRESULT hr = httpCall->Init( sharedThis->m_contextSettings, "DELETE", XblHttpCall::BuildUrl("smartmatch", subPath), xbox_live_api::delete_match_ticket ); RETURN_HR_IF_FAILED(hr); RETURN_HR_IF_FAILED(httpCall->SetXblServiceContractVersion(103)); hr = httpCall->Perform(AsyncContext{ TaskQueue::DeriveWorkerQueue(data->async->queue), [data](HttpResult httpResult) { HRESULT hr = httpResult.Hresult(); XAsyncComplete(data->async, hr, 0); }}); return SUCCEEDED(hr) ? E_PENDING : hr; } return S_OK; }); } HRESULT MatchmakingService::GetMatchTicketDetailsAsync( _In_ const xsapi_internal_string& serviceConfigurationId, _In_ const xsapi_internal_string& hopperName, _In_ const xsapi_internal_string& ticketId, _In_ XAsyncBlock* async ) { RETURN_HR_INVALIDARGUMENT_IF_NULL(async); xsapi_internal_string subPath = GetMatchmakingSubPath( serviceConfigurationId, hopperName, ticketId ); MatchTicketDetailsResponse result; return RunAsync(async, __FUNCTION__, [ result, subPath, sharedThis{ shared_from_this() } ] (XAsyncOp op, const XAsyncProviderData* data) mutable { if (op == XAsyncOp::DoWork) { Result userResult = sharedThis->m_user.Copy(); RETURN_HR_IF_FAILED(userResult.Hresult()); auto httpCall = MakeShared(userResult.ExtractPayload()); HRESULT hr = httpCall->Init( sharedThis->m_contextSettings, "GET", XblHttpCall::BuildUrl("smartmatch", subPath), xbox_live_api::get_match_ticket_details ); RETURN_HR_IF_FAILED(hr); RETURN_HR_IF_FAILED(httpCall->SetXblServiceContractVersion(103)); hr = httpCall->Perform(AsyncContext{ TaskQueue::DeriveWorkerQueue(data->async->queue), [data, &result](HttpResult httpResult) { HRESULT hr = httpResult.Hresult(); if (SUCCEEDED(hr)) { hr = httpResult.Payload()->Result(); if (SUCCEEDED(hr)) { hr = DeserializeMatchTicketDetailsResponse(httpResult.Payload()->GetResponseBodyJson(), result); } } size_t bufferSize = 0; if (!result.TicketAttributes().IsNull()) { bufferSize += JsonUtils::SerializeJson(result.TicketAttributes()).length() + 1; } bufferSize += sizeof(XblMatchTicketDetailsResponse); XAsyncComplete(data->async, hr, bufferSize); }}); return SUCCEEDED(hr) ? E_PENDING : hr; } else if (op == XAsyncOp::GetResult) { char* buffer = static_cast(data->buffer); ZeroMemory(buffer, data->bufferSize); XblMatchTicketDetailsResponse* matchTicketResult = reinterpret_cast(buffer); matchTicketResult->preserveSession = result.PreserveSession(); matchTicketResult->estimatedWaitTime = result.EstimatedWaitTime().count(); matchTicketResult->matchStatus = result.MatchStatus(); utils::strcpy(matchTicketResult->ticketSession.Scid, sizeof(result.TicketSession().Scid), result.TicketSession().Scid); utils::strcpy(matchTicketResult->ticketSession.SessionName, sizeof(result.TicketSession().SessionName), result.TicketSession().SessionName); utils::strcpy(matchTicketResult->ticketSession.SessionTemplateName, sizeof(result.TicketSession().SessionTemplateName), result.TicketSession().SessionTemplateName); utils::strcpy(matchTicketResult->targetSession.Scid, sizeof(result.TargetSession().Scid), result.TargetSession().Scid); utils::strcpy(matchTicketResult->targetSession.SessionName, sizeof(result.TargetSession().SessionName), result.TargetSession().SessionName); utils::strcpy(matchTicketResult->targetSession.SessionTemplateName, sizeof(result.TargetSession().SessionTemplateName), result.TargetSession().SessionTemplateName); if (!result.TicketAttributes().IsNull()) { buffer += sizeof(XblMatchTicketDetailsResponse); matchTicketResult->ticketAttributes = static_cast(buffer); utils::strcpy(buffer, JsonUtils::SerializeJson(result.TicketAttributes()).size() + 1, JsonUtils::SerializeJson(result.TicketAttributes()).c_str()); } else { matchTicketResult->ticketAttributes = nullptr; } } return S_OK; }); } HRESULT MatchmakingService::GetHopperStatistics( _In_ const xsapi_internal_string& serviceConfigurationId, _In_ const xsapi_internal_string& hopperName, _In_ XAsyncBlock* async ) { RETURN_HR_INVALIDARGUMENT_IF_NULL(async); xsapi_internal_string subPath = GetHopperSubPath( serviceConfigurationId, hopperName ); HopperStatisticsResponse result; return RunAsync(async, __FUNCTION__, [ result, subPath, sharedThis{ shared_from_this() } ] (XAsyncOp op, const XAsyncProviderData* data) mutable { if (op == XAsyncOp::DoWork) { Result userResult = sharedThis->m_user.Copy(); RETURN_HR_IF_FAILED(userResult.Hresult()); auto httpCall = MakeShared(userResult.ExtractPayload()); HRESULT hr = httpCall->Init( sharedThis->m_contextSettings, "GET", XblHttpCall::BuildUrl("smartmatch", subPath), xbox_live_api::get_hopper_statistics ); RETURN_HR_IF_FAILED(hr); RETURN_HR_IF_FAILED(httpCall->SetXblServiceContractVersion(103)); hr = httpCall->Perform(AsyncContext{ TaskQueue::DeriveWorkerQueue(data->async->queue), [data, &result](HttpResult httpResult) { HRESULT hr = httpResult.Hresult(); if (SUCCEEDED(hr)) { hr = httpResult.Payload()->Result(); if (SUCCEEDED(hr)) { hr = DeserializeHopperStatisticsResponseResult(httpResult.Payload()->GetResponseBodyJson(), result); } } XAsyncComplete(data->async, hr, sizeof(XblHopperStatisticsResponse) + result.HopperName().size() + 1); } }); return SUCCEEDED(hr) ? E_PENDING : hr; } else if (op == XAsyncOp::GetResult) { char* buffer = static_cast(data->buffer); ZeroMemory(buffer, data->bufferSize); auto resultPtr = reinterpret_cast(buffer); resultPtr->estimatedWaitTime = result.EstimatedWaitTime().count(); resultPtr->playersWaitingToMatch = result.PlayersWaitingToMatch(); auto hopperName = result.HopperName(); auto stringPtr = buffer + sizeof(XblHopperStatisticsResponse); utils::strcpy(stringPtr, hopperName.size() + 1, hopperName.c_str()); resultPtr->hopperName = stringPtr; } return S_OK; }); } NAMESPACE_MICROSOFT_XBOX_SERVICES_MATCHMAKING_CPP_END /// /// Sends a matchmaking request to the server and returns the match ticket with a ticket id. /// Call XblMatchmakingCreateMatchTicketResultSize and XblMatchmakingCreateMatchTicketResult upon completion to get the result /// /// Xbox live context for the local user. /// The multiplayer session to use for the match. /// The service configuration ID for the match. /// The name of the hopper. /// The maximum time to wait for players to join the session. /// Indicates if the session should be preserved. /// The ticket attributes for the session. (Optional) /// The AsyncBlock for this operation. /// The async object for notifying when the operation is completed. With the handler, a new match ticket /// object is returned. The match ticket object contains server returned information such as ticket id and wait /// time, and also contains copies of the title specified data from the ticket data object. /// Calls V103 POST /serviceconfigs/{serviceConfigId}/hoppers/{hopperName} STDAPI XblMatchmakingCreateMatchTicketAsync( _In_ XblContextHandle xboxLiveContext, _In_ XblMultiplayerSessionReference ticketSessionReference, _In_ const char* matchmakingServiceConfigurationId, _In_ const char* hopperName, _In_ const uint64_t ticketTimeout, _In_ XblPreserveSessionMode preserveSession, _In_ const char* ticketAttributesJson, _In_ XAsyncBlock* asyncBlock ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(xboxLiveContext); RETURN_HR_INVALIDARGUMENT_IF_NULL(matchmakingServiceConfigurationId); RETURN_HR_INVALIDARGUMENT_IF_NULL(asyncBlock); RETURN_HR_INVALIDARGUMENT_IF_NULL(hopperName); RETURN_HR_INVALIDARGUMENT_IF_NULL(ticketAttributesJson); VERIFY_XBL_INITIALIZED(); return xboxLiveContext->MatchmakingService()->CreateMatchTicket( ticketSessionReference, matchmakingServiceConfigurationId, hopperName, std::chrono::seconds(ticketTimeout), preserveSession, JsonDocument().Parse(ticketAttributesJson), asyncBlock); } CATCH_RETURN() /// /// Get the result for an XblMatchmakingCreateMatchTicketAsync call. /// /// The AsyncBlock for this operation. /// The size of the provided buffer. /// Byte buffer to write result into. /// Pointer to result. /// Number of bytes written to the buffer. STDAPI XblMatchmakingCreateMatchTicketResult( _In_ XAsyncBlock* asyncBlock, _Out_ XblCreateMatchTicketResponse* resultPtr ) XBL_NOEXCEPT try { auto hr = XAsyncGetResult(asyncBlock, nullptr, sizeof(XblCreateMatchTicketResponse), resultPtr, nullptr); return hr; } CATCH_RETURN() /// /// Deletes a the match ticket on the server. /// /// Xbox live context for the local user. /// The service config id that is specific for the title. /// The name of the hopper where the match ticket is located. /// The id of the ticket to delete on the server. /// The AsyncBlock for this operation. /// The async object for notifying when the operation has been completed. /// Calls V103 DELETE /serviceconfigs/{serviceConfigId}/hoppers/{hopperName}/tickets/{ticketId} STDAPI XblMatchmakingDeleteMatchTicketAsync( _In_ XblContextHandle xboxLiveContext, _In_ const char* serviceConfiguration, _In_ const char* hopperName, _In_ const char* ticketId, _In_ XAsyncBlock* asyncBlock ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(xboxLiveContext); RETURN_HR_INVALIDARGUMENT_IF_NULL(serviceConfiguration); RETURN_HR_INVALIDARGUMENT_IF_NULL(asyncBlock); RETURN_HR_INVALIDARGUMENT_IF_NULL(hopperName); RETURN_HR_INVALIDARGUMENT_IF_NULL(ticketId); VERIFY_XBL_INITIALIZED(); return xboxLiveContext->MatchmakingService()->DeleteMatchTicketAsync( serviceConfiguration, hopperName, ticketId, asyncBlock); } CATCH_RETURN() /// /// Retrieves the properties of a match ticket from the server. /// /// Xbox live context for the local user. /// The service config id that is specific for the title. /// The name of the hopper where the match ticket is located. /// The ticket id of the match ticket to retrieve. /// The AsyncBlock for this operation. /// The async object for notifying when the operation is completed. With the handler, the match /// ticket object with the data for the ticket, including ticket id and wait time information, is returned /// returned from the server. /// Calls V103 GET /serviceconfigs/{serviceConfigId}/hoppers/{hopperName}/tickets/{ticketId} STDAPI XblMatchmakingGetMatchTicketDetailsAsync( _In_ XblContextHandle xboxLiveContext, _In_ const char* serviceConfiguration, _In_ const char* hopperName, _In_ const char* ticketId, _In_ XAsyncBlock* asyncBlock ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(xboxLiveContext); RETURN_HR_INVALIDARGUMENT_IF_NULL(serviceConfiguration); RETURN_HR_INVALIDARGUMENT_IF_NULL(asyncBlock); RETURN_HR_INVALIDARGUMENT_IF_NULL(hopperName); RETURN_HR_INVALIDARGUMENT_IF_NULL(ticketId); VERIFY_XBL_INITIALIZED(); return xboxLiveContext->MatchmakingService()->GetMatchTicketDetailsAsync( serviceConfiguration, hopperName, ticketId, asyncBlock); } CATCH_RETURN() /// /// Get the result size for an XblMatchmakingGetMatchTicketDetailsAsync call. /// /// The AsyncBlock for this operation. /// The size in bytes required to store the Create Match Ticket result. STDAPI XblMatchmakingGetMatchTicketDetailsResultSize( _In_ XAsyncBlock* asyncBlock, _Out_ size_t* resultSizeInBytes ) XBL_NOEXCEPT try { return XAsyncGetResultSize(asyncBlock, resultSizeInBytes); } CATCH_RETURN() /// /// Get the result for an XblMatchmakingGetMatchTicketDetailsAsync call. /// /// The AsyncBlock for this operation. /// The size of the provided buffer. /// Byte buffer to write result into. /// Pointer to result. /// Number of bytes written to the buffer. STDAPI XblMatchmakingGetMatchTicketDetailsResult( _In_ XAsyncBlock* asyncBlock, _In_ size_t bufferSize, _Out_writes_bytes_to_(bufferSize, *bufferUsed) void* buffer, _Outptr_ XblMatchTicketDetailsResponse** ptrToBuffer, _Out_opt_ size_t* bufferUsed ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(ptrToBuffer); auto hr = XAsyncGetResult(asyncBlock, nullptr, bufferSize, buffer, bufferUsed); if (SUCCEEDED(hr)) { *ptrToBuffer = static_cast(buffer); } return hr; } CATCH_RETURN() /// /// Gets statistics about a hopper such as how many players are in it. /// /// Xbox live context for the local user. /// The service config id that is specific for the title. /// The name of the hopper to query stats for. /// The AsyncBlock for this operation. /// The async object for notifying when the operation is completed. With the handler, an object /// containing statistics about the hopper is returned. /// Calls V103 GET /serviceconfigs/{serviceConfigId}/hoppers/{hopperName}/stats STDAPI XblMatchmakingGetHopperStatisticsAsync( _In_ XblContextHandle xboxLiveContext, _In_ const char* serviceConfiguration, _In_ const char* hopperName, _In_ XAsyncBlock* asyncBlock ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(xboxLiveContext); RETURN_HR_INVALIDARGUMENT_IF_NULL(serviceConfiguration); RETURN_HR_INVALIDARGUMENT_IF_NULL(asyncBlock); RETURN_HR_INVALIDARGUMENT_IF_NULL(hopperName); VERIFY_XBL_INITIALIZED(); return xboxLiveContext->MatchmakingService()->GetHopperStatistics( serviceConfiguration, hopperName, asyncBlock ); } CATCH_RETURN() /// /// Get the result size for an XblMatchmakingGetHopperStatisticsAsync call. /// /// The AsyncBlock for this operation. /// The size in bytes required to store the Create Match Ticket result. STDAPI XblMatchmakingGetHopperStatisticsResultSize( _In_ XAsyncBlock* asyncBlock, _Out_ size_t* resultSizeInBytes ) XBL_NOEXCEPT try { return XAsyncGetResultSize(asyncBlock, resultSizeInBytes); } CATCH_RETURN() /// /// Get the result for an XblMatchmakingGetHopperStatisticsAsync call. /// /// The AsyncBlock for this operation. /// The size of the provided buffer. /// Byte buffer to write result into. /// Pointer to result. /// Number of bytes written to the buffer. STDAPI XblMatchmakingGetHopperStatisticsResult( _In_ XAsyncBlock* asyncBlock, _In_ size_t bufferSize, _Out_writes_bytes_to_(bufferSize, *bufferUsed) void* buffer, _Outptr_ XblHopperStatisticsResponse** ptrToBuffer, _Out_opt_ size_t* bufferUsed ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(ptrToBuffer); auto hr = XAsyncGetResult(asyncBlock, nullptr, bufferSize, buffer, bufferUsed); if (SUCCEEDED(hr)) { *ptrToBuffer = static_cast(buffer); } return hr; } CATCH_RETURN() ================================================ FILE: Source/Services/Multiplayer/Manager/multiplayer_client_manager.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #if !XSAPI_NO_PPL #include "pplx/pplxtasks.h" #endif #include "multiplayer_manager_internal.h" #include "multiplayer_internal.h" #include "xbox_live_app_config_internal.h" #if HC_PLATFORM != HC_PLATFORM_WIN32 && !XSAPI_NO_PPL #include "xsapi-cpp/title_callable_ui.h" #endif #if HC_PLATFORM == HC_PLATFORM_GDK #include "XGameUI.h" #endif using namespace xbox::services; using namespace xbox::services::legacy; using namespace xbox::services::multiplayer; #if __cplusplus_winrt using namespace Windows::Foundation; using namespace Windows::Foundation::Collections; #endif NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_MANAGER_CPP_BEGIN MultiplayerClientManager::MultiplayerClientManager(_In_ const MultiplayerClientManager& other) { std::lock_guard lock(other.m_clientRequestLock); m_sessionChangedContext = other.m_sessionChangedContext; m_subscriptionLostContext = other.m_subscriptionLostContext; m_rtaResyncContext = other.m_rtaResyncContext; m_primaryXboxLiveContext = other.m_primaryXboxLiveContext == nullptr ? nullptr : other.m_primaryXboxLiveContext; m_lastPendingRead = other.m_lastPendingRead == nullptr ? nullptr : other.m_lastPendingRead; m_latestPendingRead = other.m_latestPendingRead == nullptr ? nullptr : other.m_latestPendingRead; m_queue = other.m_queue; } MultiplayerClientManager::MultiplayerClientManager( _In_ const xsapi_internal_string& lobbySessionTemplateName, _In_ const TaskQueue& queue ) : m_subscriptionsLostFired(false), m_autoFillMembers(false), m_lobbySessionTemplateName(lobbySessionTemplateName), m_sessionChangedContext(0), m_subscriptionLostContext(0), m_rtaResyncContext(0), m_queue{ queue.DeriveWorkerQueue() } { m_multiplayerLocalUserManager = MakeShared(); } void MultiplayerClientManager::RegisterLocalUserManagerEvents() { std::weak_ptr weakSessionWriter = shared_from_this(); m_sessionChangedContext = m_multiplayerLocalUserManager->AddMultiplayerSessionChangedHandler([weakSessionWriter](_In_ XblMultiplayerSessionChangeEventArgs args) { std::shared_ptr pThis(weakSessionWriter.lock()); if (pThis != nullptr) { pThis->OnSessionChanged(args); } }); m_connectionIdChangedContext = m_multiplayerLocalUserManager->AddMultiplayerConnectionIdChangedHandler([weakSessionWriter](void) { std::shared_ptr pThis(weakSessionWriter.lock()); if (pThis != nullptr) { pThis->OnMultiplayerConnectionIdChanged(); } }); m_subscriptionLostContext = m_multiplayerLocalUserManager->AddMultiplayerSubscriptionLostHandler([weakSessionWriter](void) { std::shared_ptr pThis(weakSessionWriter.lock()); if (pThis != nullptr) { pThis->OnMultiplayerSubscriptionsLost(); } }); m_rtaResyncContext = m_multiplayerLocalUserManager->AddRtaResyncHandler([weakSessionWriter](void) { std::shared_ptr pThis(weakSessionWriter.lock()); if (pThis != nullptr) { pThis->OnResyncMessageReceived(); } }); } void MultiplayerClientManager::Initialize() { if (m_multiplayerLocalUserManager == nullptr) { m_multiplayerLocalUserManager = MakeShared(); RegisterLocalUserManagerEvents(); } m_latestPendingRead = MakeShared( m_queue, m_lobbySessionTemplateName, m_multiplayerLocalUserManager ); m_lastPendingRead = MakeShared(m_queue); m_subscriptionsLostFired.store(false); m_latestPendingRead->SetAutoFillMembersDuringMatchmaking(m_autoFillMembers); } void MultiplayerClientManager::Shutdown() { std::lock_guard guard(m_clientRequestLock); Destroy(); } void MultiplayerClientManager::Destroy() { m_latestPendingRead.reset(); m_lastPendingRead.reset(); if (m_multiplayerLocalUserManager != nullptr) { m_multiplayerLocalUserManager->RemoveMultiplayerSessionChangedHandler(m_sessionChangedContext); m_multiplayerLocalUserManager->RemoveMultiplayerSubscriptionLostHandler(m_subscriptionLostContext); m_multiplayerLocalUserManager->RemoveMultiplayerConnectionIdChangedHandler(m_connectionIdChangedContext); m_multiplayerLocalUserManager->RemoveRtaResyncHandler(m_rtaResyncContext); m_multiplayerLocalUserManager.reset(); } } std::shared_ptr MultiplayerClientManager::LocalUserManager() { return m_multiplayerLocalUserManager; } HRESULT MultiplayerClientManager::SetProperties( _In_ const XblMultiplayerSessionReference& sessionRef, _In_ const xsapi_internal_string& name, _In_ const JsonValue& valueJson, _In_opt_ context_t context ) { // Note: sessionRef can be empty for the lobby initially as we may have not created one yet. RETURN_HR_IF(name.empty(), E_INVALIDARG); std::lock_guard guard(m_clientRequestLock); auto latestPending = LatestPendingRead(); RETURN_HR_IF_LOG_DEBUG(latestPending == nullptr || GetXboxLiveContextMap().size() == 0, E_UNEXPECTED, "Call add_local_user() before writing lobby properties."); latestPending->SetProperties(sessionRef, name, valueJson, context); return S_OK; } HRESULT MultiplayerClientManager::SetJoinability( _In_ XblMultiplayerJoinability value, _In_opt_ context_t context ) { std::lock_guard guard(m_clientRequestLock); auto latestPending = LatestPendingRead(); RETURN_HR_IF_LOG_DEBUG(latestPending == nullptr || GetXboxLiveContextMap().size() == 0, E_UNEXPECTED, "Call add_local_user() before writing lobby properties."); return latestPending->LobbyClient()->SetJoinability(value, context); } HRESULT MultiplayerClientManager::SetSynchronizedHost( _In_ const XblMultiplayerSessionReference& sessionRef, _In_ const xsapi_internal_string& hostDeviceToken, _In_opt_ context_t context ) { // Note: sessionRef can be empty for the lobby initially as we may have not created one yet. RETURN_HR_IF(hostDeviceToken.empty(), E_INVALIDARG); std::lock_guard guard(m_clientRequestLock); auto latestPending = LatestPendingRead(); RETURN_HR_IF_LOG_DEBUG(latestPending == nullptr || GetXboxLiveContextMap().size() == 0, E_UNEXPECTED, "Call add_local_user() before writing host properties."); return latestPending->SetSynchronizedHost(sessionRef, hostDeviceToken, context); } HRESULT MultiplayerClientManager::SetSynchronizedProperties( _In_ const XblMultiplayerSessionReference& sessionRef, _In_ const xsapi_internal_string& name, _In_ const JsonValue& valueJson, _In_opt_ context_t context ) { // Note: sessionRef can be empty for the lobby initially as we may have not created one yet. RETURN_HR_IF(name.empty(), E_INVALIDARG); std::lock_guard guard(m_clientRequestLock); auto latestPending = LatestPendingRead(); RETURN_HR_IF_LOG_DEBUG(latestPending == nullptr || GetXboxLiveContextMap().size() == 0, E_UNEXPECTED, "Call add_local_user() before writing lobby properties."); return latestPending->SetSynchronizedProperties(sessionRef, name, valueJson, context); } void MultiplayerClientManager::SynchronizedWriteCompleted( _In_ std::error_code errorCode, _In_ XblMultiplayerEventType eventType, _In_ XblMultiplayerSessionType sessionType ) { AddToLatestPendingReadEventQueue(eventType, sessionType, nullptr, errorCode); } HRESULT MultiplayerClientManager::JoinLobbyByHandle( _In_ const xsapi_internal_string& handleId, _In_ const xsapi_internal_vector& users ) { RETURN_HR_INVALIDARGUMENT_IF(handleId.empty() || users.empty()); auto latestPending = LatestPendingRead(); if (latestPending == nullptr) { Initialize(); latestPending = LatestPendingRead(); } latestPending->LobbyClient()->AddLocalUsers(users, handleId); return S_OK; } #if HC_PLATFORM == HC_PLATFORM_UWP || HC_PLATFORM == HC_PLATFORM_XDK HRESULT MultiplayerClientManager::JoinLobby( _In_ Windows::ApplicationModel::Activation::IProtocolActivatedEventArgs^ eventArgs, _In_ xsapi_internal_vector users ) { RETURN_HR_INVALIDARGUMENT_IF(users.empty()); return JoinLobby(ref new Windows::Foundation::Uri(eventArgs->Uri->RawUri), users); } HRESULT MultiplayerClientManager::JoinLobby( _In_ Windows::Foundation::Uri^ url, _In_ xsapi_internal_vector users ) { xsapi_internal_string handleId; uint64_t invitedXuid; if (utils::str_icmp(url->Host->Data(), _T("inviteHandleAccept")) == 0) { handleId = utils::internal_string_from_utf16(url->QueryParsed->GetFirstValueByName("handle")->Data()); invitedXuid = utils::string_t_to_uint64(url->QueryParsed->GetFirstValueByName("invitedXuid")->Data()); } else if(utils::str_icmp(url->Host->Data(), _T("activityHandleJoin")) == 0) { handleId = utils::internal_string_from_utf16(url->QueryParsed->GetFirstValueByName("handle")->Data()); invitedXuid = utils::string_t_to_uint64(url->QueryParsed->GetFirstValueByName("joinerXuid")->Data()); } else { return E_INVALIDARG; } // Check if the xuid matches with the sent users. bool invitedUserFound = false; int invitedUserIndex = 0; for (auto& user: users) { auto userResult = User::WrapHandle(user); RETURN_HR_IF_FAILED(userResult.Hresult()); if (invitedXuid == userResult.ExtractPayload().Xuid()) { invitedUserFound = true; break; } invitedUserIndex++; } if (!invitedUserFound) { // The invited user hasn't been added. std::shared_ptr joinLobbyEventArgs = MakeShared(invitedXuid); // Since m_latestPendingRead hasn't been initialized yet, this will ensure // the event is still returned correctly through multiplayer_manager::do_work(); // InvitedXuid's user hasn't been added. Pass in the invited user into join_lobby() API. m_multiplayerEventQueue.AddEvent( XblMultiplayerEventType::JoinLobbyCompleted, XblMultiplayerSessionType::LobbySession, std::dynamic_pointer_cast(joinLobbyEventArgs), E_UNEXPECTED, "Pass in the invited user into join_lobby() API." ); LOG_DEBUG("Pass in the invited user into join_lobby() API."); return E_UNEXPECTED; } else if (invitedUserFound && invitedUserIndex > 0) { auto invitedUser = users[0]; users[0] = users[invitedUserIndex]; users[invitedUserIndex] = invitedUser; } // This will also join any game that is associated with the lobby. if (!handleId.empty()) { return JoinLobbyByHandle(handleId, users); } return S_OK; } #endif HRESULT MultiplayerClientManager::JoinGameFromLobby( _In_ const xsapi_internal_string& sessionTemplateName ) { auto primaryContext = GetPrimaryContext(); auto latestPending = LatestPendingRead(); RETURN_HR_IF_LOG_DEBUG(primaryContext == nullptr || latestPending == nullptr || latestPending->LobbyClient()->Session() == nullptr, E_UNEXPECTED, "No lobby session exists. Call add_local_user() to create a lobby first."); RETURN_HR_IF_LOG_DEBUG(latestPending->GameClient()->Session() != nullptr, E_UNEXPECTED, "A game session already exists. Call leave_game() to leave existing game before creating a new one."); RETURN_HR_IF_LOG_DEBUG (latestPending->MatchClient()->MatchStatus() > XblMultiplayerMatchStatus::None, E_UNEXPECTED, "Matchmaking is currently in progress. Call cancel_match() before joining a game.") latestPending->GameClient()->SetGameSessionTemplate(sessionTemplateName); // We don't care about the async result here, Join result raised as an event after calling do_work so just pass nullptr as callback return latestPending->GameClient()->JoinGameFromLobbyHelper( [](Result> result) { assert(SUCCEEDED(result.Hresult())); }); } HRESULT MultiplayerClientManager::JoinGame( _In_ const xsapi_internal_string& sessionName, _In_ const xsapi_internal_string& sessionTemplateName, _In_ const xsapi_internal_vector& xboxUserIds ) { RETURN_HR_INVALIDARGUMENT_IF(sessionName.empty()); std::shared_ptr primaryContext = GetPrimaryContext(); RETURN_HR_IF(primaryContext == nullptr, E_UNEXPECTED); auto latestPending = LatestPendingRead(); RETURN_HR_IF_LOG_DEBUG(latestPending == nullptr, E_UNEXPECTED, "No lobby session exists. Call add_local_user() to create a lobby first."); RETURN_HR_IF_LOG_DEBUG(latestPending->MatchClient()->MatchStatus() > XblMultiplayerMatchStatus::None, E_UNEXPECTED, "Matchmaking is currently in progress. Call cancel_match() before joining a game"); auto gameClient = latestPending->GameClient(); RETURN_HR_IF(gameClient == nullptr, E_FAIL); Function joinGameHelper = [sharedThis{ shared_from_this() }, gameClient, sessionTemplateName, sessionName]() { gameClient->SetGameSessionTemplate(sessionTemplateName); gameClient->JoinGameHelper(sessionName, nullptr); }; if (xboxUserIds.size() > 0) { auto gameSessionRef = XblMultiplayerSessionReferenceCreate( AppConfig::Instance()->OverrideScid().data(), sessionTemplateName.data(), sessionName.data() ); XblMultiplayerSessionInitArgs initArgs{}; initArgs.InitiatorXuids = xboxUserIds.data(); initArgs.InitiatorXuidsCount = static_cast(xboxUserIds.size()); auto gameSession = MakeShared( primaryContext->Xuid(), &gameSessionRef, &initArgs ); gameSession->Join(nullptr, false); for (const auto& memberXuid : xboxUserIds) { if (memberXuid != primaryContext->Xuid()) { gameSession->AddMemberReservation(memberXuid); } } std::weak_ptr weakThis = shared_from_this(); return primaryContext->MultiplayerService()->WriteSession(gameSession, XblMultiplayerSessionWriteMode::UpdateOrCreateNew, { m_queue, [weakThis, sessionTemplateName, joinGameHelper](Result> result) { std::shared_ptr pThis(weakThis.lock()); if (pThis == nullptr || pThis->m_latestPendingRead == nullptr) { return; } if (FAILED(result.Hresult())) { pThis->AddToLatestPendingReadEventQueue( XblMultiplayerEventType::JoinGameCompleted, XblMultiplayerSessionType::GameSession, nullptr, result ); } else { // Continue joining the session for all local users. joinGameHelper(); } } }); } else { joinGameHelper(); } return S_OK; } HRESULT MultiplayerClientManager::LeaveGame() { std::shared_ptr primaryContext = GetPrimaryContext(); auto latestPendingRead = LatestPendingRead(); RETURN_HR_IF_LOG_DEBUG(latestPendingRead == nullptr || primaryContext == nullptr, E_UNEXPECTED, "Call add_local_user() before committing."); auto gameSession = latestPendingRead->GameClient()->Session(); if (gameSession != nullptr) { latestPendingRead->GameClient()->LeaveRemoteSession(gameSession, true, true); } if (latestPendingRead->MatchClient()->MatchStatus() != XblMultiplayerMatchStatus::None) { latestPendingRead->MatchClient()->CancelMatch(); latestPendingRead->MatchClient()->SetMatchStatus(XblMultiplayerMatchStatus::Canceled); // Matchmaking request was canceled since leave_game() was called latestPendingRead->MatchClient()->HandleFindMatchCompleted({ xbl_error_code::generic_error, "Matchmaking request was canceled since leave_game() was called" }); } m_multiplayerLocalUserManager->ChangeAllLocalUserGameState(MultiplayerLocalUserGameState::Unknown); return S_OK; } HRESULT MultiplayerClientManager::GetActivitiesForSocialGroup( _In_ xbox_live_user_t user, _In_ const xsapi_internal_string& socialGroup, _In_ XTaskQueueHandle queue, _In_ Callback>> callback ) { RETURN_HR_INVALIDARGUMENT_IF(user == nullptr); auto wrapUserResult{ User::WrapHandle(user) }; RETURN_HR_IF_FAILED(wrapUserResult.Hresult()); auto serviceResult = GetMultiplayerService(user); RETURN_HR_IF_FAILED(serviceResult.Hresult()); return serviceResult.ExtractPayload()->GetActivitiesForSocialGroup( AppConfig::Instance()->OverrideScid(), wrapUserResult.Payload().Xuid(), socialGroup, AsyncContext>>{ queue, std::move(callback) } ); } HRESULT MultiplayerClientManager::InviteFriends( _In_ xbox_live_user_t requestingUser, _In_ const xsapi_internal_string& invitationText, _In_ const xsapi_internal_string& customActivationContext ) { UNREFERENCED_PARAMETER(customActivationContext); UNREFERENCED_PARAMETER(invitationText); RETURN_HR_INVALIDARGUMENT_IF(requestingUser == nullptr); auto latestPendingRead = LatestPendingRead(); RETURN_HR_IF_LOG_DEBUG(latestPendingRead == nullptr || latestPendingRead->LobbyClient()->Session() == nullptr, E_UNEXPECTED, "Call add_local_user() and wait for user_added completion event before sending invites."); HRESULT hr = S_OK; std::weak_ptr thisWeakPtr = shared_from_this(); #if HC_PLATFORM == HC_PLATFORM_XDK auto sessionRef = latestPendingRead->LobbyClient()->Session()->SessionReference(); auto sessionReferenceToInviteTo = ref new Windows::Xbox::Multiplayer::MultiplayerSessionReference( utils::PlatformStringFromUtf8(sessionRef.SessionName), utils::PlatformStringFromUtf8(sessionRef.Scid), utils::PlatformStringFromUtf8(sessionRef.SessionTemplateName) ); Windows::Xbox::System::IUser^ systemUser = nullptr; hr = XalUserToXboxSystemUser(requestingUser, &systemUser); if (FAILED(hr)) return hr; auto asyncOp = Windows::Xbox::UI::SystemUI::ShowSendGameInvitesAsync( systemUser, sessionReferenceToInviteTo, utils::PlatformStringFromUtf8(invitationText.data()), utils::PlatformStringFromUtf8(customActivationContext.data()) ); asyncOp->Completed = ref new AsyncActionCompletedHandler( [thisWeakPtr](IAsyncAction^ asyncInfo, AsyncStatus asyncStatus) { std::shared_ptr pThis(thisWeakPtr.lock()); if(pThis != nullptr) { if (asyncStatus == AsyncStatus::Completed && SUCCEEDED(asyncInfo->ErrorCode.Value)) { pThis->AddToLatestPendingReadEventQueue( XblMultiplayerEventType::InviteSent, XblMultiplayerSessionType::LobbySession ); } else { pThis->AddToLatestPendingReadEventQueue( XblMultiplayerEventType::InviteSent, XblMultiplayerSessionType::LobbySession, nullptr, ConvertHr(asyncInfo->ErrorCode.Value) ); } } }); #elif HC_PLATFORM == HC_PLATFORM_GDK XAsyncBlock* asyncBlock = Make(); asyncBlock->queue = m_queue.GetHandle(); asyncBlock->context = utils::store_shared_ptr(shared_from_this()); asyncBlock->callback = [](_In_ XAsyncBlock* asyncBlock) { HRESULT hr = XAsyncGetStatus(asyncBlock, false); auto pThis = utils::get_shared_ptr(asyncBlock->context); pThis->AddToLatestPendingReadEventQueue( XblMultiplayerEventType::InviteSent, XblMultiplayerSessionType::LobbySession, nullptr, make_error_code(xbl_error_code(hr))); Delete(asyncBlock); }; // TODO have a way to set async queue here hr = XGameUiShowSendGameInviteAsync( asyncBlock, requestingUser, LobbyClient()->Session()->SessionReference().Scid, LobbyClient()->Session()->SessionReference().SessionTemplateName, LobbyClient()->Session()->SessionReference().SessionName, invitationText.c_str(), customActivationContext.c_str()); #elif HC_PLATFORM != HC_PLATFORM_WIN32 && !XSAPI_NO_PPL UNREFERENCED_PARAMETER(customActivationContext); auto asyncOp = xbox::services::system::title_callable_ui::show_game_invite_ui( latestPendingRead->LobbyClient()->Session()->SessionReference(), utils::string_t_from_internal_string(invitationText) ); pplx::create_task(asyncOp) .then([thisWeakPtr](xbox_live_result result) { std::shared_ptr pThis(thisWeakPtr.lock()); if (pThis != nullptr) { pThis->AddToLatestPendingReadEventQueue( XblMultiplayerEventType::InviteSent, XblMultiplayerSessionType::LobbySession, nullptr, result.err() ); } }); #else UNREFERENCED_PARAMETER(invitationText); UNREFERENCED_PARAMETER(customActivationContext); #endif return hr; } HRESULT MultiplayerClientManager::InviteUsers( _In_ xbox_live_user_t user, _In_ const xsapi_internal_vector& xboxUserIds, _In_ const xsapi_internal_string& invitationText, _In_ const xsapi_internal_string& customActivationContext ) { RETURN_HR_INVALIDARGUMENT_IF(user == nullptr); auto latestPendingRead = LatestPendingRead(); RETURN_HR_IF_LOG_DEBUG(latestPendingRead == nullptr || latestPendingRead->LobbyClient()->Session() == nullptr, E_UNEXPECTED, "Call add_local_user() and wait for user_added completion event before sending invites."); std::weak_ptr weakSessionWriter = shared_from_this(); auto serviceResult = GetMultiplayerService(user); RETURN_HR_IF_FAILED(serviceResult.Hresult()); return serviceResult.ExtractPayload()->SendInvites( latestPendingRead->LobbyClient()->Session()->SessionReference(), xboxUserIds, AppConfig::Instance()->OverrideTitleId(), invitationText, customActivationContext, AsyncContext>>{ m_queue, [weakSessionWriter](Result> result) { std::shared_ptr pThis(weakSessionWriter.lock()); if (pThis != nullptr) { std::lock_guard guard(pThis->m_clientRequestLock); pThis->AddToLatestPendingReadEventQueue( XblMultiplayerEventType::InviteSent, XblMultiplayerSessionType::LobbySession, nullptr, result ); } } }); } Result> MultiplayerClientManager::GetMultiplayerService( _In_ xbox_live_user_t user ) { auto localUser = m_multiplayerLocalUserManager->GetLocalUser(user); if (localUser != nullptr) { return localUser->Context()->MultiplayerService(); } else { std::shared_ptr xboxLiveContextSettings = MakeShared(); std::shared_ptr appConfig = xbox::services::AppConfig::Instance(); auto wrapUserResult{ User::WrapHandle(user) }; RETURN_HR_IF_FAILED(wrapUserResult.Hresult()); auto multiplayerService = MakeShared(wrapUserResult.ExtractPayload(), xboxLiveContextSettings, appConfig, nullptr); return multiplayerService; } } std::shared_ptr MultiplayerClientManager::LatestPendingRead() const { return m_latestPendingRead; } std::shared_ptr MultiplayerClientManager::LastPendingRead() const { std::lock_guard guard(m_clientRequestLock); return m_lastPendingRead; } std::shared_ptr MultiplayerClientManager::LobbyClient() const { return m_latestPendingRead->LobbyClient(); } bool MultiplayerClientManager::IsRequestInProgress() { if (m_latestPendingRead->LobbyClient()->IsRequestInProgress() || m_latestPendingRead->GameClient()->IsRequestInProgress()) { return true; } return false; } bool MultiplayerClientManager::IsUpdateAvailable() { if (m_latestPendingRead == nullptr || m_lastPendingRead == nullptr) { return false; } if (m_lastPendingRead->IsUpdateAvailable(*m_latestPendingRead)) { return true; } if (GetXboxLiveContextMap().size() == 0 && IsRequestInProgress()) { return true; } // Always do work for match m_latestPendingRead->ProcessMatchEvents(); return false; } MultiplayerEventQueue MultiplayerClientManager::DoWork() { std::lock_guard guard(m_clientRequestLock); m_multiplayerEventQueue.Clear(); if (m_latestPendingRead == nullptr) { return m_multiplayerEventQueue; } m_latestPendingRead->DoWork(); ProcessEvents(m_latestPendingRead->LobbyClient()->Session(), m_lastPendingRead->LobbyClient()->Session(), XblMultiplayerSessionType::LobbySession); ProcessEvents(m_latestPendingRead->GameClient()->Session(), m_lastPendingRead->GameClient()->Session(), XblMultiplayerSessionType::GameSession); ProcessEvents(m_latestPendingRead->MatchClient()->Session(), m_lastPendingRead->MatchClient()->Session(), XblMultiplayerSessionType::MatchSession); m_lastPendingRead->deep_copy_if_updated(*m_latestPendingRead); auto eventQueue = m_lastPendingRead->EventQueue(); if (GetXboxLiveContextMap().size() == 0 && !IsRequestInProgress()) { if (!m_subscriptionsLostFired) { // Force client disconnected event to fire for consistent developer behavior. OnMultiplayerSubscriptionsLost(); } else { // If the last person just left and no more events left, destroy all objects. Destroy(); return eventQueue; } } m_latestPendingRead->ClearEventQueue(); m_lastPendingRead->ClearEventQueue(); return eventQueue; } xsapi_internal_map> MultiplayerClientManager::GetXboxLiveContextMap() { return m_multiplayerLocalUserManager->GetLocalUserMap(); } std::shared_ptr MultiplayerClientManager::GetPrimaryContext() { return m_multiplayerLocalUserManager->GetPrimaryContext(); } void MultiplayerClientManager::OnMultiplayerConnectionIdChanged() { std::lock_guard guard(m_clientRequestLock); auto lobbyClient = m_latestPendingRead->LobbyClient(); auto lobbySession = lobbyClient->Session(); XblMultiplayerSessionReadLockGuard lobbyClientSessionSafe(lobbySession); if (lobbySession && lobbyClientSessionSafe.CurrentUser() && lobbyClientSessionSafe.CurrentUser()->Status == XblMultiplayerSessionMemberStatus::Active) { MultiplayerSessionMember::Get(lobbyClientSessionSafe.CurrentUser())->SetStatus(lobbyClientSessionSafe.CurrentUser()->Status); auto pendingRequest = MakeShared(); lobbyClient->AddToPendingQueue(pendingRequest); } auto gameClient = m_latestPendingRead->GameClient(); auto gameSession = gameClient->Session(); XblMultiplayerSessionReadLockGuard gameClientSessionSafe(gameSession); if (gameSession && gameClientSessionSafe.CurrentUser() && gameClientSessionSafe.CurrentUser()->Status == XblMultiplayerSessionMemberStatus::Active) { MultiplayerSessionMember::Get(gameClientSessionSafe.CurrentUser())->SetStatus(gameClientSessionSafe.CurrentUser()->Status); auto pendingRequest = MakeShared(); gameClient->AddToPendingQueue(pendingRequest); } } void MultiplayerClientManager::OnMultiplayerSubscriptionsLost() { HRESULT hr = m_queue.RunWork([weakThis = std::weak_ptr{ shared_from_this() }] { auto pThis{ weakThis.lock() }; if (pThis) { std::lock_guard guard(pThis->m_clientRequestLock); bool expected = false; if (pThis->m_subscriptionsLostFired.compare_exchange_strong(expected, true)) { // Fired when the title's connection to MPSD using the real-time activity service is lost. // When this event occurs, the title should shut down the multiplayer. auto lobbyClient = pThis->LobbyClient(); if (lobbyClient != nullptr) { lobbyClient->RemoveAllLocalUsers(); } pThis->AddToLatestPendingReadEventQueue(XblMultiplayerEventType::ClientDisconnectedFromMultiplayerService, XblMultiplayerSessionType::LobbySession); } } }); if (FAILED(hr)) { LOGS_INFO << __FUNCTION__ << ": RunWork failed with hr=" << hr; } } void MultiplayerClientManager::OnResyncMessageReceived() { // Upon receiving RTA resync message, re-fetch all multiplayer sessions. // Note: You could get multiple re-sync messages. It's recommended that you only fetch once every 30 secs. if (m_latestPendingRead != nullptr) { m_latestPendingRead->LobbyClient()->SessionWriter()->OnResyncMessageReceived(); m_latestPendingRead->GameClient()->SessionWriter()->OnResyncMessageReceived(); } } void MultiplayerClientManager::OnSessionChanged( _In_ XblMultiplayerSessionChangeEventArgs args ) { std::lock_guard guard(m_synchronizeWriteWithTapLock); if (m_latestPendingRead != nullptr) { if (m_latestPendingRead->IsMatch(args.SessionReference)) { m_latestPendingRead->MatchClient()->OnSessionChanged(args); } if (m_latestPendingRead->IsLobby(args.SessionReference)) { m_latestPendingRead->LobbyClient()->SessionWriter()->OnSessionChanged(args); } else if (m_latestPendingRead->IsGame(args.SessionReference)) { m_latestPendingRead->GameClient()->SessionWriter()->OnSessionChanged(args); } } } const MultiplayerEventQueue& MultiplayerClientManager::EventQueue() const { std::lock_guard guard(m_clientRequestLock); return m_multiplayerEventQueue; } void MultiplayerClientManager::ClearEventQueue() { std::lock_guard lock(m_clientRequestLock); m_multiplayerEventQueue.Clear(); } void MultiplayerClientManager::AddToLatestPendingReadEventQueue( _In_ XblMultiplayerEventType eventType, _In_ XblMultiplayerSessionType sessionType, _In_ std::shared_ptr eventArgs, _In_opt_ Result error, _In_opt_ context_t context ) { // Note: This function does not require a lock. Caller already has a m_clientRequestLock TODO is this actually true? if (m_latestPendingRead != nullptr) { m_latestPendingRead->AddEvent(eventType, eventArgs, sessionType, error, context); } } XblMultiplayerSessionType MultiplayerClientManager::GetSessionType( _In_ std::shared_ptr session ) { XblMultiplayerSessionType sessionType = XblMultiplayerSessionType::Unknown; auto latestPendingRead = LatestPendingRead(); if (latestPendingRead != nullptr) { if (latestPendingRead->IsLobby(session->SessionReference())) { sessionType = XblMultiplayerSessionType::LobbySession; } else if (latestPendingRead->IsGame(session->SessionReference())) { sessionType = XblMultiplayerSessionType::GameSession; } } return sessionType; } void MultiplayerClientManager::ProcessEvents( _In_ std::shared_ptr currentSession, _In_ std::shared_ptr oldSession, _In_ XblMultiplayerSessionType sessionType ) { if (oldSession == nullptr || currentSession == nullptr || oldSession->SessionInfo().ChangeNumber == currentSession->SessionInfo().ChangeNumber) { return; } xbl_result diff = currentSession->XblMultiplayerSession::CompareMultiplayerSessions(oldSession); if (!diff.err() && diff.payload() == XblMultiplayerSessionChangeTypes::None) { return; } XblMultiplayerSessionChangeTypes diffType = diff.payload(); if (sessionType != XblMultiplayerSessionType::MatchSession) { if (MultiplayerManagerUtils::IsMultiplayerSessionChangeType(diffType, XblMultiplayerSessionChangeTypes::HostDeviceTokenChange)) { HandleHostChanged(currentSession, sessionType); } if (MultiplayerManagerUtils::IsMultiplayerSessionChangeType(diffType, XblMultiplayerSessionChangeTypes::MemberListChange)) { HandleMemberListChanged(currentSession, oldSession, sessionType); } if (MultiplayerManagerUtils::IsMultiplayerSessionChangeType(diffType, XblMultiplayerSessionChangeTypes::CustomPropertyChange)) { HandleSessionPropertiesChanged(currentSession, oldSession, sessionType); } if (MultiplayerManagerUtils::IsMultiplayerSessionChangeType(diffType, XblMultiplayerSessionChangeTypes::MemberCustomPropertyChange)) { HandleMemberPropertiesChanged(currentSession, oldSession, sessionType); } } if (sessionType != XblMultiplayerSessionType::GameSession) { // Don't need to process these for game. The match will take care of handling these events. if (MultiplayerManagerUtils::IsMultiplayerSessionChangeType(diffType, XblMultiplayerSessionChangeTypes::MatchmakingStatusChange) && currentSession->MatchmakingServer()) { m_latestPendingRead->MatchClient()->HandleMatchStatusChanged(currentSession); } } } void MultiplayerClientManager::HandleMemberListChanged( _In_ std::shared_ptr currentSession, _In_ std::shared_ptr oldSession, _In_ XblMultiplayerSessionType sessionType ) { xsapi_internal_map currentSessionMembers; xsapi_internal_map oldSessionMembers; XblMultiplayerSessionReadLockGuard currentSessionSafe(currentSession); for (const auto& currentSessionMember : currentSessionSafe.Members()) { currentSessionMembers[currentSessionMember.Xuid] = ¤tSessionMember; } XblMultiplayerSessionReadLockGuard oldSessionSafe(oldSession); for (const auto& oldSessionMember : oldSessionSafe.Members()) { oldSessionMembers[oldSessionMember.Xuid] = &oldSessionMember; } bool haveMembersJoined = false; bool haveMembersLeft = false; // See if any new members joined xsapi_internal_vector membersJoined; for (const auto& currentSessionMember : currentSessionSafe.Members()) { if (oldSessionMembers.find(currentSessionMember.Xuid) == oldSessionMembers.end()) { haveMembersJoined = true; membersJoined.push_back(¤tSessionMember); } } // See if any members left xsapi_internal_vector membersLeft; for (const auto& oldSessionMember : oldSessionSafe.Members()) { if (currentSessionMembers.find(oldSessionMember.Xuid) == currentSessionMembers.end()) { haveMembersLeft = true; membersLeft.push_back(&oldSessionMember); } } if (haveMembersJoined || haveMembersLeft) { auto latestPendingRead = LatestPendingRead(); if (latestPendingRead == nullptr) { return; } if (haveMembersJoined) { xsapi_internal_vector> gameMembers; for (auto member : membersJoined) { gameMembers.push_back(latestPendingRead->ConvertToGameMember(member)); } std::shared_ptr memberJoinedEventArgs = MakeShared(gameMembers); AddToLatestPendingReadEventQueue( XblMultiplayerEventType::MemberJoined, sessionType, memberJoinedEventArgs ); } if (haveMembersLeft) { xsapi_internal_vector> gameMembers; for (const auto& member : membersLeft) { gameMembers.push_back(latestPendingRead->ConvertToGameMember(member)); } std::shared_ptr memberLeftEventArgs = MakeShared( gameMembers ); AddToLatestPendingReadEventQueue( XblMultiplayerEventType::MemberLeft, sessionType, memberLeftEventArgs ); } } } void MultiplayerClientManager::HandleMemberPropertiesChanged( _In_ std::shared_ptr currentSession, _In_ std::shared_ptr oldSession, _In_ XblMultiplayerSessionType sessionType ) { xsapi_internal_map oldSessionMembers; XblMultiplayerSessionReadLockGuard oldSessionSafe(oldSession); for (const auto& oldSessionMember : oldSessionSafe.Members()) { oldSessionMembers[oldSessionMember.Xuid] = &oldSessionMember; } // See if properties changed and add them to the queue. xsapi_internal_vector memberPropertiesChanged; XblMultiplayerSessionReadLockGuard currentSessionSafe(currentSession); for (const auto& currentSessionMember : currentSessionSafe.Members()) { if (oldSessionMembers.find(currentSessionMember.Xuid) != oldSessionMembers.end()) { auto oldSessionMember = oldSessionMembers[currentSessionMember.Xuid]; if (utils::str_icmp(currentSessionMember.CustomPropertiesJson, oldSessionMember->CustomPropertiesJson) != 0) { memberPropertiesChanged.push_back(¤tSessionMember); } } } if (memberPropertiesChanged.size() > 0) { xsapi_internal_vector> gameMembers; const auto& localUsersMap = m_multiplayerLocalUserManager->GetLocalUserMap(); for (auto member : memberPropertiesChanged) { auto iter = localUsersMap.find(member->Xuid); if (iter != localUsersMap.end()) { // Don't trigger member property changed events for local users. continue; } auto latestPendingRead = LatestPendingRead(); if (latestPendingRead == nullptr) { continue; } std::shared_ptr memberPropertiesChangedArgs = MakeShared( latestPendingRead->ConvertToGameMember(member), member->CustomPropertiesJson ); AddToLatestPendingReadEventQueue( XblMultiplayerEventType::MemberPropertyChanged, sessionType, memberPropertiesChangedArgs ); } } } void MultiplayerClientManager::HandleSessionPropertiesChanged( _In_ std::shared_ptr currentSession, _In_ std::shared_ptr oldSession, _In_ XblMultiplayerSessionType sessionType ) { if (sessionType == XblMultiplayerSessionType::LobbySession && m_multiplayerLocalUserManager->IsLocalUserGameState(MultiplayerLocalUserGameState::PendingJoin)) { // Don't join the game if matchmaking is in progress. auto latestPendingRead = LatestPendingRead(); if (latestPendingRead != nullptr && latestPendingRead->MatchClient()->MatchStatus() == XblMultiplayerMatchStatus::None) { // If state is completed, or transfer handle was removed. if (latestPendingRead->LobbyClient()->IsTransferHandleState("completed") || (XblMultiplayerSession::HasSessionPropertyChanged(currentSession, oldSession, MultiplayerLobbyClient_TransferHandlePropertyName) && latestPendingRead->LobbyClient()->GetTransferHandle().empty()) ) { m_multiplayerLocalUserManager->ChangeAllLocalUserGameState(MultiplayerLocalUserGameState::Join); // Join the game session using the handleId. latestPendingRead->GameClient()->JoinGameFromLobbyHelper(nullptr); } } } // Don't trigger property changed event if the transfer handle property changes. if (XblMultiplayerSession::HasSessionPropertyChanged(currentSession, oldSession, MultiplayerLobbyClient_TransferHandlePropertyName) || XblMultiplayerSession::HasSessionPropertyChanged(currentSession, oldSession, MultiplayerLobbyClient_JoinabilityPropertyName)) { return; } XblMultiplayerSessionReadLockGuard currentSessionSafe(currentSession); auto gamePropertiesChangedArgs = MakeShared( currentSessionSafe.SessionProperties().SessionCustomPropertiesJson ); AddToLatestPendingReadEventQueue( XblMultiplayerEventType::SessionPropertyChanged, sessionType, gamePropertiesChangedArgs ); } void MultiplayerClientManager::HandleHostChanged( _In_ std::shared_ptr currentSession, _In_ XblMultiplayerSessionType sessionType ) { /// A host may have left, and there may be no new host. std::shared_ptr hostMember = nullptr; auto host = XblMultiplayerSession::HostMember(currentSession); if (host != nullptr) { auto latestPendingRead = LatestPendingRead(); if (latestPendingRead != nullptr) { hostMember = latestPendingRead->ConvertToGameMember(host); } } std::shared_ptr hostChangedEventArgs = MakeShared( hostMember ); AddToLatestPendingReadEventQueue( XblMultiplayerEventType::HostChanged, sessionType, hostChangedEventArgs ); } std::shared_ptr MultiplayerClientManager::MatchClient() { auto latestPendingRead = LatestPendingRead(); if (latestPendingRead == nullptr) { return nullptr; } return latestPendingRead->MatchClient(); } HRESULT MultiplayerClientManager::FindMatch( _In_ const xsapi_internal_string& hopperName, _In_ JsonValue& attributes, _In_ const std::chrono::seconds& timeout ) { auto latestPendingRead = LatestPendingRead(); RETURN_HR_IF_LOG_DEBUG(latestPendingRead == nullptr || latestPendingRead->LobbyClient()->Session() == nullptr, E_UNEXPECTED, "No local user added. Call add_local_user() first."); return latestPendingRead->FindMatch(hopperName, attributes, timeout); } void MultiplayerClientManager::SetAutoFillMembersDuringMatchmaking( _In_ bool autoFillMembers ) { m_autoFillMembers = autoFillMembers; auto latestPendingRead = LatestPendingRead(); if (latestPendingRead != nullptr) { latestPendingRead->SetAutoFillMembersDuringMatchmaking(autoFillMembers); } } NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_MANAGER_CPP_END ================================================ FILE: Source/Services/Multiplayer/Manager/multiplayer_client_pending_reader.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "multiplayer_manager_internal.h" using namespace xbox::services::multiplayer; NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_MANAGER_CPP_BEGIN void MultiplayerClientPendingReader::deep_copy_if_updated( _In_ const MultiplayerClientPendingReader& other ) { std::lock_guard lock(other.m_clientRequestLock); m_multiplayerEventQueue = other.m_multiplayerEventQueue; if (other.m_lobbyClient == nullptr) { m_lobbyClient = nullptr; } else { m_lobbyClient->deep_copy_if_updated(*other.m_lobbyClient); } if (other.m_gameClient == nullptr) { m_gameClient = nullptr; } else { m_gameClient->deep_copy_if_updated(*other.m_gameClient); } if (other.m_matchClient == nullptr) { m_matchClient = nullptr; } else { m_matchClient->deep_copy_if_updated(*other.m_matchClient); } } MultiplayerClientPendingReader::MultiplayerClientPendingReader(const TaskQueue& queue) : m_queue{ queue.DeriveWorkerQueue() }, m_autoFillMembers(false) { m_multiplayerLocalUserManager = MakeShared(); m_lobbyClient = MakeShared(m_queue); m_gameClient = MakeShared(m_queue); m_matchClient = MakeShared(m_queue, m_multiplayerLocalUserManager); } MultiplayerClientPendingReader::~MultiplayerClientPendingReader() { m_lobbyClient.reset(); m_gameClient.reset(); m_matchClient.reset(); m_multiplayerLocalUserManager.reset(); } MultiplayerClientPendingReader::MultiplayerClientPendingReader( _In_ const TaskQueue& queue, _In_ const xsapi_internal_string& lobbySessionTemplateName, _In_ std::shared_ptr localUserManager ) : m_queue{ queue.DeriveWorkerQueue() }, m_autoFillMembers(false), m_multiplayerLocalUserManager(localUserManager) { m_lobbyClient = MakeShared(m_queue, lobbySessionTemplateName, m_multiplayerLocalUserManager); m_gameClient = MakeShared(m_queue, m_multiplayerLocalUserManager); m_matchClient = MakeShared(m_queue, m_multiplayerLocalUserManager); m_lobbyClient->Initialize(); m_gameClient->Initialize(); } bool MultiplayerClientPendingReader::IsUpdateAvailable( _In_ const MultiplayerClientPendingReader& other ) { std::lock_guard lock(other.m_clientRequestLock); if (other.m_lobbyClient->IsPendingLobbyChanges() || other.m_gameClient->IsPendingGameChanges()) { return true; } auto lobbySession = m_lobbyClient->Session(); auto otherLobbySession = other.m_lobbyClient->Session(); auto gameSession = m_gameClient->Session(); auto otherGameSession = other.m_gameClient->Session(); if ( !MultiplayerManagerUtils::CompareSessions(lobbySession, otherLobbySession) || !MultiplayerManagerUtils::CompareSessions(gameSession, otherGameSession) || !MultiplayerManagerUtils::CompareSessions(m_matchClient->Session(), other.m_matchClient->Session()) || m_lobbyClient->EventQueue().Size() != other.m_lobbyClient->EventQueue().Size() || m_gameClient->EventQueue().Size() != other.m_gameClient->EventQueue().Size() || m_matchClient->EventQueue().Size() != other.m_matchClient->EventQueue().Size() || m_multiplayerEventQueue.Size() != other.m_multiplayerEventQueue.Size()) { return true; } return false; } std::shared_ptr MultiplayerClientPendingReader::LobbyClient() { return m_lobbyClient; } std::shared_ptr MultiplayerClientPendingReader::GameClient() { return m_gameClient; } std::shared_ptr MultiplayerClientPendingReader::MatchClient() { return m_matchClient; } void MultiplayerClientPendingReader::UpdateSession( _In_ XblMultiplayerSessionReference sessionRef, _In_ std::shared_ptr session ) { std::lock_guard lock(m_clientRequestLock); if (IsLobby(sessionRef)) { m_lobbyClient->UpdateSession(session); } else if(IsGame(sessionRef)) { m_gameClient->UpdateSession(session); } } std::shared_ptr MultiplayerClientPendingReader::GetSession( _In_ XblMultiplayerSessionReference sessionRef ) { if (IsLobby(sessionRef)) { return m_lobbyClient->Session(); } else if (IsGame(sessionRef)) { return m_gameClient->Session(); } return nullptr; } bool MultiplayerClientPendingReader::IsLobby( _In_ XblMultiplayerSessionReference sessionRef ) { if (!XblMultiplayerSessionReferenceIsValid(&sessionRef) || m_lobbyClient == nullptr || m_lobbyClient->Session() == nullptr) { return false; } return sessionRef == m_lobbyClient->Session()->SessionReference(); } bool MultiplayerClientPendingReader::IsGame( _In_ XblMultiplayerSessionReference sessionRef ) { if (!XblMultiplayerSessionReferenceIsValid(&sessionRef) || m_gameClient == nullptr || m_gameClient->Session() == nullptr) { return false; } return sessionRef == m_gameClient->Session()->SessionReference(); } bool MultiplayerClientPendingReader::IsMatch( _In_ XblMultiplayerSessionReference sessionRef ) { if (!XblMultiplayerSessionReferenceIsValid(&sessionRef) || m_matchClient == nullptr || m_matchClient->Session() == nullptr) { return false; } return sessionRef == m_matchClient->Session()->SessionReference(); } const MultiplayerEventQueue& MultiplayerClientPendingReader::EventQueue() const { return m_multiplayerEventQueue; } void MultiplayerClientPendingReader::ClearEventQueue() { m_multiplayerEventQueue.Clear(); } void MultiplayerClientPendingReader::AddEvent( _In_ XblMultiplayerEventType eventType, _In_ std::shared_ptr eventArgs, _In_ XblMultiplayerSessionType sessionType, _In_ Result error, _In_opt_ context_t context ) { // TODO: add logging for error message m_multiplayerEventQueue.AddEvent(eventType, sessionType, eventArgs, error, context); } void MultiplayerClientPendingReader::AddEvent( _In_ const XblMultiplayerEvent& multiplayerEvent ) { m_multiplayerEventQueue.AddEvent(multiplayerEvent); } void MultiplayerClientPendingReader::AddEvents( _In_ const MultiplayerEventQueue& multiplayerEventQueue ) { if (multiplayerEventQueue.Size() > 0) { for (const auto& multiplayerEvent : multiplayerEventQueue) { m_multiplayerEventQueue.AddEvent(multiplayerEvent); } } } void MultiplayerClientPendingReader::DoWork() { std::shared_ptr primaryContext = m_multiplayerLocalUserManager->GetPrimaryContext(); if (primaryContext == nullptr && m_lobbyClient->EventQueue().Size() == 0 && m_gameClient->EventQueue().Size() == 0 && m_matchClient->EventQueue().Size() == 0 && m_multiplayerEventQueue.Size() == 0) { // After the primaryContext has been deleted, the lobbyClient could have userRemoved event in the queue. // The pending reader will also fire a client_disconnected_from_multiplayer_service event. return; } auto lobbySession = m_lobbyClient->Session(); auto gameSession = m_gameClient->Session(); m_lobbyClient->UpdateObjects(lobbySession, gameSession); m_gameClient->UpdateObjects(gameSession, lobbySession); auto lobbyClientEventQueue = m_lobbyClient->DoWork(); AddEvents(lobbyClientEventQueue); auto gameClientEventQueue = m_gameClient->DoWork(); AddEvents(gameClientEventQueue); ProcessMatchEvents(); } HRESULT MultiplayerClientPendingReader::SetJoinability( _In_ XblMultiplayerJoinability value, _In_opt_ context_t context ) { return m_lobbyClient->SetJoinability(value, context); } HRESULT MultiplayerClientPendingReader::SetProperties( _In_ const XblMultiplayerSessionReference& sessionRef, _In_ const xsapi_internal_string& name, _In_ const JsonValue& valueJson, _In_opt_ context_t context ) { auto pendingRequest = MakeShared(); pendingRequest->SetSessionProperties(name, valueJson, context); AddToPendingQueue(sessionRef, pendingRequest); return S_OK; } HRESULT MultiplayerClientPendingReader::SetSynchronizedHost( _In_ const XblMultiplayerSessionReference& sessionRef, _In_ const xsapi_internal_string& hostDeviceToken, _In_opt_ context_t context ) { auto pendingRequest = MakeShared(); pendingRequest->SetSynchronizedHostDeviceToken(hostDeviceToken, context); AddToPendingQueue(sessionRef, pendingRequest); return S_OK; } HRESULT MultiplayerClientPendingReader::SetSynchronizedProperties( _In_ const XblMultiplayerSessionReference& sessionRef, _In_ const xsapi_internal_string& name, _In_ const JsonValue& valueJson, _In_opt_ context_t context ) { auto pendingRequest = MakeShared(); pendingRequest->SetSynchronizedSessionProperties(name, valueJson, context); AddToPendingQueue(sessionRef, pendingRequest); return S_OK; } void MultiplayerClientPendingReader::AddToPendingQueue( _In_ const XblMultiplayerSessionReference& sessionRef, _In_ std::shared_ptr pendingRequest ) { if (sessionRef.SessionName[0] == 0 || IsLobby(sessionRef)) { m_lobbyClient->AddToPendingQueue(pendingRequest); } else if (IsGame(sessionRef)) { m_gameClient->AddToPendingQueue(pendingRequest); } } bool MultiplayerClientPendingReader::IsLocal( _In_ uint64_t xuid, _In_ const xsapi_internal_map>& xboxLiveContextMap ) { for(auto& xboxLiveContext : xboxLiveContextMap) { std::shared_ptr localUser = xboxLiveContext.second; if (localUser != nullptr && xuid == localUser->Xuid()) { return true; } } return false; } std::shared_ptr MultiplayerClientPendingReader::ConvertToGameMember( _In_ const XblMultiplayerSessionMember* member ) { return MultiplayerMember::CreateFromSessionMember( member, m_lobbyClient->Session(), m_gameClient->Session(), m_multiplayerLocalUserManager->GetLocalUserMap() ); } void MultiplayerClientPendingReader::ProcessMatchEvents() { if (m_matchClient == nullptr || m_matchClient->MatchStatus() == XblMultiplayerMatchStatus::None) { return; } auto matchEventQueue = m_matchClient->DoWork(); auto matchSession = m_matchClient->Session(); if (matchEventQueue.Size() > 0) { for (const auto& multiplayerEvent : matchEventQueue) { AddEvent(multiplayerEvent); if (multiplayerEvent.EventType == XblMultiplayerEventType::FindMatchCompleted) { auto matchStatus = m_matchClient->MatchStatus(); if (FAILED(multiplayerEvent.Result)) { if (!XblMultiplayerSession::DoSessionsMatch(m_gameClient->Session(), matchSession) && !XblMultiplayerSession::DoSessionsMatch(m_lobbyClient->Session(), matchSession)) { // TODO should be able to control the async queue here m_lobbyClient->SessionWriter()->LeaveRemoteSession(matchSession, nullptr); } } if (matchStatus == XblMultiplayerMatchStatus::Resubmitting) { auto lobbySessionCopy = MakeShared(*m_lobbyClient->Session()); m_matchClient->ResubmitMatchmaking(lobbySessionCopy); break; } if (matchStatus == XblMultiplayerMatchStatus::Completed) { m_gameClient->UpdateGameSession(matchSession); m_gameClient->UpdateObjects(matchSession, m_lobbyClient->Session()); m_lobbyClient->AdvertiseGameSession(); } XblMultiplayerSessionReadLockGuard matchSessionSafe(matchSession); if (m_autoFillMembers && matchSession != nullptr && matchSessionSafe.Members().size() < matchSessionSafe.SessionConstants().MaxMembersInSession && (matchStatus == XblMultiplayerMatchStatus::Completed || matchStatus == XblMultiplayerMatchStatus::Expired)) { // Continue looking for more players m_matchClient->SetMatchStatus(XblMultiplayerMatchStatus::None); m_matchClient->FindMatch(matchSession, true); break; } m_matchClient->SetMatchStatus(XblMultiplayerMatchStatus::None); m_matchClient->UpdateSession(nullptr); break; } } } } HRESULT MultiplayerClientPendingReader::FindMatch( _In_ const xsapi_internal_string& hopperName, _In_ JsonValue& attributes, _In_ const std::chrono::seconds& timeout ) { RETURN_HR_IF_LOG_DEBUG(!m_autoFillMembers && m_gameClient->Session() != nullptr, E_UNEXPECTED, "A game already exists. Call leave_game() before you can start matchmaking."); RETURN_HR_IF_LOG_DEBUG(!m_autoFillMembers && !m_lobbyClient->GetTransferHandle().empty(), E_UNEXPECTED, "A game already exists for your Lobby. Call leave_game() for all Lobby members before you can start matchmaking."); if (m_autoFillMembers && m_gameClient->Session() != nullptr) { return m_matchClient->FindMatch(hopperName, attributes, timeout, m_gameClient->Session(), true); } return m_matchClient->FindMatch(hopperName, attributes, timeout, m_lobbyClient->Session(), false); } void MultiplayerClientPendingReader::SetAutoFillMembersDuringMatchmaking( _In_ bool autoFillMembers ) { m_autoFillMembers = autoFillMembers; } NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_MANAGER_CPP_END ================================================ FILE: Source/Services/Multiplayer/Manager/multiplayer_client_pending_request.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "multiplayer_manager_internal.h" using namespace xbox::services::multiplayer; using namespace xbox::services; NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_MANAGER_CPP_BEGIN MultiplayerClientPendingRequest::MultiplayerClientPendingRequest() : m_context(nullptr), m_requestType(PendingRequestType::NonSynchronizedChanges), m_localUserLobbyState(MultiplayerLocalUserLobbyState::Unknown), m_joinability(XblMultiplayerJoinability::None) { } PendingRequestType MultiplayerClientPendingRequest::RequestType() const { return m_requestType; } context_t MultiplayerClientPendingRequest::Context() { return m_context; } uint32_t MultiplayerClientPendingRequest::Identifier() const { return m_identifier; } // Local User properties std::shared_ptr MultiplayerClientPendingRequest::LocalUser() { return m_localUser; } void MultiplayerClientPendingRequest::SetLocalUser( _In_ std::shared_ptr user ) { m_localUser = user; } MultiplayerLocalUserLobbyState MultiplayerClientPendingRequest::LobbyState() { return m_localUserLobbyState; } void MultiplayerClientPendingRequest::SetLobbyState( _In_ MultiplayerLocalUserLobbyState userState ) { m_localUserLobbyState = userState; } const xsapi_internal_string& MultiplayerClientPendingRequest::LobbyHandleId() const { return m_lobbyHandleId; } void MultiplayerClientPendingRequest::SetLobbyHandleId(_In_ const xsapi_internal_string& handleId) { m_lobbyHandleId = handleId; } const xsapi_internal_string& MultiplayerClientPendingRequest::LocalUserSecureDeivceAddress() const { return m_localUserSecureDeivceAddress; } void MultiplayerClientPendingRequest::SetLocalUserConnectionAddress( _In_ std::shared_ptr localUser, _In_ const xsapi_internal_string& connectionAddress, _In_opt_ context_t context ) { m_context = context; m_localUser = localUser; m_localUserSecureDeivceAddress = utils::format_secure_device_address(connectionAddress); } const xsapi_internal_map& MultiplayerClientPendingRequest::LocalUserProperties() const { return m_localUserProperties; } void MultiplayerClientPendingRequest::SetLocalUserProperties( _In_ std::shared_ptr localUser, _In_ const xsapi_internal_string& name, _In_ const JsonValue& valueJson, _In_opt_ context_t context ) { m_context = context; m_localUser = localUser; JsonUtils::CopyFrom(m_localUserProperties[name], valueJson); } // Session non-synchronized properties XblMultiplayerJoinability MultiplayerClientPendingRequest::Joinability() { return m_joinability; } void MultiplayerClientPendingRequest::SetJoinability( _In_ XblMultiplayerJoinability value, _In_opt_ context_t context ) { m_context = context; m_joinability = value; } const xsapi_internal_map& MultiplayerClientPendingRequest::SessionProperties() const { return m_sessionProperties; } void MultiplayerClientPendingRequest::SetSessionProperties( _In_ const xsapi_internal_string& name, _In_ const JsonValue& valueJson, _In_opt_ context_t context ) { m_context = context; JsonUtils::CopyFrom(m_sessionProperties[name], valueJson); } // Session synchronized properties const xsapi_internal_string& MultiplayerClientPendingRequest::SynchronizedHostDeviceToken() const { return m_synchronizedHostDeviceToken; } void MultiplayerClientPendingRequest::SetSynchronizedHostDeviceToken( _In_ const xsapi_internal_string& hostDeviceToken, _In_opt_ context_t context ) { m_context = context; m_requestType = PendingRequestType::SynchronizedChanges; m_synchronizedHostDeviceToken = hostDeviceToken; } const xsapi_internal_map& MultiplayerClientPendingRequest::SynchronizedSessionProperties() const { return m_synchronizedSessionProperties; } void MultiplayerClientPendingRequest::SetSynchronizedSessionProperties( _In_ const xsapi_internal_string& name, _In_ const JsonValue& valueJson, _In_opt_ context_t context ) { m_context = context; m_requestType = PendingRequestType::SynchronizedChanges; JsonUtils::CopyFrom(m_synchronizedSessionProperties[name], valueJson); } void MultiplayerClientPendingRequest::AppendPendingChanges( _In_ std::shared_ptr sessionToCommit, _In_ std::shared_ptr localUser, _In_ bool isGameInProgress ) { // Apply local user properties if (localUser != nullptr && m_localUser != nullptr && localUser->Xuid() == m_localUser->Xuid()) { XblMultiplayerSessionReadLockGuard sessionToCommitSafe(sessionToCommit); if (!m_localUserSecureDeivceAddress.empty()) { // Assign connection address to the localUser instance as it's used in other places locally. localUser->SetConnectionAddress(m_localUserSecureDeivceAddress); if (sessionToCommitSafe.CurrentUser() != nullptr) { sessionToCommitSafe.CurrentUserInternal()->SetSecureDeviceBaseAddress64(m_localUserSecureDeivceAddress); } } if (m_localUserProperties.size() > 0 && sessionToCommitSafe.CurrentUser() != nullptr) { for (const auto& prop : m_localUserProperties) { sessionToCommitSafe.CurrentUserInternal()->SetCustomPropertyJson(prop.first, prop.second); } } localUser->SetWriteChangesToService(false); } // Apply session non-sync properties if (m_joinability != XblMultiplayerJoinability::None) { MultiplayerManagerUtils::SetJoinability(m_joinability, sessionToCommit, isGameInProgress); } if (m_sessionProperties.size() > 0) { for (const auto& prop : m_sessionProperties) { sessionToCommit->SetSessionCustomPropertyJson(prop.first, prop.second); } } // Apply session sync properties if (!m_synchronizedHostDeviceToken.empty()) { sessionToCommit->SetHostDeviceToken(m_synchronizedHostDeviceToken.data()); } if (m_synchronizedSessionProperties.size() > 0) { for (const auto& prop : m_synchronizedSessionProperties) { sessionToCommit->SetSessionCustomPropertyJson(prop.first, prop.second); } } } std::atomic MultiplayerClientPendingRequest::s_nextUniqueIdentifier{ 0 }; NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_MANAGER_CPP_END ================================================ FILE: Source/Services/Multiplayer/Manager/multiplayer_event_args.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "multiplayer_manager_internal.h" using namespace xbox::services; using namespace xbox::services::multiplayer::manager; using std::dynamic_pointer_cast; STDAPI XblMultiplayerEventArgsXuid( _In_ XblMultiplayerEventArgsHandle argsHandle, _Out_ uint64_t* xuid ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(argsHandle == nullptr || xuid == nullptr); VERIFY_XBL_INITIALIZED(); auto userAddedArgs = dynamic_cast(argsHandle); auto userRemovedArgs = dynamic_cast(argsHandle); auto joinLobbyCompletedArgs = dynamic_cast(argsHandle); if (userAddedArgs != nullptr) { *xuid = userAddedArgs->Xuid; } else if (userRemovedArgs != nullptr) { *xuid = userRemovedArgs->Xuid; } else if (joinLobbyCompletedArgs != nullptr) { *xuid = joinLobbyCompletedArgs->Xuid; } else { return E_INVALIDARG; } return S_OK; } CATCH_RETURN() STDAPI XblMultiplayerEventArgsMembersCount( _In_ XblMultiplayerEventArgsHandle argsHandle, _Out_ size_t* memberCount ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(argsHandle == nullptr || memberCount == nullptr); auto memberJoinedArgs = dynamic_cast(argsHandle); auto memberLeftArgs = dynamic_cast(argsHandle); if (memberJoinedArgs != nullptr) { *memberCount = memberJoinedArgs->Members.size(); } else if (memberLeftArgs != nullptr) { *memberCount = memberLeftArgs->Members.size(); } else { return E_INVALIDARG; } return S_OK; } CATCH_RETURN() STDAPI XblMultiplayerEventArgsMembers( _In_ XblMultiplayerEventArgsHandle argsHandle, _In_ size_t membersCount, _Out_writes_(membersCount) XblMultiplayerManagerMember* members ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(argsHandle == nullptr || members == nullptr); auto memberJoinedArgs = dynamic_cast(argsHandle); auto memberLeftArgs = dynamic_cast(argsHandle); xsapi_internal_vector> membersVector; if (memberJoinedArgs != nullptr) { membersVector = memberJoinedArgs->Members; } else if (memberLeftArgs != nullptr) { membersVector = memberLeftArgs->Members; } else { return E_INVALIDARG; } RETURN_HR_INVALIDARGUMENT_IF(membersCount < membersVector.size()); for (size_t i = 0; i < membersVector.size(); ++i) { members[i] = membersVector[i]->GetReference(); } return S_OK; } CATCH_RETURN() STDAPI XblMultiplayerEventArgsMember( _In_ XblMultiplayerEventArgsHandle argsHandle, _Out_ XblMultiplayerManagerMember* member ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(argsHandle == nullptr || member == nullptr); auto hostChangedArgs = dynamic_cast(argsHandle); auto memberPropertyChangedArgs = dynamic_cast(argsHandle); if (hostChangedArgs != nullptr) { if (hostChangedArgs->HostMember != nullptr) { *member = hostChangedArgs->HostMember->GetReference(); } else { return __HRESULT_FROM_WIN32(ERROR_RESOURCE_DATA_NOT_FOUND); } } else if (memberPropertyChangedArgs != nullptr) { if (memberPropertyChangedArgs->Member != nullptr) { *member = memberPropertyChangedArgs->Member->GetReference(); } else { return __HRESULT_FROM_WIN32(ERROR_RESOURCE_DATA_NOT_FOUND); } } else { return E_INVALIDARG; } return S_OK; } CATCH_RETURN() STDAPI XblMultiplayerEventArgsPropertiesJson( _In_ XblMultiplayerEventArgsHandle argsHandle, _Out_ const char** properties ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(argsHandle == nullptr || properties == nullptr); auto sessionPropertyChangedArgs = dynamic_cast(argsHandle); auto memberPropertyChangedArgs = dynamic_cast(argsHandle); if (sessionPropertyChangedArgs != nullptr) { *properties = sessionPropertyChangedArgs->Properties.data(); } else if (memberPropertyChangedArgs != nullptr) { *properties = memberPropertyChangedArgs->Properties.data(); } else { return E_INVALIDARG; } return S_OK; } CATCH_RETURN() STDAPI XblMultiplayerEventArgsFindMatchCompleted( _In_ XblMultiplayerEventArgsHandle argsHandle, _Out_opt_ XblMultiplayerMatchStatus* matchStatus, _Out_opt_ XblMultiplayerMeasurementFailure* initializationFailureCause ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(argsHandle); auto args = dynamic_cast(argsHandle); RETURN_HR_INVALIDARGUMENT_IF_NULL(args); if (matchStatus != nullptr) { *matchStatus = args->MatchStatus; } if (initializationFailureCause != nullptr) { *initializationFailureCause = args->InitializationFailure; } return S_OK; } CATCH_RETURN() STDAPI XblMultiplayerEventArgsTournamentRegistrationStateChanged( _In_ XblMultiplayerEventArgsHandle argsHandle, _Out_opt_ XblTournamentRegistrationState* registrationState, _Out_opt_ XblTournamentRegistrationReason* registrationReason ) XBL_NOEXCEPT try { UNREFERENCED_PARAMETER(argsHandle); UNREFERENCED_PARAMETER(registrationState); UNREFERENCED_PARAMETER(registrationReason); return E_NOTIMPL; } CATCH_RETURN() STDAPI XblMultiplayerEventArgsTournamentGameSessionReady( _In_ XblMultiplayerEventArgsHandle argsHandle, _Out_ time_t* startTime ) XBL_NOEXCEPT try { UNREFERENCED_PARAMETER(argsHandle); UNREFERENCED_PARAMETER(startTime); return E_NOTIMPL; } CATCH_RETURN() STDAPI XblMultiplayerEventArgsPerformQoSMeasurements( _In_ XblMultiplayerEventArgsHandle argsHandle, _Out_ XblMultiplayerPerformQoSMeasurementsArgs* performQoSMeasurementsArgs ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(argsHandle == nullptr || performQoSMeasurementsArgs == nullptr); auto args = dynamic_cast(argsHandle); RETURN_HR_INVALIDARGUMENT_IF_NULL(args); performQoSMeasurementsArgs->remoteClientsSize = args->remoteClients.size(); performQoSMeasurementsArgs->remoteClients = args->remoteClients.data(); return S_OK; } CATCH_RETURN() ================================================ FILE: Source/Services/Multiplayer/Manager/multiplayer_event_queue.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "multiplayer_manager_internal.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_MANAGER_CPP_BEGIN MultiplayerEventQueue::MultiplayerEventQueue() { } MultiplayerEventQueue::MultiplayerEventQueue(const MultiplayerEventQueue& other) { { std::lock_guard lock(other.m_lock); m_events = other.m_events; } for (auto& event : m_events) { if (event.ErrorMessage) { event.ErrorMessage = Make(event.ErrorMessage); } if (event.EventArgsHandle) { event.EventArgsHandle->AddRef(); } } } MultiplayerEventQueue& MultiplayerEventQueue::operator=(MultiplayerEventQueue other) { std::lock_guard lock(m_lock); std::swap(m_events, other.m_events); return *this; } MultiplayerEventQueue::~MultiplayerEventQueue() { for (const auto& event : m_events) { if (event.ErrorMessage) { Delete(event.ErrorMessage); } if (event.EventArgsHandle) { event.EventArgsHandle->DecRef(); } } } size_t MultiplayerEventQueue::Size() const { return m_events.size(); } bool MultiplayerEventQueue::Empty() const { return m_events.empty(); } void MultiplayerEventQueue::Clear() { std::lock_guard lock(m_lock); for (auto& e : m_events) { if (e.ErrorMessage) { Delete(e.ErrorMessage); } if (e.EventArgsHandle) { e.EventArgsHandle->DecRef(); } } m_events.clear(); } xsapi_internal_vector::const_iterator MultiplayerEventQueue::begin() const { return m_events.begin(); } xsapi_internal_vector::const_iterator MultiplayerEventQueue::end() const { return m_events.end(); } void MultiplayerEventQueue::AddEvent( _In_ XblMultiplayerEventType eventType, _In_ XblMultiplayerSessionType sessionType, _In_ std::shared_ptr eventArgs, _In_ Result error, _In_opt_ context_t context ) { std::lock_guard lock(m_lock); XblMultiplayerEvent event{}; event.EventType = eventType; event.SessionType = sessionType; if (eventArgs) { eventArgs->AddRef(); event.EventArgsHandle = eventArgs.get(); } event.Result = error.Hresult(); event.ErrorMessage = Make(error.ErrorMessage()); #if XSAPI_WINRT event.Context = reinterpret_cast(context); #else event.Context = context; #endif m_events.push_back(event); } void MultiplayerEventQueue::AddEvent(_In_ const XblMultiplayerEvent& multiplayerEvent) { std::lock_guard lock(m_lock); m_events.push_back(multiplayerEvent); auto& addedEvent = m_events.back(); if (addedEvent.ErrorMessage) { addedEvent.ErrorMessage = Make(addedEvent.ErrorMessage); } if (addedEvent.EventArgsHandle) { addedEvent.EventArgsHandle->AddRef(); } } NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_MANAGER_CPP_END ================================================ FILE: Source/Services/Multiplayer/Manager/multiplayer_game_client.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "multiplayer_manager_internal.h" #include "xbox_live_app_config_internal.h" using namespace xbox::services::multiplayer; using namespace xbox::services::system; using namespace xbox::services::legacy; NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_MANAGER_CPP_BEGIN MultiplayerGameClient::MultiplayerGameClient( const TaskQueue& queue ) noexcept : m_queue{ queue.DeriveWorkerQueue() }, m_sessionWriter{ MakeShared(queue) } { } MultiplayerGameClient::MultiplayerGameClient( const TaskQueue& queue, _In_ std::shared_ptr localUserManager ) noexcept : m_queue{ queue.DeriveWorkerQueue() }, m_sessionWriter{ MakeShared(queue, localUserManager) }, m_multiplayerLocalUserManager{ std::move(localUserManager) } { } MultiplayerGameClient::~MultiplayerGameClient() { m_sessionWriter.reset(); } void MultiplayerGameClient::Initialize() { std::weak_ptr weakSessionWriter = shared_from_this(); m_sessionWriter->AddMultiplayerSessionUpdatedHandler([weakSessionWriter](_In_ const std::shared_ptr& updatedSession) { std::shared_ptr pThis(weakSessionWriter.lock()); if (pThis != nullptr) { pThis->UpdateSession(updatedSession); } }); } std::shared_ptr MultiplayerGameClient::LobbyClient() const { // TODO this should be changed such that nothing here needs to depend on the GlobalState. // For now just try to get the global state and return null (which is handled by callers) if // it that fails. auto state{ GlobalState::Get() }; return state ? state->MultiplayerManager()->LobbyClient() : nullptr; } std::shared_ptr MultiplayerGameClient::LobbySession() const { auto lobbyClient{ LobbyClient() }; return lobbyClient ? lobbyClient->Session() : nullptr; } void MultiplayerGameClient::SetGameSessionTemplate( _In_ const xsapi_internal_string& sessionTemplateName ) { m_gameSessionTemplateName = sessionTemplateName; } void MultiplayerGameClient::deep_copy_if_updated( _In_ const MultiplayerGameClient& other ) { std::lock_guard lock(other.m_clientRequestLock); if (other.m_sessionWriter->Session() == nullptr) { m_sessionWriter->UpdateSession(nullptr); m_multiplayerGame = nullptr; } else if (other.m_multiplayerGame == nullptr) { m_multiplayerGame = nullptr; } else if (m_sessionWriter->Session() == nullptr || other.m_sessionWriter->Session()->SessionInfo().ChangeNumber > m_sessionWriter->Session()->SessionInfo().ChangeNumber || other.m_sessionWriter->Session()->ETag() > m_sessionWriter->Session()->ETag()) { m_sessionWriter->UpdateSession(MakeShared(*other.m_sessionWriter->Session())); m_multiplayerGame = MakeShared(*other.m_multiplayerGame); } else if (m_updateNumber != other.m_updateNumber) { m_multiplayerGame = MakeShared(*other.m_multiplayerGame); } } std::shared_ptr MultiplayerGameClient::SessionWriter() const { return m_sessionWriter; } std::shared_ptr MultiplayerGameClient::Game() const { return m_multiplayerGame; } void MultiplayerGameClient::UpdateGame( _In_ const std::shared_ptr& multiplayerGame ) { ++m_updateNumber; m_multiplayerGame = multiplayerGame; } std::shared_ptr MultiplayerGameClient::Session() const { std::lock_guard lock(m_clientRequestLock); return m_sessionWriter->Session(); } void MultiplayerGameClient::UpdateSession( _In_ const std::shared_ptr& updatedSession ) { auto cachedSession = Session(); if (updatedSession == nullptr || cachedSession == nullptr || updatedSession->SessionInfo().ChangeNumber > cachedSession->SessionInfo().ChangeNumber || updatedSession->ETag() != cachedSession->ETag()) { UpdateGameSession(updatedSession); } } void MultiplayerGameClient::UpdateGameSession( _In_ const std::shared_ptr& updatedSession ) { std::lock_guard lock(m_clientRequestLock); m_sessionWriter->UpdateSession(updatedSession); } void MultiplayerGameClient::UpdateObjects( const std::shared_ptr& updatedSession, const std::shared_ptr& lobbySession ) { std::lock_guard lock(m_clientRequestLock); if (updatedSession == nullptr) { UpdateGame(nullptr); } else { auto multiplayerGame = ConvertToMultiplayerGame(updatedSession, lobbySession); UpdateGame(multiplayerGame); } } std::shared_ptr MultiplayerGameClient::ConvertToMultiplayerGame( _In_ const std::shared_ptr& sessionToConvert, _In_ const std::shared_ptr& lobbySession ) { if (sessionToConvert == nullptr) { return nullptr; } std::shared_ptr hostMember = nullptr; xsapi_internal_vector> gameMembers; XblMultiplayerSessionReadLockGuard sessionToConvertSafe(sessionToConvert); for (const auto& member : sessionToConvertSafe.Members()) { auto gameMember = MultiplayerMember::CreateFromSessionMember( &member, lobbySession, sessionToConvert, m_multiplayerLocalUserManager->GetLocalUserMap() ); if (member.DeviceToken.Value[0] != 0 && utils::str_icmp(member.DeviceToken.Value, sessionToConvertSafe.SessionProperties().HostDeviceToken.Value) == 0) { hostMember = gameMember; } gameMembers.push_back(gameMember); } return MakeShared( sessionToConvert, hostMember, gameMembers ); } const MultiplayerEventQueue& MultiplayerGameClient::EventQueue() { // Don't require a lock as STL is multithread read safe return m_multiplayerEventQueue; } void MultiplayerGameClient::ClearPendingQueue() { std::lock_guard lock(m_clientRequestLock); while (!m_pendingRequestQueue.empty()) { m_pendingRequestQueue.pop(); } } void MultiplayerGameClient::AddToPendingQueue( _In_ std::shared_ptr pendingRequest ) { std::lock_guard lock(m_clientRequestLock); m_pendingRequestQueue.push(pendingRequest); } void MultiplayerGameClient::AddToProcessingQueue( _In_ xsapi_internal_vector> processingQueue ) { for (const auto& request : processingQueue) { m_processingQueue.push_back(request); } } void MultiplayerGameClient::RemoveFromProcessingQueue( _In_ uint32_t identifier ) { for (auto request = m_processingQueue.begin(); request != m_processingQueue.end(); ++request) { if ((*request)->Identifier() == identifier) { m_processingQueue.erase(request); break; } } } xsapi_internal_vector> MultiplayerGameClient::GetProcessingQueue() { return m_processingQueue; } MultiplayerEventQueue MultiplayerGameClient::DoWork() { bool expected = false; if (m_pendingCommitInProgress.compare_exchange_strong(expected, true)) { if (m_pendingRequestQueue.size() > 0) { xsapi_internal_vector> processingQueue; bool applySynchronizedChanges = false; bool doneProcessing = false; do { std::lock_guard lock(m_clientRequestLock); { auto pendingRequest = m_pendingRequestQueue.front(); processingQueue.push_back(pendingRequest); m_pendingRequestQueue.pop(); if (m_pendingRequestQueue.size() > 0) { if (pendingRequest->RequestType() != m_pendingRequestQueue.front()->RequestType()) { doneProcessing = true; } } if (!applySynchronizedChanges && pendingRequest->RequestType() == PendingRequestType::SynchronizedChanges) { applySynchronizedChanges = true; } } } while (!doneProcessing && m_pendingRequestQueue.size() > 0); if (processingQueue.size() > 0) { AddToProcessingQueue(processingQueue); std::weak_ptr weakSessionWriter = shared_from_this(); Callback> callback = [weakSessionWriter, processingQueue](Result result) { std::shared_ptr pThis(weakSessionWriter.lock()); if (pThis != nullptr) { std::lock_guard lock(pThis->m_clientRequestLock); { auto eventQueue = result.Payload(); for (const auto& ev : eventQueue) { pThis->m_multiplayerEventQueue.AddEvent(ev); } } for (const auto& processingRequest : processingQueue) { pThis->RemoveFromProcessingQueue(processingRequest->Identifier()); } pThis->m_pendingCommitInProgress.store(false); } }; // TODO we should have a way to configure the queue here if (applySynchronizedChanges) { m_sessionWriter->CommitPendingSynchronizedChanges(processingQueue, XblMultiplayerSessionType::GameSession, callback); } else { m_sessionWriter->CommitPendingChanges(processingQueue, XblMultiplayerSessionType::GameSession, false, callback); } } else { m_pendingCommitInProgress.store(false); } } else { m_pendingCommitInProgress.store(false); } } MultiplayerEventQueue eventQueue; { std::lock_guard lock(m_clientRequestLock); eventQueue = m_multiplayerEventQueue; m_multiplayerEventQueue.Clear(); } return eventQueue; } bool MultiplayerGameClient::IsPendingGameChanges() { std::lock_guard lock(m_clientRequestLock); if (m_pendingRequestQueue.size() > 0) { return true; } if (m_sessionWriter != nullptr) { auto latestSession = m_sessionWriter->Session(); if (latestSession != nullptr && m_multiplayerGame != nullptr && latestSession->SessionInfo().ChangeNumber != m_multiplayerGame->ChangeNumber()) { return true; } } return false; } bool MultiplayerGameClient::IsRequestInProgress() { return m_pendingRequestQueue.size() > 0 || m_processingQueue.size() > 0 || m_multiplayerEventQueue.Size() > 0; } void MultiplayerGameClient::SetLocalMemberPropertiesToRemoteSession( _In_ const std::shared_ptr& localUser, _In_ const Map& propertiesToWrite, _In_ const String& localUserSecureDeviceAddress ) noexcept { if (Session() == nullptr || localUser == nullptr) { return; } // This operation will wait until any pending commits complete, set the provided custom properties for the local user, // and then write the game session. struct SetPropertiesOperation : public std::enable_shared_from_this { SetPropertiesOperation( std::shared_ptr gameClient, std::shared_ptr localUser, const Map& propertiesToWrite, String localUserSecureDeviceAddressToWrite ) noexcept : m_gameClient{ std::move(gameClient) }, m_localUser{ std::move(localUser) }, m_secureDeviceAddressToWrite{std::move(localUserSecureDeviceAddressToWrite)} { for (const auto& prop : propertiesToWrite) { m_propertiesToWrite[prop.first] = JsonDocument{}; JsonUtils::CopyFrom(m_propertiesToWrite[prop.first], prop.second); } } void Run() noexcept { bool commitInProgress = false; if (!m_gameClient->m_pendingCommitInProgress.compare_exchange_weak(commitInProgress, true)) { // Already a commit in progress. Reschedule operation. // Seems like we could add a little sleep here, but keeping behavior consistent since 1806 implementation m_gameClient->m_queue.RunWork([op{ shared_from_this() }] { op->Run(); }); } else if (auto gameSession{ m_gameClient->Session() }) { assert(m_gameClient->m_pendingCommitInProgress); // Set the properties and write the session if it still exists auto sessionToCommit = MakeShared(m_localUser->Xuid(), &gameSession->SessionReference(), nullptr); sessionToCommit->Join(nullptr, false, true); // Ok to use "unsafe" method here because we just created and have the only instance of this session auto sessionUser{ sessionToCommit->CurrentUserInternalUnsafe() }; assert(sessionUser); for (const auto& prop : m_propertiesToWrite) { sessionUser->SetCustomPropertyJson(prop.first, prop.second); } if (!m_secureDeviceAddressToWrite.empty()) { sessionUser->SetSecureDeviceBaseAddress64(m_secureDeviceAddressToWrite); } m_gameClient->m_sessionWriter->WriteSession( m_localUser->Context(), sessionToCommit, XblMultiplayerSessionWriteMode::UpdateExisting, true, [gameClient{ m_gameClient }](Result>) { gameClient->m_pendingCommitInProgress = false; }); } } private: std::shared_ptr m_gameClient; std::shared_ptr m_localUser; Map m_propertiesToWrite; String m_secureDeviceAddressToWrite; }; auto operation = MakeShared(shared_from_this(), localUser, propertiesToWrite, localUserSecureDeviceAddress); operation->Run(); } void MultiplayerGameClient::RemoveStaleUsersFromRemoteSession() { auto gameSession = Session(); if (gameSession == nullptr) { return; } auto xboxLiveContextMap = m_multiplayerLocalUserManager->GetLocalUserMap(); for (auto xboxLiveContext : xboxLiveContextMap) { auto localUser = xboxLiveContext.second; if (localUser != nullptr && localUser->LobbyState() == MultiplayerLocalUserLobbyState::Remove) { // Leave the game session if it exists. if (gameSession != nullptr) { std::weak_ptr weakSessionWriter = shared_from_this(); auto sessionToCommit = MakeShared(localUser->Xuid(), &gameSession->SessionReference(), nullptr); sessionToCommit->Leave(); m_sessionWriter->WriteSession(localUser->Context(), sessionToCommit, XblMultiplayerSessionWriteMode::UpdateExisting, true, [weakSessionWriter](Result> result) { std::shared_ptr pThis(weakSessionWriter.lock()); if (pThis) { auto lobbyClient{ pThis->LobbyClient() }; if (lobbyClient) { lobbyClient->StopAdvertisingGameSession(result); } } }); } } } } HRESULT MultiplayerGameClient::JoinHelper( _In_ std::shared_ptr localUser, _In_ std::shared_ptr session, _In_ bool writeMemberPropertiesFromLobby, _In_ const String& handleId, _In_ MultiplayerSessionCallback callback ) const noexcept { RETURN_HR_IF_LOG_DEBUG(localUser == nullptr || localUser->Context() == nullptr, E_UNEXPECTED, "Call add_local_user() first."); session->Join(nullptr, false, true); XblMultiplayerSessionReadLockGuard sessionSafe(session); if (sessionSafe.CurrentUser() != nullptr) { sessionSafe.CurrentUserInternal()->SetSecureDeviceBaseAddress64(localUser->ConnectionAddress()); } session->SetSessionChangeSubscription(XblMultiplayerSessionChangeTypes::Everything); if (writeMemberPropertiesFromLobby) { auto localMember = XblMultiplayerSession::GetPlayerInSession(localUser->Xuid(), LobbySession()); if (localMember != nullptr && strlen(localMember->CustomPropertiesJson) > 0) { JsonDocument jsonPropertyObj; jsonPropertyObj.Parse(localMember->CustomPropertiesJson); for (const auto& prop : jsonPropertyObj.GetObject()) { session->SetCurrentUserMemberCustomPropertyJson(prop.name.GetString(), prop.value); } } } if (handleId.empty()) { m_sessionWriter->WriteSession( localUser->Context(), session, XblMultiplayerSessionWriteMode::UpdateOrCreateNew, true, std::move(callback) ); } else { m_sessionWriter->WriteSessionByHandle( localUser->Context(), session, XblMultiplayerSessionWriteMode::UpdateOrCreateNew, handleId, true, std::move(callback) ); } return S_OK; } HRESULT MultiplayerGameClient::JoinGameHelper( _In_ const String& sessionName, _In_ Callback> callback ) noexcept { XblMultiplayerSessionReference gameSessionRef = XblMultiplayerSessionReferenceCreate( AppConfig::Instance()->OverrideScid().data(), m_gameSessionTemplateName.data(), sessionName.data() ); return JoinGameBySessionReference(gameSessionRef, [ sharedThis{ shared_from_this() }, callback ] (Result> joinResult) { if (SUCCEEDED(joinResult.Hresult()) && joinResult.Payload() != nullptr && sharedThis->LobbyClient() != nullptr) { // If join_game succeeds, advertise game sessions via the lobby. If the advertising fails, // we simply eat the error as it isn't actionable for the title. sharedThis->LobbyClient()->AdvertiseGameSession(); } callback(joinResult.Hresult()); }); } HRESULT MultiplayerGameClient::JoinGameBySessionReference( _In_ const XblMultiplayerSessionReference& gameSessionRef, _In_ MultiplayerSessionCallback callback ) noexcept { std::shared_ptr primaryContext = m_multiplayerLocalUserManager->GetPrimaryContext(); RETURN_HR_IF_LOG_DEBUG(primaryContext == nullptr, E_UNEXPECTED, "Call add_local_user() before joining."); return JoinGameForAllLocalMembers(gameSessionRef, String{}, false, std::move(callback)); } HRESULT MultiplayerGameClient::JoinGameByHandle( _In_ const String& handleId, _In_ bool createGameIfFailedToJoin, _In_ MultiplayerSessionCallback callback ) noexcept { std::shared_ptr primaryContext = m_multiplayerLocalUserManager->GetPrimaryContext(); RETURN_HR_IF_LOG_DEBUG(primaryContext == nullptr, E_UNEXPECTED, "Call add_local_user() before joining."); return JoinGameForAllLocalMembers(XblMultiplayerSessionReference{}, handleId, createGameIfFailedToJoin, std::move(callback)); } HRESULT MultiplayerGameClient::JoinGameForAllLocalMembers( _In_ const XblMultiplayerSessionReference& sessionRefToJoin, _In_ const String& handleId, _In_ bool createGameIfFailedToJoin, _In_ MultiplayerSessionCallback callback ) noexcept { RETURN_HR_INVALIDARGUMENT_IF(!XblMultiplayerSessionReferenceIsValid(&sessionRefToJoin) && handleId.empty()); std::shared_ptr primaryContext = m_multiplayerLocalUserManager->GetPrimaryContext(); RETURN_HR_IF_LOG_DEBUG(primaryContext == nullptr, E_UNEXPECTED, "Call add_local_user() before joining."); // Leave existing game without updating the latest as the leave may comeback after the actual join and overwrite it. auto cachedSession = Session(); if (cachedSession != nullptr) { LeaveRemoteSession(cachedSession, false, false); } UpdateSession(nullptr); // Join either an existing or new game session. If attempting to join an existing session fails, // optionally creates a new game session from the lobby. When all users have joined the game, a JoinGameCompleted // event will be raised. struct JoinGameOperation : public std::enable_shared_from_this { JoinGameOperation( std::shared_ptr gameClient, const XblMultiplayerSessionReference& sessionRefToJoin, String handleIdToJoin, bool createGameIfFailedToJoin, MultiplayerSessionCallback&& callback ) noexcept : m_gameClient{ std::move(gameClient) }, m_sessionRefToJoin{ sessionRefToJoin }, m_handleIdToJoin{ std::move(handleIdToJoin) }, m_createGameIfFailedToJoin{ createGameIfFailedToJoin }, m_localUsers{ m_gameClient->m_multiplayerLocalUserManager->GetLocalUserMap() }, m_callback{ std::move(callback) } { } void Run() noexcept { JoinGameWithNextUser(); } private: void JoinGameWithNextUser() noexcept { assert(!m_localUsers.empty()); auto localUser{ m_localUsers.begin()->second }; m_localUsers.erase(m_localUsers.begin()); auto sessionToCommit = MakeShared(localUser->Xuid(), &m_sessionRefToJoin, nullptr); HRESULT hr = m_gameClient->JoinHelper(localUser, sessionToCommit, true, m_handleIdToJoin, [ this, sharedThis{ shared_from_this() }, localUser ] (Result> joinSessionResult) { if (Succeeded(joinSessionResult)) { localUser->SetGameState(MultiplayerLocalUserGameState::InSession); } if (Failed(joinSessionResult) || m_localUsers.empty()) { OnJoinCompleted(std::move(joinSessionResult)); } else { JoinGameWithNextUser(); } }); if (FAILED(hr)) { OnJoinCompleted(hr); } } void OnJoinCompleted(Result>&& joinResult) noexcept { if (auto lobbyClient{ m_gameClient->LobbyClient() }) { if (joinResult.Hresult() == HTTP_E_STATUS_NOT_FOUND && !m_handleIdToJoin.empty()) { // We tried to join an existing session but it didn't exist. Create a new game session // from the lobby and clear the existing handleId since it must be invalid. if (m_createGameIfFailedToJoin) { m_callback(lobbyClient->CreateGameFromLobby()); return; } lobbyClient->ClearGameSessionFromLobby(); } else if (Failed(joinResult) && m_handleIdToJoin.empty()) { // Tried to create a new game and we failed. Clear the transfer handle pending state (GameSessionTransferHandle=pending~xuid). lobbyClient->ClearGameSessionFromLobby(); } } if (Succeeded(joinResult)) { m_gameClient->UpdateSession(joinResult.Payload()); m_gameClient->m_multiplayerLocalUserManager->ChangeAllLocalUserGameState(MultiplayerLocalUserGameState::InSession); } { std::lock_guard lock(m_gameClient->m_clientRequestLock); m_gameClient->m_multiplayerEventQueue.AddEvent( XblMultiplayerEventType::JoinGameCompleted, XblMultiplayerSessionType::GameSession, MakeShared(), joinResult ); } m_callback(joinResult); } std::shared_ptr m_gameClient; XblMultiplayerSessionReference m_sessionRefToJoin; String m_handleIdToJoin; bool m_createGameIfFailedToJoin; Map> m_localUsers; MultiplayerSessionCallback m_callback; }; auto operation = MakeShared( shared_from_this(), sessionRefToJoin, handleId, createGameIfFailedToJoin, std::move(callback) ); operation->Run(); return S_OK; } HRESULT MultiplayerGameClient::JoinGameFromLobbyHelper( _In_ MultiplayerSessionCallback callback ) noexcept { std::shared_ptr primaryContext = m_multiplayerLocalUserManager->GetPrimaryContext(); auto lobbySession = LobbySession(); auto lobbyClient = LobbyClient(); RETURN_HR_IF_LOG_DEBUG(primaryContext == nullptr || lobbyClient == nullptr || lobbySession == nullptr, E_UNEXPECTED, "No lobby session exists. Call add_local_user() to create a lobby first."); // Check if the lobby has a game session associated with it to join. XblMultiplayerSessionReadLockGuard lobbySessionSafe(lobbySession); JsonDocument jsonDoc; jsonDoc.Parse(lobbySessionSafe.SessionProperties().SessionCustomPropertiesJson); xsapi_internal_string transferHandle; if (!jsonDoc.HasParseError()) { JsonUtils::ExtractJsonString(jsonDoc, MultiplayerLobbyClient_TransferHandlePropertyName, transferHandle, false); } if (transferHandle.empty()) // aka the field isn't there { return lobbyClient->CreateGameFromLobby(); } String handleId; if (lobbyClient->IsTransferHandleState("pending")) { // Wait for the property changed event to join. m_multiplayerLocalUserManager->ChangeAllLocalUserGameState(MultiplayerLocalUserGameState::PendingJoin); return S_OK; } else if (lobbyClient->IsTransferHandleState("completed")) { handleId = lobbyClient->GetTransferHandle(); } return JoinGameByHandle(handleId, true, std::move(callback)); } void MultiplayerGameClient::LeaveRemoteSession( _In_ std::shared_ptr session, _In_ bool stopAdvertisingGameSession, _In_ bool triggerCompletionEvent ) noexcept { auto processingRequest = MakeShared(); m_processingQueue.push_back(processingRequest); m_sessionWriter->LeaveRemoteSession(session, [ weakThis = std::weak_ptr{ shared_from_this() }, this, stopAdvertisingGameSession, triggerCompletionEvent, processingRequest ] (Result> result) { if (auto sharedThis{ weakThis.lock() }) { auto lobbyClient{ LobbyClient() }; if (stopAdvertisingGameSession && lobbyClient) { lobbyClient->StopAdvertisingGameSession(result); } // Always set your local game session to null upon leaving. UpdateSession(nullptr); if (triggerCompletionEvent) { { std::lock_guard lock(m_clientRequestLock); m_multiplayerEventQueue.AddEvent( XblMultiplayerEventType::LeaveGameCompleted, XblMultiplayerSessionType::GameSession ); } RemoveFromProcessingQueue(processingRequest->Identifier()); } } }); } NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_MANAGER_CPP_END ================================================ FILE: Source/Services/Multiplayer/Manager/multiplayer_game_session.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "multiplayer_manager_internal.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_MANAGER_CPP_BEGIN MultiplayerGameSession::MultiplayerGameSession(): m_changeNumber(0), m_sessionReference{}, m_sessionConstants{}, m_memberInitialization{} { } MultiplayerGameSession::MultiplayerGameSession(_In_ const MultiplayerGameSession& other): m_correlationId(other.m_correlationId), m_changeNumber(other.m_changeNumber), m_sessionReference(other.m_sessionReference), m_host(other.m_host), m_members(other.m_members), m_properties(other.m_properties), m_multiplayerClientManager(other.m_multiplayerClientManager), m_sessionConstants {}, m_memberInitialization{} { DeepCopyConstants(other.m_sessionConstants); } MultiplayerGameSession::MultiplayerGameSession( _In_ std::shared_ptr session, _In_ std::shared_ptr host, _In_ xsapi_internal_vector> members ): m_correlationId(session->SessionInfo().CorrelationId), m_changeNumber(session->SessionInfo().ChangeNumber), m_sessionReference(session->SessionReference()), m_host(std::move(host)), m_members(std::move(members)), m_sessionConstants{}, m_memberInitialization{} { XblMultiplayerSessionReadLockGuard sessionSafe(session); DeepCopyConstants(sessionSafe.SessionConstants()); if (sessionSafe.SessionProperties().SessionCustomPropertiesJson != nullptr) { m_properties = sessionSafe.SessionProperties().SessionCustomPropertiesJson; } } const XblMultiplayerSessionReference& MultiplayerGameSession::SessionReference() const { return m_sessionReference; } const xsapi_internal_string& MultiplayerGameSession::CorrelationId() const { return m_correlationId; } uint64_t MultiplayerGameSession::ChangeNumber() const { return m_changeNumber; } std::shared_ptr MultiplayerGameSession::Host() const { return m_host; } void MultiplayerGameSession::SetHost( _In_ std::shared_ptr hostMember ) { m_host = hostMember; } const xsapi_internal_vector>& MultiplayerGameSession::Members() const { return m_members; } const XblMultiplayerSessionConstants& MultiplayerGameSession::SessionConstants() const { return m_sessionConstants; } const xsapi_internal_string& MultiplayerGameSession::Properties() const { return m_properties; } HRESULT MultiplayerGameSession::SetProperties( _In_ const xsapi_internal_string& name, _In_ const JsonValue& valueJson, _In_opt_ context_t context ) { return m_multiplayerClientManager->SetProperties(m_sessionReference, name, valueJson, context); } bool MultiplayerGameSession::IsHost( _In_ uint64_t xuid ) { if (m_host == nullptr || m_members.size() == 0) { return false; } return xuid == m_host->Xuid(); } HRESULT MultiplayerGameSession::SetSynchronizedHost( _In_ const xsapi_internal_string& deviceToken, _In_opt_ context_t context ) { return m_multiplayerClientManager->SetSynchronizedHost(m_sessionReference, deviceToken, context); } HRESULT MultiplayerGameSession::SetSynchronizedProperties( _In_ const xsapi_internal_string& name, _In_ const JsonValue& valueJson, _In_opt_ context_t context ) { return m_multiplayerClientManager->SetSynchronizedProperties(m_sessionReference, name, valueJson, context); } void MultiplayerGameSession::SetMultiplayerClientManager( _In_ std::shared_ptr clientManager ) { m_multiplayerClientManager = clientManager; } void MultiplayerGameSession::DeepCopyConstants(const XblMultiplayerSessionConstants& other) { m_sessionConstants = other; m_initiatorXuids = xsapi_internal_vector(other.InitiatorXuids, other.InitiatorXuids + other.InitiatorXuidsCount); m_sessionConstants.InitiatorXuids = m_initiatorXuids.data(); if (m_sessionConstants.MemberInitialization) { m_memberInitialization = *m_sessionConstants.MemberInitialization; m_sessionConstants.MemberInitialization = &m_memberInitialization; } if (m_sessionConstants.CustomJson) { m_constantsCustomJson = m_sessionConstants.CustomJson; m_sessionConstants.CustomJson = m_constantsCustomJson.data(); } if (m_sessionConstants.SessionCloudComputePackageConstantsJson) { m_constantsCloudComputePackageJson = m_sessionConstants.SessionCloudComputePackageConstantsJson; m_sessionConstants.SessionCloudComputePackageConstantsJson = m_constantsCloudComputePackageJson.data(); } if (m_sessionConstants.MeasurementServerAddressesJson) { m_constantsMeasurementServerAddressesJson = m_sessionConstants.MeasurementServerAddressesJson; m_sessionConstants.MeasurementServerAddressesJson = m_constantsMeasurementServerAddressesJson.data(); } } NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_MANAGER_CPP_END ================================================ FILE: Source/Services/Multiplayer/Manager/multiplayer_lobby_client.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "multiplayer_manager_internal.h" #include "xbox_live_app_config_internal.h" using namespace xbox::services::multiplayer; using namespace xbox::services::system; using namespace xbox::services::legacy; NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_MANAGER_CPP_BEGIN #ifdef XSAPI_UNIT_TESTS #define RETRY_DELAY_MS 0 #else #define RETRY_DELAY_MS 1000 #endif #define MAX_CONNECTION_ATTEMPTS 3 MultiplayerLobbyClient::MultiplayerLobbyClient( _In_ const TaskQueue& queue ) noexcept : m_queue{ queue.DeriveWorkerQueue() }, m_sessionWriter{ MakeShared(queue) } { } MultiplayerLobbyClient::MultiplayerLobbyClient( _In_ const TaskQueue& queue, _In_ String lobbySessionTemplateName, _In_ std::shared_ptr localUserManager ) noexcept : m_queue{ queue.DeriveWorkerQueue() }, m_lobbySessionTemplateName{ std::move(lobbySessionTemplateName) }, m_sessionWriter{ MakeShared(queue, localUserManager) }, m_multiplayerLocalUserManager{ std::move(localUserManager) } { } MultiplayerLobbyClient::~MultiplayerLobbyClient() noexcept { m_sessionWriter.reset(); } void MultiplayerLobbyClient::Initialize() { std::weak_ptr weakThis = shared_from_this(); m_sessionWriter->AddMultiplayerSessionUpdatedHandler([weakThis](_In_ const std::shared_ptr& updatedSession) { std::shared_ptr pThis(weakThis.lock()); if (pThis != nullptr) { pThis->UpdateSession(updatedSession); } }); } void MultiplayerLobbyClient::deep_copy_if_updated( _In_ const MultiplayerLobbyClient& other ) { std::lock_guard lock(other.m_clientRequestLock); m_joinability = other.m_joinability; if (other.m_sessionWriter->Session() == nullptr) { m_sessionWriter->UpdateSession(nullptr); m_multiplayerLobby = nullptr; } else if (other.m_multiplayerLobby == nullptr) { m_multiplayerLobby = nullptr; } else if (m_sessionWriter->Session() == nullptr || other.m_sessionWriter->Session()->SessionInfo().ChangeNumber > m_sessionWriter->Session()->SessionInfo().ChangeNumber || other.m_sessionWriter->Session()->ETag() > m_sessionWriter->Session()->ETag()) { m_sessionWriter->UpdateSession(MakeShared(*other.m_sessionWriter->Session())); m_multiplayerLobby = MakeShared(*other.m_multiplayerLobby); } else if (m_updateNumber != other.m_updateNumber) { m_multiplayerLobby = MakeShared(*other.m_multiplayerLobby); } } std::shared_ptr MultiplayerLobbyClient::GameClient() { // TODO this should be changed such that nothing here needs to depend on the GlobalState. // For now just try to get the global state and return null (which is handled by callers) if // it that fails. auto state{ GlobalState::Get() }; return state ? state->MultiplayerManager()->GameClient() : nullptr; } std::shared_ptr MultiplayerLobbyClient::GameSession() { auto gameClient{ GameClient() }; return gameClient ? gameClient->Session() : nullptr; } const std::shared_ptr& MultiplayerLobbyClient::SessionWriter() const { return m_sessionWriter; } const std::shared_ptr& MultiplayerLobbyClient::Lobby() const { return m_multiplayerLobby; } void MultiplayerLobbyClient::UpdateLobby( _In_ std::shared_ptr multiplayerLobby ) { ++m_updateNumber; m_multiplayerLobby = multiplayerLobby; } const std::shared_ptr& MultiplayerLobbyClient::Session() const { std::lock_guard lock(m_clientRequestLock); return m_sessionWriter->Session(); } void MultiplayerLobbyClient::UpdateSession( _In_ const std::shared_ptr& updatedSession ) { auto cachedSession = Session(); if (updatedSession == nullptr || cachedSession == nullptr || updatedSession->SessionInfo().ChangeNumber > cachedSession->SessionInfo().ChangeNumber || updatedSession->ETag() != cachedSession->ETag()) { UpdateLobbySession(updatedSession); } } void MultiplayerLobbyClient::UpdateLobbySession( _In_ const std::shared_ptr& updatedSession ) { std::lock_guard lock(m_clientRequestLock); m_sessionWriter->UpdateSession(updatedSession); } void MultiplayerLobbyClient::UpdateObjects( const std::shared_ptr& updatedSession, const std::shared_ptr& gameSession ) { std::lock_guard lock(m_clientRequestLock); if (updatedSession == nullptr) { UpdateLobby(nullptr); m_localLobbyMembers.clear(); m_joinability = XblMultiplayerJoinability::None; } else { UpdateLocalLobbyMembers(updatedSession, gameSession); auto multiplayerLobby = ConvertToMultiplayerLobby(updatedSession, gameSession); UpdateLobby(multiplayerLobby); XblMultiplayerSessionReadLockGuard updatedSessionSafe(updatedSession); m_joinability = MultiplayerManagerUtils::GetJoinability(updatedSessionSafe.SessionProperties()); } } void MultiplayerLobbyClient::UpdateLocalLobbyMembers( _In_ const std::shared_ptr& updatedLobbySession, _In_ const std::shared_ptr& gameSession ) { m_localLobbyMembers.clear(); if (updatedLobbySession == nullptr) { return; } auto localLobbyGameMembers = xsapi_internal_vector>(); auto xboxLiveContextMap = m_multiplayerLocalUserManager->GetLocalUserMap(); for(auto xboxLiveContext : xboxLiveContextMap) { auto localUser = xboxLiveContext.second; if (localUser != nullptr) { auto member = XblMultiplayerSession::GetPlayerInSession(localUser->Xuid(), updatedLobbySession); if (member != nullptr) { auto localMember = MultiplayerMember::CreateFromSessionMember(member, updatedLobbySession, gameSession, true); localLobbyGameMembers.push_back(localMember); } } } m_localLobbyMembers = localLobbyGameMembers; } std::shared_ptr MultiplayerLobbyClient::ConvertToMultiplayerLobby( _In_ const std::shared_ptr& sessionToConvert, _In_ const std::shared_ptr& gameSession ) { if (sessionToConvert == nullptr) { return nullptr; } std::shared_ptr hostMember = nullptr; xsapi_internal_vector> gameMembers; XblMultiplayerSessionReadLockGuard sessionToConvertSafe(sessionToConvert); for (const auto& member : sessionToConvertSafe.Members()) { auto gameMember = MultiplayerMember::CreateFromSessionMember( &member, sessionToConvert, gameSession, m_multiplayerLocalUserManager->GetLocalUserMap() ); if (member.DeviceToken.Value[0] != 0 && utils::str_icmp(member.DeviceToken.Value, sessionToConvertSafe.SessionProperties().HostDeviceToken.Value) == 0) { hostMember = gameMember; } gameMembers.push_back(gameMember); } return MakeShared( sessionToConvert, hostMember, gameMembers, m_localLobbyMembers ); } XblMultiplayerJoinability MultiplayerLobbyClient::Joinability() { return m_joinability; } const MultiplayerEventQueue& MultiplayerLobbyClient::EventQueue() { // Don't require a lock as STL is multithread read safe return m_multiplayerEventQueue; } void MultiplayerLobbyClient::ClearPendingQueue() { std::lock_guard lock(m_clientRequestLock); while (!m_pendingRequestQueue.empty()) { m_pendingRequestQueue.pop(); } } void MultiplayerLobbyClient::AddToPendingQueue( _In_ std::shared_ptr pendingRequest ) { // No lock required. Methods that call this already has a lock m_pendingRequestQueue.push(pendingRequest); } void MultiplayerLobbyClient::AddToProcessingQueue( _In_ xsapi_internal_vector> processingQueue ) { for (const auto& request : processingQueue) { m_processingQueue.push_back(request); } } void MultiplayerLobbyClient::RemoveFromProcessingQueue( _In_ uint32_t identifier ) { for (auto request = m_processingQueue.begin(); request != m_processingQueue.end(); ++request) { if ((*request)->Identifier() == identifier) { m_processingQueue.erase(request); break; } } } xsapi_internal_vector> MultiplayerLobbyClient::GetProcessingQueue() { return m_processingQueue; } HRESULT MultiplayerLobbyClient::AddLocalUser( _In_ xbox_live_user_t user, _In_ MultiplayerLocalUserLobbyState userState, _In_ const xsapi_internal_string& handleId ) { std::lock_guard lock(m_clientRequestLock); RETURN_HR_INVALIDARGUMENT_IF(user == nullptr); auto localUser = m_multiplayerLocalUserManager->GetLocalUserHelper(user); RETURN_HR_IF_LOG_DEBUG(localUser != nullptr && userState == MultiplayerLocalUserLobbyState::Add, E_UNEXPECTED, "User already added."); if (localUser == nullptr) { auto localUserResult = m_multiplayerLocalUserManager->AddUserToXboxLiveContextToMap(user); RETURN_HR_IF_FAILED(localUserResult.Hresult()); localUser = localUserResult.ExtractPayload(); } auto pendingRequest = MakeShared(); pendingRequest->SetLocalUser(localUser); pendingRequest->SetLobbyState(userState); if (userState == MultiplayerLocalUserLobbyState::Join) { if (!handleId.empty()) { pendingRequest->SetLobbyHandleId(handleId); } } AddToPendingQueue(pendingRequest); return S_OK; } void MultiplayerLobbyClient::AddLocalUsers( _In_ xsapi_internal_vector users, _In_ const xsapi_internal_string& handleId ) { for (const auto& user : users) { AddLocalUser(user, MultiplayerLocalUserLobbyState::Join, handleId); } } void MultiplayerLobbyClient::AddLocalUsers( _In_ xsapi_internal_vector users ) { for (const auto& user : users) { AddLocalUser(user, MultiplayerLocalUserLobbyState::Join, xsapi_internal_string()); } } HRESULT MultiplayerLobbyClient::RemoveLocalUser( _In_ xbox_live_user_t user ) { std::lock_guard lock(m_clientRequestLock); RETURN_HR_INVALIDARGUMENT_IF(user == nullptr); auto localUser = m_multiplayerLocalUserManager->GetLocalUserHelper(user); RETURN_HR_IF_LOG_DEBUG(localUser == nullptr || localUser->Context() == nullptr, E_UNEXPECTED, "Call add_local_user() first."); auto pendingRequest = MakeShared(); pendingRequest->SetLocalUser(localUser); pendingRequest->SetLobbyState(MultiplayerLocalUserLobbyState::Leave); AddToPendingQueue(pendingRequest); return S_OK; } void MultiplayerLobbyClient::RemoveAllLocalUsers() { std::lock_guard lock(m_clientRequestLock); if (m_multiplayerLocalUserManager != nullptr) { const auto& xboxLiveContextMap = m_multiplayerLocalUserManager->GetLocalUserMap(); for (auto xboxLiveContext : xboxLiveContextMap) { const auto& localUser = xboxLiveContext.second; if (localUser != nullptr) { auto pendingRequest = MakeShared(); pendingRequest->SetLocalUser(localUser); pendingRequest->SetLobbyState(MultiplayerLocalUserLobbyState::Leave); AddToPendingQueue(pendingRequest); } } } } HRESULT MultiplayerLobbyClient::SetLocalMemberProperties( _In_ xbox_live_user_t user, _In_ const xsapi_internal_string& name, _In_ const JsonValue& valueJson, _In_opt_ context_t context ) { std::lock_guard lock(m_clientRequestLock); RETURN_HR_INVALIDARGUMENT_IF(user == nullptr); auto localUser = m_multiplayerLocalUserManager->GetLocalUserHelper(user); RETURN_HR_IF_LOG_DEBUG(localUser == nullptr || localUser->Context() == nullptr, E_UNEXPECTED, "Call add_local_user() before setting local member properties."); auto pendingRequest = MakeShared(); pendingRequest->SetLocalUserProperties(localUser, name, valueJson, context); AddToPendingQueue(pendingRequest); return S_OK; } HRESULT MultiplayerLobbyClient::DeleteLocalMemberProperties( _In_ xbox_live_user_t user, _In_ const xsapi_internal_string& name, _In_opt_ context_t context ) { std::lock_guard lock(m_clientRequestLock); RETURN_HR_INVALIDARGUMENT_IF(user == nullptr); auto localUser = m_multiplayerLocalUserManager->GetLocalUserHelper(user); RETURN_HR_IF_LOG_DEBUG(localUser == nullptr || localUser->Context() == nullptr, E_UNEXPECTED, "Call add_local_user() before deleting local member properties."); auto pendingRequest = MakeShared(); pendingRequest->SetLocalUserProperties(localUser, name, JsonValue(), context); AddToPendingQueue(pendingRequest); return S_OK; } HRESULT MultiplayerLobbyClient::SetLocalMemberConnectionAddress( _In_ xbox_live_user_t user, _In_ const xsapi_internal_string& address, _In_opt_ context_t context ) { std::lock_guard lock(m_clientRequestLock); RETURN_HR_INVALIDARGUMENT_IF(user == nullptr); auto localUser = m_multiplayerLocalUserManager->GetLocalUserHelper(user); RETURN_HR_IF_LOG_DEBUG(localUser == nullptr || localUser->Context() == nullptr, E_UNEXPECTED, "Call add_local_user() before setting local member connection address."); auto pendingRequest = MakeShared(); pendingRequest->SetLocalUserConnectionAddress(localUser, address, context); AddToPendingQueue(pendingRequest); return S_OK; } HRESULT MultiplayerLobbyClient::SetJoinability( _In_ XblMultiplayerJoinability value, _In_opt_ context_t context ) { RETURN_HR_INVALIDARGUMENT_IF(value < XblMultiplayerJoinability::JoinableByFriends || value > XblMultiplayerJoinability::Closed); auto pendingRequest = MakeShared(); pendingRequest->SetJoinability(value, context); AddToPendingQueue(pendingRequest); return S_OK; } MultiplayerEventQueue MultiplayerLobbyClient::DoWork() { bool expected = false; if (m_pendingCommitInProgress.compare_exchange_strong(expected, true)) { if (m_pendingRequestQueue.size() > 0) { xsapi_internal_vector> processingQueue; XblMultiplayerSessionReference teamSessionRef{}; bool applySynchronizedChanges = false; bool lobbyStateIsJoin = false; bool joinByHandleId = false; bool doneProcessing = false; do { std::lock_guard lock(m_clientRequestLock); { auto pendingRequest = m_pendingRequestQueue.front(); processingQueue.push_back(pendingRequest); m_pendingRequestQueue.pop(); if (m_pendingRequestQueue.size() > 0) { if (pendingRequest->RequestType() != m_pendingRequestQueue.front()->RequestType()) { doneProcessing = true; } } if (!applySynchronizedChanges && pendingRequest->RequestType() == PendingRequestType::SynchronizedChanges) { applySynchronizedChanges = true; } if (pendingRequest->LocalUser() != nullptr) { auto lobbyState = pendingRequest->LobbyState(); if (lobbyState == MultiplayerLocalUserLobbyState::Join) { lobbyStateIsJoin = true; // Leave existing lobby without updating the latest as the leave may comeback after the actual join and overwrite it. auto latestSession = m_sessionWriter->Session(); if (latestSession != nullptr) { LeaveRemoteSession(latestSession); } m_sessionWriter->UpdateSession(nullptr); UpdateLobby(nullptr); m_localLobbyMembers.clear(); m_joinability = XblMultiplayerJoinability::None; if (!pendingRequest->LobbyHandleId().empty()) { pendingRequest->LocalUser()->SetLobbyHandleId(pendingRequest->LobbyHandleId()); joinByHandleId = true; } } if (lobbyState != MultiplayerLocalUserLobbyState::Unknown) { pendingRequest->LocalUser()->SetLobbyState(lobbyState); } pendingRequest->LocalUser()->SetWriteChangesToService(true); } } } while (!doneProcessing && m_pendingRequestQueue.size() > 0); if (processingQueue.size() > 0) { AddToProcessingQueue(processingQueue); std::weak_ptr weakThis = shared_from_this(); MultiplayerEventQueueCallback callback = [weakThis, processingQueue](Result result) { std::shared_ptr pThis(weakThis.lock()); if (pThis != nullptr) { std::lock_guard lock(pThis->m_clientRequestLock); { auto eventQueue = result.Payload(); for (const auto& ev : eventQueue) { pThis->m_multiplayerEventQueue.AddEvent(ev); } } for (const auto& processingRequest : processingQueue) { pThis->RemoveFromProcessingQueue(processingRequest->Identifier()); } pThis->m_pendingCommitInProgress.store(false); } }; if (applySynchronizedChanges) { m_sessionWriter->CommitPendingSynchronizedChanges(processingQueue, XblMultiplayerSessionType::LobbySession, callback); } else { Vector xuidsInOrder; if (lobbyStateIsJoin) { // If users are joining from an invite than the first multiplayer_client_pending_request in processingQueue references was the invited user. // We want the invited user to join first since it is possible that users in the local graph might not meet the criteria // to join the invited session until the invited user joins. Imagine that the inviting session has the session privacy set to joinable_by_friends // and only the invited user is a friend of the inviting user. If any additional users attempt to join the inviting session before the invited user // than the join fails for the whole list of users. for (auto pendingRequest : processingQueue) { if (pendingRequest->LocalUser()) { xuidsInOrder.push_back(pendingRequest->LocalUser()->Xuid()); } } } CommitPendingLobbyChanges(xuidsInOrder, joinByHandleId, teamSessionRef, callback); } } else { m_pendingCommitInProgress.store(false); } } else { m_pendingCommitInProgress.store(false); } } MultiplayerEventQueue eventQueue; { std::lock_guard lock(m_clientRequestLock); eventQueue = m_multiplayerEventQueue; m_multiplayerEventQueue.Clear(); } return eventQueue; } bool MultiplayerLobbyClient::IsPendingLobbyChanges() { std::lock_guard lock(m_clientRequestLock); if (m_pendingRequestQueue.size() > 0 || IsPendingLobbyLocalUserChanges()) { return true; } if (m_sessionWriter != nullptr) { auto latestSession = m_sessionWriter->Session(); if (latestSession != nullptr && m_multiplayerLobby != nullptr && latestSession->SessionInfo().ChangeNumber != m_multiplayerLobby->ChangeNumber()) { return true; } } return false; } bool MultiplayerLobbyClient::IsRequestInProgress() { return m_pendingRequestQueue.size() > 0 || m_processingQueue.size() > 0 || m_multiplayerEventQueue.Size() > 0; } bool MultiplayerLobbyClient::IsPendingLobbyLocalUserChanges() { const auto& xboxLiveContextMap = m_multiplayerLocalUserManager->GetLocalUserMap(); for (auto xboxLiveContext : xboxLiveContextMap) { const auto& localUser = xboxLiveContext.second; if (localUser != nullptr && localUser->WriteChangesToService()) { return true; } } return false; } HRESULT MultiplayerLobbyClient::CommitPendingLobbyChanges( _In_ const Vector& xuidsInOrder, _In_ bool joinByHandleId, _In_ XblMultiplayerSessionReference sessionRef, _In_ MultiplayerEventQueueCallback callback ) noexcept { if (IsPendingLobbyLocalUserChanges()) { // All local member changes always happen on the lobby session. std::shared_ptr latestLobbySession; auto lobbySession = Session(); if (lobbySession == nullptr) { auto primaryContext = m_multiplayerLocalUserManager->GetPrimaryContext(); if (joinByHandleId) { latestLobbySession = MakeShared(primaryContext->Xuid(), nullptr, nullptr); } else { if (sessionRef.Scid[0] == 0) { String sessionName = utils::create_guid(true); sessionRef = XblMultiplayerSessionReferenceCreate( AppConfig::Instance()->OverrideScid().data(), m_lobbySessionTemplateName.data(), sessionName.data() ); } latestLobbySession = MakeShared(primaryContext->Xuid(), &sessionRef, nullptr); } } else { latestLobbySession = MakeShared(*lobbySession); } // Committing local user changes will also update any pending lobby properties. return CommitLobbyChanges(xuidsInOrder, latestLobbySession, [callback](Result result) { callback(Result{ result.Hresult(), result.ErrorMessage() }); }); } else { bool isGameInProgress = GameSession() != nullptr; return m_sessionWriter->CommitPendingChanges(GetProcessingQueue(), XblMultiplayerSessionType::LobbySession, isGameInProgress, callback); } } HRESULT MultiplayerLobbyClient::CommitLobbyChanges( _In_ const Vector& xuidsInOrder, _In_ std::shared_ptr lobbySessionToCommit, _In_ Callback> callback ) noexcept { // For each User in xuidsInOrder, prepare an XblMultiplayerSession with their pending lobby changes // and write it to MPSD. Will also update the user's multiplayer activity & set the session host token // as neeeded. Consistent with 1806 XDK behavior, if any individual MPSD call fails unexpectedly, // we abort the entire operation and return that result. struct CommitLobbyChangesOperation : public std::enable_shared_from_this { CommitLobbyChangesOperation( std::shared_ptr lobbyClient, const Vector& xuidsInOrder, std::shared_ptr lobbySessionToCommit, Callback>&& callback ) noexcept : m_lobbyClient{ std::move(lobbyClient) }, m_lobbySessionToCommit{ std::move(lobbySessionToCommit) }, m_callback{ std::move(callback) } { auto& localUsers{ m_lobbyClient->GetLocalUserMap() }; if (xuidsInOrder.empty()) { for (auto pair : localUsers) { m_users.push_back(pair.second); } } else { for (auto xuid : xuidsInOrder) { auto iter = localUsers.find(xuid); if (iter != localUsers.end()) { m_users.push_back(iter->second); } } } } void Run() noexcept { CommitChangesForNextUser(); } private: void CommitChangesForNextUser() noexcept { if (m_users.empty()) { Complete(S_OK); } else { auto user{ m_users.front() }; m_users.pop_front(); CommitChangesForUser(user); } } void CommitChangesForUser( std::shared_ptr user ) noexcept { // Prepare a session with changes specific to this user. // Ok to use MPSD 'unsafe' methods here since we created the session locally, thus it is the only reference auto session = MakeShared(user->Xuid(), &m_lobbySessionToCommit->SessionReference(), nullptr); switch (user->LobbyState()) { case MultiplayerLocalUserLobbyState::Add: { session->Join(); if (!user->ConnectionAddress().empty()) { session->CurrentUserInternalUnsafe()->SetSecureDeviceBaseAddress64(user->ConnectionAddress()); } session->SetSessionChangeSubscription(XblMultiplayerSessionChangeTypes::Everything); // Only set this for the first user. Could cause a race condition if XblMultiplayerJoinability was changed during the second write. if (m_setJoinability) { m_setJoinability = false; session->SetJoinRestriction(XblMultiplayerSessionRestriction::Followed); session->SetReadRestriction(XblMultiplayerSessionRestriction::Followed); String jsonValueStr = MultiplayerManagerUtils::ConvertJoinabilityToString(XblMultiplayerJoinability::JoinableByFriends); JsonDocument jsonValue; jsonValue.SetString(jsonValueStr.c_str(), jsonValue.GetAllocator()); session->SetSessionCustomPropertyJson(MultiplayerLobbyClient_JoinabilityPropertyName, jsonValue); } break; } case MultiplayerLocalUserLobbyState::Join: { session->Join(); if (!user->ConnectionAddress().empty()) { session->CurrentUserInternalUnsafe()->SetSecureDeviceBaseAddress64(user->ConnectionAddress()); } session->SetSessionChangeSubscription(XblMultiplayerSessionChangeTypes::Everything); break; } case MultiplayerLocalUserLobbyState::InSession: { // Forces to set the current user to yourself. session->Join(); break; } case MultiplayerLocalUserLobbyState::Leave: { if (XblMultiplayerSession::IsPlayerInSession(user->Xuid(), m_lobbySessionToCommit)) { session->Leave(); } else { // In a scenario where the user was removed before he could have been added. m_lobbyClient->UserStateChanged({ xbl_error_code::logic_error, "The user was removed before they could be added" }, MultiplayerLocalUserLobbyState::Add, user->Xuid()); m_lobbyClient->UserStateChanged({ xbl_error_code::no_error }, MultiplayerLocalUserLobbyState::Leave, user->Xuid()); m_removeStaleUsers = true; user->SetLobbyState(MultiplayerLocalUserLobbyState::Remove); // Since the user has been removed, no work to be done for this user this->CommitChangesForNextUser(); return; } break; } default: { break; } } bool isGameInProgress = m_lobbyClient->GameSession() != nullptr; // Update any pending local user or lobby session properties. for (auto& request : m_lobbyClient->m_processingQueue) { request->AppendPendingChanges(session, user, isGameInProgress); } // Now write the session to MPSD HRESULT hr{ S_OK }; if (user->LobbyHandleId().empty()) { hr = m_lobbyClient->m_sessionWriter->WriteSession( user->Context(), session, XblMultiplayerSessionWriteMode::UpdateOrCreateNew, true, [ op{ shared_from_this() }, user ] (Result> result) { op->HandleWriteSessionResult(user, std::move(result)); }); } else { hr = m_lobbyClient->m_sessionWriter->WriteSessionByHandle( user->Context(), session, XblMultiplayerSessionWriteMode::UpdateOrCreateNew, user->LobbyHandleId(), true, [ op{ shared_from_this() }, user ] (Result> result) { op->HandleWriteSessionResult(user, std::move(result)); }); user->SetLobbyHandleId(String{}); } if (FAILED(hr)) { Complete(hr); } } void HandleWriteSessionResult( std::shared_ptr user, Result>&& sessionResult ) noexcept { m_lobbyClient->UserStateChanged(sessionResult, user->LobbyState(), user->Xuid()); m_lobbyClient->HandleLobbyChangeEvents(sessionResult, user, m_lobbyClient->m_processingQueue); if (Failed(sessionResult)) { m_lobbyClient->JoinLobbyCompleted(sessionResult, user->Xuid()); // If we failed to join the lobby, make sure the local user context is cleaned up switch (user->LobbyState()) { case MultiplayerLocalUserLobbyState::Add: case MultiplayerLocalUserLobbyState::Join: { m_removeStaleUsers = true; user->SetLobbyState(MultiplayerLocalUserLobbyState::Remove); } default: { break; } } Complete(sessionResult); return; } switch (user->LobbyState()) { case MultiplayerLocalUserLobbyState::Add: case MultiplayerLocalUserLobbyState::Join: { auto updatedSession = sessionResult.ExtractPayload(); m_lobbyClient->UpdateSession(updatedSession); auto oldLobbyState = user->LobbyState(); user->SetLobbyState(MultiplayerLocalUserLobbyState::InSession); if (oldLobbyState == MultiplayerLocalUserLobbyState::Join) { m_lobbyClient->HandleJoinLobbyCompleted(sessionResult, user->Xuid()); } if (oldLobbyState == MultiplayerLocalUserLobbyState::Add && m_lobbyClient->ShouldUpdateHostToken(user, updatedSession)) { UpdateHostDeviceToken(user, updatedSession); } else { SetActivityForUser(user, updatedSession); } return; } case MultiplayerLocalUserLobbyState::Leave: { m_removeStaleUsers = true; // If you leave the session you were advertising, you don't need to clear the activity. user->SetLobbyState(MultiplayerLocalUserLobbyState::Remove); // Intentional fallthrough } default: { // Work done for this user, continue operation CommitChangesForNextUser(); return; } } } void UpdateHostDeviceToken( std::shared_ptr user, std::shared_ptr session ) noexcept { XblMultiplayerSessionReadLockGuard sessionSafe{ session }; session->SetHostDeviceToken(sessionSafe.CurrentUser()->DeviceToken); auto hr = m_lobbyClient->m_sessionWriter->WriteSession( user->Context(), session, XblMultiplayerSessionWriteMode::UpdateExisting, true, [ op{ shared_from_this() }, user ] (Result> writeHostTokenResult) { if (Failed(writeHostTokenResult)) { op->Complete(writeHostTokenResult); } else { op->SetActivityForUser(user, writeHostTokenResult.ExtractPayload()); } }); if (FAILED(hr)) { Complete(hr); } } void SetActivityForUser( std::shared_ptr user, std::shared_ptr session ) noexcept { auto hr = user->Context()->MultiplayerService()->SetActivity( session->SessionReference(), AsyncContext>{ m_lobbyClient->m_queue, [ op{ shared_from_this() } ] (Result setActivityResult) { if (Failed(setActivityResult)) { op->Complete(std::move(setActivityResult)); } else { op->CommitChangesForNextUser(); } } }); if (FAILED(hr)) { Complete(hr); } } void Complete(Result&& result) { LOGS_DEBUG << __FUNCTION__ << ": HRESULT=" << result.Hresult() << ", ErrorMessage=" << result.ErrorMessage(); if (m_removeStaleUsers) { m_lobbyClient->RemoveStaleXboxLiveContextFromMap(); } m_callback(result); } std::shared_ptr m_lobbyClient; List> m_users; std::shared_ptr m_lobbySessionToCommit; bool m_setJoinability{ true }; bool m_removeStaleUsers{ false }; Callback> m_callback; }; auto operation = MakeShared( shared_from_this(), xuidsInOrder, lobbySessionToCommit, std::move(callback) ); operation->Run(); return S_OK; } void MultiplayerLobbyClient::RemoveStaleXboxLiveContextFromMap() { auto gameClient = GameClient(); if (gameClient != nullptr) { gameClient->RemoveStaleUsersFromRemoteSession(); } // Remove stale context, switch primary context and re-activate multiplayer events. m_multiplayerLocalUserManager->RemoveStaleLocalUsersFromMap(); } const xsapi_internal_map>& MultiplayerLobbyClient::GetLocalUserMap() { return m_multiplayerLocalUserManager->GetLocalUserMap(); } std::shared_ptr MultiplayerLobbyClient::GetPrimaryContext() { return m_multiplayerLocalUserManager->GetPrimaryContext(); } HRESULT MultiplayerLobbyClient::CreateGameFromLobby() noexcept { auto lobbySession = Session(); RETURN_HR_IF_LOG_DEBUG(lobbySession == nullptr, E_UNEXPECTED, "No lobby session exists. Call add_local_user() to create a lobby first"); std::shared_ptr primaryContext = m_multiplayerLocalUserManager->GetPrimaryContext(); RETURN_HR_IF_LOG_DEBUG(primaryContext == nullptr, E_UNEXPECTED, "Call add_local_user() before joining."); // Try to write transfer handle to the lobby as pending state // If succeeded, then create the game session, and join it. The game session will later be advertised. // If failed, complete the operation. The game session will later be joined when we get a SessionPropertyChanged event struct CreateGameOperation : public std::enable_shared_from_this { CreateGameOperation( std::shared_ptr lobbyClient, std::shared_ptr lobbySession, uint64_t primaryXuid ) noexcept : m_lobbyClient{ std::move(lobbyClient) }, m_sessionToCommit{ MakeShared(*lobbySession) }, m_primaryXuid{ primaryXuid } { } void Run() noexcept { SetTransferHandleToPending(); } private: void SetTransferHandleToPending() { // Add transfer handle property to lobby session Stringstream jsonValue; jsonValue << "pending~" << m_primaryXuid; JsonDocument value; value.SetString(jsonValue.str().data(), value.GetAllocator()); m_sessionToCommit->SetSessionCustomPropertyJson(MultiplayerLobbyClient_TransferHandlePropertyName, value); auto hr = m_lobbyClient->m_sessionWriter->CommitSynchronizedChanges(m_sessionToCommit, [ sharedThis{ shared_from_this() } ] (Result> result) { sharedThis->HandleWriteSessionResult(std::move(result)); }); if (FAILED(hr)) { Complete(hr); } } void HandleWriteSessionResult(Result>&& writeSessionResult) { auto op{ shared_from_this() }; auto gameClient = m_lobbyClient->GameClient(); if (gameClient == nullptr) { op->Complete(Result{ E_FAIL, "MultiplayerGameClient destroyed" }); } else if (writeSessionResult.Hresult() == HTTP_E_STATUS_PRECOND_FAILED) { if (m_lobbyClient->IsTransferHandleState("completed") || m_lobbyClient->IsTransferHandleState("pending")) { // We couldn't set transfer handle, but a game session already exists. Join that instead HRESULT hr = gameClient->JoinGameFromLobbyHelper([op](Result> result) { op->Complete(result); }); if (FAILED(hr)) { Complete(hr); } } else if(m_setTransferHandleAttempt++ < MAX_CONNECTION_ATTEMPTS) { std::shared_ptr sessionToCommitTemp = writeSessionResult.ExtractPayload(); HRESULT hr = S_OK; if (sessionToCommitTemp != nullptr) // handle rare case where an empty body is returned { m_sessionToCommit = sessionToCommitTemp; // Retry setting transfer handle after a small delay hr = m_lobbyClient->m_queue.RunWork([op] { op->SetTransferHandleToPending(); }, RETRY_DELAY_MS); } else { hr = writeSessionResult.Hresult(); } if (FAILED(hr)) { op->Complete(hr); } } else { // We were unable to set the transfer handle after MAX_CONNECTION_ATTEMPTS m_lobbyClient->UpdateSession(m_sessionToCommit); Result opResult{ writeSessionResult.Hresult(), "Max connection attempts exceeded" }; m_lobbyClient->JoinLobbyCompleted( opResult, m_primaryXuid ); op->Complete(std::move(opResult)); } } else { // Should we not check for WriteSession failures other than 412? // 1806 XDK assumes the transfer handle was successfully set, creates a game session, and joins it auto sessionName = utils::create_guid(true); HRESULT hr = gameClient->JoinGameHelper(sessionName, [op](Result result) { op->Complete(std::move(result)); }); if (FAILED(hr)) { op->Complete(hr); } } } void Complete(Result&& result) noexcept { // This operation is strange in that nothing directly looks at the result. // The result is exposed to title via events, but MPM doesn't seem to handle failures. // Trying to keep the purpose of each operation as consistent with 1806 XDK as possible, so leaving as is for now. LOGS_DEBUG << __FUNCTION__ << ": HRESULT=" << result.Hresult() << ", ErrorMessage=" << result.ErrorMessage(); } std::shared_ptr m_lobbyClient; std::shared_ptr m_sessionToCommit; uint64_t const m_primaryXuid; uint8_t m_setTransferHandleAttempt{ 0 }; }; auto operation = MakeShared( shared_from_this(), lobbySession, primaryContext->Xuid() ); operation->Run(); return S_OK; } void MultiplayerLobbyClient::AdvertiseGameSession() noexcept { std::shared_ptr primaryContext = m_multiplayerLocalUserManager->GetPrimaryContext(); if (primaryContext == nullptr || GameSession() == nullptr) { return; } // This operation performs several sub-steps as follows: // 1. If a pending commit is currently in progress, wait until it is completed // 2. Establish a lobby session & commit any pending lobby changes // 3. Create an MPSD transfer handle from the lobby session to the game session // 4. Update the lobby session properties (custom transfer handle & joinability) struct AdvertiseGameSessionOperation : public std::enable_shared_from_this { AdvertiseGameSessionOperation( std::shared_ptr lobbyClient, std::shared_ptr primaryContext ) noexcept : m_lobbyClient{ std::move(lobbyClient) }, m_primaryContext{ std::move(primaryContext) } { } void Run() noexcept { bool expected{ false }; if (!m_lobbyClient->m_pendingCommitInProgress.compare_exchange_strong(expected, true)) { // Wait until there isn't commit in progress before continuing. // Reschedule op with no delay (1806 XDK behavior) HRESULT hr = m_lobbyClient->m_queue.RunWork([op{ shared_from_this() }] { op->Run(); }); if (FAILED(hr)) { Complete(hr); } } else { EstablishLobbySession(); } } private: void EstablishLobbySession() noexcept { auto lobbySession{ m_lobbyClient->Session() }; if (!lobbySession) { if (m_lobbyClient->m_multiplayerLocalUserManager->GetLocalUserMap().empty()) { // There are no remaining local users. Complete the operation return Complete(S_OK); } m_lobbyClient->m_multiplayerLocalUserManager->ChangeAllLocalUserLobbyState(MultiplayerLocalUserLobbyState::Add); auto hr = m_lobbyClient->CommitPendingLobbyChanges(Vector{}, false, XblMultiplayerSessionReference{}, [ op{ shared_from_this() } ] (Result joinLobbyResult) { op->m_lobbyClient->m_pendingCommitInProgress = false; op->m_lobbyClient->JoinLobbyCompleted(joinLobbyResult, op->m_primaryContext->Xuid()); if (Failed(joinLobbyResult)) { op->Complete(joinLobbyResult); } else { op->CreateTransferHandle(op->m_lobbyClient->Session()); } }); if (FAILED(hr)) { Complete(hr); } } else { m_lobbyClient->m_pendingCommitInProgress = false; CreateTransferHandle(lobbySession); } } void CreateTransferHandle(std::shared_ptr lobbySession) noexcept { JsonDocument lobbyProperties; { XblMultiplayerSessionReadLockGuard lobbySessionSafe(lobbySession); lobbyProperties.Parse(lobbySessionSafe.SessionProperties().SessionCustomPropertiesJson); } if (!lobbyProperties.HasMember(MultiplayerLobbyClient_TransferHandlePropertyName) || (m_lobbyClient->IsTransferHandleState("pending") && m_lobbyClient->GetTransferHandle() == utils::uint64_to_internal_string(m_primaryContext->Xuid()))) { auto gameSession{ m_lobbyClient->GameSession() }; if (!gameSession) { return Complete(Result{E_ABORT, "GameSession null"}); } auto hr = m_primaryContext->MultiplayerService()->SetTransferHandle( gameSession->SessionReference(), lobbySession->SessionReference(), AsyncContext>{ m_lobbyClient->m_queue, [ op{ shared_from_this() }, lobbySession ] (Result result) { if (Succeeded(result)) { op->UpdateLobbySession(MakeShared(*lobbySession), result.ExtractPayload()); } else { if (result.Hresult() == HTTP_E_STATUS_FORBIDDEN) { // By MPSD design, if the game session doesn't exist on transfer handle creation, it throws a 403. op->m_lobbyClient->ClearGameSessionFromLobby(); } op->Complete(result); } } }); if (FAILED(hr)) { return Complete(hr); } } } void UpdateLobbySession( std::shared_ptr lobbySession, String&& transferHandle ) noexcept { if (m_lobbyClient->m_joinability == XblMultiplayerJoinability::DisableWhileGameInProgress) { lobbySession->SetClosed(true); } Stringstream jsonHandleValue; jsonHandleValue << "completed~" << transferHandle; JsonDocument jsonValue; jsonValue.SetString(jsonHandleValue.str().data(), jsonValue.GetAllocator()); lobbySession->SetSessionCustomPropertyJson( MultiplayerLobbyClient_TransferHandlePropertyName, jsonValue ); HRESULT hr = m_lobbyClient->m_sessionWriter->WriteSession( m_primaryContext, lobbySession, XblMultiplayerSessionWriteMode::UpdateExisting, true, [ op{ shared_from_this() } ] (Result> result) { op->Complete(result); }); if (FAILED(hr)) { Complete(hr); } } void Complete(Result&& result) { // This operation is strange in that nothing directly looks at the result. // The result is exposed to title via events, but MPM doesn't seem to handle failures. // Trying to keep the purpose of each operation as consistent with 1806 XDK as possible, so leaving as is for now. LOGS_DEBUG << __FUNCTION__ << ": HRESULT=" << result.Hresult() << ", ErrorMessage=" << result.ErrorMessage(); } std::shared_ptr m_lobbyClient; std::shared_ptr m_primaryContext; }; auto operation = MakeShared(shared_from_this(), primaryContext); operation->Run(); } void MultiplayerLobbyClient::StopAdvertisingGameSession( _In_ Result> result ) { bool bClearGameSession = false; auto lobbySession = Session(); XblMultiplayerSessionReadLockGuard lobbySessionSafe(lobbySession); if (SUCCEEDED(result.Hresult()) && result.Payload() == nullptr) { // When you are the last person to leave the session (handling err 204). bClearGameSession = true; } else if (result.Payload() != nullptr && lobbySession != nullptr) { auto latestGameSession = result.Payload(); bool found = false; auto members = lobbySessionSafe.Members(); for (const auto& member : members) { if (XblMultiplayerSession::IsPlayerInSession(member.Xuid, latestGameSession)) { found = true; break; } } if (!found && members.size() > 0) { bClearGameSession = true; } } if (bClearGameSession) { ClearGameSessionFromLobby(); } } void MultiplayerLobbyClient::ClearGameSessionFromLobby() { auto lobbySession = Session(); if (lobbySession != nullptr) { auto lobbySessionCopy = MakeShared(*lobbySession); if (m_joinability == XblMultiplayerJoinability::DisableWhileGameInProgress) { // Re-open the lobby to be joinable when leaving the game session. lobbySessionCopy->SetClosed(false); } lobbySessionCopy->DeleteSessionCustomPropertyJson(MultiplayerLobbyClient_TransferHandlePropertyName); m_sessionWriter->WriteSession(m_multiplayerLocalUserManager->GetPrimaryContext(), lobbySessionCopy, XblMultiplayerSessionWriteMode::UpdateExisting, true, nullptr); } } bool MultiplayerLobbyClient::IsTransferHandleState( _In_ const xsapi_internal_string& state ) { auto lobbySession = Session(); if (lobbySession == nullptr) { return false; } XblMultiplayerSessionReadLockGuard lobbySessionSafe(lobbySession); JsonDocument jsonDoc; jsonDoc.Parse(lobbySessionSafe.SessionProperties().SessionCustomPropertiesJson); if (!jsonDoc.HasParseError()) { xsapi_internal_string transferHandleProp; if (SUCCEEDED(JsonUtils::ExtractJsonString(jsonDoc, MultiplayerLobbyClient_TransferHandlePropertyName, transferHandleProp, true))) { xsapi_internal_vector transferHandleSplit = utils::string_split_internal(transferHandleProp, '~'); if (transferHandleSplit.size() > 0 && utils::str_icmp_internal(transferHandleSplit[0], state) == 0) { return true; } } } return false; } xsapi_internal_string MultiplayerLobbyClient::GetTransferHandle() { auto lobbySession = Session(); if (lobbySession == nullptr) { return xsapi_internal_string(); } XblMultiplayerSessionReadLockGuard lobbySessionSafe(lobbySession); JsonDocument jsonDoc; jsonDoc.Parse(lobbySessionSafe.SessionProperties().SessionCustomPropertiesJson); if (!jsonDoc.HasParseError()) { xsapi_internal_string transferHandleProp; if (SUCCEEDED(JsonUtils::ExtractJsonString(jsonDoc, MultiplayerLobbyClient_TransferHandlePropertyName, transferHandleProp, true))) { xsapi_internal_vector transferHandleSplit = utils::string_split_internal(transferHandleProp, '~'); if (transferHandleSplit.size() == 2) { return transferHandleSplit[1]; } } } return xsapi_internal_string(); } void MultiplayerLobbyClient::LeaveRemoteSession( _In_ std::shared_ptr session ) { m_sessionWriter->LeaveRemoteSession(session, nullptr); } bool MultiplayerLobbyClient::ShouldUpdateHostToken( _In_ std::shared_ptr localUser, _In_ std::shared_ptr session ) { if(XblMultiplayerSession::HostMember(session) != nullptr) { return false; } if(!localUser->IsPrimaryXboxLiveContext()) { return false; } // If the local user is already the host, skip it. if (XblMultiplayerSession::IsHost(utils::uint64_to_internal_string(localUser->Xuid()), session)) { return false; } return true; } void MultiplayerLobbyClient::UserStateChanged( _In_ Result error, _In_ MultiplayerLocalUserLobbyState localUserLobbyState, _In_ uint64_t xboxUserId ) { if (localUserLobbyState != MultiplayerLocalUserLobbyState::Add && localUserLobbyState != MultiplayerLocalUserLobbyState::Leave) { return; } std::shared_ptr eventArgs; XblMultiplayerEventType eventType = XblMultiplayerEventType::UserAdded; if (localUserLobbyState == MultiplayerLocalUserLobbyState::Add) { eventType = XblMultiplayerEventType::UserAdded; std::shared_ptr userAddedEventArgs = MakeShared(xboxUserId); eventArgs = std::dynamic_pointer_cast(userAddedEventArgs); } else if (localUserLobbyState == MultiplayerLocalUserLobbyState::Leave) { eventType = XblMultiplayerEventType::UserRemoved; std::shared_ptr userRemovedEventArgs = MakeShared(xboxUserId); eventArgs = std::dynamic_pointer_cast(userRemovedEventArgs); } else { // We only care about user_added & user_removed. return; } AddEvent(eventType, eventArgs, error); } void MultiplayerLobbyClient::HandleLobbyChangeEvents( _In_ Result error, _In_ std::shared_ptr localUser, _In_ const xsapi_internal_vector>& processingQueue ) { xsapi_internal_map localUsersMap; xsapi_internal_string localUserSecureDeviceAddress; for (auto& request : processingQueue) { // Handle local user change events if (localUser != nullptr && request->LocalUser() != nullptr && localUser->Xuid() == request->LocalUser()->Xuid()) { if (!request->LocalUserSecureDeivceAddress().empty()) { AddEvent( XblMultiplayerEventType::LocalMemberConnectionAddressWriteCompleted, nullptr, error, request->Context() ); localUserSecureDeviceAddress = request->LocalUserSecureDeivceAddress(); } // Fire events for each of the properties for (const auto& prop : request->LocalUserProperties()) { AddEvent( XblMultiplayerEventType::LocalMemberPropertyWriteCompleted, nullptr, error, request->Context() ); localUsersMap[prop.first] = JsonDocument(); JsonUtils::CopyFrom(localUsersMap[prop.first], prop.second); } } } // Handle lobby change events (session properties, etc) auto eventQueue = m_sessionWriter->HandleEvents(processingQueue, error, XblMultiplayerSessionType::LobbySession); if (eventQueue.Size() > 0) { std::lock_guard lock(m_clientRequestLock); for (auto& ev : eventQueue) { m_multiplayerEventQueue.AddEvent(ev); } } if (localUsersMap.size() != 0 || !localUserSecureDeviceAddress.empty()) { // write member properties to the game session. auto gameClient = GameClient(); if (gameClient != nullptr) { gameClient->SetLocalMemberPropertiesToRemoteSession(localUser, localUsersMap, localUserSecureDeviceAddress); } } } void MultiplayerLobbyClient::HandleJoinLobbyCompleted( _In_ Result error, _In_ uint64_t joinedXuid ) { JoinLobbyCompleted(error, joinedXuid); if (FAILED(error.Hresult())) { // If joining the lobby succeeded, check if it has a game session associated with it to join. auto gameClient = GameClient(); if (gameClient == nullptr) { return; } auto lobbySession = Session(); // Join game via the transfer handle. XblMultiplayerSessionReadLockGuard lobbySessionSafe(lobbySession); JsonDocument lobbyProperties; lobbyProperties.Parse(lobbySessionSafe.SessionProperties().SessionCustomPropertiesJson); if (!lobbyProperties.HasParseError()) { if (lobbyProperties.IsObject() && lobbyProperties.MemberCount() > 0) { xsapi_internal_string transferHandle; if (IsTransferHandleState("completed")) { transferHandle = GetTransferHandle(); } else { // No existing game session return; } gameClient->JoinGameByHandle(transferHandle, false, nullptr); } } } } void MultiplayerLobbyClient::JoinLobbyCompleted( _In_ Result error, _In_ uint64_t invitedXboxUserId ) { std::shared_ptr joinLobbyEventArgs = MakeShared( invitedXboxUserId ); AddEvent( XblMultiplayerEventType::JoinLobbyCompleted, std::dynamic_pointer_cast(joinLobbyEventArgs), error ); } // TODO remove this and add locking to event queue class void MultiplayerLobbyClient::AddEvent( _In_ XblMultiplayerEventType eventType, _In_opt_ std::shared_ptr eventArgs, _In_opt_ Result error, _In_opt_ context_t context ) { std::lock_guard lock(m_clientRequestLock); m_multiplayerEventQueue.AddEvent(eventType, XblMultiplayerSessionType::LobbySession, eventArgs, error, context ); } NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_MANAGER_CPP_END ================================================ FILE: Source/Services/Multiplayer/Manager/multiplayer_lobby_session.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "multiplayer_manager_internal.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_MANAGER_CPP_BEGIN MultiplayerLobbySession::MultiplayerLobbySession( _In_ std::shared_ptr multiplayerClientManagerInstance ): m_multiplayerClientManager(std::move(multiplayerClientManagerInstance)), m_changeNumber(0), m_sessionReference{}, m_sessionConstants{}, m_memberInitialization{} { } MultiplayerLobbySession::MultiplayerLobbySession(): m_changeNumber(0), m_sessionReference{}, m_sessionConstants{}, m_memberInitialization{} { } MultiplayerLobbySession::MultiplayerLobbySession(_In_ const MultiplayerLobbySession& other): m_multiplayerClientManager(other.m_multiplayerClientManager), m_correlationId(other.m_correlationId), m_changeNumber(other.m_changeNumber), m_sessionReference(other.m_sessionReference), m_host(other.m_host), m_members(other.m_members), m_localMembers(other.m_localMembers), m_customPropertiesJson(other.m_customPropertiesJson) { DeepCopyConstants(other.m_sessionConstants); } MultiplayerLobbySession::MultiplayerLobbySession( _In_ std::shared_ptr session, _In_ std::shared_ptr host, _In_ const xsapi_internal_vector>& members, _In_ const xsapi_internal_vector>& localMembers ) : m_correlationId(session->SessionInfo().CorrelationId), m_changeNumber(session->SessionInfo().ChangeNumber), m_sessionReference(session->SessionReference()), m_host(std::move(host)), m_members(members), m_localMembers(localMembers) { XblMultiplayerSessionReadLockGuard sessionSafe(session); m_customPropertiesJson = sessionSafe.SessionProperties().SessionCustomPropertiesJson; m_sessionConstants = sessionSafe.SessionConstants(); DeepCopyConstants(sessionSafe.SessionConstants()); } MultiplayerLobbySession::~MultiplayerLobbySession() { } HRESULT MultiplayerLobbySession::AddLocalUser( _In_ xbox_live_user_t user ) { if (m_multiplayerClientManager->LatestPendingRead() == nullptr) { m_multiplayerClientManager->Initialize(); } return m_multiplayerClientManager->LobbyClient()->AddLocalUser(user, MultiplayerLocalUserLobbyState::Add); } HRESULT MultiplayerLobbySession::RemoveLocalUser( _In_ xbox_live_user_t user ) { RETURN_HR_IF_LOG_DEBUG(m_multiplayerClientManager->LatestPendingRead() == nullptr, E_UNEXPECTED, "No user added. Call add_local_user() first."); return m_multiplayerClientManager->LobbyClient()->RemoveLocalUser(user); } HRESULT MultiplayerLobbySession::SetLocalMemberProperties( _In_ xbox_live_user_t user, _In_ const xsapi_internal_string& name, _In_ const JsonValue& valueJson, _In_opt_ context_t context ) { RETURN_HR_IF_LOG_DEBUG(m_multiplayerClientManager->LatestPendingRead() == nullptr, E_UNEXPECTED, "No user added. Call add_local_user() first."); return m_multiplayerClientManager->LobbyClient()->SetLocalMemberProperties(user, name, valueJson, context); } HRESULT MultiplayerLobbySession::DeleteLocalMemberProperties( _In_ xbox_live_user_t user, _In_ const xsapi_internal_string& name, _In_opt_ context_t context ) { RETURN_HR_IF_LOG_DEBUG(m_multiplayerClientManager->LatestPendingRead() == nullptr, E_UNEXPECTED, "No user added. Call add_local_user() first."); return m_multiplayerClientManager->LobbyClient()->DeleteLocalMemberProperties(user, name, context); } HRESULT MultiplayerLobbySession::SetLocalMemberConnectionAddress( _In_ xbox_live_user_t user, _In_ const xsapi_internal_string& connectionAddress, _In_opt_ context_t context ) { RETURN_HR_IF_LOG_DEBUG(m_multiplayerClientManager->LatestPendingRead() == nullptr, E_UNEXPECTED, "No user added. Call add_local_user() first."); return m_multiplayerClientManager->LobbyClient()->SetLocalMemberConnectionAddress(user, connectionAddress, context); } const XblMultiplayerSessionReference& MultiplayerLobbySession::SessionReference() const { return m_sessionReference; } const xsapi_internal_string& MultiplayerLobbySession::CorrelationId() const { return m_correlationId; } uint64_t MultiplayerLobbySession::ChangeNumber() const { return m_changeNumber; } std::shared_ptr MultiplayerLobbySession::Host() const { return m_host; } void MultiplayerLobbySession::SetHost( _In_ std::shared_ptr hostMember ) { m_host = hostMember; } const xsapi_internal_vector>& MultiplayerLobbySession::Members() const { return m_members; } const XblMultiplayerSessionConstants& MultiplayerLobbySession::SessionConstants() const { return m_sessionConstants; } const xsapi_internal_vector>& MultiplayerLobbySession::LocalMembers() const { return m_localMembers; } const xsapi_internal_string& MultiplayerLobbySession::CustomPropertiesJson() const { return m_customPropertiesJson; } HRESULT MultiplayerLobbySession::SetProperties( _In_ const xsapi_internal_string& name, _In_ const JsonValue& valueJson, _In_opt_ context_t context ) { RETURN_HR_IF_LOG_DEBUG(m_multiplayerClientManager->LatestPendingRead() == nullptr, E_UNEXPECTED, "No user added. Call add_local_user() first."); return m_multiplayerClientManager->SetProperties(m_sessionReference, name, valueJson, context); } bool MultiplayerLobbySession::IsHost( _In_ uint64_t xuid ) { if (m_host == nullptr || m_members.size() == 0) { return false; } return xuid == m_host->Xuid(); } HRESULT MultiplayerLobbySession::SetSynchronizedHost( _In_ const xsapi_internal_string& hostDeviceToken, _In_opt_ context_t context ) { RETURN_HR_IF_LOG_DEBUG(m_multiplayerClientManager->LatestPendingRead() == nullptr, E_UNEXPECTED, "No user added. Call add_local_user() first."); return m_multiplayerClientManager->SetSynchronizedHost(m_sessionReference, hostDeviceToken, context); } HRESULT MultiplayerLobbySession::SetSynchronizedProperties( _In_ const xsapi_internal_string& name, _In_ const JsonValue& valueJson, _In_opt_ context_t context ) { RETURN_HR_IF_LOG_DEBUG(m_multiplayerClientManager->LatestPendingRead() == nullptr, E_UNEXPECTED, "No user added. Call add_local_user() first."); return m_multiplayerClientManager->SetSynchronizedProperties(m_sessionReference, name, valueJson, context); } void MultiplayerLobbySession::SetMultiplayerClientManager( _In_ std::shared_ptr clientManager ) { m_multiplayerClientManager = clientManager; } #if HC_PLATFORM_IS_MICROSOFT HRESULT MultiplayerLobbySession::InviteFriends( _In_ xbox_live_user_t user, _In_ const xsapi_internal_string& contextStringId, _In_ const xsapi_internal_string& customActivationContext ) { RETURN_HR_IF_LOG_DEBUG(m_multiplayerClientManager->LatestPendingRead() == nullptr, E_UNEXPECTED, "No user added. Call add_local_user() first."); return m_multiplayerClientManager->InviteFriends(user, contextStringId, customActivationContext); } #endif HRESULT MultiplayerLobbySession::InviteUsers( _In_ xbox_live_user_t user, _In_ const xsapi_internal_vector& xboxUserIds, _In_ const xsapi_internal_string& contextStringId, _In_ const xsapi_internal_string& customActivationContext ) { RETURN_HR_IF_LOG_DEBUG(m_multiplayerClientManager->LatestPendingRead() == nullptr, E_UNEXPECTED, "No user added. Call add_local_user() first."); return m_multiplayerClientManager->InviteUsers(user, xboxUserIds, contextStringId, customActivationContext); } void MultiplayerLobbySession::DeepCopyConstants(const XblMultiplayerSessionConstants& other) { m_sessionConstants = other; m_initiatorXuids = xsapi_internal_vector(other.InitiatorXuids, other.InitiatorXuids + other.InitiatorXuidsCount); m_sessionConstants.InitiatorXuids = m_initiatorXuids.data(); if (m_sessionConstants.MemberInitialization) { m_memberInitialization = *m_sessionConstants.MemberInitialization; m_sessionConstants.MemberInitialization = &m_memberInitialization; } if (m_sessionConstants.CustomJson) { m_constantsCustomJson = m_sessionConstants.CustomJson; m_sessionConstants.CustomJson = m_constantsCustomJson.data(); } if (m_sessionConstants.SessionCloudComputePackageConstantsJson) { m_constantsCloudComputePackageJson = m_sessionConstants.SessionCloudComputePackageConstantsJson; m_sessionConstants.SessionCloudComputePackageConstantsJson = m_constantsCloudComputePackageJson.data(); } if (m_sessionConstants.MeasurementServerAddressesJson) { m_constantsMeasurementServerAddressesJson = m_sessionConstants.MeasurementServerAddressesJson; m_sessionConstants.MeasurementServerAddressesJson = m_constantsMeasurementServerAddressesJson.data(); } } NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_MANAGER_CPP_END ================================================ FILE: Source/Services/Multiplayer/Manager/multiplayer_local_user.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "multiplayer_manager_internal.h" using namespace xbox::services::multiplayer; NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_MANAGER_CPP_BEGIN MultiplayerLocalUser::MultiplayerLocalUser( _In_ User&& user, _In_ uint64_t xuid, _In_ bool isPrimary ) : m_sessionChangedContext(0), m_subscriptionLostContext(0), m_rtaResyncContext(0), m_writeChangesToService(false), m_xuid(xuid), m_lobbyState(MultiplayerLocalUserLobbyState::Unknown), m_gameState(MultiplayerLocalUserGameState::Unknown), m_isPrimaryXboxLiveContext(isPrimary) { // TODO parameterize RTAManager m_xboxLiveContextImpl = XblContext::Make(std::move(user)); m_xboxLiveContextImpl->Initialize(GlobalState::Get()->RTAManager()); m_xboxLiveContextImpl->Settings()->SetHttpUserAgent(HttpCallAgent::MultiplayerManager); } MultiplayerLocalUser::~MultiplayerLocalUser() { m_xboxLiveContextImpl.reset(); } uint64_t MultiplayerLocalUser::Xuid() const { return m_xuid; } MultiplayerLocalUserLobbyState MultiplayerLocalUser::LobbyState() const { return m_lobbyState; } void MultiplayerLocalUser::SetLobbyState( _In_ MultiplayerLocalUserLobbyState userState ) { m_lobbyState = userState; if (userState == MultiplayerLocalUserLobbyState::Add || userState == MultiplayerLocalUserLobbyState::Join || userState == MultiplayerLocalUserLobbyState::Leave ) { m_writeChangesToService = true; } } MultiplayerLocalUserGameState MultiplayerLocalUser::GameState() const { return m_gameState; } void MultiplayerLocalUser::SetGameState( _In_ MultiplayerLocalUserGameState userState ) { m_gameState = userState; } const xsapi_internal_string& MultiplayerLocalUser::LobbyHandleId() const { return m_lobbyHandleId; } void MultiplayerLocalUser::SetLobbyHandleId(_In_ const xsapi_internal_string& handleId) { m_lobbyHandleId = handleId; } const xsapi_internal_string& MultiplayerLocalUser::ConnectionAddress() const { return m_connectionAddress; } void MultiplayerLocalUser::SetConnectionAddress(_In_ const xsapi_internal_string& address) { m_connectionAddress = address; } bool MultiplayerLocalUser::IsPrimaryXboxLiveContext() const { return m_isPrimaryXboxLiveContext; } void MultiplayerLocalUser::SetIsPrimaryXboxLiveContext( _In_ bool isPrimary ) { m_isPrimaryXboxLiveContext = isPrimary; } bool MultiplayerLocalUser::WriteChangesToService() const { return m_writeChangesToService; } void MultiplayerLocalUser::SetWriteChangesToService(_In_ bool value) { m_writeChangesToService = value; } std::shared_ptr MultiplayerLocalUser::Context() const { return m_xboxLiveContextImpl; } XblFunctionContext MultiplayerLocalUser::SessionChangedContext() const { return m_sessionChangedContext; } void MultiplayerLocalUser::SetSessionChangedContext( _In_ XblFunctionContext functionContext ) { m_sessionChangedContext = functionContext; } XblFunctionContext MultiplayerLocalUser::ConnectionIdChangedContext() const { return m_connectionIdChangedContext; } void MultiplayerLocalUser::SetConnectionIdChangedContext( _In_ XblFunctionContext functionContext ) { m_connectionIdChangedContext = functionContext; } XblFunctionContext MultiplayerLocalUser::SubscriptionLostContext() const { return m_subscriptionLostContext; } void MultiplayerLocalUser::SetSubscriptionLostContext( _In_ XblFunctionContext functionContext ) { m_subscriptionLostContext = functionContext; } XblFunctionContext MultiplayerLocalUser::RtaResyncContext() const { return m_rtaResyncContext; } void MultiplayerLocalUser::SetRtaResyncContext( _In_ XblFunctionContext functionContext ) { m_rtaResyncContext = functionContext; } NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_MANAGER_CPP_END ================================================ FILE: Source/Services/Multiplayer/Manager/multiplayer_local_user_manager.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "multiplayer_manager_internal.h" #include "real_time_activity_manager.h" using namespace xbox::services; using namespace xbox::services::multiplayer; NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_MANAGER_CPP_BEGIN MultiplayerLocalUserManager::~MultiplayerLocalUserManager() { ChangeAllLocalUserLobbyState(MultiplayerLocalUserLobbyState::Remove); RemoveStaleLocalUsersFromMap(); m_sessionChangeEventHandler.clear(); m_multiplayerSubscriptionLostEventHandler.clear(); m_rtaResyncEventHandler.clear(); } const xsapi_internal_map>& MultiplayerLocalUserManager::GetLocalUserMap() { std::lock_guard lock(m_lock); return m_localUserRequestMap; } std::shared_ptr MultiplayerLocalUserManager::GetContext( _In_ uint64_t xboxUserId ) { std::lock_guard lock(m_lock); auto iter = m_localUserRequestMap.find(xboxUserId); if (iter != m_localUserRequestMap.end()) { return iter->second->Context(); } return nullptr; } std::shared_ptr MultiplayerLocalUserManager::GetLocalUser( _In_ xbox_live_user_t user ) { if (user == nullptr) { return nullptr; } std::lock_guard lock(m_lock); return GetLocalUserHelper(user); } std::shared_ptr MultiplayerLocalUserManager::GetLocalUser( _In_ uint64_t xuid ) { std::lock_guard lock(m_lock); return GetLocalUserHelper(xuid); } std::shared_ptr MultiplayerLocalUserManager::GetLocalUserHelper( _In_ xbox_live_user_t user ) { auto wrapUserResult{ User::WrapHandle(user) }; if (Failed(wrapUserResult)) { return nullptr; } return GetLocalUserHelper(wrapUserResult.Payload().Xuid()); } std::shared_ptr MultiplayerLocalUserManager::GetLocalUserHelper( _In_ uint64_t xuid ) { auto iter = m_localUserRequestMap.find(xuid); if (iter != m_localUserRequestMap.end()) { return iter->second; } return nullptr; } std::shared_ptr MultiplayerLocalUserManager::GetPrimaryContext() { std::lock_guard lock(m_lock); return m_primaryXboxLiveContext; } void MultiplayerLocalUserManager::ChangeAllLocalUserLobbyState( _In_ MultiplayerLocalUserLobbyState state ) { std::lock_guard lock(m_lock); for(const auto& user : m_localUserRequestMap) { const auto& localUser = user.second; if (localUser != nullptr) { localUser->SetLobbyState(state); } } } void MultiplayerLocalUserManager::ChangeAllLocalUserGameState( _In_ MultiplayerLocalUserGameState state ) { std::lock_guard lock(m_lock); for(const auto& user : m_localUserRequestMap) { const auto& localUser = user.second; if (localUser != nullptr) { localUser->SetGameState(state); } } } bool MultiplayerLocalUserManager::IsLocalUserGameState( _In_ MultiplayerLocalUserGameState state ) { std::lock_guard lock(m_lock); for(const auto& user : m_localUserRequestMap) { const auto& localUser = user.second; if (localUser != nullptr && localUser->GameState() != state) { return false; } } return true; } Result> MultiplayerLocalUserManager::AddUserToXboxLiveContextToMap( _In_ xbox_live_user_t user ) { auto wrapUserResult{ User::WrapHandle(user) }; if (Failed(wrapUserResult)) { return wrapUserResult.Hresult(); } uint64_t xboxUserId = wrapUserResult.Payload().Xuid(); bool isPrimary = m_localUserRequestMap.size() == 0 ? true : false; auto iter = m_localUserRequestMap.find(xboxUserId); if (iter == m_localUserRequestMap.end()) { auto innerWrapUserResult{ User::WrapHandle(user) }; if (Failed(innerWrapUserResult)) { return innerWrapUserResult.Hresult(); } auto localUser = MakeShared( innerWrapUserResult.ExtractPayload(), xboxUserId, isPrimary ); auto ret = m_localUserRequestMap.insert(std::pair>(xboxUserId, localUser)); iter = ret.first; if (isPrimary) { m_primaryXboxLiveContext = localUser->Context(); } // Activate events only for all users ActivateMultiplayerEvents(localUser); } return iter->second; } void MultiplayerLocalUserManager::RemoveStaleLocalUsersFromMap() { std::lock_guard lock(m_lock); bool swtichPrimaryXboxLiveContext = false; for(auto iter = m_localUserRequestMap.begin(); iter != m_localUserRequestMap.end(); ) { const auto& localUser = iter->second; if (localUser != nullptr && localUser->LobbyState() == MultiplayerLocalUserLobbyState::Remove) { // De-activate rta events for the old user. DeactivateMultiplayerEvents(localUser); swtichPrimaryXboxLiveContext = localUser->IsPrimaryXboxLiveContext(); m_localUserRequestMap.erase(iter++); } else { ++iter; } } if (m_localUserRequestMap.size() == 0) { m_primaryXboxLiveContext = nullptr; return; } if (swtichPrimaryXboxLiveContext) { // Assign the first guy in the map to be the primary user. std::shared_ptr user = m_localUserRequestMap.begin()->second; if (user != nullptr) { user->SetIsPrimaryXboxLiveContext(true); m_primaryXboxLiveContext = user->Context(); } } } void MultiplayerLocalUserManager::ActivateMultiplayerEvents( _In_ const std::shared_ptr& localUser ) { XSAPI_ASSERT(localUser != nullptr); if (localUser == nullptr) return; std::weak_ptr weakThis = shared_from_this(); auto xboxUserId = localUser->Xuid(); if (auto globalState{ GlobalState::Get() }) { globalState->RTAManager()->Activate(localUser->Context()->User()); XblFunctionContext rtaResyncContext = globalState->RTAManager()->AddResyncHandler(localUser->Context()->User(), [weakThis] { std::shared_ptr pThis(weakThis.lock()); if (pThis != nullptr) { pThis->OnResyncMessageReceived(); } }); localUser->SetRtaResyncContext(rtaResyncContext); } if (!localUser->Context()->MultiplayerService()->SubscriptionsEnabled()) { localUser->Context()->MultiplayerService()->EnableMultiplayerSubscriptions(); XblFunctionContext sessionChangedContext = localUser->Context()->MultiplayerService()->AddMultiplayerSessionChangedHandler([weakThis](_In_ XblMultiplayerSessionChangeEventArgs args) { std::shared_ptr pThis(weakThis.lock()); if (pThis != nullptr) { pThis->OnSessionChanged(args); } }); localUser->SetSessionChangedContext(sessionChangedContext); XblFunctionContext connectionIdChangedContext = localUser->Context()->MultiplayerService()->AddMultiplayerConnectionIdChangedHandler([weakThis](const String&) { std::shared_ptr pThis(weakThis.lock()); if (pThis != nullptr) { pThis->OnConnectionIdChanged(); } }); localUser->SetConnectionIdChangedContext(connectionIdChangedContext); XblFunctionContext subscriptionLostContext = localUser->Context()->MultiplayerService()->AddMultiplayerSubscriptionLostHandler([weakThis, xboxUserId](void) { std::shared_ptr pThis(weakThis.lock()); if (pThis != nullptr) { pThis->OnSubscriptionsLost(xboxUserId); } }); localUser->SetSubscriptionLostContext(subscriptionLostContext); } } void MultiplayerLocalUserManager::DeactivateMultiplayerEvents( _In_ const std::shared_ptr& localUser ) { XSAPI_ASSERT(localUser != nullptr); if (localUser == nullptr) { return; } // TODO allow setting queue HRESULT hr = m_queue.RunWork([localUser = std::shared_ptr{ localUser }] { if (localUser->Context()->MultiplayerService()->SubscriptionsEnabled()) { localUser->Context()->MultiplayerService()->RemoveMultiplayerSessionChangedHandler(localUser->SessionChangedContext()); localUser->Context()->MultiplayerService()->DisableMultiplayerSubscriptions(); localUser->Context()->MultiplayerService()->RemoveMultiplayerConnectionIdChangedHandler(localUser->ConnectionIdChangedContext()); } auto globalState{ GlobalState::Get() }; if (globalState != nullptr) { globalState->RTAManager()->RemoveResyncHandler(localUser->Context()->User(), localUser->RtaResyncContext()); globalState->RTAManager()->Deactivate(localUser->Context()->User()); } }); if (FAILED(hr)) { LOGS_INFO << __FUNCTION__ << " RunAsync failed with hr=" << hr; } } XblFunctionContext MultiplayerLocalUserManager::AddMultiplayerSessionChangedHandler( _In_ Callback handler ) { std::lock_guard lock(m_subscriptionLock); XblFunctionContext context = 0; if (handler != nullptr) { context = m_sessionChangeEventHandlerCounter; m_sessionChangeEventHandler[m_sessionChangeEventHandlerCounter++] = std::move(handler); } return context; } void MultiplayerLocalUserManager::RemoveMultiplayerSessionChangedHandler( _In_ XblFunctionContext context ) { std::lock_guard lock(m_subscriptionLock); m_sessionChangeEventHandler.erase(context); } void MultiplayerLocalUserManager::OnSessionChanged( _In_ const XblMultiplayerSessionChangeEventArgs& args ) { xsapi_internal_unordered_map> sessionChangeEventHandlerCopy; { std::lock_guard lock(m_subscriptionLock); sessionChangeEventHandlerCopy = m_sessionChangeEventHandler; } for(auto& handler : sessionChangeEventHandlerCopy) { XSAPI_ASSERT(handler.second != nullptr); if (handler.second != nullptr) { try { handler.second(args); } catch (...) { LOG_ERROR("MultiplayerLocalUserManager::on_session_changed call threw an exception"); } } } } XblFunctionContext MultiplayerLocalUserManager::AddMultiplayerConnectionIdChangedHandler( _In_ Function handler ) { std::lock_guard lock(m_subscriptionLock); XblFunctionContext context = 0; if (handler != nullptr) { context = m_multiplayerConnectionIdChangedEventHandlerCounter; m_multiplayerConnectionIdChangedEventHandler[m_multiplayerConnectionIdChangedEventHandlerCounter++] = std::move(handler); } return context; } void MultiplayerLocalUserManager::RemoveMultiplayerConnectionIdChangedHandler( _In_ XblFunctionContext context ) { std::lock_guard lock(m_subscriptionLock); m_multiplayerConnectionIdChangedEventHandler.erase(context); } void MultiplayerLocalUserManager::OnConnectionIdChanged( ) { xsapi_internal_unordered_map> multiplayerConnectionIdChangedEventHandlerCopy; { std::lock_guard lock(m_subscriptionLock); multiplayerConnectionIdChangedEventHandlerCopy = m_multiplayerConnectionIdChangedEventHandler; } for (auto& handler : multiplayerConnectionIdChangedEventHandlerCopy) { XSAPI_ASSERT(handler.second != nullptr); if (handler.second != nullptr) { try { handler.second(); } catch (...) { LOG_ERROR("MultiplayerLocalUserManager::on_connection_id_changed call threw an exception"); } } } } XblFunctionContext MultiplayerLocalUserManager::AddMultiplayerSubscriptionLostHandler( _In_ Function handler ) { std::lock_guard lock(m_subscriptionLock); XblFunctionContext context = 0; if (handler != nullptr) { context = m_multiplayerSubscriptionLostEventHandlerCounter; m_multiplayerSubscriptionLostEventHandler[m_multiplayerSubscriptionLostEventHandlerCounter++] = std::move(handler); } return context; } void MultiplayerLocalUserManager::RemoveMultiplayerSubscriptionLostHandler( _In_ XblFunctionContext context ) { std::lock_guard lock(m_subscriptionLock); m_multiplayerSubscriptionLostEventHandler.erase(context); } void MultiplayerLocalUserManager::OnSubscriptionsLost( _In_ uint64_t xuid ) { { // Only fire this for the last user. // Note: This will be fired from the previous user's deactivation. std::lock_guard lock(m_lock); auto user = GetLocalUserHelper(xuid); if (user == nullptr && m_localUserRequestMap.size() > 0) { return; } } xsapi_internal_unordered_map> multiplayerSubscriptionLostEventHandlerCopy; { std::lock_guard lock(m_subscriptionLock); multiplayerSubscriptionLostEventHandlerCopy = m_multiplayerSubscriptionLostEventHandler; } for(auto& handler : multiplayerSubscriptionLostEventHandlerCopy) { XSAPI_ASSERT(handler.second != nullptr); if (handler.second != nullptr) { try { handler.second(); } catch (...) { LOG_ERROR("MultiplayerLocalUserManager::on_subscriptions_lost call threw an exception"); } } } } XblFunctionContext MultiplayerLocalUserManager::AddRtaResyncHandler( _In_ Function handler ) { std::lock_guard lock(m_subscriptionLock); XblFunctionContext context = 0; if (handler != nullptr) { context = m_rtaResyncEventHandlerCounter; m_rtaResyncEventHandler[m_rtaResyncEventHandlerCounter++] = std::move(handler); } return context; } void MultiplayerLocalUserManager::RemoveRtaResyncHandler( _In_ XblFunctionContext context ) { std::lock_guard lock(m_subscriptionLock); m_rtaResyncEventHandler.erase(context); } void MultiplayerLocalUserManager::OnResyncMessageReceived() { xsapi_internal_unordered_map> rtaResyncEventHandlerCopy; { std::lock_guard lock(m_subscriptionLock); rtaResyncEventHandlerCopy = m_rtaResyncEventHandler; } for(auto& handler : rtaResyncEventHandlerCopy) { XSAPI_ASSERT(handler.second != nullptr); if (handler.second != nullptr) { try { handler.second(); } catch (...) { LOG_ERROR("MultiplayerLocalUserManager::on_resync_message_received call threw an exception"); } } } } NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_MANAGER_CPP_END ================================================ FILE: Source/Services/Multiplayer/Manager/multiplayer_manager.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "multiplayer_manager_internal.h" using namespace xbox::services; using namespace xbox::services::system; using namespace xbox::services::multiplayer; using namespace xbox::services::multiplayer::manager; NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_MANAGER_CPP_BEGIN MultiplayerManager::~MultiplayerManager() { if (m_queue) { XTaskQueueCloseHandle(m_queue); } if (m_multiplayerClientManager != nullptr) { m_multiplayerClientManager->Shutdown(); } } bool MultiplayerManager::IsInitialized() { return m_multiplayerClientManager != nullptr; } void MultiplayerManager::Initialize( _In_ const xsapi_internal_string& lobbySessionTemplateName, _In_opt_ XTaskQueueHandle asyncQueue ) { XSAPI_ASSERT(!lobbySessionTemplateName.empty()); if (asyncQueue) { XTaskQueueDuplicateHandle(asyncQueue, &m_queue); } std::lock_guard guard(m_lock); if(m_multiplayerClientManager != nullptr) { m_multiplayerClientManager->Shutdown(); m_multiplayerLobbySession = nullptr; m_multiplayerGameSession = nullptr; m_multiplayerClientManager = nullptr; } m_multiplayerClientManager = MakeShared(lobbySessionTemplateName, m_queue); m_multiplayerClientManager->RegisterLocalUserManagerEvents(); m_multiplayerLobbySession = MakeShared(m_multiplayerClientManager); } bool MultiplayerManager::IsDirty() { return m_isDirty; } const MultiplayerEventQueue& MultiplayerManager::DoWork() { std::lock_guard guard(m_lock); m_eventQueue.Clear(); if (!IsInitialized()) { // The title hasn't called initialize() on the manager yet. m_isDirty = false; return m_eventQueue; } else if (!m_multiplayerClientManager->IsUpdateAvailable()) { m_isDirty = false; // To handle the scenario of returning InvitedXuid info in the join_lobby_completed event if (m_multiplayerClientManager->EventQueue().Size() > 0) { m_eventQueue = m_multiplayerClientManager->EventQueue(); m_multiplayerClientManager->ClearEventQueue(); } return m_eventQueue; } try { m_isDirty = true; m_eventQueue = m_multiplayerClientManager->DoWork(); std::shared_ptr clientRequest = m_multiplayerClientManager->LastPendingRead(); if (clientRequest != nullptr) { m_joinability = clientRequest->LobbyClient()->Joinability(); SetMultiplayerGameSession(clientRequest->GameClient()->Game()); SetMultiplayerLobbySession(clientRequest->LobbyClient()->Lobby()); } else { m_joinability = XblMultiplayerJoinability::None; SetMultiplayerGameSession(nullptr); SetMultiplayerLobbySession(nullptr); } } catch(...){} return m_eventQueue; } std::shared_ptr MultiplayerManager::GameSession() const { std::lock_guard guard(m_lock); return m_multiplayerGameSession; } std::shared_ptr MultiplayerManager::LobbySession() const { std::lock_guard guard(m_lock); return m_multiplayerLobbySession; } void MultiplayerManager::SetMultiplayerGameSession( _In_ std::shared_ptr gameSession ) { // Note: This function does not require a lock as the functions that call this already has a m_lock. if (gameSession == nullptr) { m_multiplayerGameSession = nullptr; } else { m_multiplayerGameSession = gameSession; m_multiplayerGameSession->SetMultiplayerClientManager(m_multiplayerClientManager); } } void MultiplayerManager::SetMultiplayerLobbySession( _In_ std::shared_ptr multiplayerLobby ) { // Note: This function does not require a lock as the functions that call this already has a m_lock. if (multiplayerLobby == nullptr) { if (m_multiplayerLobbySession != nullptr) { m_multiplayerLobbySession.reset(); } // Don't set the lobby to null so that the title can add local users whenever it chooses. m_multiplayerLobbySession = MakeShared(m_multiplayerClientManager); } else { m_multiplayerLobbySession = multiplayerLobby; m_multiplayerLobbySession->SetMultiplayerClientManager(m_multiplayerClientManager); } } HRESULT MultiplayerManager::JoinLobby( _In_ const xsapi_internal_string& handleId, _In_ xbox_live_user_t user ) { RETURN_HR_IF_LOG_DEBUG(m_multiplayerClientManager == nullptr, E_UNEXPECTED, "Call multiplayer_manager::initialize() first."); xsapi_internal_vector users; users.push_back(user); return m_multiplayerClientManager->JoinLobbyByHandle(handleId, users); } #if HC_PLATFORM == HC_PLATFORM_UWP || HC_PLATFORM == HC_PLATFORM_XDK HRESULT MultiplayerManager::JoinLobby( _In_ Windows::ApplicationModel::Activation::IProtocolActivatedEventArgs^ eventArgs, _In_ xbox_live_user_t user ) { RETURN_HR_IF_LOG_DEBUG(m_multiplayerClientManager == nullptr, E_UNEXPECTED, "Call multiplayer_manager::initialize() first."); xsapi_internal_vector users{ user }; return m_multiplayerClientManager->JoinLobby(eventArgs, users); } #endif HRESULT MultiplayerManager::JoinGameFromLobby( _In_ const xsapi_internal_string& sessionTemplateName ) { RETURN_HR_IF_LOG_DEBUG(m_multiplayerClientManager == nullptr, E_UNEXPECTED, "Call multiplayer_manager::initialize() first."); return m_multiplayerClientManager->JoinGameFromLobby(sessionTemplateName); } HRESULT MultiplayerManager::JoinGame( _In_ const xsapi_internal_string& sessionName, _In_ const xsapi_internal_string& sessionTemplateName, _In_ const xsapi_internal_vector& xuids ) { RETURN_HR_IF(m_multiplayerClientManager == nullptr, E_UNEXPECTED); RETURN_EXCEPTION_FREE_HRESULT(m_multiplayerClientManager->JoinGame(sessionName, sessionTemplateName, xuids)); } HRESULT MultiplayerManager::LeaveGame() { RETURN_HR_IF_LOG_DEBUG(m_multiplayerClientManager == nullptr, E_UNEXPECTED, "Call multiplayer_manager::initialize() first."); return m_multiplayerClientManager->LeaveGame(); } HRESULT MultiplayerManager::FindMatch( _In_ const xsapi_internal_string& hopperName, _In_ JsonValue& attributes, _In_ const std::chrono::seconds& timeout ) { RETURN_HR_IF_LOG_DEBUG(m_multiplayerClientManager == nullptr, E_UNEXPECTED, "Call multiplayer_manager::initialize() first."); return m_multiplayerClientManager->FindMatch(hopperName, attributes, timeout); } void MultiplayerManager::CancelMatch() { if (m_multiplayerClientManager != nullptr && m_multiplayerClientManager->MatchClient() != nullptr) { m_multiplayerClientManager->MatchClient()->CancelMatch(); } } XblMultiplayerMatchStatus MultiplayerManager::MatchStatus() const { if (m_multiplayerClientManager != nullptr && m_multiplayerClientManager->MatchClient() != nullptr) { return m_multiplayerClientManager->MatchClient()->MatchStatus(); } return XblMultiplayerMatchStatus::None; } std::chrono::seconds MultiplayerManager::EstimatedMatchWaitTime() const { if (m_multiplayerClientManager != nullptr && m_multiplayerClientManager->MatchClient() != nullptr) { return m_multiplayerClientManager->MatchClient()->EstimatedMatchWaitTime(); } return std::chrono::seconds(0); } void MultiplayerManager::SetQosMeasurements( _In_ const JsonValue& measurements ) { if (m_multiplayerClientManager != nullptr && m_multiplayerClientManager->MatchClient() != nullptr) { m_multiplayerClientManager->MatchClient()->SetQosMeasurements(measurements); } } bool MultiplayerManager::AutoFillMembersDuringMatchmaking() const { return false; } void MultiplayerManager::SetAutoFillMembersDuringMatchmaking( _In_ bool autoFillMembers ) { if (m_multiplayerClientManager != nullptr) { m_multiplayerClientManager->SetAutoFillMembersDuringMatchmaking(autoFillMembers); } } XblMultiplayerJoinability MultiplayerManager::Joinability() const { return m_joinability; } HRESULT MultiplayerManager::SetJoinability( _In_ XblMultiplayerJoinability value, _In_opt_ context_t context ) { RETURN_HR_IF_LOG_DEBUG(m_multiplayerClientManager == nullptr, E_UNEXPECTED, "Call multiplayer_manager::initialize() first."); return m_multiplayerClientManager->SetJoinability(value, context); } std::shared_ptr MultiplayerManager::GameClient() { auto clientManger = m_multiplayerClientManager; if (clientManger == nullptr || clientManger->LatestPendingRead() == nullptr) { return nullptr; } return clientManger->LatestPendingRead()->GameClient(); } std::shared_ptr MultiplayerManager::LobbyClient() { auto clientManger = m_multiplayerClientManager; if (clientManger == nullptr || clientManger->LatestPendingRead() == nullptr) { return nullptr; } return clientManger->LatestPendingRead()->LobbyClient(); } #ifdef XSAPI_UNIT_TESTS void MultiplayerManager::Shutdown() { auto clientManger = m_multiplayerClientManager; if (clientManger == nullptr || clientManger->LatestPendingRead() == nullptr) { return; } return clientManger->Shutdown(); } #endif NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_MANAGER_CPP_END ================================================ FILE: Source/Services/Multiplayer/Manager/multiplayer_manager_api.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "multiplayer_manager_internal.h" using namespace xbox::services; using namespace xbox::services::multiplayer::manager; NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_MANAGER_CPP_BEGIN template Ret ApiImpl(Ret&& fallbackReturnValue, TWork&& work, bool requireInit = true) noexcept { auto state{ GlobalState::Get() }; if (!state) { return fallbackReturnValue; } assert(state->MultiplayerManager()); if (requireInit) { RETURN_HR_IF(state->MultiplayerManager()->IsInitialized() == false, fallbackReturnValue); } return work(*state->MultiplayerManager()); } template HRESULT ApiImpl(TWork&& work, bool requireInit = true) noexcept { return ApiImpl(E_XBL_NOT_INITIALIZED, std::move(work), requireInit); } NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_MANAGER_CPP_END STDAPI XblMultiplayerManagerInitialize( _In_z_ const char* lobbySessionTemplateName, _In_opt_ XTaskQueueHandle asyncQueue ) XBL_NOEXCEPT try { return ApiImpl([&](MultiplayerManager& mpm) { RETURN_HR_INVALIDARGUMENT_IF_NULL(lobbySessionTemplateName); mpm.Initialize(lobbySessionTemplateName, asyncQueue); return S_OK; }, false); // doesn't require init } CATCH_RETURN() STDAPI XblMultiplayerManagerDoWork( _Deref_out_opt_ const XblMultiplayerEvent** multiplayerEvents, _Out_ size_t* multiplayerEventsCount ) XBL_NOEXCEPT try { INIT_OUT_PTR_PARAM(multiplayerEvents); return ApiImpl([&](MultiplayerManager& mpm) { RETURN_HR_INVALIDARGUMENT_IF(multiplayerEvents == nullptr || multiplayerEventsCount == nullptr); auto& events = mpm.DoWork(); if (!events.Empty()) { *multiplayerEvents = &(*events.begin()); } else { *multiplayerEvents = nullptr; } *multiplayerEventsCount = events.Size(); return S_OK; }); } CATCH_RETURN() STDAPI XblMultiplayerManagerJoinLobby( _In_z_ const char* handleId, _In_ XblUserHandle user ) XBL_NOEXCEPT try { return ApiImpl([&](MultiplayerManager& mpm) { RETURN_HR_INVALIDARGUMENT_IF_NULL(handleId); return mpm.JoinLobby(handleId, user); }); } CATCH_RETURN() STDAPI XblMultiplayerManagerJoinGameFromLobby( _In_z_ const char* sessionTemplateName ) XBL_NOEXCEPT try { return ApiImpl([&](MultiplayerManager& mpm) { RETURN_HR_INVALIDARGUMENT_IF_NULL(sessionTemplateName); return mpm.JoinGameFromLobby(sessionTemplateName); }); } CATCH_RETURN() STDAPI XblMultiplayerManagerJoinGame( _In_z_ const char* sessionName, _In_z_ const char* sessionTemplateName, _In_opt_ const uint64_t* xuids, _In_ size_t xuidsCount ) XBL_NOEXCEPT try { return ApiImpl([&](MultiplayerManager& mpm) { RETURN_HR_INVALIDARGUMENT_IF(sessionName == nullptr || sessionTemplateName == nullptr || (xuids == nullptr && xuidsCount > 0)); return mpm.JoinGame( sessionName, sessionTemplateName, xsapi_internal_vector(xuids, xuids + xuidsCount) ); }); } CATCH_RETURN() STDAPI XblMultiplayerManagerLeaveGame() XBL_NOEXCEPT try { return ApiImpl([](MultiplayerManager& mpm) { return mpm.LeaveGame(); }); } CATCH_RETURN() STDAPI XblMultiplayerManagerFindMatch( _In_z_ const char* hopperName, _In_opt_z_ const char* attributesJson, _In_ uint32_t timeoutInSeconds ) XBL_NOEXCEPT try { return ApiImpl([&](MultiplayerManager& mpm) { RETURN_HR_INVALIDARGUMENT_IF(hopperName == nullptr); JsonDocument json; if (attributesJson) { json.Parse(attributesJson); if (json.HasParseError() && json.GetParseError() != rapidjson::kParseErrorDocumentEmpty) { return E_INVALIDARG; } } return mpm.FindMatch( hopperName, json, std::chrono::seconds(timeoutInSeconds) ); }); } CATCH_RETURN() STDAPI_(void) XblMultiplayerManagerCancelMatch() XBL_NOEXCEPT try { ApiImpl([](MultiplayerManager& mpm) { mpm.CancelMatch(); return S_OK; }); } CATCH_RETURN_WITH(;) STDAPI_(XblMultiplayerMatchStatus) XblMultiplayerManagerMatchStatus() XBL_NOEXCEPT try { return ApiImpl(XblMultiplayerMatchStatus::None, [](MultiplayerManager& mpm) { return mpm.MatchStatus(); }); } CATCH_RETURN_WITH(XblMultiplayerMatchStatus::None) STDAPI_(uint32_t) XblMultiplayerManagerEstimatedMatchWaitTime() XBL_NOEXCEPT try { return ApiImpl(0, [](MultiplayerManager& mpm) { return static_cast(mpm.EstimatedMatchWaitTime().count()); }); } CATCH_RETURN_WITH(0) STDAPI_(bool) XblMultiplayerManagerAutoFillMembersDuringMatchmaking() XBL_NOEXCEPT try { return ApiImpl(false, [](MultiplayerManager& mpm) { return mpm.AutoFillMembersDuringMatchmaking(); }); } CATCH_RETURN_WITH(false) STDAPI_(void) XblMultiplayerManagerSetAutoFillMembersDuringMatchmaking( _In_ bool autoFillMembers ) XBL_NOEXCEPT try { ApiImpl([&](MultiplayerManager& mpm) { mpm.SetAutoFillMembersDuringMatchmaking(autoFillMembers); return S_OK; }); } CATCH_RETURN_WITH(;) STDAPI XblMultiplayerManagerSetQosMeasurements( _In_z_ const char* measurementsJson ) XBL_NOEXCEPT try { return ApiImpl([&](MultiplayerManager& mpm) { RETURN_HR_INVALIDARGUMENT_IF_NULL(measurementsJson); JsonDocument json; json.Parse(measurementsJson); if (json.HasParseError() && json.GetParseError() != rapidjson::kParseErrorDocumentEmpty) { return E_INVALIDARG; } mpm.SetQosMeasurements(json); return S_OK; }); } CATCH_RETURN() STDAPI_(XblMultiplayerJoinability) XblMultiplayerManagerJoinability() XBL_NOEXCEPT try { return ApiImpl(XblMultiplayerJoinability::None, [](MultiplayerManager& mpm) { return mpm.Joinability(); }); } CATCH_RETURN_WITH(XblMultiplayerJoinability::None) STDAPI XblMultiplayerManagerSetJoinability( _In_ XblMultiplayerJoinability joinability, _In_opt_ void* context ) XBL_NOEXCEPT try { return ApiImpl([&](MultiplayerManager& mpm) { return mpm.SetJoinability(joinability, reinterpret_cast(context)); }); } CATCH_RETURN() STDAPI XblMultiplayerManagerLobbySessionCorrelationId( _Out_ XblGuid* correlationId ) XBL_NOEXCEPT try { return ApiImpl([&](MultiplayerManager& mpm) { RETURN_HR_INVALIDARGUMENT_IF_NULL(correlationId); utils::strcpy(correlationId->value, sizeof(correlationId->value), mpm.LobbySession()->CorrelationId().data()); return S_OK; }); } CATCH_RETURN() STDAPI XblMultiplayerManagerLobbySessionSessionReference( _Out_ XblMultiplayerSessionReference* sessionReference ) XBL_NOEXCEPT try { return ApiImpl([&](MultiplayerManager& mpm) { RETURN_HR_INVALIDARGUMENT_IF_NULL(sessionReference); *sessionReference = mpm.LobbySession()->SessionReference(); return S_OK; }); } CATCH_RETURN() STDAPI_(size_t) XblMultiplayerManagerLobbySessionLocalMembersCount() XBL_NOEXCEPT try { return ApiImpl(0, [](MultiplayerManager& mpm) { return mpm.LobbySession()->LocalMembers().size(); }); } CATCH_RETURN() STDAPI XblMultiplayerManagerLobbySessionLocalMembers( _In_ size_t localMembersCount, _Out_writes_(localMembersCount) XblMultiplayerManagerMember* localMembers ) XBL_NOEXCEPT try { return ApiImpl([&](MultiplayerManager& mpm) { auto& localMembersVector = mpm.LobbySession()->LocalMembers(); RETURN_HR_INVALIDARGUMENT_IF(localMembersCount < localMembersVector.size()); RETURN_HR_INVALIDARGUMENT_IF(localMembers == nullptr && localMembersCount > 0); for (size_t i = 0; i < localMembersVector.size(); ++i) { DISABLE_WARNING_PUSH; SUPPRESS_WARNING_NULL_PTR_DEREFERENCE; // null pointer deref localMembers[i] = localMembersVector[i]->GetReference(); DISABLE_WARNING_POP; } return S_OK; }); } CATCH_RETURN() STDAPI_(size_t) XblMultiplayerManagerLobbySessionMembersCount() XBL_NOEXCEPT try { return ApiImpl(0, [](MultiplayerManager& mpm) { return mpm.LobbySession()->Members().size(); }); } CATCH_RETURN() STDAPI XblMultiplayerManagerLobbySessionMembers( _In_ size_t membersCount, _Out_writes_(membersCount) XblMultiplayerManagerMember* members ) XBL_NOEXCEPT try { return ApiImpl([&](MultiplayerManager& mpm) { auto& membersVector = mpm.LobbySession()->Members(); RETURN_HR_INVALIDARGUMENT_IF(membersCount < membersVector.size() || members == nullptr); for (size_t i = 0; i < membersVector.size(); ++i) { members[i] = membersVector[i]->GetReference(); } return S_OK; }); } CATCH_RETURN() STDAPI XblMultiplayerManagerLobbySessionHost( _Out_ XblMultiplayerManagerMember* hostMember ) XBL_NOEXCEPT try { return ApiImpl([&](MultiplayerManager& mpm) { if (mpm.LobbySession()->Host()) { *hostMember = mpm.LobbySession()->Host()->GetReference(); return S_OK; } else { return __HRESULT_FROM_WIN32(ERROR_NO_SUCH_USER); } }); } CATCH_RETURN() STDAPI_(const char*) XblMultiplayerManagerLobbySessionPropertiesJson() XBL_NOEXCEPT try { return ApiImpl(nullptr, [](MultiplayerManager& mpm)-> const char* { auto& customProperties = mpm.LobbySession()->CustomPropertiesJson(); if (!customProperties.empty()) { return customProperties.data(); } return nullptr; }); } CATCH_RETURN_WITH(nullptr) STDAPI_(const XblMultiplayerSessionConstants*) XblMultiplayerManagerLobbySessionConstants() XBL_NOEXCEPT try { return ApiImpl(nullptr, [](MultiplayerManager& mpm) { return &mpm.LobbySession()->SessionConstants(); }); } CATCH_RETURN_WITH(nullptr) XBL_WARNING_PUSH XBL_WARNING_DISABLE_DEPRECATED STDAPI_(const XblTournamentTeamResult*) XblMultiplayerManagerLobbySessionLastTournamentTeamResult() XBL_NOEXCEPT try { return nullptr; } CATCH_RETURN_WITH(nullptr) XBL_WARNING_POP STDAPI XblMultiplayerManagerLobbySessionAddLocalUser( _In_ XblUserHandle user ) XBL_NOEXCEPT try { return ApiImpl([&](MultiplayerManager& mpm) { return mpm.LobbySession()->AddLocalUser(user); }); } CATCH_RETURN() STDAPI XblMultiplayerManagerLobbySessionRemoveLocalUser( _In_ XblUserHandle user ) XBL_NOEXCEPT try { return ApiImpl([&](MultiplayerManager& mpm) { return mpm.LobbySession()->RemoveLocalUser(user); }); } CATCH_RETURN() STDAPI XblMultiplayerManagerLobbySessionSetLocalMemberProperties( _In_ XblUserHandle user, _In_z_ const char* name, _In_z_ const char* valueJson, _In_opt_ void* context ) XBL_NOEXCEPT try { return ApiImpl([&](MultiplayerManager& mpm) { RETURN_HR_INVALIDARGUMENT_IF(name == nullptr || valueJson == nullptr); JsonDocument json; json.Parse(valueJson); if (json.HasParseError() && json.GetParseError() != rapidjson::kParseErrorDocumentEmpty) { return E_INVALIDARG; } return mpm.LobbySession()->SetLocalMemberProperties( user, name, json, reinterpret_cast(context) ); }); } CATCH_RETURN() STDAPI XblMultiplayerManagerLobbySessionDeleteLocalMemberProperties( _In_ XblUserHandle user, _In_z_ const char* name, _In_opt_ void* context ) XBL_NOEXCEPT try { return ApiImpl([&](MultiplayerManager& mpm) { RETURN_HR_INVALIDARGUMENT_IF_NULL(name); return mpm.LobbySession()->DeleteLocalMemberProperties( user, name, reinterpret_cast(context) ); }); } CATCH_RETURN() STDAPI XblMultiplayerManagerLobbySessionSetLocalMemberConnectionAddress( _In_ XblUserHandle user, _In_z_ const char* connectionAddress, _In_opt_ void* context ) XBL_NOEXCEPT try { return ApiImpl([&](MultiplayerManager& mpm) { RETURN_HR_INVALIDARGUMENT_IF_NULL(connectionAddress); return mpm.LobbySession()->SetLocalMemberConnectionAddress( user, connectionAddress, reinterpret_cast(context) ); }); } CATCH_RETURN() STDAPI_(bool) XblMultiplayerManagerLobbySessionIsHost( _In_ uint64_t xuid ) XBL_NOEXCEPT try { return ApiImpl(false, [&](MultiplayerManager& mpm) { return mpm.LobbySession()->IsHost(xuid); }); } CATCH_RETURN() STDAPI XblMultiplayerManagerLobbySessionSetProperties( _In_z_ const char* name, _In_z_ const char* valueJson, _In_opt_ void* context ) XBL_NOEXCEPT try { return ApiImpl([&](MultiplayerManager& mpm) { RETURN_HR_INVALIDARGUMENT_IF(name == nullptr || valueJson == nullptr); JsonDocument json; json.Parse(valueJson); if (json.HasParseError() && json.GetParseError() != rapidjson::kParseErrorDocumentEmpty) { return E_INVALIDARG; } return mpm.LobbySession()->SetProperties( name, json, reinterpret_cast(context) ); }); } CATCH_RETURN() STDAPI XblMultiplayerManagerLobbySessionSetSynchronizedProperties( _In_z_ const char* name, _In_z_ const char* valueJson, _In_opt_ void* context ) XBL_NOEXCEPT try { return ApiImpl([&](MultiplayerManager& mpm) { RETURN_HR_INVALIDARGUMENT_IF(name == nullptr || valueJson == nullptr); JsonDocument json; json.Parse(valueJson); if (json.HasParseError() && json.GetParseError() != rapidjson::kParseErrorDocumentEmpty) { return E_INVALIDARG; } return mpm.LobbySession()->SetSynchronizedProperties( name, json, reinterpret_cast(context) ); }); } CATCH_RETURN() STDAPI XblMultiplayerManagerLobbySessionSetSynchronizedHost( _In_ const char* deviceToken, _In_opt_ void* context ) XBL_NOEXCEPT try { return ApiImpl([&](MultiplayerManager& mpm) { RETURN_HR_INVALIDARGUMENT_IF_NULL(deviceToken); return mpm.LobbySession()->SetSynchronizedHost( deviceToken, reinterpret_cast(context) ); }); } CATCH_RETURN() #if HC_PLATFORM_IS_MICROSOFT STDAPI XblMultiplayerManagerLobbySessionInviteFriends( _In_ XblUserHandle user, _In_opt_z_ const char* contextStringId, _In_opt_z_ const char* customActivationContext ) XBL_NOEXCEPT try { return ApiImpl([&](MultiplayerManager& mpm) { return mpm.LobbySession()->InviteFriends( user, contextStringId != nullptr ? contextStringId : xsapi_internal_string(), customActivationContext != nullptr ? customActivationContext : xsapi_internal_string() ); }); } CATCH_RETURN() #endif STDAPI XblMultiplayerManagerLobbySessionInviteUsers( _In_ XblUserHandle user, _In_ const uint64_t* xuids, _In_ size_t xuidsCount, _In_opt_z_ const char* contextStringId, _In_opt_z_ const char* customActivationContext ) XBL_NOEXCEPT try { return ApiImpl([&](MultiplayerManager& mpm) { return mpm.LobbySession()->InviteUsers( user, xsapi_internal_vector(xuids, xuids + xuidsCount), contextStringId != nullptr ? contextStringId : xsapi_internal_string(), customActivationContext != nullptr ? customActivationContext : xsapi_internal_string() ); }); } CATCH_RETURN() NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_MANAGER_CPP_BEGIN // GameSession APIs template Ret GameSessionApiImpl(Ret&& fallbackReturnValue, TWork&& work) noexcept { auto state{ GlobalState::Get() }; if (!state) { return fallbackReturnValue; } assert(state->MultiplayerManager()); auto gameSession{ state->MultiplayerManager()->GameSession() }; RETURN_HR_IF(state->MultiplayerManager()->IsInitialized() == false, fallbackReturnValue); if (!gameSession) { LOGS_ERROR << "XblMultiplayerManagerGameSession* API called before GameSession was established"; return fallbackReturnValue; } return work(state->MultiplayerManager()->GameSession()); } template HRESULT GameSessionApiImpl(TWork&& work) noexcept { auto state{ GlobalState::Get() }; if (!state) { return E_XBL_NOT_INITIALIZED; } assert(state->MultiplayerManager()); RETURN_HR_IF(state->MultiplayerManager()->IsInitialized() == false, E_XBL_NOT_INITIALIZED); auto gameSession{ state->MultiplayerManager()->GameSession() }; if (!gameSession) { LOGS_ERROR << "XblMultiplayerManagerGameSession* API called before GameSession was established"; return E_UNEXPECTED; } return work(state->MultiplayerManager()->GameSession()); } typedef std::shared_ptr GameSessionPtr; NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_MANAGER_CPP_END STDAPI_(bool) XblMultiplayerManagerGameSessionActive() XBL_NOEXCEPT try { return ApiImpl(false, [](MultiplayerManager& mpm) { return mpm.GameSession() != nullptr; }); } CATCH_RETURN() STDAPI_(const char*) XblMultiplayerManagerGameSessionCorrelationId() XBL_NOEXCEPT try { return GameSessionApiImpl(nullptr, [](GameSessionPtr gameSession) { return gameSession->CorrelationId().data(); }); } CATCH_RETURN_WITH(nullptr) STDAPI_(const XblMultiplayerSessionReference*) XblMultiplayerManagerGameSessionSessionReference() XBL_NOEXCEPT try { return GameSessionApiImpl(nullptr, [](GameSessionPtr gameSession) { return &gameSession->SessionReference(); }); } CATCH_RETURN_WITH(nullptr) STDAPI_(size_t) XblMultiplayerManagerGameSessionMembersCount() XBL_NOEXCEPT try { return GameSessionApiImpl(0, [](GameSessionPtr gameSession) { return gameSession->Members().size(); }); } CATCH_RETURN() STDAPI XblMultiplayerManagerGameSessionMembers( _In_ size_t membersCount, _Out_writes_(membersCount) XblMultiplayerManagerMember* members ) XBL_NOEXCEPT try { return GameSessionApiImpl([&](GameSessionPtr gameSession) { auto& membersVector = gameSession->Members(); RETURN_HR_INVALIDARGUMENT_IF(membersCount < membersVector.size()); RETURN_HR_INVALIDARGUMENT_IF(members == nullptr && membersCount > 0); for (size_t i = 0; i < membersVector.size(); ++i) { DISABLE_WARNING_PUSH; SUPPRESS_WARNING_NULL_PTR_DEREFERENCE; members[i] = membersVector[i]->GetReference(); DISABLE_WARNING_POP; } return S_OK; }); } CATCH_RETURN() STDAPI XblMultiplayerManagerGameSessionHost( _Out_ XblMultiplayerManagerMember* hostMember ) XBL_NOEXCEPT try { return GameSessionApiImpl([&](GameSessionPtr gameSession) { if (gameSession->Host()) { *hostMember = gameSession->Host()->GetReference(); return S_OK; } else { return __HRESULT_FROM_WIN32(ERROR_NO_SUCH_USER); } }); } CATCH_RETURN() STDAPI_(const char*) XblMultiplayerManagerGameSessionPropertiesJson() XBL_NOEXCEPT try { return GameSessionApiImpl(nullptr, [](GameSessionPtr gameSession) { return gameSession->Properties().data(); }); } CATCH_RETURN_WITH(nullptr) STDAPI_(const XblMultiplayerSessionConstants*) XblMultiplayerManagerGameSessionConstants() XBL_NOEXCEPT try { return GameSessionApiImpl(nullptr, [](GameSessionPtr gameSession) { return &gameSession->SessionConstants(); }); } CATCH_RETURN_WITH(nullptr) STDAPI_(bool) XblMultiplayerManagerGameSessionIsHost( _In_ uint64_t xuid ) XBL_NOEXCEPT try { return GameSessionApiImpl(false, [&](GameSessionPtr gameSession) { return gameSession->IsHost(xuid); }); } CATCH_RETURN() STDAPI XblMultiplayerManagerGameSessionSetProperties( _In_z_ const char* name, _In_z_ const char* valueJson, _In_opt_ void* context ) XBL_NOEXCEPT try { return GameSessionApiImpl([&](GameSessionPtr gameSession) { RETURN_HR_INVALIDARGUMENT_IF(name == nullptr || valueJson == nullptr); JsonDocument json; json.Parse(valueJson); if (json.HasParseError() && json.GetParseError() != rapidjson::kParseErrorDocumentEmpty) { return E_INVALIDARG; } return gameSession->SetProperties( name, json, reinterpret_cast(context) ); }); } CATCH_RETURN() STDAPI XblMultiplayerManagerGameSessionSetSynchronizedProperties( _In_z_ const char* name, _In_z_ const char* valueJson, _In_opt_ void* context ) XBL_NOEXCEPT try { return GameSessionApiImpl([&](GameSessionPtr gameSession) { RETURN_HR_INVALIDARGUMENT_IF(name == nullptr || valueJson == nullptr); JsonDocument json; json.Parse(valueJson); if (json.HasParseError() && json.GetParseError() != rapidjson::kParseErrorDocumentEmpty) { return E_INVALIDARG; } return gameSession->SetSynchronizedProperties( name, json, reinterpret_cast(context) ); }); } CATCH_RETURN() STDAPI XblMultiplayerManagerGameSessionSetSynchronizedHost( _In_ const char* deviceToken, _In_opt_ void* context ) XBL_NOEXCEPT try { return GameSessionApiImpl([&](GameSessionPtr gameSession) { RETURN_HR_INVALIDARGUMENT_IF_NULL(deviceToken); return gameSession->SetSynchronizedHost(deviceToken, reinterpret_cast(context)); }); } CATCH_RETURN() ================================================ FILE: Source/Services/Multiplayer/Manager/multiplayer_manager_internal.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include #include #include "xbox_live_context_internal.h" #include "multiplayer_internal.h" #include "xsapi-c/multiplayer_manager_c.h" typedef void* context_t; struct XblMultiplayerEventArgs : public xbox::services::RefCounter, public std::enable_shared_from_this { XblMultiplayerEventArgs() = default; virtual ~XblMultiplayerEventArgs() = default; private: std::shared_ptr GetSharedThis() override { return shared_from_this(); } }; namespace xbox { namespace services { namespace multiplayer { namespace manager { class MultiplayerMatchClient; class MultiplayerClientManager; class MultiplayerLocalUserManager; class MultiplayerLobbyClient; class MultiplayerGameClient; class MultiplayerLocalUser; enum class MultiplayerLocalUserLobbyState { Unknown, Add, Join, InSession, Leave, Remove }; enum class MultiplayerLocalUserGameState { Unknown, PendingJoin, Join, InSession, Leave }; enum class PendingRequestType { SynchronizedChanges, NonSynchronizedChanges }; class MultiplayerMember { public: MultiplayerMember(); MultiplayerMember( _In_ const XblMultiplayerSessionMember* member, _In_ bool isLocal, _In_ bool isGameHost, _In_ bool isLobbyHost, _In_ bool isInLobby, _In_ bool isInGame ); uint32_t MemberId() const; const xsapi_internal_string& TeamId() const; const xsapi_internal_string& InitialTeam() const; uint64_t Xuid() const; const xsapi_internal_string& DebugGamertag() const; bool IsLocal() const; bool IsInLobby() const; bool IsInGame() const; XblMultiplayerSessionMemberStatus Status() const; const xsapi_internal_string& ConnectionAddress() const; const xsapi_internal_string& CustomPropertiesJson() const; bool IsMemberOnSameDevice( _In_ std::shared_ptr member ) const; const xsapi_internal_string& DeviceToken() const; static std::shared_ptr CreateFromSessionMember( _In_ const XblMultiplayerSessionMember* member, _In_ const std::shared_ptr& lobbySession, _In_ const std::shared_ptr& gameSession, _In_ const xsapi_internal_map>& xboxLiveContextMap ); static std::shared_ptr CreateFromSessionMember( _In_ const XblMultiplayerSessionMember* member, _In_ const std::shared_ptr& lobbySession, _In_ const std::shared_ptr& gameSession, _In_ bool isLocal ); XblMultiplayerManagerMember GetReference() const; private: xsapi_internal_string m_teamId; xsapi_internal_string m_initialTeam; uint32_t m_memberId; uint64_t m_xuid; xsapi_internal_string m_gamertag; xsapi_internal_string m_deviceToken; bool m_isLocal; bool m_isInLobby; bool m_isInGame; XblMultiplayerSessionMemberStatus m_status; xsapi_internal_string m_connectionAddress; xsapi_internal_string m_jsonProperties; }; class MultiplayerLobbySession { public: MultiplayerLobbySession(); MultiplayerLobbySession(_In_ const MultiplayerLobbySession& other); MultiplayerLobbySession(_In_ std::shared_ptr multiplayerClientManagerInstance); MultiplayerLobbySession( _In_ std::shared_ptr session, _In_ std::shared_ptr host, _In_ const xsapi_internal_vector>& members, _In_ const xsapi_internal_vector>& localMmembers ); ~MultiplayerLobbySession(); const xsapi_internal_string& CorrelationId() const; const XblMultiplayerSessionReference& SessionReference() const; const xsapi_internal_vector>& LocalMembers() const; const xsapi_internal_vector>& Members() const; std::shared_ptr Host() const; const xsapi_internal_string& CustomPropertiesJson() const; const XblMultiplayerSessionConstants& SessionConstants() const; HRESULT AddLocalUser( _In_ xbox_live_user_t user ); HRESULT RemoveLocalUser( _In_ xbox_live_user_t user ); HRESULT SetLocalMemberProperties( _In_ xbox_live_user_t user, _In_ const xsapi_internal_string& name, _In_ const JsonValue& valueJson, _In_opt_ context_t context = nullptr ); HRESULT DeleteLocalMemberProperties( _In_ xbox_live_user_t user, _In_ const xsapi_internal_string& name, _In_opt_ context_t context = nullptr ); HRESULT SetLocalMemberConnectionAddress( _In_ xbox_live_user_t user, _In_ const xsapi_internal_string& connectionAddress, _In_opt_ context_t context = nullptr ); bool IsHost( _In_ uint64_t xuid ); HRESULT SetProperties( _In_ const xsapi_internal_string& name, _In_ const JsonValue& valueJson, _In_opt_ context_t context = nullptr ); HRESULT SetSynchronizedProperties( _In_ const xsapi_internal_string& name, _In_ const JsonValue& valueJson, _In_opt_ context_t context = nullptr ); HRESULT SetSynchronizedHost( _In_ const xsapi_internal_string& hostDeviceToken, _In_opt_ context_t context = nullptr ); #if HC_PLATFORM_IS_MICROSOFT HRESULT InviteFriends( _In_ xbox_live_user_t user, _In_ const xsapi_internal_string& contextStringId = xsapi_internal_string(), _In_ const xsapi_internal_string& customActivationContext = xsapi_internal_string() ); #endif HRESULT InviteUsers( _In_ xbox_live_user_t user, _In_ const xsapi_internal_vector& xuids, _In_ const xsapi_internal_string& contextStringId = xsapi_internal_string(), _In_ const xsapi_internal_string& customActivationContext = xsapi_internal_string() ); uint64_t ChangeNumber() const; void SetMultiplayerClientManager( _In_ std::shared_ptr clientManager ); void SetHost(_In_ std::shared_ptr hostMember); #if defined(XSAPI_CPPWINRT) #if HC_PLATFORM == HC_PLATFORM_XDK // TODO is there a better way to do this? HRESULT AddLocalUser( _In_ winrt::Windows::Xbox::System::User user ) { return AddLocalUser(convert_user_to_cppcx(user)); } HRESULT RemoveLocalUser( _In_ winrt::Windows::Xbox::System::User user ) { return RemoveLocalUser(convert_user_to_cppcx(user)); } HRESULT SetLocalMemberProperties( _In_ winrt::Windows::Xbox::System::User user, _In_ const xsapi_internal_string& name, _In_ const JsonValue& valueJson, _In_opt_ context_t context = nullptr ) { return SetLocalMemberProperties( convert_user_to_cppcx(user), name, valueJson, context ); } HRESULT DeleteLocalMemberProperties( _In_ winrt::Windows::Xbox::System::User user, _In_ const xsapi_internal_string& name, _In_opt_ context_t context = nullptr ) { return DeleteLocalMemberProperties( convert_user_to_cppcx(user), name, context ); } HRESULT SetLocalMemberConnectionAddress( _In_ winrt::Windows::Xbox::System::User user, _In_ const xsapi_internal_string& connectionAddress, _In_opt_ context_t context = nullptr ) { return SetLocalMemberConnectionAddress( convert_user_to_cppcx(user), connectionAddress, context ); } HRESULT InviteFriends( _In_ winrt::Windows::Xbox::System::User user, _In_ const xsapi_internal_string& contextStringId = xsapi_internal_string(), _In_ const xsapi_internal_string& customActivationContext = xsapi_internal_string() ) { return InviteFriends( convert_user_to_cppcx(user), contextStringId, customActivationContext ); } HRESULT InviteUsers( _In_ winrt::Windows::Xbox::System::User user, _In_ const xsapi_internal_vector& xboxUserIds, _In_ const xsapi_internal_string& contextStringId = xsapi_internal_string(), _In_ const xsapi_internal_string& customActivationContext = xsapi_internal_string() ) { return InviteUsers( convert_user_to_cppcx(user), xboxUserIds, contextStringId, customActivationContext ); } #endif #endif private: void DeepCopyConstants(const XblMultiplayerSessionConstants& other); std::shared_ptr m_multiplayerClientManager; xsapi_internal_string m_correlationId; uint64_t m_changeNumber; XblMultiplayerSessionReference m_sessionReference; std::shared_ptr m_host; xsapi_internal_vector> m_members; xsapi_internal_vector> m_localMembers; xsapi_internal_string m_customPropertiesJson; XblMultiplayerSessionConstants m_sessionConstants{}; xsapi_internal_vector m_initiatorXuids; XblMultiplayerMemberInitialization m_memberInitialization{}; xsapi_internal_string m_constantsCustomJson; xsapi_internal_string m_constantsCloudComputePackageJson; xsapi_internal_string m_constantsMeasurementServerAddressesJson; }; class MultiplayerGameSession { public: MultiplayerGameSession(); MultiplayerGameSession(_In_ const MultiplayerGameSession& other); MultiplayerGameSession( _In_ std::shared_ptr session, _In_ std::shared_ptr host, _In_ xsapi_internal_vector> members ); const xsapi_internal_string& CorrelationId() const; const XblMultiplayerSessionReference& SessionReference() const; const xsapi_internal_vector>& Members() const; std::shared_ptr Host() const; const xsapi_internal_string& Properties() const; const XblMultiplayerSessionConstants& SessionConstants() const; bool IsHost( _In_ uint64_t xuid ); HRESULT SetProperties( _In_ const xsapi_internal_string& name, _In_ const JsonValue& valueJson, _In_opt_ context_t context = nullptr ); HRESULT SetSynchronizedProperties( _In_ const xsapi_internal_string& name, _In_ const JsonValue& valueJson, _In_opt_ context_t context = nullptr ); HRESULT SetSynchronizedHost( _In_ const xsapi_internal_string& deviceToken, _In_opt_ context_t context = nullptr ); uint64_t ChangeNumber() const; void SetMultiplayerClientManager( _In_ std::shared_ptr clientManager ); void SetHost(_In_ std::shared_ptr hostMember); private: void DeepCopyConstants(const XblMultiplayerSessionConstants& other); xsapi_internal_string m_correlationId; uint64_t m_changeNumber; XblMultiplayerSessionReference m_sessionReference; std::shared_ptr m_host; xsapi_internal_vector> m_members; xsapi_internal_string m_properties; std::shared_ptr m_multiplayerClientManager; // Constants XblMultiplayerSessionConstants m_sessionConstants; xsapi_internal_vector m_initiatorXuids; XblMultiplayerMemberInitialization m_memberInitialization; xsapi_internal_string m_constantsCustomJson; xsapi_internal_string m_constantsCloudComputePackageJson; xsapi_internal_string m_constantsMeasurementServerAddressesJson; }; struct UserAddedEventArgs : public XblMultiplayerEventArgs { UserAddedEventArgs(_In_ uint64_t xuid) : Xuid(xuid) {} uint64_t Xuid; }; struct UserRemovedEventArgs : public XblMultiplayerEventArgs { UserRemovedEventArgs(_In_ uint64_t xuid) : Xuid(xuid) {} uint64_t Xuid; }; struct MemberJoinedEventArgs : public XblMultiplayerEventArgs { MemberJoinedEventArgs(const _In_ xsapi_internal_vector>& members) : Members(members) {} xsapi_internal_vector> Members; }; struct MemberLeftEventArgs : public XblMultiplayerEventArgs { MemberLeftEventArgs(const _In_ xsapi_internal_vector>& members) : Members(members) {} xsapi_internal_vector> Members; }; struct HostChangedEventArgs : public XblMultiplayerEventArgs { HostChangedEventArgs(_In_ std::shared_ptr hostMember) : HostMember(hostMember) {} std::shared_ptr HostMember; }; struct MemberPropertyChangedEventArgs : public XblMultiplayerEventArgs { MemberPropertyChangedEventArgs( _In_ std::shared_ptr member, _In_ const xsapi_internal_string& jsonProperties ) : Member(member), Properties(jsonProperties) { } std::shared_ptr Member; xsapi_internal_string Properties; }; struct SessionPropertyChangedEventArgs : public XblMultiplayerEventArgs { SessionPropertyChangedEventArgs(_In_ const xsapi_internal_string& jsonProperties) : Properties(jsonProperties) {} xsapi_internal_string Properties; }; struct JoinLobbyCompletedEventArgs : public XblMultiplayerEventArgs { JoinLobbyCompletedEventArgs(_In_ uint64_t xuid) : Xuid(xuid) {} uint64_t Xuid; }; struct FindMatchCompletedEventArgs : public XblMultiplayerEventArgs { FindMatchCompletedEventArgs( _In_ XblMultiplayerMatchStatus status, _In_ XblMultiplayerMeasurementFailure failure ) : MatchStatus(status), InitializationFailure(failure) { } XblMultiplayerMatchStatus MatchStatus; XblMultiplayerMeasurementFailure InitializationFailure; }; struct PerformQosMeasurementsEventArgs : public XblMultiplayerEventArgs { PerformQosMeasurementsEventArgs() { } ~PerformQosMeasurementsEventArgs() { for (auto& client : remoteClients) { Delete(client.connectionAddress); } } void AddRemoteClient(const xsapi_internal_string& connectionAddress, const xsapi_internal_string& deviceToken) { XblMultiplayerConnectionAddressDeviceTokenPair client{}; client.connectionAddress = Make(connectionAddress); utils::strcpy(client.deviceToken.Value, sizeof(client.deviceToken.Value), deviceToken.data()); remoteClients.push_back(std::move(client)); } xsapi_internal_vector remoteClients; }; class MultiplayerEventQueue { public: MultiplayerEventQueue(); MultiplayerEventQueue(const MultiplayerEventQueue& other); MultiplayerEventQueue& operator=(MultiplayerEventQueue other); ~MultiplayerEventQueue(); size_t Size() const; bool Empty() const; void Clear(); xsapi_internal_vector::const_iterator begin() const; xsapi_internal_vector::const_iterator end() const; void AddEvent( _In_ XblMultiplayerEventType eventType, _In_ XblMultiplayerSessionType sessionType, _In_ std::shared_ptr eventArgs = nullptr, _In_ Result error = {}, _In_opt_ context_t context = nullptr ); void AddEvent(_In_ const XblMultiplayerEvent& multiplayerEvent); private: xsapi_internal_vector m_events; mutable std::mutex m_lock; }; class MultiplayerManager { public: MultiplayerManager() = default; ~MultiplayerManager(); void Initialize( _In_ const xsapi_internal_string& lobbySessionTemplateName, _In_opt_ XTaskQueueHandle asyncQueue ); bool IsInitialized(); const MultiplayerEventQueue& DoWork(); std::shared_ptr LobbySession() const; std::shared_ptr GameSession() const; HRESULT JoinLobby( _In_ const xsapi_internal_string& handleId, _In_ xbox_live_user_t user ); #if HC_PLATFORM == HC_PLATFORM_UWP || HC_PLATFORM == HC_PLATFORM_XDK HRESULT JoinLobby( _In_ Windows::ApplicationModel::Activation::IProtocolActivatedEventArgs^ eventArgs, _In_ xbox_live_user_t user ); #endif #if HC_PLATFORM == HC_PLATFORM_XDK HRESULT JoinLobby( _In_ const xsapi_internal_string& handleId, _In_ xsapi_internal_vector users ); HRESULT JoinLobby( _In_ Windows::ApplicationModel::Activation::IProtocolActivatedEventArgs^ eventArgs, _In_ xsapi_internal_vector users ); void InvitePartyToGame(); #endif HRESULT JoinGameFromLobby( _In_ const xsapi_internal_string& sessionTemplateName ); HRESULT JoinGame( _In_ const xsapi_internal_string& sessionName, _In_ const xsapi_internal_string& sessionTemplateName, _In_ const xsapi_internal_vector& xuids = xsapi_internal_vector() ); HRESULT LeaveGame(); HRESULT FindMatch( _In_ const xsapi_internal_string& hopperName, _In_ JsonValue& attributes, _In_ const std::chrono::seconds& timeout = std::chrono::seconds(60) ); void CancelMatch(); XblMultiplayerMatchStatus MatchStatus() const; std::chrono::seconds EstimatedMatchWaitTime() const; bool AutoFillMembersDuringMatchmaking() const; void SetAutoFillMembersDuringMatchmaking( _In_ bool autoFillMembers ); void SetQosMeasurements( _In_ const JsonValue& measurements ); XblMultiplayerJoinability Joinability() const; HRESULT SetJoinability( _In_ XblMultiplayerJoinability value, _In_opt_ context_t context = nullptr ); bool IsDirty(); std::shared_ptr GetMultiplayerClientManager() { return m_multiplayerClientManager; } std::shared_ptr GameClient(); std::shared_ptr LobbyClient(); #ifdef XSAPI_UNIT_TESTS void Shutdown(); #endif #if defined(XSAPI_CPPWINRT) #if HC_PLATFORM == HC_PLATFORM_XDK xbox_live_result join_lobby( _In_ const xsapi_internal_string& handleId, _In_ xsapi_internal_vector users ) { return join_lobby(handleId, convert_user_vector_to_cppcx(users)); } #endif #endif private: MultiplayerManager(MultiplayerManager const&) = delete; void operator=(MultiplayerManager const&) = delete; bool m_isDirty = false; void SetMultiplayerGameSession(_In_ std::shared_ptr gameSession); void SetMultiplayerLobbySession(_In_ std::shared_ptr multiplayerLobby); mutable std::mutex m_lock; XblMultiplayerJoinability m_joinability = XblMultiplayerJoinability::None; std::shared_ptr m_multiplayerLobbySession; std::shared_ptr m_multiplayerGameSession; std::shared_ptr m_multiplayerClientManager; MultiplayerEventQueue m_eventQueue; XTaskQueueHandle m_queue{ nullptr }; }; typedef Callback>> MultiplayerSessionCallback; typedef Callback> MultiplayerEventQueueCallback; // Maybe just pass queue class MultiplayerClientPendingRequest { public: MultiplayerClientPendingRequest(); PendingRequestType RequestType() const; context_t Context(); uint32_t Identifier() const; // Local user properties std::shared_ptr LocalUser(); void SetLocalUser(_In_ std::shared_ptr user); MultiplayerLocalUserLobbyState LobbyState(); void SetLobbyState(_In_ MultiplayerLocalUserLobbyState userState); const xsapi_internal_string& LobbyHandleId() const; void SetLobbyHandleId(_In_ const xsapi_internal_string& handleId); const xsapi_internal_string& LocalUserSecureDeivceAddress() const; void SetLocalUserConnectionAddress( _In_ std::shared_ptr localUser, _In_ const xsapi_internal_string& connectionAddress, _In_opt_ context_t context ); const xsapi_internal_map& LocalUserProperties() const; void SetLocalUserProperties( _In_ std::shared_ptr localUser, _In_ const xsapi_internal_string& name, _In_ const JsonValue& valueJson, _In_opt_ context_t context ); // Session non-synchronized properties XblMultiplayerJoinability Joinability(); void SetJoinability(_In_ XblMultiplayerJoinability value, _In_opt_ context_t context); const xsapi_internal_map& SessionProperties() const; void SetSessionProperties(_In_ const xsapi_internal_string& name, _In_ const JsonValue& valueJson, _In_opt_ context_t context); // Session synchronized properties const xsapi_internal_string& SynchronizedHostDeviceToken() const; void SetSynchronizedHostDeviceToken(_In_ const xsapi_internal_string& hostDeviceToken, _In_opt_ context_t context); const xsapi_internal_map& SynchronizedSessionProperties() const; void SetSynchronizedSessionProperties(_In_ const xsapi_internal_string& name, const _In_ JsonValue& valueJson, _In_opt_ context_t context); void AppendPendingChanges( _In_ std::shared_ptr sessionToCommit, _In_ std::shared_ptr localUser, _In_ bool isGameInProgress = false ); private: context_t m_context; PendingRequestType m_requestType{}; uint32_t m_identifier{ s_nextUniqueIdentifier++ }; // Local user properties std::shared_ptr m_localUser; MultiplayerLocalUserLobbyState m_localUserLobbyState; xsapi_internal_map m_localUserProperties; xsapi_internal_string m_localUserSecureDeivceAddress; xsapi_internal_string m_lobbyHandleId; // Only used while joining a friend's lobby // Session non-synchronized properties xsapi_internal_map m_sessionProperties; XblMultiplayerJoinability m_joinability{}; // Session synchronized properties xsapi_internal_string m_synchronizedHostDeviceToken; xsapi_internal_map m_synchronizedSessionProperties; static std::atomic s_nextUniqueIdentifier; }; class MultiplayerSessionWriter : public std::enable_shared_from_this { public: MultiplayerSessionWriter(const TaskQueue& queue) noexcept; MultiplayerSessionWriter( const TaskQueue& queue, _In_ std::shared_ptr localUserManager ) noexcept; uint64_t Id() const; const std::shared_ptr& Session() const; void UpdateSession(_In_ const std::shared_ptr& updatedSession); uint64_t TapChangeNumber() const; void SetTapChangeNumber(_In_ uint64_t changeNumber); bool IsTapReceived() const; void SetTapReceived(_In_ bool bReceived); bool IsWriteInProgress() const; void SetWriteInProgress(_In_ bool writeInProgress); void OnSessionChanged( _In_ XblMultiplayerSessionChangeEventArgs args ) noexcept; HRESULT CommitSynchronizedChanges( _In_ std::shared_ptr sessionToCommit, _In_ MultiplayerSessionCallback callback ) noexcept; HRESULT LeaveRemoteSession( _In_ std::shared_ptr session, _In_ MultiplayerSessionCallback callback ) noexcept; HRESULT CommitPendingChanges( _In_ Vector> processingQueue, _In_ XblMultiplayerSessionType sessionType, _In_ bool isGameInProgress, _In_ MultiplayerEventQueueCallback callback ) noexcept; HRESULT CommitPendingSynchronizedChanges( _In_ Vector> processingQueue, _In_ XblMultiplayerSessionType sessionType, _In_ MultiplayerEventQueueCallback callback ) noexcept; HRESULT WriteSession( _In_ std::shared_ptr xboxLiveContext, _In_ std::shared_ptr session, _In_ XblMultiplayerSessionWriteMode mode, _In_ bool updateLatest, _In_ MultiplayerSessionCallback callback ) noexcept; HRESULT WriteSessionByHandle( _In_ std::shared_ptr xboxLiveContext, _In_ std::shared_ptr session, _In_ XblMultiplayerSessionWriteMode mode, _In_ const String& handleId, _In_ bool updateLatest, _In_ MultiplayerSessionCallback callback ) noexcept; void OnSessionUpdated( _In_ const std::shared_ptr& updatedSession ); XblFunctionContext AddMultiplayerSessionUpdatedHandler( _In_ Callback&> handler ); void OnResyncMessageReceived(); std::shared_ptr GetPrimaryContext(); MultiplayerEventQueue HandleEvents( _In_ xsapi_internal_vector> processingQueue, _In_ Result error, _In_ XblMultiplayerSessionType sessionType ); private: void Destroy(); void Resync(); // Synchronize write session result with any changes that happened in the interim. void HandleWriteSessionResult( _In_ Result> writeSessionResult, _In_ bool updateLatest, _In_ MultiplayerSessionCallback callback ) noexcept; HRESULT GetCurrentSessionHelper( _In_ std::shared_ptr xboxLiveContext, _In_ const XblMultiplayerSessionReference& sessionReference, _In_ MultiplayerSessionCallback callback ) noexcept; TaskQueue m_queue; // resync bool m_isResyncTaskInProgress{ false }; XblFunctionContext m_handleResyncEventCounter{ 0 }; std::mutex m_resyncLock; std::mutex m_stateLock; XblFunctionContext m_sessionUpdateEventHandlerCounter{ 1 }; UnorderedMap>> m_sessionUpdateEventHandler; uint64_t m_id{ 0 }; // used to ignore calls made before resetting the state via destory() std::mutex m_synchronizeWriteWithTapLock; uint64_t m_tapChangeNumber{ 0 }; bool m_isTapReceived{ false }; uint64_t m_numOfWritesInProgress{ 0 }; std::shared_ptr m_session; std::shared_ptr m_multiplayerLocalUserManager; }; class MultiplayerGameClient : public std::enable_shared_from_this { public: MultiplayerGameClient(const TaskQueue& queue) noexcept; MultiplayerGameClient( const TaskQueue& queue, _In_ std::shared_ptr localUserManager ) noexcept; ~MultiplayerGameClient(); // TODO void deep_copy_if_updated(_In_ const MultiplayerGameClient& other); void Initialize(); void SetGameSessionTemplate(_In_ const xsapi_internal_string& sessionTemplateName); std::shared_ptr SessionWriter() const; std::shared_ptr Game() const; void UpdateGame(_In_ const std::shared_ptr& multiplayerGame); MultiplayerEventQueue DoWork(); bool IsRequestInProgress(); bool IsPendingGameChanges(); void ClearPendingQueue(); void AddToPendingQueue(_In_ std::shared_ptr pendingRequest); void AddToProcessingQueue(_In_ xsapi_internal_vector> processingQueue); void RemoveFromProcessingQueue(_In_ uint32_t identifier); const MultiplayerEventQueue& EventQueue(); xsapi_internal_vector> GetProcessingQueue(); void UpdateGameSession(_In_ const std::shared_ptr& session); void UpdateObjects( const std::shared_ptr& updatedSession, const std::shared_ptr& lobbySession ); std::shared_ptr Session() const; void UpdateSession(_In_ const std::shared_ptr& session); void RemoveStaleUsersFromRemoteSession(); void SetLocalMemberPropertiesToRemoteSession( _In_ const std::shared_ptr& localUser, _In_ const Map& propertiesToWrite, _In_ const String& localUserSecureDeviceAddress ) noexcept; HRESULT JoinGameHelper( _In_ const String& sessionName, _In_ Callback> callback ) noexcept; void LeaveRemoteSession( _In_ std::shared_ptr session, _In_ bool stopAdvertisingGameSession, _In_ bool triggerCompletionEvent ) noexcept; HRESULT JoinGameFromLobbyHelper( _In_ MultiplayerSessionCallback callback ) noexcept; HRESULT JoinGameBySessionReference( _In_ const XblMultiplayerSessionReference& gameSessionRef, _In_ MultiplayerSessionCallback callback ) noexcept; HRESULT JoinGameByHandle( _In_ const String& handleId, _In_ bool createGameIfFailedToJoin, _In_ MultiplayerSessionCallback callback ) noexcept; private: std::shared_ptr LobbySession() const; std::shared_ptr LobbyClient() const; std::shared_ptr ConvertToMultiplayerGame( _In_ const std::shared_ptr& sessionToConvert, _In_ const std::shared_ptr& lobbySession ); HRESULT JoinGameForAllLocalMembers( _In_ const XblMultiplayerSessionReference& sessionRefToJoin, _In_ const String& handleIdToJoin, _In_ bool createGameIfFailedToJoin, _In_ MultiplayerSessionCallback callback ) noexcept; HRESULT JoinHelper( _In_ std::shared_ptr localUser, _In_ std::shared_ptr session, _In_ bool writeMemberPropertiesFromLobby, _In_ const String& handleId, _In_ MultiplayerSessionCallback callback ) const noexcept; TaskQueue m_queue; mutable std::mutex m_clientRequestLock; std::atomic m_pendingCommitInProgress{ false }; String m_gameSessionTemplateName; uint64_t m_updateNumber{ 0 }; std::shared_ptr m_sessionWriter; MultiplayerEventQueue m_multiplayerEventQueue; std::shared_ptr m_multiplayerGame; std::shared_ptr m_multiplayerLocalUserManager; Queue> m_pendingRequestQueue; Vector> m_processingQueue; }; #define MultiplayerLobbyClient_TransferHandlePropertyName "GameSessionTransferHandle" #define MultiplayerLobbyClient_JoinabilityPropertyName "Joinability" class MultiplayerLobbyClient : public std::enable_shared_from_this { public: MultiplayerLobbyClient(_In_ const TaskQueue& queue) noexcept; MultiplayerLobbyClient( _In_ const TaskQueue& queue, _In_ String lobbySessionTemplateName, _In_ std::shared_ptr localUserManager ) noexcept; ~MultiplayerLobbyClient() noexcept; // TODO void deep_copy_if_updated(_In_ const MultiplayerLobbyClient& other); void Initialize(); const std::shared_ptr& SessionWriter() const; const std::shared_ptr& Lobby() const; void ClearPendingQueue(); void AddToPendingQueue(_In_ std::shared_ptr pendingRequest); void AddToProcessingQueue(_In_ xsapi_internal_vector> processingQueue); void RemoveFromProcessingQueue(_In_ uint32_t identifier); HRESULT AddLocalUser( _In_ xbox_live_user_t user, _In_ MultiplayerLocalUserLobbyState userState, _In_ const xsapi_internal_string& handleId = xsapi_internal_string() ); void AddLocalUsers(_In_ xsapi_internal_vector user, _In_ const xsapi_internal_string& handleId); void AddLocalUsers(_In_ xsapi_internal_vector user); HRESULT RemoveLocalUser(_In_ xbox_live_user_t user); void RemoveAllLocalUsers(); HRESULT SetLocalMemberProperties( _In_ xbox_live_user_t user, _In_ const xsapi_internal_string& name, _In_ const JsonValue& valueJson, _In_opt_ context_t context ); HRESULT DeleteLocalMemberProperties( _In_ xbox_live_user_t user, _In_ const xsapi_internal_string& name, _In_opt_ context_t context ); HRESULT SetLocalMemberConnectionAddress( _In_ xbox_live_user_t user, _In_ const xsapi_internal_string& address, _In_opt_ context_t context ); MultiplayerEventQueue DoWork(); bool IsPendingLobbyChanges(); bool IsRequestInProgress(); HRESULT CreateGameFromLobby() noexcept; XblMultiplayerJoinability Joinability(); HRESULT SetJoinability( _In_ XblMultiplayerJoinability value, _In_opt_ context_t context ); const MultiplayerEventQueue& EventQueue(); xsapi_internal_vector> GetProcessingQueue(); void UpdateLobbySession(_In_ const std::shared_ptr& updatedSession); void UpdateObjects( const std::shared_ptr& updatedSession, const std::shared_ptr& gameSession ); const std::shared_ptr& Session() const; void UpdateSession( _In_ const std::shared_ptr& updatedSession ); void LeaveRemoteSession( _In_ std::shared_ptr session ); void StopAdvertisingGameSession( _In_ Result> result ); void AdvertiseGameSession() noexcept; void ClearGameSessionFromLobby(); bool IsTransferHandleState(_In_ const xsapi_internal_string& state); xsapi_internal_string GetTransferHandle(); void RemoveStaleXboxLiveContextFromMap(); const xsapi_internal_map>& GetLocalUserMap(); std::shared_ptr GetPrimaryContext(); private: std::shared_ptr GameClient(); std::shared_ptr GameSession(); HRESULT CommitPendingLobbyChanges( _In_ const Vector& xuidsInOrder, _In_ bool joinByHandleId, _In_ XblMultiplayerSessionReference sessionRef, _In_ MultiplayerEventQueueCallback callback ) noexcept; HRESULT CommitLobbyChanges( _In_ const Vector& xuidsInOrder, _In_ std::shared_ptr lobbySessionToCommit, _In_ Callback> callback ) noexcept; void UpdateLobby(_In_ std::shared_ptr multiplayerLobby); void UpdateLocalLobbyMembers( _In_ const std::shared_ptr& lobbySession, _In_ const std::shared_ptr& gameSession ); bool IsPendingLobbyLocalUserChanges(); bool ShouldUpdateHostToken( _In_ std::shared_ptr localUser, _In_ std::shared_ptr session ); void UserStateChanged( _In_ Result error, _In_ MultiplayerLocalUserLobbyState localUserLobbyState, _In_ uint64_t xboxUserId ); void HandleLobbyChangeEvents( _In_ Result error, _In_ std::shared_ptr localUser, _In_ const xsapi_internal_vector>& processingQueue ); void HandleJoinLobbyCompleted( _In_ Result error, _In_ uint64_t joinedXuid ); void JoinLobbyCompleted( _In_ Result error, _In_ uint64_t invitedXboxUserId ); void AddEvent( _In_ XblMultiplayerEventType eventType, _In_opt_ std::shared_ptr eventArgs = nullptr, _In_opt_ Result error = {}, _In_opt_ context_t context = nullptr ); std::shared_ptr ConvertToMultiplayerLobby( _In_ const std::shared_ptr& sessionToConvert, _In_ const std::shared_ptr& gameSession ); TaskQueue m_queue; String m_lobbySessionTemplateName; std::atomic m_pendingCommitInProgress{ false }; uint64_t m_updateNumber{ 0 }; XblMultiplayerJoinability m_joinability{ XblMultiplayerJoinability::None }; mutable std::mutex m_clientRequestLock; Queue> m_pendingRequestQueue; MultiplayerEventQueue m_multiplayerEventQueue; std::shared_ptr m_sessionWriter; std::shared_ptr m_multiplayerLobby; Vector> m_localLobbyMembers; std::shared_ptr m_multiplayerLocalUserManager; Vector> m_processingQueue; }; class MultiplayerClientPendingReader : public std::enable_shared_from_this { public: MultiplayerClientPendingReader(const TaskQueue& queue); ~MultiplayerClientPendingReader(); MultiplayerClientPendingReader( _In_ const TaskQueue& queue, _In_ const xsapi_internal_string& lobbySessionTemplateName, _In_ std::shared_ptr localUserManager ); // TODO void deep_copy_if_updated(_In_ const MultiplayerClientPendingReader& other); bool IsUpdateAvailable(_In_ const MultiplayerClientPendingReader& other); void DoWork(); void ProcessMatchEvents(); std::shared_ptr LobbyClient(); std::shared_ptr GameClient(); std::shared_ptr MatchClient(); const MultiplayerEventQueue& EventQueue() const; void ClearEventQueue(); void AddEvent( _In_ XblMultiplayerEventType eventType, _In_ std::shared_ptr eventArgs, _In_ XblMultiplayerSessionType sessionType, _In_ Result error, _In_opt_ context_t context = nullptr ); void AddEvent(_In_ const XblMultiplayerEvent& multiplayerEvent); void AddEvents(_In_ const MultiplayerEventQueue& multiplayerEventQueue); std::shared_ptr GetSession(_In_ XblMultiplayerSessionReference sessionRef); void UpdateSession(_In_ XblMultiplayerSessionReference sessionRef, _In_ std::shared_ptr session); bool IsLobby(_In_ XblMultiplayerSessionReference sessionRef); bool IsGame(_In_ XblMultiplayerSessionReference sessionRef); bool IsMatch(_In_ XblMultiplayerSessionReference sessionRef); HRESULT FindMatch( _In_ const xsapi_internal_string& hopperName, _In_ JsonValue& attributes, _In_ const std::chrono::seconds& timeout ); void SetAutoFillMembersDuringMatchmaking(_In_ bool autoFillMembers); HRESULT SetJoinability( _In_ XblMultiplayerJoinability value, _In_opt_ context_t context ); HRESULT SetProperties( _In_ const XblMultiplayerSessionReference& sessionRef, _In_ const xsapi_internal_string& name, _In_ const JsonValue& valueJson, _In_opt_ context_t context ); HRESULT SetSynchronizedProperties( _In_ const XblMultiplayerSessionReference& sessionRef, _In_ const xsapi_internal_string& name, _In_ const JsonValue& valueJson, _In_opt_ context_t context ); HRESULT SetSynchronizedHost( _In_ const XblMultiplayerSessionReference& sessionRef, _In_ const xsapi_internal_string& hostDeviceToken, _In_opt_ context_t context ); std::shared_ptr ConvertToGameMember(_In_ const XblMultiplayerSessionMember* member); private: void AddToPendingQueue( _In_ const XblMultiplayerSessionReference& sessionRef, _In_ std::shared_ptr pendingRequest ); static bool IsLocal(_In_ uint64_t xuid, _In_ const xsapi_internal_map>& xboxLiveContextMap); TaskQueue m_queue; bool m_autoFillMembers; MultiplayerEventQueue m_multiplayerEventQueue; mutable std::mutex m_clientRequestLock; std::shared_ptr m_lobbyClient; std::shared_ptr m_gameClient; std::shared_ptr m_matchClient; std::shared_ptr m_multiplayerLocalUserManager; }; class MultiplayerLocalUser { public: MultiplayerLocalUser( _In_ User&& user, _In_ uint64_t xboxUserId, _In_ bool isPrimary ); ~MultiplayerLocalUser(); uint64_t Xuid() const; std::shared_ptr Context() const; const xsapi_internal_string& LobbyHandleId() const; void SetLobbyHandleId(_In_ const xsapi_internal_string& handleId); MultiplayerLocalUserLobbyState LobbyState() const; void SetLobbyState(_In_ MultiplayerLocalUserLobbyState userState); MultiplayerLocalUserGameState GameState() const; void SetGameState(_In_ MultiplayerLocalUserGameState userState); const xsapi_internal_string& ConnectionAddress() const; void SetConnectionAddress(_In_ const xsapi_internal_string& address); bool IsPrimaryXboxLiveContext() const; void SetIsPrimaryXboxLiveContext(_In_ bool isPrimary); bool WriteChangesToService() const; void SetWriteChangesToService(_In_ bool value); XblFunctionContext SessionChangedContext() const; void SetSessionChangedContext(_In_ XblFunctionContext functionContext); XblFunctionContext RtaResyncContext() const; void SetRtaResyncContext(_In_ XblFunctionContext functionContext); XblFunctionContext ConnectionIdChangedContext() const; void SetConnectionIdChangedContext(_In_ XblFunctionContext functionContext); XblFunctionContext SubscriptionLostContext() const; void SetSubscriptionLostContext(_In_ XblFunctionContext functionContext); private: XblFunctionContext m_sessionChangedContext{}; XblFunctionContext m_connectionIdChangedContext{}; XblFunctionContext m_subscriptionLostContext{}; XblFunctionContext m_rtaResyncContext{}; bool m_writeChangesToService{ false }; uint64_t m_xuid{ 0 }; xsapi_internal_string m_connectionAddress; xsapi_internal_string m_lobbyHandleId; MultiplayerLocalUserLobbyState m_lobbyState{ MultiplayerLocalUserLobbyState::Unknown }; MultiplayerLocalUserGameState m_gameState{ MultiplayerLocalUserGameState::Unknown }; bool m_isPrimaryXboxLiveContext{ false }; std::shared_ptr m_xboxLiveContextImpl; }; class MultiplayerLocalUserManager : public std::enable_shared_from_this { public: MultiplayerLocalUserManager() = default; ~MultiplayerLocalUserManager(); std::shared_ptr GetPrimaryContext(); void ChangeAllLocalUserLobbyState(_In_ MultiplayerLocalUserLobbyState state); void ChangeAllLocalUserGameState(_In_ MultiplayerLocalUserGameState state); bool IsLocalUserGameState(_In_ MultiplayerLocalUserGameState state); const xsapi_internal_map>& GetLocalUserMap(); std::shared_ptr GetContext(_In_ uint64_t xuid); std::shared_ptr GetLocalUser(_In_ uint64_t xuid); std::shared_ptr GetLocalUserHelper(_In_ uint64_t xuid); std::shared_ptr GetLocalUser( _In_ xbox_live_user_t user ); std::shared_ptr GetLocalUserHelper( _In_ xbox_live_user_t user ); Result> AddUserToXboxLiveContextToMap(_In_ xbox_live_user_t user); void RemoveStaleLocalUsersFromMap(); void ActivateMultiplayerEvents(_In_ const std::shared_ptr& localuser); void DeactivateMultiplayerEvents(_In_ const std::shared_ptr& localUser); XblFunctionContext AddMultiplayerSessionChangedHandler( _In_ Callback handler ); void RemoveMultiplayerSessionChangedHandler(_In_ XblFunctionContext context); XblFunctionContext AddMultiplayerConnectionIdChangedHandler(_In_ Function handler); void RemoveMultiplayerConnectionIdChangedHandler(_In_ XblFunctionContext context); XblFunctionContext AddMultiplayerSubscriptionLostHandler(_In_ Function handler); void RemoveMultiplayerSubscriptionLostHandler(_In_ XblFunctionContext context); XblFunctionContext AddRtaResyncHandler(_In_ Function handler); void RemoveRtaResyncHandler(_In_ XblFunctionContext context); private: std::mutex m_lock; void OnConnectionIdChanged(); void OnSubscriptionsLost(_In_ uint64_t xuid); void OnSessionChanged( _In_ const XblMultiplayerSessionChangeEventArgs& args ); void OnConnectionStateChanged( _In_ uint64_t xuid, _In_ XblRealTimeActivityConnectionState state ); void OnResyncMessageReceived(); std::mutex m_subscriptionLock; XblFunctionContext m_sessionChangeEventHandlerCounter{ 1 }; XblFunctionContext m_multiplayerConnectionIdChangedEventHandlerCounter{ 1 }; XblFunctionContext m_multiplayerSubscriptionLostEventHandlerCounter{ 1 }; XblFunctionContext m_rtaResyncEventHandlerCounter{ 1 }; xsapi_internal_unordered_map> m_sessionChangeEventHandler; xsapi_internal_unordered_map> m_multiplayerConnectionIdChangedEventHandler; xsapi_internal_unordered_map> m_multiplayerSubscriptionLostEventHandler; xsapi_internal_unordered_map> m_rtaResyncEventHandler; xsapi_internal_map> m_localUserRequestMap; std::shared_ptr m_primaryXboxLiveContext; TaskQueue m_queue; }; class MultiplayerClientManager : public std::enable_shared_from_this { public: MultiplayerClientManager(_In_ const MultiplayerClientManager& other); MultiplayerClientManager( _In_ const xsapi_internal_string& lobbySessionTemplateName, _In_ const TaskQueue& queue ); ~MultiplayerClientManager() = default; void Initialize(); void Shutdown(); void RegisterLocalUserManagerEvents(); bool IsUpdateAvailable(); bool IsRequestInProgress(); std::shared_ptr LocalUserManager(); std::shared_ptr GetPrimaryContext(); std::shared_ptr LobbyClient() const; std::shared_ptr LatestPendingRead() const; std::shared_ptr LastPendingRead() const; HRESULT JoinLobbyByHandle( _In_ const xsapi_internal_string& handleId, _In_ const xsapi_internal_vector& users ); #if HC_PLATFORM == HC_PLATFORM_UWP || HC_PLATFORM == HC_PLATFORM_XDK HRESULT JoinLobby( _In_ Windows::ApplicationModel::Activation::IProtocolActivatedEventArgs^ eventArgs, _In_ xsapi_internal_vector users ); HRESULT JoinLobby( _In_ Windows::Foundation::Uri^ url, _In_ xsapi_internal_vector users); #endif HRESULT JoinGameFromLobby(_In_ const xsapi_internal_string& sessionTemplateName); HRESULT JoinGame( _In_ const xsapi_internal_string& sessionName, _In_ const xsapi_internal_string& sessionTemplateName, _In_ const xsapi_internal_vector& xuids ); HRESULT LeaveGame(); MultiplayerEventQueue DoWork(); // TODO see if we can switch these to ref HRESULT GetActivitiesForSocialGroup( _In_ xbox_live_user_t user, _In_ const xsapi_internal_string& socialGroup, _In_ XTaskQueueHandle queue, _In_ Callback>> callback ); HRESULT InviteFriends( _In_ xbox_live_user_t user, _In_ const xsapi_internal_string& contextStringId, _In_ const xsapi_internal_string& customActivationContext ); HRESULT InviteUsers( _In_ xbox_live_user_t user, _In_ const xsapi_internal_vector& xboxUserIds, _In_ const xsapi_internal_string& contextStringId, _In_ const xsapi_internal_string& customActivationContext ); HRESULT SetProperties( _In_ const XblMultiplayerSessionReference& sessionRef, _In_ const xsapi_internal_string& name, _In_ const JsonValue& valueJson, _In_opt_ context_t context ); HRESULT SetJoinability( _In_ XblMultiplayerJoinability value, _In_opt_ context_t context ); HRESULT SetSynchronizedHost( _In_ const XblMultiplayerSessionReference& sessionRef, _In_ const xsapi_internal_string& hostDeviceToken, _In_opt_ context_t context ); HRESULT SetSynchronizedProperties( _In_ const XblMultiplayerSessionReference& sessionRef, _In_ const xsapi_internal_string& name, _In_ const JsonValue& valueJson, _In_opt_ context_t context ); std::shared_ptr MatchClient(); HRESULT FindMatch( _In_ const xsapi_internal_string& hopperName, _In_ JsonValue& attributes, _In_ const std::chrono::seconds& timeout ); void SetAutoFillMembersDuringMatchmaking(_In_ bool autoFillMembers); void OnSessionChanged( _In_ XblMultiplayerSessionChangeEventArgs args ); const MultiplayerEventQueue& EventQueue() const; void ClearEventQueue(); void OnMultiplayerConnectionIdChanged(); void OnMultiplayerSubscriptionsLost(); private: MultiplayerClientManager& operator=(MultiplayerClientManager other) = delete; void Destroy(); Result> GetMultiplayerService( _In_ xbox_live_user_t user ); xsapi_internal_map> GetXboxLiveContextMap(); void OnResyncMessageReceived(); void AddToLatestPendingReadEventQueue( _In_ XblMultiplayerEventType eventType, _In_ XblMultiplayerSessionType sessionType, _In_ std::shared_ptr eventArgs = nullptr, _In_opt_ Result error = {}, _In_opt_ context_t context = nullptr ); XblMultiplayerSessionType GetSessionType( _In_ std::shared_ptr session ); void ProcessEvents( _In_ std::shared_ptr currentSession, _In_ std::shared_ptr oldSession, _In_ XblMultiplayerSessionType sessionType ); void HandleMemberListChanged( _In_ std::shared_ptr currentSession, _In_ std::shared_ptr oldSession, _In_ XblMultiplayerSessionType sessionType ); void HandleMemberPropertiesChanged( _In_ std::shared_ptr currentSession, _In_ std::shared_ptr oldSession, _In_ XblMultiplayerSessionType sessionType ); void HandleSessionPropertiesChanged( _In_ std::shared_ptr currentSession, _In_ std::shared_ptr oldSession, _In_ XblMultiplayerSessionType sessionType ); void HandleHostChanged( _In_ std::shared_ptr currentSession, _In_ XblMultiplayerSessionType sessionType ); void HandleMatchStatusChanged( _In_ std::shared_ptr currentSession ); void HandleInitializationStateChanged( _In_ std::shared_ptr currentSession ); void SynchronizedWriteCompleted( _In_ std::error_code errorCode, _In_ XblMultiplayerEventType eventType, _In_ XblMultiplayerSessionType sessionType ); mutable std::mutex m_clientRequestLock; std::mutex m_synchronizeWriteWithTapLock; std::atomic m_subscriptionsLostFired; bool m_autoFillMembers{ false }; xsapi_internal_string m_lobbySessionTemplateName; XblFunctionContext m_sessionChangedContext{ 0 }; XblFunctionContext m_connectionIdChangedContext{ 0 }; XblFunctionContext m_subscriptionLostContext{ 0 }; XblFunctionContext m_rtaResyncContext{ 0 }; MultiplayerEventQueue m_multiplayerEventQueue; std::shared_ptr m_primaryXboxLiveContext; std::shared_ptr m_multiplayerLocalUserManager; std::shared_ptr m_lastPendingRead; std::shared_ptr m_latestPendingRead; TaskQueue m_queue; }; class MultiplayerMatchClient : public std::enable_shared_from_this { public: MultiplayerMatchClient( _In_ const TaskQueue& queue, _In_ std::shared_ptr localUserManager ) noexcept; ~MultiplayerMatchClient() noexcept = default; MultiplayerEventQueue DoWork(); const MultiplayerEventQueue& EventQueue(); // TODO remove in favor of assignment operator void deep_copy_if_updated(_In_ const MultiplayerMatchClient& other); XblMultiplayerMatchStatus MatchStatus() const; void SetMatchStatus(_In_ XblMultiplayerMatchStatus status); const std::chrono::seconds EstimatedMatchWaitTime() const; HRESULT FindMatch( _In_ const xsapi_internal_string& hopperName, _In_ JsonValue& attributes, _In_ const std::chrono::seconds& timeout, _In_ std::shared_ptr session, _In_ bool preserveSession = false ); HRESULT FindMatch( _In_ std::shared_ptr session, _In_ bool preserveSession ); void CancelMatch(); void UpdateSession(_In_ std::shared_ptr currentSession); std::shared_ptr Session(); void HandleMatchStatusChanged( _In_ std::shared_ptr matchSession ); void HandleInitializationStateChanged( _In_ std::shared_ptr matchSession ); void OnSessionChanged( _In_ const XblMultiplayerSessionChangeEventArgs& args ); void SetQosMeasurements( _In_ const JsonValue& measurements ) noexcept; void ResubmitMatchmaking( _In_ std::shared_ptr session ); void HandleFindMatchCompleted( _In_ Result error ); void DisableNextTimer(bool value); bool m_disableNextTimer{ false }; private: void CheckNextTimer(); void HandleSessionJoined(); void GetLatestSession(); void HandleQosMeasurements(); void HandleMatchFound( _In_ std::shared_ptr currentSession ) noexcept; HRESULT JoinSession( _In_ std::shared_ptr session, _In_ MultiplayerSessionCallback callback ) noexcept; TaskQueue m_queue; std::mutex m_lock; std::mutex m_getSessionLock; xbox::services::datetime m_nextTimerToFetchSession; String m_hopperName; JsonDocument m_attributes; std::chrono::seconds m_timeout{}; bool m_preservingMatchmakingSession{ false }; std::atomic m_matchStatus{ XblMultiplayerMatchStatus::None }; mutable std::mutex m_multiplayerEventQueueLock; MultiplayerEventQueue m_multiplayerEventQueue; XblCreateMatchTicketResponse m_matchTicketResponse{}; XblMultiplayerSessionReference m_matchTicketSessionRef{}; std::shared_ptr m_matchSession; std::shared_ptr m_multiplayerLocalUserManager; std::atomic m_getSessionInProgress{ false }; std::atomic m_joinTargetSessionComplete{ false }; Result> m_joinTargetSessionResult; }; class MultiplayerManagerUtils { public: static bool IsMultiplayerSessionChangeType( _In_ XblMultiplayerSessionChangeTypes diffType, _In_ XblMultiplayerSessionChangeTypes value ) { return (diffType & value) == value; } static bool CompareSessions( _In_ const std::shared_ptr& session1, _In_ const std::shared_ptr& session2 ); static void SetJoinability( _In_ XblMultiplayerJoinability value, _In_ std::shared_ptr sessionToCommit, _In_ bool isGameInProgress ); static XblMultiplayerJoinability GetJoinability( _In_ const XblMultiplayerSessionProperties& sessionProperties ); static XblMultiplayerJoinability ConvertStringToJoinability( _In_ const xsapi_internal_string& value ); static xsapi_internal_string ConvertJoinabilityToString(_In_ XblMultiplayerJoinability value); }; }}}} ================================================ FILE: Source/Services/Multiplayer/Manager/multiplayer_manager_utils.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "multiplayer_manager_internal.h" using namespace xbox::services::multiplayer; using namespace xbox::services::legacy; NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_MANAGER_CPP_BEGIN bool MultiplayerManagerUtils::CompareSessions( _In_ const std::shared_ptr& session1, _In_ const std::shared_ptr& session2 ) { if (session1 == nullptr && session2 == nullptr) { return true; } if ( (session1 == nullptr && session2 != nullptr) || (session1 != nullptr && session2 == nullptr) || session1->SessionInfo().ChangeNumber != session2->SessionInfo().ChangeNumber) { return false; } return true; } // joinable_by_friends : !closed & JoinRestricion::Followed // invite_only : !closed & JoinRestricion::Local // disable_while_game_in_progress : closed & JoinRestricion::Local (when in game) // closed : closed & JoinRestricion::Local void MultiplayerManagerUtils::SetJoinability( _In_ XblMultiplayerJoinability value, _In_ std::shared_ptr sessionToCommit, _In_ bool isGameInProgress ) { sessionToCommit->SetClosed(false); sessionToCommit->SetJoinRestriction(XblMultiplayerSessionRestriction::Local); if (value == XblMultiplayerJoinability::JoinableByFriends) { sessionToCommit->SetJoinRestriction(XblMultiplayerSessionRestriction::Followed); } else if (value == XblMultiplayerJoinability::Closed || (isGameInProgress && value == XblMultiplayerJoinability::DisableWhileGameInProgress)) { sessionToCommit->SetClosed(true); } xsapi_internal_string jsonValueStr = ConvertJoinabilityToString(value); JsonDocument json; json.SetString(jsonValueStr.c_str(), json.GetAllocator()); sessionToCommit->SetSessionCustomPropertyJson(MultiplayerLobbyClient_JoinabilityPropertyName, json); } XblMultiplayerJoinability MultiplayerManagerUtils::GetJoinability( _In_ const XblMultiplayerSessionProperties& sessionProperties ) { xsapi_internal_string joinableStr; JsonDocument jsonDoc; jsonDoc.Parse(sessionProperties.SessionCustomPropertiesJson); if (!jsonDoc.HasParseError()) { JsonUtils::ExtractJsonString(jsonDoc, MultiplayerLobbyClient_JoinabilityPropertyName, joinableStr, false); } return ConvertStringToJoinability(joinableStr); } XblMultiplayerJoinability MultiplayerManagerUtils::ConvertStringToJoinability( _In_ const xsapi_internal_string& value ) { if (utils::str_icmp_internal(value, "joinable_by_friends") == 0) { return XblMultiplayerJoinability::JoinableByFriends; } else if (utils::str_icmp_internal(value, "invite_only") == 0) { return XblMultiplayerJoinability::InviteOnly; } else if (utils::str_icmp_internal(value, "disable_while_game_in_progress") == 0) { return XblMultiplayerJoinability::DisableWhileGameInProgress; } else if (utils::str_icmp_internal(value, "closed") == 0) { return XblMultiplayerJoinability::Closed; } return XblMultiplayerJoinability::None; } xsapi_internal_string MultiplayerManagerUtils::ConvertJoinabilityToString( _In_ XblMultiplayerJoinability value ) { switch (value) { case XblMultiplayerJoinability::JoinableByFriends: return "joinable_by_friends"; case XblMultiplayerJoinability::InviteOnly: return "invite_only"; case XblMultiplayerJoinability::DisableWhileGameInProgress: return "disable_while_game_in_progress"; case XblMultiplayerJoinability::Closed: return "closed"; default: return "none"; } } NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_MANAGER_CPP_END ================================================ FILE: Source/Services/Multiplayer/Manager/multiplayer_match_client.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "multiplayer_manager_internal.h" #include "multiplayer_internal.h" using namespace xbox::services; using namespace xbox::services::legacy; using namespace xbox::services::system; using namespace xbox::services::multiplayer; using namespace xbox::services::matchmaking; #if HC_PLATFORM == HC_PLATFORM_XDK using namespace Windows::Xbox::Networking; #endif NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_MANAGER_CPP_BEGIN MultiplayerMatchClient::MultiplayerMatchClient( _In_ const TaskQueue& queue, _In_ std::shared_ptr localUserManager ) noexcept : m_queue{ queue.DeriveWorkerQueue() }, m_multiplayerLocalUserManager{ localUserManager } { } void MultiplayerMatchClient::deep_copy_if_updated( _In_ const MultiplayerMatchClient& other ) { std::lock_guard lock(m_lock); if (other.m_matchSession == nullptr) { m_matchSession = nullptr; } else if (m_matchSession == nullptr || other.m_matchSession->SessionInfo().ChangeNumber > m_matchSession->SessionInfo().ChangeNumber) { m_matchSession = MakeShared(*other.m_matchSession); } } const MultiplayerEventQueue& MultiplayerMatchClient::EventQueue() { return m_multiplayerEventQueue; } MultiplayerEventQueue MultiplayerMatchClient::DoWork() { switch (static_cast(m_matchStatus)) { case XblMultiplayerMatchStatus::None: { return MultiplayerEventQueue(); } case XblMultiplayerMatchStatus::Searching: { CheckNextTimer(); break; } case XblMultiplayerMatchStatus::Found: case XblMultiplayerMatchStatus::Canceled: { // Nothing to do here. Wait for match_status_changed event. break; } case XblMultiplayerMatchStatus::Joining: { if(m_joinTargetSessionComplete) { HandleSessionJoined(); } break; } case XblMultiplayerMatchStatus::WaitingForRemoteClientsToJoin: case XblMultiplayerMatchStatus::WaitingForRemoteClientsToUploadQos: { HandleInitializationStateChanged(Session()); break; } case XblMultiplayerMatchStatus::Measuring: { // Waiting for title to perform qos and provide qos measurements (via set_quality_of_service_measurements) HandleInitializationStateChanged(Session()); break; } case XblMultiplayerMatchStatus::Evaluating: { // Nothing to do here. Wait for initialization stage changed event. break; } default: break; } MultiplayerEventQueue eventQueue; { std::lock_guard lock(m_multiplayerEventQueueLock); eventQueue = m_multiplayerEventQueue; m_multiplayerEventQueue.Clear(); } return eventQueue; } void MultiplayerMatchClient::DisableNextTimer(bool value) { m_disableNextTimer = value; } void MultiplayerMatchClient::CheckNextTimer() { if (m_disableNextTimer) return; // Only used for Unit tests int64_t delta = m_nextTimerToFetchSession.to_interval() - xbox::services::datetime::utc_now().to_interval(); if ( delta < 0) { if (m_matchStatus == XblMultiplayerMatchStatus::Searching) { std::shared_ptr primaryContext = m_multiplayerLocalUserManager->GetPrimaryContext(); if (primaryContext == nullptr || m_getSessionInProgress) { return; } // Fetch ticket session std::weak_ptr weakSessionWriter = shared_from_this(); m_getSessionInProgress = true; primaryContext->MultiplayerService()->GetCurrentSession(m_matchTicketSessionRef, { m_queue, [weakSessionWriter](Result> sessionResult) { std::shared_ptr pThis(weakSessionWriter.lock()); if (pThis != nullptr) { if (SUCCEEDED(sessionResult.Hresult())) { pThis->HandleMatchStatusChanged(sessionResult.Payload()); } pThis->m_getSessionInProgress = false; } } }); } else { GetLatestSession(); } } } void MultiplayerMatchClient::HandleQosMeasurements() { std::shared_ptr performQosEventArgs = MakeShared(); XblMultiplayerSessionReadLockGuard sessionSafe(Session()); for (const auto& member : sessionSafe.Members()) { if (!member.IsCurrentUser) { std::vector base64ConnectionAddress(xbox::services::convert::from_base64(member.SecureDeviceBaseAddress64)); const xsapi_internal_string& secureDeviceAddress = xsapi_internal_string(base64ConnectionAddress.begin(), base64ConnectionAddress.end()); if (!secureDeviceAddress.empty()) { performQosEventArgs->AddRemoteClient(secureDeviceAddress, member.DeviceToken.Value); } } } if (performQosEventArgs->remoteClients.size() > 0) { m_matchStatus = XblMultiplayerMatchStatus::Measuring; { std::lock_guard lock(m_multiplayerEventQueueLock); m_multiplayerEventQueue.AddEvent( XblMultiplayerEventType::PerformQosMeasurements, XblMultiplayerSessionType::GameSession, std::dynamic_pointer_cast(performQosEventArgs) ); } } else { // If clients fail to join, the stage advances to "measuring". // Wait until memberInitialization either succeeds or fails. CheckNextTimer(); } } void MultiplayerMatchClient::HandleFindMatchCompleted( _In_ Result error ) { XblMultiplayerMeasurementFailure failure = XblMultiplayerMeasurementFailure::Unknown; auto matchSession = Session(); XblMultiplayerSessionReadLockGuard matchSessionSafe(matchSession); if (matchSession != nullptr && matchSessionSafe.CurrentUser() != nullptr) { failure = matchSessionSafe.CurrentUser()->InitializationFailureCause; } std::shared_ptr findMatchEventArgs = MakeShared( m_matchStatus, failure ); { std::lock_guard lock(m_multiplayerEventQueueLock); m_multiplayerEventQueue.AddEvent( XblMultiplayerEventType::FindMatchCompleted, XblMultiplayerSessionType::GameSession, std::dynamic_pointer_cast(findMatchEventArgs), error ); } } void MultiplayerMatchClient::HandleMatchStatusChanged( _In_ std::shared_ptr matchSession ) { switch (matchSession->MatchmakingServer()->Status) { case XblMatchmakingStatus::Searching: { XblMultiplayerMatchStatus expected = XblMultiplayerMatchStatus::None; if (m_matchStatus.compare_exchange_strong(expected, XblMultiplayerMatchStatus::Searching, std::memory_order_release)) { // Wait for status to change on ticket session or fetch after 2 mins. m_nextTimerToFetchSession = xbox::services::datetime::utc_now() + xbox::services::datetime::from_seconds(120); } else { if (m_disableNextTimer) return; // Only used for Unit tests int64_t delta = m_nextTimerToFetchSession.to_interval() - xbox::services::datetime::utc_now().to_interval(); if ( delta < 0) { // Delete the match ticket and let the title know that it failed. if (!m_hopperName.empty() && m_matchTicketResponse.matchTicketId[0] != '\0') { // Only the host has the ticketId info. Since we aren't the host who started the match, we cannot cancel it either. std::shared_ptr primaryContext = m_multiplayerLocalUserManager->GetPrimaryContext(); if (primaryContext == nullptr) { return; } auto asyncBlock = utils::MakeDefaultAsyncBlock(m_queue.GetHandle()); primaryContext->MatchmakingService()->DeleteMatchTicketAsync( AppConfig::Instance()->Scid(), m_hopperName, m_matchTicketResponse.matchTicketId, asyncBlock ); } m_matchStatus = XblMultiplayerMatchStatus::Failed; HandleFindMatchCompleted({ xbl_error_code::generic_error, "Timer exceeded" }); } } break; } case XblMatchmakingStatus::Expired: { m_matchStatus = XblMultiplayerMatchStatus::Expired; HandleFindMatchCompleted({ xbl_error_code::generic_error, "Match status: Expired" }); break; } case XblMatchmakingStatus::Canceled: { m_matchStatus = XblMultiplayerMatchStatus::Canceled; HandleFindMatchCompleted({ xbl_error_code::generic_error, "Match status: Canceled" }); break; } case XblMatchmakingStatus::Found: { HandleMatchFound(matchSession); break; } default: break; } } void MultiplayerMatchClient::HandleInitializationStateChanged( _In_ std::shared_ptr matchSession ) { UpdateSession(matchSession); if (matchSession->InitializationInfo().Episode > 0) { switch (matchSession->InitializationInfo().Stage) { case XblMultiplayerInitializationStage::Joining: { CheckNextTimer(); break; } case XblMultiplayerInitializationStage::Measuring: { if (m_matchStatus == XblMultiplayerMatchStatus::WaitingForRemoteClientsToUploadQos || m_matchStatus == XblMultiplayerMatchStatus::Measuring) { CheckNextTimer(); } else if (m_matchStatus == XblMultiplayerMatchStatus::WaitingForRemoteClientsToJoin) { HandleQosMeasurements(); } break; } case XblMultiplayerInitializationStage::Failed: { m_matchStatus = XblMultiplayerMatchStatus::Failed; HandleFindMatchCompleted({ xbl_error_code::generic_error, "Initialization failed" }); return; } default: break; } } else { XblMultiplayerSessionReadLockGuard matchSessionSafe(matchSession); if (matchSessionSafe.CurrentUser()->InitializationFailureCause == XblMultiplayerMeasurementFailure::None) { // QoS succeeded. m_matchStatus = XblMultiplayerMatchStatus::Completed; HandleFindMatchCompleted({ xbl_error_code::no_error }); } else { // Resubmit m_matchStatus = XblMultiplayerMatchStatus::Resubmitting; HandleFindMatchCompleted({ xbl_error_code::generic_error, "Measurement failure" }); } } } HRESULT MultiplayerMatchClient::FindMatch( _In_ std::shared_ptr session, _In_ bool preserveSession ) { return FindMatch(m_hopperName, m_attributes, m_timeout, session, preserveSession); } HRESULT MultiplayerMatchClient::FindMatch( _In_ const xsapi_internal_string& hopperName, _In_ JsonValue& attributes, _In_ const std::chrono::seconds& timeout, _In_ std::shared_ptr session, _In_ bool preserveSession ) { std::shared_ptr primaryContext = m_multiplayerLocalUserManager->GetPrimaryContext(); RETURN_HR_IF_LOG_DEBUG(primaryContext == nullptr || session == nullptr, E_UNEXPECTED, "No local user added. Call add_local_user() first."); XblMultiplayerMatchStatus expected = XblMultiplayerMatchStatus::None; RETURN_HR_IF_LOG_DEBUG(!m_matchStatus.compare_exchange_strong(expected, XblMultiplayerMatchStatus::SubmittingMatchTicket, std::memory_order_release), E_UNEXPECTED, "Match search already in progress."); if (preserveSession) { UpdateSession(session); } else { UpdateSession(nullptr); } m_hopperName = hopperName; JsonUtils::CopyFrom(m_attributes, attributes); m_timeout = timeout; m_preservingMatchmakingSession = preserveSession; m_matchTicketSessionRef = session->SessionReference(); std::weak_ptr thisWeakPtr = shared_from_this(); auto asyncBlock = utils::MakeAsyncBlock( m_queue.GetHandle(), utils::store_weak_ptr(shared_from_this()), [](XAsyncBlock* asyncBlock) { auto pThis = utils::get_shared_ptr(asyncBlock->context); if (pThis != nullptr) { XblCreateMatchTicketResponse result; auto hr = XblMatchmakingCreateMatchTicketResult(asyncBlock, &result); if (SUCCEEDED(hr)) { pThis->m_matchTicketResponse = result; pThis->m_matchStatus = XblMultiplayerMatchStatus::Searching; pThis->m_nextTimerToFetchSession = xbox::services::datetime::utc_now() + xbox::services::datetime::from_seconds(static_cast(pThis ->m_timeout.count())) + xbox::services::datetime::from_seconds(5); // some extra delay to be safe } if (FAILED(hr)) { pThis->m_matchStatus = XblMultiplayerMatchStatus::Failed; pThis->HandleFindMatchCompleted({ hr, "MatchTicketResult failed" }); } } Delete(asyncBlock); }); return primaryContext->MatchmakingService()->CreateMatchTicket( session->SessionReference(), session->SessionReference().Scid, hopperName, timeout, preserveSession ? XblPreserveSessionMode::Always : XblPreserveSessionMode::Never, attributes, asyncBlock); } void MultiplayerMatchClient::CancelMatch() { std::shared_ptr primaryContext = m_multiplayerLocalUserManager->GetPrimaryContext(); if (primaryContext == nullptr) { return; } if (m_matchStatus == XblMultiplayerMatchStatus::None || m_matchStatus == XblMultiplayerMatchStatus::Expired || m_matchStatus == XblMultiplayerMatchStatus::Canceled || m_matchStatus == XblMultiplayerMatchStatus::Failed) { return; } if (m_hopperName.empty() && m_matchTicketResponse.matchTicketId[0] != '\0') { // Since we aren't the host who started the match, we cannot cancel it either. return; } m_matchStatus = XblMultiplayerMatchStatus::Canceling; auto asyncBlock = utils::MakeDefaultAsyncBlock(m_queue.GetHandle()); primaryContext->MatchmakingService()->DeleteMatchTicketAsync( AppConfig::Instance()->Scid(), m_hopperName, m_matchTicketResponse.matchTicketId, asyncBlock ); } XblMultiplayerMatchStatus MultiplayerMatchClient::MatchStatus() const { return m_matchStatus; } void MultiplayerMatchClient::SetMatchStatus( _In_ XblMultiplayerMatchStatus status ) { m_matchStatus = status; } const std::chrono::seconds MultiplayerMatchClient::EstimatedMatchWaitTime() const { return std::chrono::seconds(m_matchTicketResponse.estimatedWaitTime); } void MultiplayerMatchClient::OnSessionChanged( _In_ const XblMultiplayerSessionChangeEventArgs& args ) { auto matchSession = Session(); if (matchSession != nullptr && matchSession->SessionReference() == args.SessionReference && args.ChangeNumber > matchSession->SessionInfo().ChangeNumber) { GetLatestSession(); } } void MultiplayerMatchClient::UpdateSession( _In_ std::shared_ptr session ) { std::lock_guard lock(m_lock); if (m_matchSession == nullptr || session == nullptr) { m_matchSession = session; m_matchTicketSessionRef = {}; } else if(XblMultiplayerSession::DoSessionsMatch(m_matchSession, session) && session->SessionInfo().ChangeNumber > m_matchSession->SessionInfo().ChangeNumber) { m_matchSession = session; m_nextTimerToFetchSession = xbox::services::datetime::utc_now() + xbox::services::datetime::from_seconds(static_cast(session->SessionInfo().NextTimer - session->TimeOfSession())); } } std::shared_ptr MultiplayerMatchClient::Session() { std::lock_guard lock(m_lock); return m_matchSession; } void MultiplayerMatchClient::HandleSessionJoined() { if (FAILED(m_joinTargetSessionResult.Hresult())) { m_matchStatus = XblMultiplayerMatchStatus::Failed; HandleFindMatchCompleted({ m_joinTargetSessionResult.Hresult(), "JoinSession failed" }); return; } if (Session()->InitializationInfo().Episode == 0) { m_matchStatus = XblMultiplayerMatchStatus::Completed; HandleFindMatchCompleted({ xbl_error_code::no_error }); } else { m_matchStatus = XblMultiplayerMatchStatus::WaitingForRemoteClientsToJoin; } } void MultiplayerMatchClient::HandleMatchFound( _In_ std::shared_ptr currentSession ) noexcept { std::shared_ptr primaryXboxLiveContext = m_multiplayerLocalUserManager->GetPrimaryContext(); if(primaryXboxLiveContext == nullptr) { m_matchStatus = XblMultiplayerMatchStatus::Failed; // No primary xbox live context HandleFindMatchCompleted({ xbl_error_code::runtime_error, "No primary xbox live context" }); return; } m_matchStatus = XblMultiplayerMatchStatus::Found; auto& targetSessionRef = currentSession->MatchmakingServer()->TargetSessionRef; auto targetGameSession = MakeShared( primaryXboxLiveContext->Xuid(), &targetSessionRef, nullptr ); auto state{ GlobalState::Get() }; auto gameClient = state ? state->MultiplayerManager()->GameClient() : nullptr; if (m_preservingMatchmakingSession && gameClient != nullptr) { auto gameSession = gameClient->Session(); if (gameSession != nullptr && gameSession->SessionReference() == targetSessionRef) { targetGameSession = gameSession; } } UpdateSession(targetGameSession); JoinSession(targetGameSession, [ weakThis = std::weak_ptr{ shared_from_this() } ] (Result> result) { auto pThis = weakThis.lock(); if (pThis) { // Perhaps its OK to call handle_session_joined() immediately here, but to maintain behavioral parody with // pplx code, differ that until the next do_work call pThis->m_joinTargetSessionResult = std::move(result); pThis->m_joinTargetSessionComplete = true; } }); } HRESULT MultiplayerMatchClient::JoinSession( _In_ std::shared_ptr session, _In_ MultiplayerSessionCallback callback ) noexcept { // Join Match session for each local user. struct JoinSessionOperation : public std::enable_shared_from_this { JoinSessionOperation( std::shared_ptr matchClient, std::shared_ptr session, MultiplayerSessionCallback&& callback ) noexcept : m_matchClient{ std::move(matchClient) }, m_latestSession{ std::move(session) }, m_callback{ std::move(callback) } { for (auto& pair : m_matchClient->m_multiplayerLocalUserManager->GetLocalUserMap()) { m_users.push_back(pair.second); } } void Run() noexcept { m_matchClient->m_matchStatus = XblMultiplayerMatchStatus::Joining; if (m_matchClient->m_preservingMatchmakingSession) { // Matchmaking session was preserved so we're already part of it Complete(m_latestSession); } else { JoinNextUser(); } } private: void JoinNextUser() noexcept { if (m_users.empty()) { Complete(m_latestSession); } else { auto user{ m_users.front() }; m_users.pop_front(); JoinSession(user); } } void JoinSession(std::shared_ptr user) { auto userSession = MakeShared(user->Xuid(), &m_latestSession->SessionReference(), nullptr); userSession->Join(); // Ok to use 'unsafe' method here since we have the only instance userSession->CurrentUserInternalUnsafe()->SetSecureDeviceBaseAddress64(user->ConnectionAddress()); userSession->SetSessionChangeSubscription(XblMultiplayerSessionChangeTypes::Everything); HRESULT hr = user->Context()->MultiplayerService()->WriteSession( userSession, XblMultiplayerSessionWriteMode::UpdateOrCreateNew, AsyncContext>>{ m_matchClient->m_queue, [ op{ shared_from_this() } ] (Result> writeSessionResult) { if (Failed(writeSessionResult)) { op->Complete(std::move(writeSessionResult)); } else { op->m_latestSession = writeSessionResult.ExtractPayload(); op->JoinNextUser(); } } }); if (FAILED(hr)) { Complete(hr); } } void Complete(Result>&& result) noexcept { m_matchClient->m_queue.RunCompletion([op{shared_from_this()}, result] { op->m_matchClient->UpdateSession(result.Payload()); op->m_callback(std::move(result)); }); } std::shared_ptr m_matchClient; std::shared_ptr m_latestSession; List> m_users; MultiplayerSessionCallback m_callback; }; auto operation = MakeShared(shared_from_this(), session, std::move(callback)); operation->Run(); return S_OK; } void MultiplayerMatchClient::GetLatestSession() { std::lock_guard lock(m_getSessionLock); if (Session() == nullptr) return; std::shared_ptr primaryContext = m_multiplayerLocalUserManager->GetPrimaryContext(); if (primaryContext == nullptr || m_getSessionInProgress) { return; } std::weak_ptr weakSessionWriter = shared_from_this(); m_getSessionInProgress = true; primaryContext->MultiplayerService()->GetCurrentSession(Session()->SessionReference(), { m_queue, [weakSessionWriter](Result> sessionResult) { if (SUCCEEDED(sessionResult.Hresult())) { std::shared_ptr pThis(weakSessionWriter.lock()); if (pThis != nullptr) { pThis->UpdateSession(sessionResult.Payload()); pThis->m_getSessionInProgress = false; } } } }); } void MultiplayerMatchClient::SetQosMeasurements( _In_ const JsonValue& measurements ) noexcept { // Set QoS measurements for each local user. // Result delivered by updating the latest session and updating the match status. struct SetQosMeasurementsOperation : public std::enable_shared_from_this { SetQosMeasurementsOperation( std::shared_ptr matchClient, const JsonValue& measurements, const XblMultiplayerSessionReference& matchSessionRef ) noexcept : m_matchClient{ std::move(matchClient) }, m_matchSessionRef{ matchSessionRef } { JsonUtils::CopyFrom(m_measurements, measurements); for (auto& pair : m_matchClient->m_multiplayerLocalUserManager->GetLocalUserMap()) { m_users.push_back(pair.second); } } void Run() noexcept { m_matchClient->m_matchStatus = XblMultiplayerMatchStatus::UploadingQosMeasurements; SetMeasurementsForNextUser(); } private: void SetMeasurementsForNextUser() noexcept { if (m_users.empty()) { Complete(S_OK); } else { auto user{ m_users.front() }; m_users.pop_front(); auto session = MakeShared(user->Xuid(), &m_matchSessionRef, nullptr); session->Join(); // Fine to use 'unsafe' method here since this is the only instance of the session session->CurrentUserInternalUnsafe()->SetQosMeasurementsJson(JsonUtils::SerializeJson(m_measurements)); HRESULT hr = user->Context()->MultiplayerService()->WriteSession( session, XblMultiplayerSessionWriteMode::UpdateExisting, AsyncContext>>{ m_matchClient->m_queue, [ op{ shared_from_this() } ] (Result> result) { if (Failed(result)) { op->Complete(result); } else { op->m_latestSession = result.ExtractPayload(); op->SetMeasurementsForNextUser(); } } }); if (FAILED(hr)) { Complete(hr); } } } void Complete(Result&& result) { LOGS_DEBUG << __FUNCTION__ << ": HRESULT=" << result.Hresult() << ", ErrorMessage=" << result.ErrorMessage(); m_matchClient->UpdateSession(m_latestSession); m_matchClient->m_matchStatus = XblMultiplayerMatchStatus::WaitingForRemoteClientsToUploadQos; } std::shared_ptr m_matchClient; JsonDocument m_measurements; std::shared_ptr m_latestSession; List> m_users; XblMultiplayerSessionReference m_matchSessionRef; }; if (auto matchSession{ Session() }) { auto operation = MakeShared(shared_from_this(), measurements, matchSession->SessionReference()); operation->Run(); } } void MultiplayerMatchClient::ResubmitMatchmaking( _In_ std::shared_ptr session ) { if (session == nullptr) { return; } std::shared_ptr primaryContext = m_multiplayerLocalUserManager->GetPrimaryContext(); if (primaryContext == nullptr) { return; } m_matchStatus = XblMultiplayerMatchStatus::Resubmitting; session->SetMatchmakingResubmit(true); std::weak_ptr weakSessionWriter = shared_from_this(); primaryContext->MultiplayerService()->WriteSession(session, XblMultiplayerSessionWriteMode::UpdateExisting, { m_queue, [weakSessionWriter](Result> sessionResult) { if (FAILED(sessionResult.Hresult())) { std::shared_ptr pThis(weakSessionWriter.lock()); if (pThis != nullptr) { pThis->m_matchStatus = XblMultiplayerMatchStatus::Failed; pThis->HandleFindMatchCompleted({ sessionResult.Hresult(), "Resubmit failed" }); } } } }); } NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_MANAGER_CPP_END ================================================ FILE: Source/Services/Multiplayer/Manager/multiplayer_member.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "multiplayer_manager_internal.h" using namespace xbox::services; using namespace xbox::services::multiplayer; STDAPI_(bool) XblMultiplayerManagerMemberAreMembersOnSameDevice( _In_ const XblMultiplayerManagerMember* first, _In_ const XblMultiplayerManagerMember* second ) XBL_NOEXCEPT try { if (first != nullptr && second != nullptr) { return utils::str_icmp(first->DeviceToken, second->DeviceToken) == 0; } return false; } CATCH_RETURN() NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_MANAGER_CPP_BEGIN MultiplayerMember::MultiplayerMember() : m_memberId(0), m_xuid(0), m_isLocal(false), m_isInLobby(false), m_isInGame(false), m_status(XblMultiplayerSessionMemberStatus::Inactive) { } MultiplayerMember::MultiplayerMember( _In_ const XblMultiplayerSessionMember* member, _In_ bool isLocal, _In_ bool /*isGameHost*/, _In_ bool /*isLobbyHost*/, _In_ bool isInLobby, _In_ bool isInGame ): m_memberId(member->MemberId), m_xuid(member->Xuid), m_gamertag(member->Gamertag), m_deviceToken(member->DeviceToken.Value), m_isLocal(isLocal), m_isInLobby(isInLobby), m_isInGame(isInGame), m_status(member->Status) { if (member->InitialTeam) { m_initialTeam = member->InitialTeam; } if (member->CustomPropertiesJson) { m_jsonProperties = member->CustomPropertiesJson; } if (member->SecureDeviceBaseAddress64) { m_connectionAddress = utils::parse_secure_device_address(member->SecureDeviceBaseAddress64); } } uint32_t MultiplayerMember::MemberId() const { return m_memberId; } const xsapi_internal_string& MultiplayerMember::TeamId() const { return m_teamId; } const xsapi_internal_string& MultiplayerMember::InitialTeam() const { return m_initialTeam; } uint64_t MultiplayerMember::Xuid() const { return m_xuid; } const xsapi_internal_string& MultiplayerMember::DebugGamertag() const { return m_gamertag; } const xsapi_internal_string& MultiplayerMember::DeviceToken() const { return m_deviceToken; } bool MultiplayerMember::IsLocal() const { return m_isLocal; } bool MultiplayerMember::IsInLobby() const { return m_isInLobby; } bool MultiplayerMember::IsInGame() const { return m_isInGame; } XblMultiplayerSessionMemberStatus MultiplayerMember::Status() const { return m_status; } const xsapi_internal_string& MultiplayerMember::ConnectionAddress() const { return m_connectionAddress; } const xsapi_internal_string& MultiplayerMember::CustomPropertiesJson() const { return m_jsonProperties; } bool MultiplayerMember::IsMemberOnSameDevice( _In_ std::shared_ptr member ) const { if(member == nullptr) { return false; } return utils::str_icmp_internal(m_deviceToken, member->DeviceToken()) == 0; } std::shared_ptr MultiplayerMember::CreateFromSessionMember( _In_ const XblMultiplayerSessionMember* member, _In_ const std::shared_ptr& lobbySession, _In_ const std::shared_ptr& gameSession, _In_ const xsapi_internal_map>& xboxLiveContextMap ) { bool isLocal = false; for (const auto& xboxLiveContext : xboxLiveContextMap) { const auto& localUser = xboxLiveContext.second; if (localUser != nullptr && member->Xuid == localUser->Xuid()) { isLocal = true; break; } } return CreateFromSessionMember(member, lobbySession, gameSession, isLocal); } std::shared_ptr MultiplayerMember::CreateFromSessionMember( _In_ const XblMultiplayerSessionMember* member, _In_ const std::shared_ptr& lobbySession, _In_ const std::shared_ptr& gameSession, _In_ bool isLocal ) { return MakeShared( member, isLocal, // isLocal XblMultiplayerSession::IsHost(member->DeviceToken.Value, gameSession), // isGameHost XblMultiplayerSession::IsHost(member->DeviceToken.Value, lobbySession), // isLobbyHost XblMultiplayerSession::IsPlayerInSession(member->Xuid, lobbySession), // isinLobby XblMultiplayerSession::IsPlayerInSession(member->Xuid, gameSession) // isinGame ); } XblMultiplayerManagerMember MultiplayerMember::GetReference() const { XblMultiplayerManagerMember ref = XblMultiplayerManagerMember{}; ref.MemberId = m_memberId; ref.InitialTeam = m_initialTeam.data(); ref.Xuid = m_xuid; ref.DebugGamertag = m_gamertag.data(); ref.IsLocal = m_isLocal; ref.IsInLobby = m_isInLobby; ref.IsInGame = m_isInGame; ref.Status = m_status; ref.ConnectionAddress = m_connectionAddress.data(); ref.PropertiesJson = m_jsonProperties.data(); ref.DeviceToken = m_deviceToken.data(); return ref; } NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_MANAGER_CPP_END ================================================ FILE: Source/Services/Multiplayer/Manager/multiplayer_session_writer.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "multiplayer_manager_internal.h" using namespace xbox::services::system; NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_MANAGER_CPP_BEGIN #if HC_PLATFORM == HC_PLATFORM_UWP || HC_PLATFORM == HC_PLATFORM_XDK #define TIME_PER_CALL_MS (1 * 1000) #elif defined(XSAPI_UNIT_TESTS) #define TIME_PER_CALL_MS (30 * 1000) #endif MultiplayerSessionWriter::MultiplayerSessionWriter( const TaskQueue& queue ) noexcept : m_queue{ queue.DeriveWorkerQueue() } { } MultiplayerSessionWriter::MultiplayerSessionWriter( const TaskQueue& queue, _In_ std::shared_ptr localUserManager ) noexcept : m_queue{ queue.DeriveWorkerQueue() }, m_multiplayerLocalUserManager{ std::move(localUserManager) } { } void MultiplayerSessionWriter::Destroy() { m_id++; m_session = nullptr; m_isTapReceived = false; m_numOfWritesInProgress = 0; m_tapChangeNumber = 0; } std::shared_ptr MultiplayerSessionWriter::GetPrimaryContext() { return m_multiplayerLocalUserManager->GetPrimaryContext(); } uint64_t MultiplayerSessionWriter::Id() const { return m_id; } const std::shared_ptr& MultiplayerSessionWriter::Session() const { return m_session; } void MultiplayerSessionWriter::UpdateSession( _In_ const std::shared_ptr& updatedSession ) { if (updatedSession == nullptr) { Destroy(); } else { m_session = updatedSession; } } XblFunctionContext MultiplayerSessionWriter::AddMultiplayerSessionUpdatedHandler( _In_ Callback&> handler ) { std::lock_guard lock(m_stateLock); XblFunctionContext context = 0; if (handler != nullptr) { context = m_sessionUpdateEventHandlerCounter++; m_sessionUpdateEventHandler[m_sessionUpdateEventHandlerCounter] = std::move(handler); } return context; } void MultiplayerSessionWriter::OnResyncMessageReceived() { m_handleResyncEventCounter++; Resync(); } void MultiplayerSessionWriter::OnSessionUpdated( _In_ const std::shared_ptr& updatedSession ) { std::lock_guard lock(m_stateLock); for (const auto& handler : m_sessionUpdateEventHandler) { XSAPI_ASSERT(handler.second != nullptr); if (handler.second != nullptr) { try { handler.second(updatedSession); } catch (...) { LOG_ERROR("MultiplayerSessionWriter::on_session_updated call threw an exception"); } } } } bool MultiplayerSessionWriter::IsTapReceived() const { return m_isTapReceived; } void MultiplayerSessionWriter::SetTapReceived( _In_ bool bReceived ) { m_isTapReceived = bReceived; } bool MultiplayerSessionWriter::IsWriteInProgress() const { return m_numOfWritesInProgress > 0; } void MultiplayerSessionWriter::SetWriteInProgress( _In_ bool writeInProgress ) { if (writeInProgress) { m_numOfWritesInProgress++; } else { m_numOfWritesInProgress--; } } uint64_t MultiplayerSessionWriter::TapChangeNumber() const { return m_tapChangeNumber; } void MultiplayerSessionWriter::SetTapChangeNumber( _In_ uint64_t changeNumber ) { m_tapChangeNumber = changeNumber; } HRESULT MultiplayerSessionWriter::CommitSynchronizedChanges( _In_ std::shared_ptr sessionToCommit, _In_ MultiplayerSessionCallback callback ) noexcept { // Retrieve the latest session and write the pending commit changes to it. return WriteSession( m_multiplayerLocalUserManager->GetPrimaryContext(), sessionToCommit, XblMultiplayerSessionWriteMode::SynchronizedUpdate, true, std::move(callback) ); } HRESULT MultiplayerSessionWriter::CommitPendingSynchronizedChanges( _In_ Vector> processingQueue, _In_ XblMultiplayerSessionType sessionType, _In_ MultiplayerEventQueueCallback callback ) noexcept { if (m_session == nullptr) { auto eventQueue = HandleEvents(processingQueue, { E_FAIL, "Session null" }, sessionType); // TODO this may need to be invoked on another thread callback(Result(eventQueue, xbl_error_code::generic_error)); return S_OK; } auto sessionToCommitCopy = MakeShared(*m_session); // Update any pending local user or lobby session properties. for (auto& request : processingQueue) { request->AppendPendingChanges(sessionToCommitCopy, nullptr); } return WriteSession(GetPrimaryContext(), sessionToCommitCopy, XblMultiplayerSessionWriteMode::SynchronizedUpdate, true, [ weakThis = std::weak_ptr{ shared_from_this() }, processingQueue, sessionType, callback ] (Result> sessionResult) { MultiplayerEventQueue eventQueue; auto sharedThis{ weakThis.lock() }; if (sharedThis) { std::lock_guard lock(sharedThis->m_stateLock); eventQueue = sharedThis->HandleEvents(processingQueue, sessionResult, sessionType); } callback(Result(eventQueue, sessionResult.Hresult())); }); } HRESULT MultiplayerSessionWriter::CommitPendingChanges( _In_ Vector> processingQueue, _In_ XblMultiplayerSessionType sessionType, _In_ bool isGameInProgress, _In_ MultiplayerEventQueueCallback callback ) noexcept { if (m_session == nullptr) { auto eventQueue = HandleEvents(processingQueue, { E_FAIL, "Session is null" }, sessionType); // TODO this may need to be invoked on another thread callback(Result(eventQueue, xbl_error_code::generic_error, "Session is null")); return S_OK; } std::shared_ptr sessionToCommitCopy = MakeShared(*m_session); // Update any pending local user or lobby session properties. for (auto& request : processingQueue) { request->AppendPendingChanges(sessionToCommitCopy, nullptr, isGameInProgress); } return WriteSession(m_multiplayerLocalUserManager->GetPrimaryContext(), sessionToCommitCopy, XblMultiplayerSessionWriteMode::UpdateExisting, true, [ weakThis = std::weak_ptr{ shared_from_this() }, processingQueue, sessionType, callback ] (Result> sessionResult) { MultiplayerEventQueue eventQueue; auto sharedThis{ weakThis.lock() }; if (sharedThis) { std::lock_guard lock(sharedThis->m_stateLock); eventQueue = sharedThis->HandleEvents(processingQueue, sessionResult, sessionType); } callback(Result(eventQueue, sessionResult.Hresult(), sessionResult.ErrorMessage())); }); } HRESULT MultiplayerSessionWriter::WriteSession( _In_ std::shared_ptr xboxLiveContext, _In_ std::shared_ptr session, _In_ XblMultiplayerSessionWriteMode mode, _In_ bool updateLatest, _In_ MultiplayerSessionCallback callback ) noexcept { RETURN_HR_IF_LOG_DEBUG(xboxLiveContext == nullptr, E_UNEXPECTED, "Call add_local_user() first."); auto hr = xboxLiveContext->MultiplayerService()->WriteSession( session, mode, AsyncContext>>{ m_queue, [ weakThis = std::weak_ptr{ shared_from_this() }, sessionWriterId = m_id, updateLatest, callback ] (Result> sessionResult) { if (auto sharedThis{ weakThis.lock() }) { if (sharedThis->Id() != sessionWriterId) { callback({ E_FAIL, "Session writer has been reset" }); } else { sharedThis->HandleWriteSessionResult(sessionResult, updateLatest, callback); } } else { callback({ E_FAIL, "Session writer is null" }); } } }); if (SUCCEEDED(hr)) { SetWriteInProgress(true); } return hr; } HRESULT MultiplayerSessionWriter::WriteSessionByHandle( _In_ std::shared_ptr xboxLiveContext, _In_ std::shared_ptr session, _In_ XblMultiplayerSessionWriteMode mode, _In_ const String& handleId, _In_ bool updateLatest, _In_ MultiplayerSessionCallback callback ) noexcept { RETURN_HR_IF_LOG_DEBUG(xboxLiveContext == nullptr, E_UNEXPECTED, "Call add_local_user() first."); auto hr = xboxLiveContext->MultiplayerService()->WriteSessionByHandle( session, mode, handleId, AsyncContext>>{ m_queue, [ weakThis = std::weak_ptr{ shared_from_this() }, sessionWriterId = m_id, updateLatest, callback ] (Result> sessionResult) { if (auto sharedThis{ weakThis.lock() }) { if (sharedThis->Id() != sessionWriterId) { callback({ E_FAIL, "Session writer has been reset" }); } else { sharedThis->HandleWriteSessionResult(sessionResult, updateLatest, callback); } } else { callback({ E_FAIL, "Session writer is null" }); } } }); if (SUCCEEDED(hr)) { SetWriteInProgress(true); } return hr; } void MultiplayerSessionWriter::HandleWriteSessionResult( _In_ Result> writeSessionResult, _In_ bool updateLatest, _In_ MultiplayerSessionCallback callback ) noexcept { std::lock_guard guard{ m_synchronizeWriteWithTapLock }; if (Succeeded(writeSessionResult) || writeSessionResult.Hresult() == HTTP_E_STATUS_PRECOND_FAILED) { if (updateLatest) { OnSessionUpdated(writeSessionResult.Payload()); } } SetWriteInProgress(false); if (IsTapReceived()) { SetTapReceived(false); auto latestSession = m_session; // always check against the latest session(). if(updateLatest && latestSession != nullptr && latestSession->SessionInfo().ChangeNumber < TapChangeNumber()) { GetCurrentSessionHelper(m_multiplayerLocalUserManager->GetPrimaryContext(), latestSession->SessionReference(), callback); } else { callback(writeSessionResult); } } else { callback(writeSessionResult); } } void MultiplayerSessionWriter::OnSessionChanged( _In_ XblMultiplayerSessionChangeEventArgs args ) noexcept { std::lock_guard guard(m_synchronizeWriteWithTapLock); if (IsWriteInProgress()) { if (args.ChangeNumber > TapChangeNumber()) { SetTapReceived(true); SetTapChangeNumber(args.ChangeNumber); } } else { auto latestSession = Session(); if(latestSession != nullptr && args.ChangeNumber > latestSession->SessionInfo().ChangeNumber) { GetCurrentSessionHelper(m_multiplayerLocalUserManager->GetPrimaryContext(), latestSession->SessionReference(), nullptr); } } } HRESULT MultiplayerSessionWriter::GetCurrentSessionHelper( _In_ std::shared_ptr xboxLiveContext, _In_ const XblMultiplayerSessionReference& sessionReference, _In_ MultiplayerSessionCallback callback ) noexcept { RETURN_HR_IF_LOG_DEBUG(xboxLiveContext == nullptr, E_UNEXPECTED, "Call add_local_user() first."); return xboxLiveContext->MultiplayerService()->GetCurrentSession( sessionReference, AsyncContext>>{ m_queue, [ weakThis = std::weak_ptr{ shared_from_this() }, callback ] (Result> sessionResult) { if (auto sharedThis{ weakThis.lock() }) { if (Succeeded(sessionResult) || sessionResult.Hresult() == HTTP_E_STATUS_PRECOND_FAILED) { sharedThis->OnSessionUpdated(sessionResult.Payload()); } callback(sessionResult); } else { callback({E_FAIL, "Session writer is null"}); } } }); } void MultiplayerSessionWriter::Resync() { #if HC_PLATFORM == HC_PLATFORM_UWP || HC_PLATFORM == HC_PLATFORM_XDK || defined(XSAPI_UNIT_TESTS) std::lock_guard lock(m_resyncLock); auto cachedSession = Session(); if (cachedSession != nullptr && !m_isResyncTaskInProgress && m_handleResyncEventCounter > 0) { m_isResyncTaskInProgress = true; m_handleResyncEventCounter = 0; XblMultiplayerSessionChangeEventArgs changeEventArgs{ cachedSession->SessionReference(), "", cachedSession->SessionInfo().ChangeNumber + 1 }; OnSessionChanged(changeEventArgs); // You could get multiple resync events. To avoid fetching the session too often, only check back after TIME_PER_CALL_MS secs m_queue.RunWork([ weakThis = std::weak_ptr{ shared_from_this() }, this ] { if (auto sharedThis{ weakThis.lock() }) { std::unique_lock lock{ m_resyncLock }; m_isResyncTaskInProgress = false; lock.unlock(); Resync(); } }, TIME_PER_CALL_MS); } #else UNREFERENCED_PARAMETER(m_isResyncTaskInProgress); #endif } HRESULT MultiplayerSessionWriter::LeaveRemoteSession( _In_ std::shared_ptr session, _In_ MultiplayerSessionCallback callback ) noexcept { RETURN_HR_IF_LOG_DEBUG(session == nullptr, E_UNEXPECTED, "Session is null"); // LeaveSessionOperation will leave the provided session for each local user. // Because a User must leave the session on their own, this requires an MPSD call for each local user. // To be consistent with 1806 XDK behavior, if any of these MPSD calls fail, the rest of the operation // will be aborted and that failure will be returned. struct LeaveSessionOperation : public std::enable_shared_from_this { LeaveSessionOperation( std::shared_ptr sessionWriter, const XblMultiplayerSessionReference& sessionRefToLeave, TaskQueue& queue, MultiplayerSessionCallback&& callback ) noexcept : m_sessionWriter{ std::move(sessionWriter) }, m_sessionToLeaveRef{ sessionRefToLeave }, m_queue{ queue }, m_localUsers{ m_sessionWriter->m_multiplayerLocalUserManager->GetLocalUserMap() }, m_callback{ std::move(callback) } { } void Run() noexcept { if (m_localUsers.empty()) { // Nothing left to do, return latest session m_callback(m_latestSession); } else { auto userContext{ m_localUsers.begin()->second->Context() }; m_localUsers.erase(m_localUsers.begin()); HRESULT hr = LeaveSession(userContext); if (FAILED(hr)) { m_callback(hr); } } } private: HRESULT LeaveSession(std::shared_ptr userContext) noexcept { auto sessionToCommit = MakeShared(userContext->Xuid(), &m_sessionToLeaveRef, nullptr); RETURN_HR_IF_FAILED(sessionToCommit->Leave()); // Never update latest copy upon leaving. return userContext->MultiplayerService()->WriteSession( sessionToCommit, XblMultiplayerSessionWriteMode::UpdateExisting, AsyncContext>>{ m_queue, [ sharedThis{ shared_from_this() }, this ] (Result> writeSessionResult) { if (Failed(writeSessionResult)) { m_callback(writeSessionResult); } else { m_latestSession = writeSessionResult.ExtractPayload(); sharedThis->Run(); } }}); } std::shared_ptr m_sessionWriter; XblMultiplayerSessionReference m_sessionToLeaveRef; TaskQueue m_queue; Map> m_localUsers; std::shared_ptr m_latestSession{ nullptr }; MultiplayerSessionCallback m_callback; }; auto operation = MakeShared( shared_from_this(), session->SessionReference(), m_queue, std::move(callback) ); operation->Run(); return S_OK; } MultiplayerEventQueue MultiplayerSessionWriter::HandleEvents( _In_ xsapi_internal_vector> processingQueue, _In_ Result error, _In_ XblMultiplayerSessionType sessionType ) { MultiplayerEventQueue eventQueue; for (auto& request : processingQueue) { if (request->Joinability() != XblMultiplayerJoinability::None) { eventQueue.AddEvent( XblMultiplayerEventType::JoinabilityStateChanged, sessionType, nullptr, error, request->Context() ); } if (request->SessionProperties().size() > 0) { // Fire events for each of the properties for (auto iter = request->SessionProperties().begin(); iter != request->SessionProperties().end(); ++iter) { eventQueue.AddEvent( XblMultiplayerEventType::SessionPropertyWriteCompleted, sessionType, nullptr, error, request->Context() ); } } if (!request->SynchronizedHostDeviceToken().empty()) { eventQueue.AddEvent( XblMultiplayerEventType::SynchronizedHostWriteCompleted, sessionType, MakeShared(), error, request->Context() ); } if (request->SynchronizedSessionProperties().size() > 0) { // Fire events for each of the properties for (auto iter = request->SynchronizedSessionProperties().begin(); iter != request->SynchronizedSessionProperties().end(); ++iter) { eventQueue.AddEvent( XblMultiplayerEventType::SessionSynchronizedPropertyWriteCompleted, sessionType, nullptr, error, request->Context() ); } } } return eventQueue; } NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_MANAGER_CPP_END ================================================ FILE: Source/Services/Multiplayer/multiplayer_activity_handle_post_request.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "multiplayer_internal.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_CPP_BEGIN MultiplayerActivityHandlePostRequest::MultiplayerActivityHandlePostRequest( _In_ XblMultiplayerSessionReference sessionReference ) : m_sessionReference(std::move(sessionReference)) { XSAPI_ASSERT(XblMultiplayerSessionReferenceIsValid(&m_sessionReference)); } const XblMultiplayerSessionReference& MultiplayerActivityHandlePostRequest::SessionReference() const { return m_sessionReference; } void MultiplayerActivityHandlePostRequest::Serialize(_Out_ JsonValue& json, _In_ JsonDocument::AllocatorType& allocator) const { json.SetObject(); json.AddMember("type", "activity", allocator); JsonValue sessionRefJson; Serializers::SerializeSessionReference(m_sessionReference, sessionRefJson, allocator); json.AddMember("sessionRef", sessionRefJson, allocator); json.AddMember("version", MULTIPLAYER_HANDLE_VERSION, allocator); } NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_CPP_END ================================================ FILE: Source/Services/Multiplayer/multiplayer_activity_query_post_request.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "multiplayer_internal.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_CPP_BEGIN MultiplayerActivityQueryPostRequest::MultiplayerActivityQueryPostRequest() { } MultiplayerActivityQueryPostRequest::MultiplayerActivityQueryPostRequest( _In_ const xsapi_internal_string& scid, _In_ const xsapi_internal_vector& xuids ) : m_scid(scid), m_xuids(xuids) { } MultiplayerActivityQueryPostRequest::MultiplayerActivityQueryPostRequest( _In_ const xsapi_internal_string& scid, _In_ const xsapi_internal_string& socialGroup, _In_ uint64_t socialGroupXuid ) : m_scid(scid), m_socialGroupXuid(socialGroupXuid), m_socialGroup(socialGroup) { } const xsapi_internal_string& MultiplayerActivityQueryPostRequest::Scid() const { return m_scid; } const xsapi_internal_vector& MultiplayerActivityQueryPostRequest::Xuids() const { return m_xuids; } const xsapi_internal_string& MultiplayerActivityQueryPostRequest::SocialGroup() const { return m_socialGroup; } uint64_t MultiplayerActivityQueryPostRequest::SocialGroupXuid() const { return m_socialGroupXuid; } void MultiplayerActivityQueryPostRequest::Serialize(_Out_ JsonValue& json, _In_ JsonDocument::AllocatorType& allocator) { XSAPI_ASSERT(m_socialGroup.empty() || m_xuids.empty()); XSAPI_ASSERT(!m_socialGroup.empty() || !m_xuids.empty()); json.SetObject(); json.AddMember("type", "activity", allocator); json.AddMember("scid", JsonValue(m_scid.c_str(), allocator).Move(), allocator); JsonValue ownerObject(rapidjson::kObjectType); if (!m_xuids.empty()) { JsonValue xuidsJson; JsonUtils::SerializeVector(JsonUtils::JsonXuidSerializer, m_xuids, xuidsJson, allocator); ownerObject.AddMember("xuids", xuidsJson, allocator); } else { JsonValue peopleObject(rapidjson::kObjectType); peopleObject.AddMember("moniker", JsonValue(m_socialGroup.c_str(), allocator).Move(), allocator); peopleObject.AddMember("monikerXuid", JsonValue(utils::uint64_to_internal_string(m_socialGroupXuid).c_str(), allocator).Move(), allocator); ownerObject.AddMember("people", peopleObject, allocator); } json.AddMember("owners", ownerObject, allocator); } NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_CPP_END ================================================ FILE: Source/Services/Multiplayer/multiplayer_api.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "xsapi-c/multiplayer_c.h" #include "multiplayer_internal.h" #include "xbox_live_context_internal.h" using namespace xbox::services; using namespace xbox::services::multiplayer; STDAPI XblMultiplayerSearchHandleDuplicateHandle( _In_ XblMultiplayerSearchHandle handle, _Out_ XblMultiplayerSearchHandle* duplicatedHandle ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(handle == nullptr || duplicatedHandle == nullptr); handle->AddRef(); *duplicatedHandle = handle; return S_OK; } CATCH_RETURN() STDAPI_(void) XblMultiplayerSearchHandleCloseHandle( _In_ XblMultiplayerSearchHandle handle ) XBL_NOEXCEPT try { if (handle) { handle->DecRef(); } } CATCH_RETURN_WITH(;) STDAPI XblMultiplayerSearchHandleGetSessionReference( _In_ XblMultiplayerSearchHandle handle, _Out_ XblMultiplayerSessionReference* sessionRef ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(handle == nullptr || sessionRef == nullptr); *sessionRef = handle->SessionReference(); return S_OK; } CATCH_RETURN() STDAPI XblMultiplayerSearchHandleGetId( _In_ XblMultiplayerSearchHandle handle, _Out_ const char** id ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(handle == nullptr || id == nullptr); *id = handle->HandleId().data(); return S_OK; } CATCH_RETURN() STDAPI XblMultiplayerSearchHandleGetSessionOwnerXuids( _In_ XblMultiplayerSearchHandle handle, _Out_ const uint64_t** xuids, _Out_ size_t* xuidsCount ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(handle == nullptr || xuids == nullptr || xuidsCount == nullptr); *xuids = handle->SessionOwnerXuids().data(); *xuidsCount = handle->SessionOwnerXuids().size(); return S_OK; } CATCH_RETURN() STDAPI XblMultiplayerSearchHandleGetTags( _In_ XblMultiplayerSearchHandle handle, _Out_ const XblMultiplayerSessionTag** tags, _Out_ size_t* tagsCount ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(handle == nullptr || tags == nullptr || tagsCount == nullptr); *tags = handle->Tags().data(); *tagsCount = handle->Tags().size(); return S_OK; } CATCH_RETURN() STDAPI XblMultiplayerSearchHandleGetStringAttributes( _In_ XblMultiplayerSearchHandle handle, _Out_ const XblMultiplayerSessionStringAttribute** attributes, _Out_ size_t* attributesCount ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(handle == nullptr || attributes == nullptr || attributesCount == nullptr); *attributes = handle->StringAttributes().data(); *attributesCount = handle->StringAttributes().size(); return S_OK; } CATCH_RETURN() STDAPI XblMultiplayerSearchHandleGetNumberAttributes( _In_ XblMultiplayerSearchHandle handle, _Out_ const XblMultiplayerSessionNumberAttribute** attributes, _Out_ size_t* attributesCount ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(handle == nullptr || attributes == nullptr || attributesCount == nullptr); *attributes = handle->NumberAttributes().data(); *attributesCount = handle->NumberAttributes().size(); return S_OK; } CATCH_RETURN() STDAPI XblMultiplayerSearchHandleGetVisibility( _In_ XblMultiplayerSearchHandle handle, _Out_ XblMultiplayerSessionVisibility* visibility ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(handle == nullptr || visibility == nullptr); *visibility = handle->Visibility(); return S_OK; } CATCH_RETURN() STDAPI XblMultiplayerSearchHandleGetJoinRestriction( _In_ XblMultiplayerSearchHandle handle, _Out_ XblMultiplayerSessionRestriction* joinRestriction ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(handle == nullptr || joinRestriction == nullptr); *joinRestriction = handle->JoinRestriction(); return S_OK; } CATCH_RETURN() STDAPI XblMultiplayerSearchHandleGetSessionClosed( _In_ XblMultiplayerSearchHandle handle, _Out_ bool* closed ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(handle == nullptr || closed == nullptr); *closed = handle->Closed(); return S_OK; } CATCH_RETURN() STDAPI XblMultiplayerSearchHandleGetMemberCounts( _In_ XblMultiplayerSearchHandle handle, _Out_opt_ size_t* maxMembers, _Out_opt_ size_t* currentMembers ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(handle == nullptr); if (maxMembers) { *maxMembers = handle->MaxMembersCount(); } if (currentMembers) { *currentMembers = handle->MembersCount(); } return S_OK; } CATCH_RETURN() STDAPI XblMultiplayerSearchHandleGetCreationTime( _In_ XblMultiplayerSearchHandle handle, _Out_ time_t* creationTime ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(handle == nullptr || creationTime == nullptr); *creationTime = utils::time_t_from_datetime(handle->HandleCreationTime()); return S_OK; } CATCH_RETURN() STDAPI XblMultiplayerSearchHandleGetCustomSessionPropertiesJson( _In_ XblMultiplayerSearchHandle handle, _Out_ const char** customPropertiesJson ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(handle == nullptr || customPropertiesJson == nullptr); auto& customPropertiesString{ handle->CustomSessionPropertiesJson() }; if (customPropertiesString.empty()) { *customPropertiesJson = nullptr; } else { *customPropertiesJson = customPropertiesString.data(); } return S_OK; } CATCH_RETURN() STDAPI XblMultiplayerCreateSearchHandleAsync( _In_ XblContextHandle xblContext, _In_ const XblMultiplayerSessionReference* sessionRef, _In_reads_opt_(tagsCount) const XblMultiplayerSessionTag* tags, _In_ size_t tagsCount, _In_reads_opt_(numberAttributesCount) const XblMultiplayerSessionNumberAttribute* numberAttributes, _In_ size_t numberAttributesCount, _In_reads_opt_(stringAttributesCount) const XblMultiplayerSessionStringAttribute* stringAttributes, _In_ size_t stringAttributesCount, _In_ XAsyncBlock* async ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(xblContext == nullptr || sessionRef == nullptr || async == nullptr); RETURN_HR_INVALIDARGUMENT_IF(tagsCount > 0 && tags == nullptr); RETURN_HR_INVALIDARGUMENT_IF(numberAttributesCount > 0 && numberAttributes == nullptr); RETURN_HR_INVALIDARGUMENT_IF(stringAttributesCount > 0 && stringAttributes == nullptr); //ensure all attributes are properly terminated, otherwise we'll write a property //longer than is allowed into the json, which will crash eventually on deserialization if (tagsCount > 0) { for (size_t i = 0; i < tagsCount; i++) { RETURN_HR_INVALIDARGUMENT_IF(utils::EnsureLessThanMaxLength(tags[i].value, XBL_MULTIPLAYER_SEARCH_HANDLE_MAX_FIELD_LENGTH) == false); } } if (numberAttributesCount > 0) { for (size_t i = 0; i < numberAttributesCount; i++) { RETURN_HR_INVALIDARGUMENT_IF(utils::EnsureLessThanMaxLength(numberAttributes[i].name, XBL_MULTIPLAYER_SEARCH_HANDLE_MAX_FIELD_LENGTH) == false); } } if (stringAttributesCount > 0) { for (size_t i = 0; i < stringAttributesCount; i++) { RETURN_HR_INVALIDARGUMENT_IF(utils::EnsureLessThanMaxLength(stringAttributes[i].name, XBL_MULTIPLAYER_SEARCH_HANDLE_MAX_FIELD_LENGTH) == false); RETURN_HR_INVALIDARGUMENT_IF(utils::EnsureLessThanMaxLength(stringAttributes[i].value, XBL_MULTIPLAYER_SEARCH_HANDLE_MAX_FIELD_LENGTH) == false); } } MultiplayerSearchHandleRequest request{ *sessionRef }; if (tagsCount > 0) { request.SetTags(xsapi_internal_vector{ tags, tags + tagsCount }); } if (numberAttributesCount > 0) { request.SetNumberAttributes(xsapi_internal_vector{ numberAttributes, numberAttributes + numberAttributesCount }); } if (stringAttributesCount > 0) { request.SetStringAttributes(xsapi_internal_vector{ stringAttributes, stringAttributes + stringAttributesCount }); } return RunAsync(async, __FUNCTION__, [ context{ xblContext->shared_from_this() }, request, resultHandle = std::shared_ptr{} ] (XAsyncOp op, const XAsyncProviderData* data) mutable { switch (op) { case XAsyncOp::DoWork: { RETURN_HR_IF_FAILED(context->MultiplayerService()->CreateSearchHandle( request, AsyncContext>> { data->async->queue, [ &resultHandle, data ] (Result> result) { if (Succeeded(result)) { resultHandle = result.ExtractPayload(); } XAsyncComplete(data->async, result.Hresult(), sizeof(XblMultiplayerSearchHandle)); } })); return E_PENDING; } case XAsyncOp::GetResult: { auto handlePtr = static_cast(data->buffer); resultHandle->AddRef(); *handlePtr = resultHandle.get(); return S_OK; } default: return S_OK; } }); } CATCH_RETURN() STDAPI XblMultiplayerCreateSearchHandleResult( _In_ XAsyncBlock* async, _Out_opt_ XblMultiplayerSearchHandle* handle ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(async); XblMultiplayerSearchHandle handleCopy{ nullptr }; auto hr = XAsyncGetResult(async, nullptr, sizeof(XblMultiplayerSearchHandle), &handleCopy, nullptr); if (SUCCEEDED(hr)) { if (handle != nullptr) { *handle = handleCopy; } else { XblMultiplayerSearchHandleCloseHandle(handleCopy); } } return hr; } CATCH_RETURN() STDAPI XblMultiplayerDeleteSearchHandleAsync( _In_ XblContextHandle xblContext, _In_ const char* handleId, _In_ XAsyncBlock* async ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(xblContext == nullptr || handleId == nullptr || async == nullptr); return RunAsync(async, __FUNCTION__, [ context{ xblContext->shared_from_this() }, handleId = xsapi_internal_string{ handleId } ] (XAsyncOp op, const XAsyncProviderData* data) { switch (op) { case XAsyncOp::DoWork: { RETURN_HR_IF_FAILED(context->MultiplayerService()->DeleteSearchHandle(handleId, data->async)); return E_PENDING; } default: return S_OK; } }); } CATCH_RETURN() STDAPI XblMultiplayerGetSearchHandlesAsync( _In_ XblContextHandle xblContext, _In_z_ const char* scid, _In_z_ const char* sessionTemplateName, _In_opt_z_ const char* orderByAttribute, _In_ bool orderAscending, _In_opt_z_ const char* searchFilter, _In_opt_z_ const char* socialGroup, _In_ XAsyncBlock* async ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(xblContext == nullptr || scid == nullptr || sessionTemplateName == nullptr || async == nullptr); MultiplayerQuerySearchHandleRequest request{ scid, sessionTemplateName }; if (orderByAttribute) { request.SetOrderBy(orderByAttribute); request.SetOrderAscending(orderAscending); } if (searchFilter) { request.SetSearchFilter(searchFilter); } if (socialGroup) { request.SetSocialGroup(socialGroup); } return RunAsync(async, __FUNCTION__, [ context{ xblContext->shared_from_this() }, request, searchHandles = xsapi_internal_vector>{} ] (XAsyncOp op, const XAsyncProviderData* data) mutable { switch (op) { case XAsyncOp::DoWork: { RETURN_HR_IF_FAILED(context->MultiplayerService()->GetSearchHandles( request, AsyncContext>>>{ data->async->queue, [ &searchHandles, data ] (Result>> result) { if (Succeeded(result)) { searchHandles = result.ExtractPayload(); } XAsyncComplete(data->async, result.Hresult(), searchHandles.size() * sizeof(XblMultiplayerSearchHandle)); } })); return E_PENDING; } case XAsyncOp::GetResult: { auto handlesPtr = static_cast(data->buffer); for (const auto& searchHandle : searchHandles) { searchHandle->AddRef(); *handlesPtr++ = searchHandle.get(); } return S_OK; } default: return S_OK; } }); } CATCH_RETURN() STDAPI XblMultiplayerGetSearchHandlesResultCount( _In_ XAsyncBlock* async, _Out_ size_t* searchHandleCount ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(async == nullptr || searchHandleCount == nullptr); size_t bufferSize{ 0 }; auto hr = XAsyncGetResultSize(async, &bufferSize); if (SUCCEEDED(hr)) { *searchHandleCount = bufferSize / sizeof(XblMultiplayerSearchHandle); } return hr; } CATCH_RETURN() STDAPI XblMultiplayerGetSearchHandlesResult( _In_ XAsyncBlock* async, _Out_writes_(searchHandlesCount) XblMultiplayerSearchHandle* searchHandles, _In_ size_t searchHandlesCount ) XBL_NOEXCEPT try { RETURN_HR_IF(searchHandlesCount == 0, S_OK); RETURN_HR_INVALIDARGUMENT_IF(async == nullptr || searchHandles == nullptr); return XAsyncGetResult(async, nullptr, searchHandlesCount * sizeof(XblMultiplayerSearchHandle), searchHandles, nullptr); } CATCH_RETURN() STDAPI XblMultiplayerSetTransferHandleAsync( _In_ XblContextHandle xblContextHandle, _In_ XblMultiplayerSessionReference targetSessionReference, _In_ XblMultiplayerSessionReference originSessionReference, _In_ XAsyncBlock* async ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(async); return RunAsync(async, __FUNCTION__, [ xblContext{ xblContextHandle->shared_from_this() }, targetSessionReference, originSessionReference, result = XblMultiplayerSessionHandleId{} ] (XAsyncOp op, const XAsyncProviderData* data) mutable { switch (op) { case XAsyncOp::DoWork: { RETURN_HR_IF_FAILED(xblContext->MultiplayerService()->SetTransferHandle( targetSessionReference, originSessionReference, AsyncContext>{ data->async->queue, [ async{ data->async }, &result ] (Result internalResult) { auto hr = internalResult.Hresult(); if (SUCCEEDED(hr)) { utils::strcpy(result.value, sizeof(result.value), internalResult.Payload().data()); } XAsyncComplete(async, hr, sizeof(XblMultiplayerSessionHandleId)); } })); return E_PENDING; } case XAsyncOp::GetResult: { auto resultPtr = static_cast(data->buffer); *resultPtr = result; return S_OK; } default: return S_OK; } }); } CATCH_RETURN() STDAPI XblMultiplayerSetTransferHandleResult( _In_ XAsyncBlock* async, _Out_ XblMultiplayerSessionHandleId* handleId ) XBL_NOEXCEPT try { return XAsyncGetResult(async, nullptr, sizeof(XblMultiplayerSessionHandleId), handleId, nullptr); } CATCH_RETURN() ================================================ FILE: Source/Services/Multiplayer/multiplayer_internal.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "xsapi-c/multiplayer_c.h" #include "real_time_activity_subscription.h" #define MULTIPLAYER_HANDLE_VERSION 1 NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_CPP_BEGIN // Forward declarations struct SendInvitesContext; bool operator==(const XblMultiplayerSessionReference& lhs, const XblMultiplayerSessionReference& rhs); struct Serializers { static Result DeserializeMultiplayerActivityDetails(_In_ const JsonValue& json); static Result DeserializeMultiplayerSessionQueryResult(_In_ const JsonValue& json); static Result DeserializeSessionReference(_In_ const JsonValue& json); static void SerializeSessionReference(const XblMultiplayerSessionReference& sessionReference, _Out_ JsonValue& json, _In_ JsonDocument::AllocatorType& allocator); static Result DeserializeMultiplayerInvite(_In_ const JsonValue& json); static XblMultiplayerSessionRestriction MultiplayerSessionRestrictionFromString(_In_ const xsapi_internal_string &value); static xsapi_internal_string StringFromMultiplayerSessionRestriction(_In_ XblMultiplayerSessionRestriction joinRestriction); static XblMultiplayerSessionStatus MultiplayerSessionStatusFromString(_In_ const xsapi_internal_string& value); static XblMultiplayerSessionVisibility MultiplayerSessionVisibilityFromString(_In_ const xsapi_internal_string& value); static xsapi_internal_string StringFromMultiplayerSessionVisibility(_In_ XblMultiplayerSessionVisibility sessionVisibility); static XblNetworkAddressTranslationSetting MultiplayerNatSettingFromString(_In_ const xsapi_internal_string& value); static XblMultiplayerMeasurementFailure MultiplayerMeasurementFailureFromString(_In_ const xsapi_internal_string& value); static XblMultiplayerSessionChangeTypes MultiplayerSessionChangeTypesFromStringVector(_In_ const xsapi_internal_vector& changeTypeList); }; /// /// Represents a reference to member in a multiplayer session. /// class MultiplayerSessionMember { public: MultiplayerSessionMember() = default; MultiplayerSessionMember(const xsapi_internal_string& memberId); MultiplayerSessionMember(const MultiplayerSessionMember& other); static Result Deserialize( _In_ const JsonValue& json ); static XblMultiplayerSessionMember Construct( _In_ bool isCurrentUser, _In_ const xsapi_internal_string& memberId, _In_ uint64_t xuid, _In_opt_z_ const char* customConstantsJson, _In_ bool initializeRequested ); static MultiplayerSessionMember* Get(const XblMultiplayerSessionMember* member); // Sets the internal member's m_member pointer and updates the fields of member accordingly // TODO fix this. If external member objects move around things break static void SetExternalMemberPointer(XblMultiplayerSessionMember& member); ~MultiplayerSessionMember(); xsapi_internal_string MemberId() const; XblMultiplayerSessionChangeTypes SubscribedChangeTypes() const; void StateLock() const; void StateUnlock() const; // Use MultiplayerSessionMemberReadLockGuard that wraps StateLock/StateUnlock const xsapi_internal_vector& MembersInGroupUnsafe() const; const xsapi_internal_vector& GroupsUnsafe() const; const xsapi_internal_vector& EncountersUnSafe() const; const JsonValue& CustomPropertiesJsonUnsafe() const; void SetSecureDeviceBaseAddress64(_In_ const xsapi_internal_string& deviceBaseAddress); void SetRoles(_In_ const xsapi_internal_vector& roles); void SetGroups(_In_reads_(groupsCount) const char** groups, _In_ size_t groupsCount); void SetEncounters(_In_reads_(encountersCount) const char** encounters, _In_ size_t encountersCount); HRESULT SetStatus(_In_ XblMultiplayerSessionMemberStatus status); void SetMembersInGroup(_In_ const xsapi_internal_vector& membersInGroup); HRESULT SetCustomPropertyJson( _In_ const xsapi_internal_string& name, _In_ const JsonValue& valueJson ); void DeleteCustomPropertyJson(_In_ const xsapi_internal_string& name); HRESULT SetServerMeasurementsJson(_In_ const xsapi_internal_string& serverMeasurementsJson); HRESULT SetQosMeasurementsJson(_In_ const xsapi_internal_string& qosMeasurementsJson); void SetRtaConnectionId(_In_ const xsapi_internal_string& rtaConnectionId); void SetSessionChangeSubscription( _In_ XblMultiplayerSessionChangeTypes changeTypes, _In_ const xsapi_internal_string& subscriptionId ); void Serialize(_Out_ JsonValue& json, _In_ JsonDocument::AllocatorType& allocator); private: static xsapi_internal_vector GetVectorViewForChangeTypes(_In_ XblMultiplayerSessionChangeTypes changeTypes); XblMultiplayerSessionMember* m_member = nullptr; xsapi_internal_string m_customConstantsJson; xsapi_internal_string m_customPropertiesString; JsonDocument m_customPropertiesJson{ rapidjson::Type::kObjectType }; xsapi_internal_string m_secureDeviceAddressBase64; xsapi_internal_vector m_roles; JsonDocument m_resultsJson; xsapi_internal_string m_teamId; xsapi_internal_string m_initialTeam; xsapi_internal_vector m_groups; xsapi_internal_vector m_encounters; xsapi_internal_vector m_membersInGroupIds; XblMultiplayerSessionChangeTypes m_subscribedChangeTypes{ XblMultiplayerSessionChangeTypes::None }; xsapi_internal_string m_matchmakingResultServerMeasurementsJson; xsapi_internal_string m_serverMeasurementsJson; xsapi_internal_string m_qosMeasurementsJson; xsapi_internal_string m_subscriptionId; xsapi_internal_string m_rtaConnectionId; xsapi_internal_string m_memberIdToWrite; bool m_newMember{ false }; bool m_writeConstants{ false }; bool m_writeIsActive{ false }; bool m_writeRoleInfo{ false }; bool m_writeSecureDeviceAddressBase64{ false }; bool m_writeQoSMeasurementsJson{ false }; bool m_writeServerMeasurementsJson{ false }; bool m_writeMembersInGroup{ false }; bool m_writeGroups{ false }; bool m_writeEncounters{ false }; bool m_writeSubscribedChangeTypes{ false }; bool m_writeResults{ false }; bool m_writeCustomPropertiesJson{ false }; // needs to be recursive mutex since CompareMultiplayerSessions will lock both currentMember and olderSessionMember which might be same mutable std::recursive_mutex m_lockMember; }; class MultiplayerSessionMemberReadLockGuard { public: MultiplayerSessionMemberReadLockGuard(_In_opt_ MultiplayerSessionMember* member) : m_member(member) { if (m_member) { m_member->StateLock(); } } ~MultiplayerSessionMemberReadLockGuard() { if (m_member) { m_member->StateUnlock(); } } const xsapi_internal_vector& MembersInGroup() const { return m_member->MembersInGroupUnsafe(); } const xsapi_internal_vector& Groups() const { return m_member->GroupsUnsafe(); } const xsapi_internal_vector& Encounters() const { return m_member->EncountersUnSafe(); } const JsonValue& CustomPropertiesJson() const { return m_member->CustomPropertiesJsonUnsafe(); } private: MultiplayerSessionMember* m_member; }; class MultiplayerQuerySearchHandleRequest { public: MultiplayerQuerySearchHandleRequest( _In_ const xsapi_internal_string& scid, _In_ const xsapi_internal_string& sessionTemplateName ); MultiplayerQuerySearchHandleRequest( _In_ const xsapi_internal_string& scid, _In_ const xsapi_internal_string& sessionTemplateName, _In_ const xsapi_internal_string& orderBy, _In_ bool orderAscending, _In_ const xsapi_internal_string& searchFilter ); const xsapi_internal_string& Scid() const; const xsapi_internal_string& SessionTemplateName() const; const xsapi_internal_string& OrderBy() const; void SetOrderBy(_In_ const xsapi_internal_string& orderBy); bool OrderAscending(); void SetOrderAscending(_In_ bool orderAscending); const xsapi_internal_string& SearchFilter() const; void SetSearchFilter(_In_ const xsapi_internal_string& searchFilter); const xsapi_internal_string& SocialGroup() const; void SetSocialGroup(_In_ const xsapi_internal_string& socialGroup); void Serialize(_In_ uint64_t socialGroupXuid, _Out_ JsonValue& json, _In_ JsonDocument::AllocatorType& allocator) const; private: xsapi_internal_string m_serviceConfigurationId; xsapi_internal_string m_sessionTemplateName; xsapi_internal_string m_orderBy; bool m_orderAscending{ false }; xsapi_internal_string m_searchFilter; xsapi_internal_string m_socialGroup; }; class MultiplayerSearchHandleRequest { public: MultiplayerSearchHandleRequest( _In_ XblMultiplayerSessionReference sessionRef ); const XblMultiplayerSessionReference& SessionReference() const; const xsapi_internal_vector& Tags() const; void SetTags(_In_ xsapi_internal_vector&& value); const xsapi_internal_vector& NumberAttributes() const; void SetNumberAttributes(_In_ xsapi_internal_vector&& attributes); const xsapi_internal_vector& StringAttributes() const; void SetStringAttributes(_In_ xsapi_internal_vector&& attributes); void Serialize(_Out_ JsonValue& json, _In_ JsonDocument::AllocatorType& allocator) const; private: uint32_t m_version{ MULTIPLAYER_HANDLE_VERSION }; XblMultiplayerSessionReference m_sessionReference; xsapi_internal_vector m_tags; xsapi_internal_vector m_stringAttributes; xsapi_internal_vector m_numberAttributes; }; class MultiplayerInviteHandlePostRequest { public: MultiplayerInviteHandlePostRequest( _In_ const XblMultiplayerSessionReference& sessionReference, _In_ uint64_t invitedXuid, _In_ uint32_t titleId, _In_ const String& contextString, _In_ const String& customActivationContext ) noexcept; void SetInvitedXuid(uint64_t invitedXuid) noexcept; const JsonValue& Json() const noexcept; private: JsonDocument m_json; }; class MultiplayerSubscription : public real_time_activity::Subscription { public: MultiplayerSubscription() noexcept; const String& RtaConnectionId() const; typedef Function SessionChangedHandler; XblFunctionContext AddSessionChangedHandler( SessionChangedHandler handler ) noexcept; size_t RemoveSessionChangedHandler( XblFunctionContext token ) noexcept; typedef Function ConnectionIdChangedHandler; XblFunctionContext AddConnectionIdChangedHandler( ConnectionIdChangedHandler handler ) noexcept; size_t RemoveConnectionIdChangedHandler( XblFunctionContext token ) noexcept; protected: void OnSubscribe(_In_ const JsonValue& data) noexcept override; void OnEvent(_In_ const JsonValue& data) noexcept override; private: String m_connectionId; XblFunctionContext m_nextToken{ 1 }; Map m_sessionChangedHandlers; Map m_connectionIdChangedHandlers; std::mutex m_mutexMultiplayerSubscription; }; class MultiplayerActivityQueryPostRequest { public: MultiplayerActivityQueryPostRequest(); MultiplayerActivityQueryPostRequest(_In_ const xsapi_internal_string& scid, _In_ const xsapi_internal_vector& xuids); MultiplayerActivityQueryPostRequest(_In_ const xsapi_internal_string& scid, _In_ const xsapi_internal_string& socialGroup, _In_ uint64_t socialGroupXuid); const xsapi_internal_string& Scid() const; const xsapi_internal_vector& Xuids() const; const xsapi_internal_string& SocialGroup() const; uint64_t SocialGroupXuid() const; void Serialize(_Out_ JsonValue& json, _In_ JsonDocument::AllocatorType& allocator); private: xsapi_internal_string m_scid; xsapi_internal_vector m_xuids; uint64_t m_socialGroupXuid{ 0 }; xsapi_internal_string m_socialGroup; }; class MultiplayerActivityHandlePostRequest { public: MultiplayerActivityHandlePostRequest(_In_ XblMultiplayerSessionReference sessionReference); const XblMultiplayerSessionReference& SessionReference() const; void Serialize(_Out_ JsonValue&, _In_ JsonDocument::AllocatorType& allocator) const; private: XblMultiplayerSessionReference m_sessionReference; }; class MultiplayerTransferHandlePostRequest { public: MultiplayerTransferHandlePostRequest( _In_ XblMultiplayerSessionReference targetSessionReference, _In_ XblMultiplayerSessionReference originSessionReference ); const XblMultiplayerSessionReference& OriginSessionReference() const; const XblMultiplayerSessionReference& TargetSessionReference() const; void Serialize(_Out_ JsonValue& json, _In_ JsonDocument::AllocatorType& allocator) const; private: XblMultiplayerSessionReference m_originSessionReference; XblMultiplayerSessionReference m_targetSessionReference; }; struct SessionQuery : public XblMultiplayerSessionQuery { public: SessionQuery(const XblMultiplayerSessionQuery* other) noexcept; SessionQuery(const SessionQuery& other) noexcept; SessionQuery& operator=(SessionQuery other) noexcept = delete; ~SessionQuery() noexcept = default; String PathAndQuery() const noexcept; JsonDocument RequestBody() const noexcept; private: Vector m_xuidFilters; String m_keywordFilter; }; class MultiplayerService : public std::enable_shared_from_this { public: MultiplayerService( _In_ User&& user, _In_ std::shared_ptr xboxLiveContextSettings, _In_ std::shared_ptr appConfig, _In_ std::shared_ptr rtaService ) noexcept; ~MultiplayerService() noexcept; HRESULT WriteSession( _In_ std::shared_ptr multiplayerSession, _In_ XblMultiplayerSessionWriteMode writeMode, _In_ AsyncContext>> async ) noexcept; HRESULT WriteSessionByHandle( _In_ std::shared_ptr multiplayerSession, _In_ XblMultiplayerSessionWriteMode writeMode, _In_ const String& handleId, _In_ AsyncContext>> async ) noexcept; HRESULT GetCurrentSession( _In_ XblMultiplayerSessionReference sessionReference, _In_ AsyncContext>> async ) const noexcept; HRESULT GetCurrentSessionByHandle( _In_ const String& handleId, _In_ AsyncContext>> async ) const noexcept; HRESULT GetSessions( _In_ const SessionQuery& getSessionsRequest, _In_ AsyncContext>> async ) const noexcept; HRESULT SetActivity( _In_ const XblMultiplayerSessionReference& sessionReference, _In_ AsyncContext> async ) const noexcept; HRESULT SetTransferHandle( _In_ const XblMultiplayerSessionReference& targetSessionReference, _In_ const XblMultiplayerSessionReference& originSessionReference, _In_ AsyncContext> async ) const noexcept; HRESULT CreateSearchHandle( _In_ MultiplayerSearchHandleRequest searchHandleRequest, _In_ AsyncContext>> async ) const noexcept; HRESULT ClearActivity( _In_ const String& scid, _In_ AsyncContext> callback ) const noexcept; HRESULT DeleteSearchHandle( _In_ const String& handleId, _In_ AsyncContext> async ) const noexcept; HRESULT SendInvites( _In_ XblMultiplayerSessionReference sessionReference, _In_ const Vector& xboxUserIds, _In_ uint32_t titleId, _In_ const String& contextStringId, _In_ const String& customActivationContext, _In_ AsyncContext>> async ) const noexcept; HRESULT GetActivitiesForSocialGroup( _In_ const String& scid, _In_ uint64_t socialGroupOwnerXuid, _In_ const String& socialGroup, _In_ AsyncContext>> async ) const noexcept; HRESULT GetActivitiesForUsers( _In_ const String& scid, _In_ const Vector& xuids, _In_ AsyncContext>> async ) const noexcept; HRESULT GetSearchHandles( _In_ const MultiplayerQuerySearchHandleRequest& searchHandleRequest, _In_ AsyncContext>>> async ) const noexcept; HRESULT EnableMultiplayerSubscriptions() noexcept; HRESULT DisableMultiplayerSubscriptions() noexcept; bool SubscriptionsEnabled() noexcept; XblFunctionContext AddMultiplayerSessionChangedHandler( _In_ MultiplayerSubscription::SessionChangedHandler handler ) noexcept; void RemoveMultiplayerSessionChangedHandler( _In_ XblFunctionContext token ) noexcept; typedef Function SubscriptionLostHandler; XblFunctionContext AddMultiplayerSubscriptionLostHandler( _In_ SubscriptionLostHandler handler ) noexcept; void RemoveMultiplayerSubscriptionLostHandler( _In_ XblFunctionContext token ) noexcept; XblFunctionContext AddMultiplayerConnectionIdChangedHandler( _In_ MultiplayerSubscription::ConnectionIdChangedHandler handler ) noexcept; void RemoveMultiplayerConnectionIdChangedHandler( _In_ XblFunctionContext token ) noexcept; private: // Sets the RTA connection Id on a session if subscriptions are enabled HRESULT SetRtaConnectionId( _In_ std::shared_ptr session, _In_ AsyncContext> async ) noexcept; HRESULT SubscribeToRta(std::unique_lock lock) noexcept; HRESULT UnsubscribeFromRta() noexcept; HRESULT WriteSessionUsingSubpath( _In_ std::shared_ptr session, _In_ XblMultiplayerSessionWriteMode mode, _In_ const String& subpathAndQuery, _In_ AsyncContext>> async ) noexcept; static String MultiplayerSessionDirectoryCreateOrUpdateSubpath( _In_ const String& serviceConfigurationId, _In_ const String& sessionTemplateName, _In_ const String& sessionName ) noexcept; static String MultiplayerSessionDirectoryCreateOrUpdateByHandleSubpath( _In_ const String& handleId ) noexcept; User m_user; std::shared_ptr m_xboxLiveContextSettings; std::shared_ptr m_appConfig; std::shared_ptr m_rtaManager; // RTA state std::shared_ptr m_subscription; XblFunctionContext m_rtaConnectionStateChangedToken{ 0 }; XblFunctionContext m_nextClientToken{ 1 }; Map m_subscriptionLostHandlers; Map m_connectionIdChangedHandlers; List, AsyncContext>>> m_sessionsAwaitingConnectionId; // Since MPSA RTA subscription can be used both to get session changed events and to enable // automatic session member removal, we allow titles to force enable the RTA subscription bool m_forceEnableRtaSubscription{ false }; std::mutex m_mutexMultiplayerService; }; class RoleTypes { public: RoleTypes() = default; RoleTypes(const RoleTypes& other) noexcept; RoleTypes& operator=(RoleTypes other) noexcept; ~RoleTypes() noexcept; static Result Deserialize(const JsonValue& json) noexcept; JsonValue Serialize(JsonDocument::AllocatorType& allocator) const noexcept; const Vector& Values() const noexcept; HRESULT SetRoleSettings( String&& roleTypeName, String&& roleName, uint32_t* maxCount, uint32_t* targetCount ) noexcept; XblMultiplayerRole* GetRole( String&& roleType, String&& roleName ) const noexcept; private: Vector m_values{}; }; NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_CPP_END struct XblMultiplayerSession : public xbox::services::RefCounter, public std::enable_shared_from_this { public: XblMultiplayerSession( _In_ uint64_t xuid, _In_opt_ const XblMultiplayerSessionReference* sessionReference = nullptr, _In_opt_ const XblMultiplayerSessionInitArgs* initArgs = nullptr ); XblMultiplayerSession( _In_ uint64_t xboxUserId, _In_ XblMultiplayerSessionReference sessionReference, _In_ const xsapi_internal_string& eTag, _In_ const xsapi_internal_string& responseDate, _In_ const JsonValue& json ); XblMultiplayerSession(const XblMultiplayerSession& other); XblMultiplayerSession& operator=(const XblMultiplayerSession& rhs) = delete; ~XblMultiplayerSession(); bool operator==(const XblMultiplayerSession& rhs) const; void Initialize(); time_t TimeOfSession() const; const xsapi_internal_string ETag() const; const XblMultiplayerSessionInfo& SessionInfo() const; // only written during Deserialize so safe to return ref const XblMultiplayerSessionInitializationInfo& InitializationInfo() const; // only written during Deserialize so safe to return ref const XblMultiplayerSessionReference& SessionReference() const; // only written during ctor so safe to return ref const xsapi_internal_vector& HostCandidates() const; // only written during Deserialize so safe to return ref void StateLock() const; void StateUnlock() const; // Use XblMultiplayerSessionReadLockGuard that wraps StateLock/StateUnlock const XblMultiplayerSessionConstants& SessionConstantsUnsafe() const; const XblMultiplayerSessionProperties& SessionPropertiesUnsafe() const; const xbox::services::multiplayer::RoleTypes& RoleTypesUnsafe() const; const xsapi_internal_vector& MembersUnsafe() const; const xsapi_internal_vector& ServerConnectionStringCandidatesUnsafe() const; const xsapi_internal_vector& TurnCollectionUnsafe() const; const xsapi_internal_vector& KeywordsUnsafe() const; const XblMultiplayerSessionMember* CurrentUserUnsafe() const; xbox::services::multiplayer::MultiplayerSessionMember* CurrentUserInternalUnsafe() const; const XblMultiplayerSessionMember* GetMemberUnsafe(uint32_t memberId) const; const xsapi_internal_string& RawServersJsonUnsafe() const; const xsapi_internal_string& ETagUnsafe() const; uint32_t MembersAccepted() const; const xsapi_internal_string RawServersJson() const; std::shared_ptr MatchmakingServer() const; // only written during Deserialize so safe to return ref XblWriteSessionStatus WriteStatus() const; HRESULT DeserializationError() const; HRESULT SetServersJson( _In_ const xsapi_internal_string& serversJson ); HRESULT AddMemberReservation( _In_ uint64_t xuid, _In_opt_z_ const char* memberCustomConstantsJson = nullptr, _In_ bool initializeRequested = false ); HRESULT Join( _In_opt_z_ const char* memberCustomConstantsJson = nullptr, _In_ bool initializeRequested = true, _In_ bool joinWithActiveStatus = true ); void SetVisibility( _In_ XblMultiplayerSessionVisibility visibility ); void SetMaxMembersInSession( _In_ uint32_t maxMembersInSession ); HRESULT SetMutableRoleSettings( _In_ String&& roleTypeName, _In_ String&& roleName, _In_opt_ uint32_t * maxCount, _In_opt_ uint32_t * targetCount ); HRESULT SetTimeouts( _In_ uint64_t memberReservedTimeout, _In_ uint64_t memberInactiveTimeout, _In_ uint64_t memberReadyTimeout, _In_ uint64_t sessionEmptyTimeout ); HRESULT SetQosConnectivityMetrics( _In_ bool enableLatencyMetric, _In_ bool enableBandwidthDownMetric, _In_ bool enableBandwidthUpMetric, _In_ bool enableCustomMetric ); HRESULT SetMemberInitialization( _In_ const XblMultiplayerMemberInitialization& memberInitialization ); HRESULT SetPeerToPeerRequirements( _In_ const XblMultiplayerPeerToPeerRequirements& requirements ); HRESULT SetPeerToHostRequirements( _In_ const XblMultiplayerPeerToHostRequirements& requirements ); HRESULT SetMeasurementServerAddresses( _In_ const xsapi_internal_string& measurementServerAddresses ); HRESULT SetSessionCapabilities( _In_ const XblMultiplayerSessionCapabilities& capabilities ); HRESULT SetCloudComputePackageJson( _In_ const xsapi_internal_string& sessionCloudComputePackageConstantsJson ); void SetInitializationStatus( _In_ bool initializationSucceeded ); void SetHostDeviceToken( _In_ const XblDeviceToken hostDeviceToken ); void SetHostDeviceToken( _In_ const xsapi_internal_string& deviceToken ); void SetMatchmakingServerConnectionPath( _In_ const xsapi_internal_string& serverConnectionPath ); void SetClosed( _In_ bool closed ); void SetLocked( _In_ bool locked ); void SetAllocateCloudCompute( _In_ bool allocateCloudCompute ); void SetMatchmakingResubmit( _In_ bool matchResubmit ); HRESULT SetServerConnectionStringCandidates( _In_reads_(serverConnectionStringCandidatesCount) const char** serverConnectionStringCandidates, _In_ size_t serverConnectionStringCandidatesCount ); HRESULT SetSessionChangeSubscription( _In_ XblMultiplayerSessionChangeTypes changeTypes ); HRESULT Leave(); HRESULT SetCurrentUserStatus( _In_ XblMultiplayerSessionMemberStatus status ); HRESULT SetCurrentUserSecureDeviceAddressBase64( _In_ const xsapi_internal_string& value ); HRESULT SetCurrentUserRoleInfo( _In_ const xsapi_internal_vector& roles ); HRESULT SetCurrentUserMembersInGroup( _In_ const xsapi_internal_vector& membersInGroup ); HRESULT SetCurrentUserGroups( _In_reads_(groupsCount) const char** groups, _In_ size_t groupsCount ); HRESULT SetCurrentUserEncounters( _In_reads_(encountersCount) const char** encounters, _In_ size_t encountersCount ); HRESULT SetCurrentUserServerMeasurementsJson( _In_ const xsapi_internal_string& serverMeasurementsJson ); HRESULT SetCurrentUserQosMeasurementsJson( _In_ const xsapi_internal_string& qosMeasurementsJson ); HRESULT SetCurrentUserMemberCustomPropertyJson( _In_ const xsapi_internal_string& name, _In_ const JsonValue& valueJson = JsonValue() ); HRESULT DeleteCurrentUserMemberCustomPropertyJson( _In_ const xsapi_internal_string& name ); HRESULT SetMatchmakingTargetSessionConstantsJson( _In_ const xsapi_internal_string& matchmakingTargetSessionConstantsJson ); HRESULT SetSessionCustomPropertyJson( _In_ const xsapi_internal_string& name, _In_ const JsonValue& valueJson = JsonValue() ); HRESULT DeleteSessionCustomPropertyJson( _In_ const xsapi_internal_string& name ); HRESULT SetKeywords( _In_ const char** keywords, _In_ size_t keywordsCount ); void SetJoinRestriction( _In_ XblMultiplayerSessionRestriction joinRestriction ); void SetReadRestriction( _In_ XblMultiplayerSessionRestriction readRestriction ); HRESULT SetTurnCollection( _In_ const xsapi_internal_vector& turnCollection ); XblMultiplayerSessionChangeTypes CompareMultiplayerSessions( _In_ std::shared_ptr other ); static XblWriteSessionStatus ConvertHttpStatusToWriteSessionStatus( _In_ int32_t httpStatusCode ); void SetWriteSessionStatus( int32_t httpStatusCode ); static xsapi_internal_string ConvertMultiplayerHostSelectionMetricToString(_In_ XblMultiplayerMetrics multiplayMetric); static XblMultiplayerMetrics ConvertStringToMultiplayerHostSelectionMetric(_In_ const xsapi_internal_string& value); static XblMultiplayerInitializationStage ConvertStringToMultiplayerInitializationStage(_In_ const xsapi_internal_string& value); static XblMatchmakingStatus ConvertStringToMatchmakingStatus(_In_ const xsapi_internal_string& value); static xsapi_internal_string ConvertMatchmakingStatusToString(_In_ XblMatchmakingStatus matchmakingStatus); static bool IsHost( _In_ const xsapi_internal_string& memberDeviceToken, _In_ const std::shared_ptr& session ); static bool IsPlayerInSession( _In_ uint64_t xboxUserId, _In_ const std::shared_ptr& session ); static const XblMultiplayerSessionMember* GetPlayerInSession( _In_ uint64_t xboxUserId, _In_ std::shared_ptr session ); static const XblMultiplayerSessionMember* HostMember(_In_ std::shared_ptr session); static bool HasSessionPropertyChanged( _In_ const std::shared_ptr& session1, _In_ const std::shared_ptr& session2, _In_ const xsapi_internal_string& propertyName ); static bool DoSessionsMatch(_In_ std::shared_ptr lhs, _In_ std::shared_ptr rhs); void Serialize(_Out_ JsonValue& json, _In_ JsonDocument::AllocatorType& allocator); private: // Deserialization helpers HRESULT Deserialize(_In_ const JsonValue& json); HRESULT DeserializeMembers(_In_ const JsonValue& json); HRESULT DeserializeMatchmakingServer(_In_ const JsonValue& json); HRESULT DeserializeSessionProperties(_In_ const JsonValue& json); HRESULT DeserializeSessionConstants(_In_ const JsonValue& json); // Serialization helpers void SerializeSessionProperties(_Out_ JsonValue& json, _In_ JsonDocument::AllocatorType& allocator); void SerializeSessionConstants(_Out_ JsonValue& json, _In_ JsonDocument::AllocatorType& allocator); // Fields not part of MPSD document uint64_t m_xuid{ 0 }; xsapi_internal_string m_eTag; time_t m_sessionRetrievedTime; // MPSD document fields XblMultiplayerSessionInfo m_info{}; XblMultiplayerSessionInitializationInfo m_initialization{}; XblMultiplayerSessionReference m_sessionReference{}; xsapi_internal_vector m_hostCandidates; // Constants XblMultiplayerSessionConstants m_sessionConstants{}; xsapi_internal_vector m_initiatorXuids; XblMultiplayerMemberInitialization m_memberInitialization{}; xsapi_internal_string m_constantsCustomJson; xsapi_internal_string m_constantsCloudComputePackageJson; xsapi_internal_string m_constantsMeasurementServerAddressesJson; // Properties XblMultiplayerSessionProperties m_sessionProperties{}; xsapi_internal_vector m_keywords; xsapi_internal_vector m_sessionOwnerIndices; xsapi_internal_vector m_turnCollection; xsapi_internal_vector m_serverConnectionStringCandidates; xsapi_internal_string m_matchmakingServerConnectionString; xsapi_internal_string m_matchmakingTargetSessionConstantsJson; xsapi_internal_string m_sessionCustomPropertiesJson; // Roles xbox::services::multiplayer::RoleTypes m_roleTypes; // Member info xsapi_internal_vector m_members; XblMultiplayerSessionMember* m_memberCurrentUser{ nullptr }; uint32_t m_membersAccepted{ 0 }; // Servers info xsapi_internal_string m_serversJson; std::shared_ptr m_matchmakingServer; xsapi_internal_string m_matchmakingStatusDetails; xsapi_internal_string m_lastTeamResultTeam; XblWriteSessionStatus m_writeSessionStatus{}; std::atomic m_joiningSession; bool m_newSession{ false }; HRESULT m_deserializationError; xsapi_internal_string m_sessionSubscriptionGuid; bool m_writePropertiesKeywords{ false }; bool m_writePropertiesTurns{ false }; bool m_writeInitializationStatus{ false }; bool m_initializationSucceeded{ false }; bool m_writeHostDeviceToken{ false }; bool m_writeMatchmakingServerConnectionPath{ false }; bool m_writeMatchmakingResubmit{ false }; bool m_writeServerConnectionStringCandidates{ false }; bool m_leaveSession{ false }; bool m_writeClosed{ false }; bool m_writeLocked{ false }; bool m_writeAllocateCloudCompute{ false }; bool m_writeRoleTypes{ false }; bool m_writeTimeouts{ false }; bool m_writeQosConnectivityMetrics{ false }; bool m_writeMemberInitialization{ false }; bool m_writePeerToPeerRequirements{ false }; bool m_writePeerToHostRequirements{ false }; bool m_writeMeasurementServerAddresses{ false }; bool m_writeJoinRestriction{ false }; bool m_writeReadRestriction{ false }; bool m_writeServersJson{ false }; bool m_writeMatchmakingTargetSessionConstants{ false }; bool m_writeSessionCustomPropertiesJson{ false }; bool m_writeConstants{ false }; mutable std::recursive_mutex m_lockSession; uint32_t m_memberRequestIndex{ 0 }; // RefCounter override std::shared_ptr GetSharedThis() override; }; class XblMultiplayerSessionReadLockGuard { public: XblMultiplayerSessionReadLockGuard(_In_opt_ std::shared_ptr session) : m_session(session) { if (m_session) { m_session->StateLock(); } } ~XblMultiplayerSessionReadLockGuard() { if (m_session) { m_session->StateUnlock(); } } const XblMultiplayerSessionConstants& SessionConstants() const { return m_session->SessionConstantsUnsafe(); } const XblMultiplayerSessionProperties& SessionProperties() const { return m_session->SessionPropertiesUnsafe(); } const xbox::services::multiplayer::RoleTypes& RoleTypes() const { return m_session->RoleTypesUnsafe(); } const xsapi_internal_vector& Members() const { return m_session->MembersUnsafe(); } const xsapi_internal_vector& ServerConnectionStringCandidates() const { return m_session->ServerConnectionStringCandidatesUnsafe(); } const xsapi_internal_vector& TurnCollection() const { return m_session->TurnCollectionUnsafe(); } const xsapi_internal_vector& Keywords() const { return m_session->KeywordsUnsafe(); } const XblMultiplayerSessionMember* CurrentUser() const { return m_session->CurrentUserUnsafe(); } xbox::services::multiplayer::MultiplayerSessionMember* CurrentUserInternal() const { return m_session->CurrentUserInternalUnsafe(); } const XblMultiplayerSessionMember* GetMember(uint32_t memberId) const { return m_session->GetMemberUnsafe(memberId); } private: std::shared_ptr m_session; }; struct XblMultiplayerSearchHandleDetails : public xbox::services::RefCounter, public std::enable_shared_from_this { public: XblMultiplayerSearchHandleDetails() = default; static xbox::services::Result> Deserialize(_In_ const JsonValue& json); const XblMultiplayerSessionReference& SessionReference() const; const xsapi_internal_string& HandleId() const; const xsapi_internal_vector& SessionOwnerXuids() const; const xsapi_internal_vector& Tags() const; const xsapi_internal_vector& NumberAttributes() const; const xsapi_internal_vector& StringAttributes() const; const xbox::services::multiplayer::RoleTypes& RoleTypes() const; XblMultiplayerSessionVisibility Visibility() const; XblMultiplayerSessionRestriction JoinRestriction() const; bool Closed() const; size_t MaxMembersCount() const; size_t MembersCount() const; const xsapi_internal_string& CustomSessionPropertiesJson() const; const xbox::services::datetime& HandleCreationTime() const; private: XblMultiplayerSearchHandleDetails(const XblMultiplayerSearchHandleDetails&) = delete; XblMultiplayerSearchHandleDetails& operator=(XblMultiplayerSearchHandleDetails) = delete; // RefCounter std::shared_ptr GetSharedThis() override; XblMultiplayerSessionReference m_sessionReference{}; xsapi_internal_string m_handleId; xsapi_internal_vector m_tags; xsapi_internal_vector m_sessionOwners; bool m_closed{ false }; xsapi_internal_vector m_stringAttributes; xsapi_internal_vector m_numberAttributes; xbox::services::multiplayer::RoleTypes m_roleTypes; XblMultiplayerSessionVisibility m_visibility{ XblMultiplayerSessionVisibility::Unknown }; XblMultiplayerSessionRestriction m_joinRestriction{ XblMultiplayerSessionRestriction::Unknown }; size_t m_maxMembersCount{ 0 }; size_t m_membersCount{ 0 }; xbox::services::datetime m_handleCreationTime; xsapi_internal_string m_customSessionPropertiesJson; }; ================================================ FILE: Source/Services/Multiplayer/multiplayer_invite_handle_post_request.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "multiplayer_internal.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_CPP_BEGIN MultiplayerInviteHandlePostRequest::MultiplayerInviteHandlePostRequest( _In_ const XblMultiplayerSessionReference& sessionReference, _In_ uint64_t invitedXuid, _In_ uint32_t titleId, _In_ const String& contextString, _In_ const String& customActivationContext ) noexcept : m_json{ rapidjson::kObjectType } { assert(XblMultiplayerSessionReferenceIsValid(&sessionReference)); auto& a{ m_json.GetAllocator() }; m_json.AddMember("type", "invite", a); JsonValue sessionRefJson; Serializers::SerializeSessionReference(sessionReference, sessionRefJson, a); m_json.AddMember("sessionRef", sessionRefJson, a); m_json.AddMember("version", MULTIPLAYER_HANDLE_VERSION, a); m_json.AddMember("invitedXuid", JsonValue{ utils::uint64_to_internal_string(invitedXuid).c_str(), a }.Move(), a); JsonValue jsonInviteAttributes(rapidjson::kObjectType); jsonInviteAttributes.AddMember("titleId", JsonValue{ utils::uint32_to_internal_string(titleId).c_str(), a }.Move(), a); if (!contextString.empty()) { jsonInviteAttributes.AddMember("contextString", JsonValue{ contextString.c_str(), a }.Move(), a); } if (!customActivationContext.empty()) { jsonInviteAttributes.AddMember("context", JsonValue{ customActivationContext.c_str(), a }.Move(), a); } m_json.AddMember("inviteAttributes", jsonInviteAttributes, a); } void MultiplayerInviteHandlePostRequest::SetInvitedXuid(uint64_t invitedXuid) noexcept { JsonUtils::SetMember(m_json, "invitedXuid", JsonValue{ utils::uint64_to_internal_string(invitedXuid).c_str(), m_json.GetAllocator() }.Move()); } const JsonValue& MultiplayerInviteHandlePostRequest::Json() const noexcept { return m_json; } NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_CPP_END ================================================ FILE: Source/Services/Multiplayer/multiplayer_query_search_handle_request.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "multiplayer_internal.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_CPP_BEGIN MultiplayerQuerySearchHandleRequest::MultiplayerQuerySearchHandleRequest( _In_ const xsapi_internal_string& scid, _In_ const xsapi_internal_string& sessionTemplateName, _In_ const xsapi_internal_string& orderBy, _In_ bool orderAscending, _In_ const xsapi_internal_string& searchFilter ) : m_serviceConfigurationId(scid), m_sessionTemplateName(sessionTemplateName), m_orderBy(orderBy), m_orderAscending(orderAscending), m_searchFilter(searchFilter) { } MultiplayerQuerySearchHandleRequest::MultiplayerQuerySearchHandleRequest( _In_ const xsapi_internal_string& scid, _In_ const xsapi_internal_string& sessionTemplateName ) : m_serviceConfigurationId(scid), m_sessionTemplateName(sessionTemplateName) { } const xsapi_internal_string& MultiplayerQuerySearchHandleRequest::Scid() const { return m_serviceConfigurationId; } const xsapi_internal_string& MultiplayerQuerySearchHandleRequest::SessionTemplateName() const { return m_sessionTemplateName; } bool MultiplayerQuerySearchHandleRequest::OrderAscending() { return m_orderAscending; } void MultiplayerQuerySearchHandleRequest::SetOrderAscending(_In_ bool orderAscending) { m_orderAscending = orderAscending; } const xsapi_internal_string& MultiplayerQuerySearchHandleRequest::OrderBy() const { return m_orderBy; } void MultiplayerQuerySearchHandleRequest::SetOrderBy(_In_ const xsapi_internal_string& orderBy) { m_orderBy = orderBy; } const xsapi_internal_string& MultiplayerQuerySearchHandleRequest::SearchFilter() const { return m_searchFilter; } void MultiplayerQuerySearchHandleRequest::SetSearchFilter(_In_ const xsapi_internal_string& searchFilter) { m_searchFilter = searchFilter; } const xsapi_internal_string& MultiplayerQuerySearchHandleRequest::SocialGroup() const { return m_socialGroup; } void MultiplayerQuerySearchHandleRequest::SetSocialGroup(_In_ const xsapi_internal_string& socialGroup) { m_socialGroup = socialGroup; } void MultiplayerQuerySearchHandleRequest::Serialize( _In_ uint64_t socialGroupXuid, _Out_ JsonValue& json, _In_ JsonDocument::AllocatorType& allocator ) const { json.SetObject(); json.AddMember("type", JsonValue("search", allocator).Move(), allocator); json.AddMember("scid", JsonValue(m_serviceConfigurationId.c_str(), allocator).Move(), allocator); json.AddMember("templateName", JsonValue(m_sessionTemplateName.c_str(), allocator).Move(), allocator); json.AddMember("global", true, allocator); if (!m_searchFilter.empty()) { json.AddMember("filter", JsonValue(m_searchFilter.c_str(), allocator).Move(), allocator); } if (!m_orderBy.empty()) { xsapi_internal_stringstream orderByQuery; orderByQuery << m_orderBy; if (m_orderAscending) { orderByQuery << " asc"; } else { orderByQuery << " desc"; } json.AddMember("orderBy", JsonValue(orderByQuery.str().c_str(), allocator).Move(), allocator); } if (!m_socialGroup.empty()) { JsonValue ownerObject(rapidjson::kObjectType); JsonValue peopleObject(rapidjson::kObjectType); peopleObject.AddMember("moniker", JsonValue(m_socialGroup.c_str(), allocator).Move(), allocator); peopleObject.AddMember("monikerXuid", JsonValue(utils::uint64_to_internal_string(socialGroupXuid).c_str(), allocator).Move(), allocator); ownerObject.AddMember("people", peopleObject, allocator); json.AddMember("sessionMembers", ownerObject, allocator); } } NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_CPP_END ================================================ FILE: Source/Services/Multiplayer/multiplayer_search_handle_details.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "multiplayer_internal.h" using namespace xbox::services; using namespace xbox::services::multiplayer; std::shared_ptr XblMultiplayerSearchHandleDetails::GetSharedThis() { return shared_from_this(); } const XblMultiplayerSessionReference& XblMultiplayerSearchHandleDetails::SessionReference() const { return m_sessionReference; } const xsapi_internal_string& XblMultiplayerSearchHandleDetails::HandleId() const { return m_handleId; } const xsapi_internal_vector& XblMultiplayerSearchHandleDetails::SessionOwnerXuids() const { return m_sessionOwners; } const RoleTypes& XblMultiplayerSearchHandleDetails::RoleTypes() const { return m_roleTypes; } const xsapi_internal_vector& XblMultiplayerSearchHandleDetails::Tags() const { return m_tags; } const xsapi_internal_vector& XblMultiplayerSearchHandleDetails::NumberAttributes() const { return m_numberAttributes; } const xsapi_internal_vector& XblMultiplayerSearchHandleDetails::StringAttributes() const { return m_stringAttributes; } XblMultiplayerSessionVisibility XblMultiplayerSearchHandleDetails::Visibility() const { return m_visibility; } XblMultiplayerSessionRestriction XblMultiplayerSearchHandleDetails::JoinRestriction() const { return m_joinRestriction; } bool XblMultiplayerSearchHandleDetails::Closed() const { return m_closed; } size_t XblMultiplayerSearchHandleDetails::MaxMembersCount() const { return m_maxMembersCount; } size_t XblMultiplayerSearchHandleDetails::MembersCount() const { return m_membersCount; } const xbox::services::datetime& XblMultiplayerSearchHandleDetails::HandleCreationTime() const { return m_handleCreationTime; } const xsapi_internal_string& XblMultiplayerSearchHandleDetails::CustomSessionPropertiesJson() const { return m_customSessionPropertiesJson; } Result> XblMultiplayerSearchHandleDetails::Deserialize( _In_ const JsonValue& json ) { if (json.IsNull()) { return Result>{ S_OK }; } auto returnResult{ MakeShared() }; HRESULT errc = S_OK; xsapi_internal_string type; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "type", type)); if (utils::str_icmp_internal(type, "search") != 0) { return Result>{ E_UNEXPECTED }; } RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "id", returnResult->m_handleId)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonTime(json, "createTime", returnResult->m_handleCreationTime)); if (json.IsObject() && json.HasMember("sessionRef")) { returnResult->m_sessionReference = Serializers::DeserializeSessionReference(json["sessionRef"]).Payload(); } else { returnResult->m_sessionReference = XblMultiplayerSessionReference(); } if (json.IsObject() && json.HasMember("customProperties")) { const JsonValue& customPropertiesObject = json["customProperties"]; if (!customPropertiesObject.IsNull()) { returnResult->m_customSessionPropertiesJson = JsonUtils::SerializeJson(customPropertiesObject); } } if (json.IsObject() && json.HasMember("searchAttributes")) { const JsonValue& searchAttributesObject = json["searchAttributes"]; if (!searchAttributesObject.IsNull()) { if (searchAttributesObject.IsObject() && searchAttributesObject.HasMember("tags")) { const JsonValue& tagAttributesJson = searchAttributesObject["tags"]; if (!tagAttributesJson.IsNull() && tagAttributesJson.IsArray()) { RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonVector( [](const JsonValue& json) { if (!json.IsString()) { return Result{ WEB_E_INVALID_JSON_STRING }; } XblMultiplayerSessionTag tag{}; utils::strcpy(tag.value, sizeof(tag.value), json.GetString()); return Result{ tag }; }, searchAttributesObject, "tags", returnResult->m_tags, false )); } } if (searchAttributesObject.IsObject() && searchAttributesObject.HasMember("strings")) { const JsonValue& stringAttributesJson = searchAttributesObject["strings"]; if (!stringAttributesJson.IsNull() && stringAttributesJson.IsObject()) { for (const auto& stringsMetadata : stringAttributesJson.GetObject()) { XblMultiplayerSessionStringAttribute attr{}; utils::strcpy(attr.name, sizeof(attr.name), stringsMetadata.name.GetString()); utils::strcpy(attr.value, sizeof(attr.name), stringsMetadata.value.GetString()); returnResult->m_stringAttributes.push_back(std::move(attr)); } } } if (searchAttributesObject.IsObject() && searchAttributesObject.HasMember("numbers")) { const JsonValue& numbersMetadataJson = searchAttributesObject["numbers"]; if (!numbersMetadataJson.IsNull() && numbersMetadataJson.IsObject()) { for (const auto& numbersMetadata : numbersMetadataJson.GetObject()) { XblMultiplayerSessionNumberAttribute attr{}; utils::strcpy(attr.name, sizeof(attr.name), numbersMetadata.name.GetString()); attr.value = numbersMetadata.value.GetDouble(); returnResult->m_numberAttributes.push_back(std::move(attr)); } } } } } if (json.IsObject() && json.HasMember("relatedInfo")) { const JsonValue& relatedInfoObject = json["relatedInfo"]; if (!relatedInfoObject.IsNull()) { int membersCount = 0; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonInt(relatedInfoObject, "membersCount", membersCount, true)); returnResult->m_membersCount = membersCount; int maxMembersCount = 0; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonInt(relatedInfoObject, "maxMembersCount", maxMembersCount, true)); returnResult->m_maxMembersCount = maxMembersCount; xsapi_internal_string joinRestriction; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(relatedInfoObject, "joinRestriction", joinRestriction)); returnResult->m_joinRestriction = Serializers::MultiplayerSessionRestrictionFromString(joinRestriction); xsapi_internal_string visibility; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(relatedInfoObject, "visibility", visibility)); returnResult->m_visibility = Serializers::MultiplayerSessionVisibilityFromString(visibility); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonBool(relatedInfoObject, "closed", returnResult->m_closed)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonVector(JsonUtils::JsonXuidExtractor, relatedInfoObject, "sessionOwners", returnResult->m_sessionOwners, false)); } } if (json.IsObject() && json.HasMember("roleInfo")) { const JsonValue& roleInfo = json["roleInfo"]; if (!roleInfo.IsNull()) { if (roleInfo.IsObject() && roleInfo.HasMember("roleTypes")) { auto roleTypesResult{ RoleTypes::Deserialize(roleInfo["roleTypes"]) }; RETURN_HR_IF_FAILED(roleTypesResult.Hresult()); returnResult->m_roleTypes = roleTypesResult.ExtractPayload(); } } } return Result>{ returnResult, errc }; } ================================================ FILE: Source/Services/Multiplayer/multiplayer_search_handle_request.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "multiplayer_internal.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_CPP_BEGIN MultiplayerSearchHandleRequest::MultiplayerSearchHandleRequest( _In_ XblMultiplayerSessionReference sessionReference ) : m_sessionReference(std::move(sessionReference)) { } const XblMultiplayerSessionReference& MultiplayerSearchHandleRequest::SessionReference() const { return m_sessionReference; } const xsapi_internal_vector& MultiplayerSearchHandleRequest::Tags() const { return m_tags; } void MultiplayerSearchHandleRequest::SetTags( _In_ xsapi_internal_vector&& value ) { m_tags = value; } const xsapi_internal_vector& MultiplayerSearchHandleRequest::NumberAttributes() const { return m_numberAttributes; } void MultiplayerSearchHandleRequest::SetNumberAttributes( _In_ xsapi_internal_vector&& attributes ) { m_numberAttributes = attributes; } const xsapi_internal_vector& MultiplayerSearchHandleRequest::StringAttributes() const { return m_stringAttributes; } void MultiplayerSearchHandleRequest::SetStringAttributes( _In_ xsapi_internal_vector&& attributes ) { m_stringAttributes = attributes; } void MultiplayerSearchHandleRequest::Serialize(_Out_ JsonValue& json, _In_ JsonDocument::AllocatorType& allocator) const { json.SetObject(); json.AddMember("type", "search", allocator); json.AddMember("version", m_version, allocator); JsonValue sessionRefJson; Serializers::SerializeSessionReference(m_sessionReference, sessionRefJson, allocator); json.AddMember("sessionRef", sessionRefJson, allocator); JsonValue searchAttributesJson(rapidjson::kObjectType); bool setSearchAttributes = false; if (!m_tags.empty()) { setSearchAttributes = true; JsonValue tagsJson(rapidjson::kArrayType); JsonUtils::SerializeVector( [](XblMultiplayerSessionTag tag, JsonValue& j, JsonDocument::AllocatorType& a) { j.SetString(tag.value, a); }, m_tags, tagsJson, allocator); searchAttributesJson.AddMember("tags", tagsJson, allocator); } if (!m_numberAttributes.empty()) { setSearchAttributes = true; JsonValue numberAttributesJson(rapidjson::kObjectType); for (auto& attr : m_numberAttributes) { numberAttributesJson.AddMember(JsonValue(attr.name, allocator).Move(), attr.value, allocator); } searchAttributesJson.AddMember("numbers", numberAttributesJson, allocator); } if (!m_stringAttributes.empty()) { setSearchAttributes = true; JsonValue stringAttributesJson(rapidjson::kObjectType); for (auto& attr : m_stringAttributes) { stringAttributesJson.AddMember(JsonValue(attr.name, allocator).Move(), JsonValue(attr.value, allocator).Move(), allocator); } searchAttributesJson.AddMember("strings", stringAttributesJson, allocator); } if (setSearchAttributes) { json.AddMember("searchAttributes", searchAttributesJson, allocator); } } NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_CPP_END ================================================ FILE: Source/Services/Multiplayer/multiplayer_serializers.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "multiplayer_internal.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_CPP_BEGIN Result Serializers::DeserializeMultiplayerActivityDetails( _In_ const JsonValue& json ) { XblMultiplayerActivityDetails returnResult{}; XSAPI_ASSERT(!json.IsNull()); xsapi_internal_string type; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "type", type)); if (type.compare("activity") != 0) { return returnResult; } xsapi_internal_string id; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "id", id)); utils::strcpy(returnResult.HandleId, sizeof(returnResult.HandleId), id.data()); if (json.IsObject() && json.HasMember("sessionRef")) { returnResult.SessionReference = DeserializeSessionReference(json["sessionRef"]).Payload(); } else { returnResult.SessionReference = XblMultiplayerSessionReference(); } RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonXuid(json, "ownerXuid", returnResult.OwnerXuid)); xsapi_internal_string titleId; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "titleId", titleId)); returnResult.TitleId = utils::internal_string_to_uint32(titleId); xsapi_internal_string customProperties = JsonUtils::SerializeJson(json["customProperties"]); returnResult.CustomSessionPropertiesJson = Make(customProperties.c_str()); if (json.IsObject() && json.HasMember("relatedInfo")) { const JsonValue& relatedInfoObject = json["relatedInfo"]; if (!relatedInfoObject.IsNull()) { RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonInt(relatedInfoObject, "membersCount", returnResult.MembersCount)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonInt(relatedInfoObject, "maxMembersCount", returnResult.MaxMembersCount)); xsapi_internal_string joinRestriction; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(relatedInfoObject, "joinRestriction", joinRestriction)); returnResult.JoinRestriction = MultiplayerSessionRestrictionFromString(joinRestriction); xsapi_internal_string visibility; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(relatedInfoObject, "visibility", visibility)); returnResult.Visibility = MultiplayerSessionVisibilityFromString(visibility); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonBool(relatedInfoObject, "closed", returnResult.Closed)); } } return returnResult; } Result Serializers::DeserializeMultiplayerSessionQueryResult( _In_ const JsonValue& json ) { if (json.IsNull()) { return E_INVALIDARG; } XblMultiplayerSessionQueryResult returnResult{}; if (json.IsObject() && json.HasMember("sessionRef")) { const JsonValue& sessionRefJson = json["sessionRef"]; returnResult.SessionReference = DeserializeSessionReference(sessionRefJson).Payload(); } else { returnResult.SessionReference = XblMultiplayerSessionReference(); } RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonBool(json, "myTurn", returnResult.IsMyTurn)); xsapi_internal_string sessionStatus; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "status", sessionStatus)); if (!sessionStatus.empty()) { returnResult.Status = MultiplayerSessionStatusFromString(std::move(sessionStatus)); } xsapi_internal_string sessionVisibility; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "visibility", sessionVisibility)); if (!sessionVisibility.empty()) { returnResult.Visibility = MultiplayerSessionVisibilityFromString(sessionVisibility); } xsapi_internal_string joinRestriction; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "joinRestriction", joinRestriction)); if (!joinRestriction.empty()) { returnResult.JoinRestriction = MultiplayerSessionRestrictionFromString(joinRestriction); } RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonTimeT(json, "startTime", returnResult.StartTime)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonXuid(json, "xuid", returnResult.Xuid)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonInt(json, "accepted", returnResult.AcceptedMemberCount)); return returnResult; } Result Serializers::DeserializeSessionReference( _In_ const JsonValue& json ) { XblMultiplayerSessionReference result{}; if (json.IsNull()) { return result; } RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonStringToCharArray(json, "scid", result.Scid, sizeof(result.Scid))); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonStringToCharArray(json, "templateName", result.SessionTemplateName, sizeof(result.SessionTemplateName))); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonStringToCharArray(json, "name", result.SessionName, sizeof(result.SessionName))); return result; } void Serializers::SerializeSessionReference(const XblMultiplayerSessionReference& sessionReference, _Out_ JsonValue& json, _In_ JsonDocument::AllocatorType& allocator) { json.SetObject(); json.AddMember("scid", JsonValue(sessionReference.Scid, allocator).Move(), allocator); json.AddMember("templateName", JsonValue(sessionReference.SessionTemplateName, allocator).Move(), allocator); json.AddMember("name", JsonValue(sessionReference.SessionName, allocator).Move(), allocator); } Result Serializers::DeserializeMultiplayerInvite( _In_ const JsonValue& json ) { XblMultiplayerInviteHandle returnResult{}; if (json.IsNull()) { return returnResult; } RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonStringToCharArray(json, "id", returnResult.Data, sizeof(returnResult.Data))); return returnResult; } XblMultiplayerSessionRestriction Serializers::MultiplayerSessionRestrictionFromString( _In_ const xsapi_internal_string& value ) { if (utils::str_icmp_internal(value, "none") == 0) { return XblMultiplayerSessionRestriction::None; } else if (utils::str_icmp_internal(value, "local") == 0) { return XblMultiplayerSessionRestriction::Local; } else if (utils::str_icmp_internal(value, "followed") == 0) { return XblMultiplayerSessionRestriction::Followed; } return XblMultiplayerSessionRestriction::Unknown; } xsapi_internal_string Serializers::StringFromMultiplayerSessionRestriction( _In_ XblMultiplayerSessionRestriction joinRestriction ) { switch (joinRestriction) { case XblMultiplayerSessionRestriction::Unknown: return "unknown"; case XblMultiplayerSessionRestriction::None: return "none"; case XblMultiplayerSessionRestriction::Local: return "local"; case XblMultiplayerSessionRestriction::Followed: return "followed"; default: { XSAPI_ASSERT(false); return "unknown"; } } } XblMultiplayerSessionStatus Serializers::MultiplayerSessionStatusFromString( _In_ const xsapi_internal_string& value ) { if (utils::str_icmp_internal(value, "active") == 0) { return XblMultiplayerSessionStatus::Active; } else if (utils::str_icmp_internal(value, "inactive") == 0) { return XblMultiplayerSessionStatus::Inactive; } else if (utils::str_icmp_internal(value, "reserved") == 0) { return XblMultiplayerSessionStatus::Reserved; } return XblMultiplayerSessionStatus::Unknown; } XblMultiplayerSessionVisibility Serializers::MultiplayerSessionVisibilityFromString( _In_ const xsapi_internal_string& value ) { if (utils::str_icmp_internal(value, "private") == 0) { return XblMultiplayerSessionVisibility::PrivateSession; } else if (utils::str_icmp_internal(value, "visible") == 0) { return XblMultiplayerSessionVisibility::Visible; } else if (utils::str_icmp_internal(value, "full") == 0) { return XblMultiplayerSessionVisibility::Full; } else if (utils::str_icmp_internal(value, "open") == 0) { return XblMultiplayerSessionVisibility::Open; } return XblMultiplayerSessionVisibility::Unknown; } xsapi_internal_string Serializers::StringFromMultiplayerSessionVisibility(_In_ XblMultiplayerSessionVisibility sessionVisibility) { switch (sessionVisibility) { case XblMultiplayerSessionVisibility::Unknown: return "unknown"; case XblMultiplayerSessionVisibility::Any: return "any"; case XblMultiplayerSessionVisibility::PrivateSession: return "private"; case XblMultiplayerSessionVisibility::Visible: return "visible"; case XblMultiplayerSessionVisibility::Full: return "full"; case XblMultiplayerSessionVisibility::Open: return "open"; default: { XSAPI_ASSERT(false); return "unknown"; } } } XblNetworkAddressTranslationSetting Serializers::MultiplayerNatSettingFromString( _In_ const xsapi_internal_string& value ) { if (value.empty()) { return XblNetworkAddressTranslationSetting::Unknown; } else if (utils::str_icmp_internal(value, "strict") == 0) { return XblNetworkAddressTranslationSetting::Strict; } else if (utils::str_icmp_internal(value, "moderate") == 0) { return XblNetworkAddressTranslationSetting::Moderate; } else if (utils::str_icmp_internal(value, "open") == 0) { return XblNetworkAddressTranslationSetting::Open; } return XblNetworkAddressTranslationSetting::Unknown; } XblMultiplayerMeasurementFailure Serializers::MultiplayerMeasurementFailureFromString( _In_ const xsapi_internal_string& value ) { if (value.empty()) { return XblMultiplayerMeasurementFailure::None; } else if (utils::str_icmp_internal(value, "bandwidthUp") == 0) { return XblMultiplayerMeasurementFailure::BandwidthUp; } else if (utils::str_icmp_internal(value, "bandwidthDown") == 0) { return XblMultiplayerMeasurementFailure::BandwidthDown; } else if (utils::str_icmp_internal(value, "latency") == 0) { return XblMultiplayerMeasurementFailure::Latency; } else if (utils::str_icmp_internal(value, "timeout") == 0) { return XblMultiplayerMeasurementFailure::Timeout; } else if (utils::str_icmp_internal(value, "group") == 0) { return XblMultiplayerMeasurementFailure::Group; } else if (utils::str_icmp_internal(value, "network") == 0) { return XblMultiplayerMeasurementFailure::Network; } else if (utils::str_icmp_internal(value, "episode") == 0) { return XblMultiplayerMeasurementFailure::Episode; } return XblMultiplayerMeasurementFailure::Unknown; } XblMultiplayerSessionChangeTypes Serializers::MultiplayerSessionChangeTypesFromStringVector( _In_ const xsapi_internal_vector& changeTypeList ) { XblMultiplayerSessionChangeTypes resultingChangeTypes = XblMultiplayerSessionChangeTypes::None; for (auto& current : changeTypeList) { if (utils::str_icmp_internal(current, "everything") == 0) { resultingChangeTypes |= XblMultiplayerSessionChangeTypes::Everything; } else if (utils::str_icmp_internal(current, "host") == 0) { resultingChangeTypes |= XblMultiplayerSessionChangeTypes::HostDeviceTokenChange; } else if (utils::str_icmp_internal(current, "initialization") == 0) { resultingChangeTypes |= XblMultiplayerSessionChangeTypes::InitializationStateChange; } else if (utils::str_icmp_internal(current, "matchmakingStatus") == 0) { resultingChangeTypes |= XblMultiplayerSessionChangeTypes::MatchmakingStatusChange; } else if (utils::str_icmp_internal(current, "membersList") == 0) { resultingChangeTypes |= XblMultiplayerSessionChangeTypes::MemberListChange; } else if (utils::str_icmp_internal(current, "membersStatus") == 0) { resultingChangeTypes |= XblMultiplayerSessionChangeTypes::MemberStatusChange; } else if (utils::str_icmp_internal(current, "XblMultiplayerJoinability") == 0) { resultingChangeTypes |= XblMultiplayerSessionChangeTypes::SessionJoinabilityChange; } else if (utils::str_icmp_internal(current, "customProperty") == 0) { resultingChangeTypes |= XblMultiplayerSessionChangeTypes::CustomPropertyChange; } else if (utils::str_icmp_internal(current, "membersCustomProperty") == 0) { resultingChangeTypes |= XblMultiplayerSessionChangeTypes::MemberCustomPropertyChange; } } return static_cast(resultingChangeTypes); } NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_CPP_END ================================================ FILE: Source/Services/Multiplayer/multiplayer_service.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "multiplayer_internal.h" #include "xbox_live_context_internal.h" #include "real_time_activity_manager.h" using namespace xbox::services::multiplayer; using namespace xbox::services::system; using namespace xbox::services::legacy; using namespace xbox::services; NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_CPP_BEGIN #define GET_ACTIIVITIES_SUBPATH "/handles/query?include=relatedInfo,customProperties" #define GET_SEARCH_HANDLES_SUBPATH "/handles/query?include=relatedInfo,roleInfo,customProperties" #define MULTIPLAYER_SERVICE_CONTRACT_VERSION 107 MultiplayerService::MultiplayerService( _In_ User&& user, _In_ std::shared_ptr xboxLiveContextSettings, _In_ std::shared_ptr appConfig, _In_ std::shared_ptr realTimeActivity ) noexcept : m_user{ std::move(user) }, m_xboxLiveContextSettings{ std::move(xboxLiveContextSettings) }, m_appConfig{ std::move(appConfig) }, m_rtaManager{ std::move(realTimeActivity) } { } MultiplayerService::~MultiplayerService() noexcept { std::lock_guard lock{ m_mutexMultiplayerService }; UnsubscribeFromRta(); } HRESULT MultiplayerService::WriteSession( _In_ std::shared_ptr session, _In_ XblMultiplayerSessionWriteMode mode, _In_ AsyncContext>> async ) noexcept { auto& sessionReference = session->SessionReference(); auto pathAndQuery = MultiplayerSessionDirectoryCreateOrUpdateSubpath( sessionReference.Scid, sessionReference.SessionTemplateName, sessionReference.SessionName ); return WriteSessionUsingSubpath( session, mode, pathAndQuery, std::move(async) ); } HRESULT MultiplayerService::WriteSessionByHandle( _In_ std::shared_ptr session, _In_ XblMultiplayerSessionWriteMode mode, _In_ const String& handleId, _In_ AsyncContext>> async ) noexcept { RETURN_HR_INVALIDARGUMENT_IF(handleId.empty()); auto pathAndQuery = MultiplayerSessionDirectoryCreateOrUpdateByHandleSubpath(handleId); return WriteSessionUsingSubpath( session, mode, pathAndQuery, std::move(async) ); } HRESULT MultiplayerService::GetCurrentSession( _In_ XblMultiplayerSessionReference sessionReference, _In_ AsyncContext>> async ) const noexcept { RETURN_HR_INVALIDARGUMENT_IF_EMPTY_STRING(sessionReference.Scid); String pathAndQuery = MultiplayerSessionDirectoryCreateOrUpdateSubpath( sessionReference.Scid, sessionReference.SessionTemplateName, sessionReference.SessionName ); Result userResult = m_user.Copy(); RETURN_HR_IF_FAILED(userResult.Hresult()); auto httpCall = MakeShared(userResult.ExtractPayload()); HRESULT hr = httpCall->Init( m_xboxLiveContextSettings, "GET", XblHttpCall::BuildUrl("sessiondirectory", pathAndQuery), xbox_live_api::get_current_session ); RETURN_HR_IF_FAILED(hr); RETURN_HR_IF_FAILED(httpCall->SetXblServiceContractVersion(MULTIPLAYER_SERVICE_CONTRACT_VERSION)); return httpCall->Perform(AsyncContext{ async.Queue().DeriveWorkerQueue(), [ sessionReference, xuid{ m_user.Xuid() }, async ] (HttpResult httpResult) { HRESULT hr = httpResult.Hresult(); if (FAILED(hr)) { return async.Complete(Result>(hr, "Http call failed")); } hr = httpResult.Payload()->Result(); if (FAILED(hr)) { const char* errorMessagePtr{}; std::unique_ptr errorMessage{ errorMessagePtr }; httpResult.Payload()->GetErrorMessage(&errorMessagePtr); return async.Complete(Result>(hr, errorMessagePtr)); } else if (httpResult.Payload()->HttpStatus() == 204) { // Return a not found error when trying to get an non-existing session return async.Complete(__HRESULT_FROM_WIN32(ERROR_RESOURCE_DATA_NOT_FOUND)); } auto session = MakeShared( xuid, sessionReference, httpResult.Payload()->GetResponseHeader(ETAG_HEADER), httpResult.Payload()->GetResponseHeader(DATE_HEADER), httpResult.Payload()->GetResponseBodyJson() ); async.Complete(Result>(session, session->DeserializationError(), "Deserialize error")); } }); } HRESULT MultiplayerService::GetCurrentSessionByHandle( _In_ const String& handleId, _In_ AsyncContext>> async ) const noexcept { RETURN_HR_INVALIDARGUMENT_IF(handleId.empty()); String pathAndQuery = MultiplayerSessionDirectoryCreateOrUpdateByHandleSubpath(handleId); Result userResult = m_user.Copy(); RETURN_HR_IF_FAILED(userResult.Hresult()); auto httpCall = MakeShared(userResult.ExtractPayload()); HRESULT hr = httpCall->Init( m_xboxLiveContextSettings, "GET", XblHttpCall::BuildUrl("sessiondirectory", pathAndQuery), xbox_live_api::get_current_session_by_handle ); RETURN_HR_IF_FAILED(hr); RETURN_HR_IF_FAILED(httpCall->SetXblServiceContractVersion(MULTIPLAYER_SERVICE_CONTRACT_VERSION)); return httpCall->Perform(AsyncContext{ async.Queue().DeriveWorkerQueue(), [ xuid{ m_user.Xuid() }, async ] (HttpResult httpResult) { HRESULT hr = httpResult.Hresult(); if (FAILED(hr)) { return async.Complete(Result>(hr)); } hr = httpResult.Payload()->Result(); if (FAILED(hr)) { return async.Complete(Result>(hr)); } else if (httpResult.Payload()->HttpStatus() == 204) { // Return a not found error when trying to get an non-existing session return async.Complete(__HRESULT_FROM_WIN32(ERROR_RESOURCE_DATA_NOT_FOUND)); } auto contentLocation = httpResult.Payload()->GetResponseHeader("Content-Location"); XblMultiplayerSessionReference sessionReference; hr = XblMultiplayerSessionReferenceParseFromUriPath(contentLocation.c_str(), &sessionReference); if (FAILED(hr)) { // Failed to parse session reference from URI return async.Complete(Result>(hr)); } auto session = MakeShared( xuid, sessionReference, httpResult.Payload()->GetResponseHeader(ETAG_HEADER), httpResult.Payload()->GetResponseHeader(DATE_HEADER), httpResult.Payload()->GetResponseBodyJson() ); async.Complete(Result>(session, session->DeserializationError())); } }); } SessionQuery::SessionQuery(const XblMultiplayerSessionQuery* other) noexcept : XblMultiplayerSessionQuery{ *other } { // Deep copy xuid filters & keyword filter so that we own them for (size_t i = 0; i < XuidFiltersCount; ++i) { m_xuidFilters.push_back(other->XuidFilters[i]); } XuidFilters = m_xuidFilters.data(); if (KeywordFilter) { m_keywordFilter = KeywordFilter; KeywordFilter = m_keywordFilter.data(); } } SessionQuery::SessionQuery(const SessionQuery& other) noexcept : SessionQuery{ &other } { } String SessionQuery::PathAndQuery() const noexcept { Stringstream source; source << "/serviceconfigs/"; source << Scid; if (SessionTemplateNameFilter[0] != 0) { source << "/sessiontemplates/"; source << SessionTemplateNameFilter; } if (XuidFiltersCount > 1) { source << "/batch"; } else { source << "/sessions"; } Vector params; if (XuidFiltersCount == 1) { Stringstream param; param << "xuid="; param << xbox::services::uri::encode_uri(utils::uint64_to_internal_string(XuidFilters[0])); params.push_back(param.str()); } if (!m_keywordFilter.empty()) { Stringstream param; param << "keyword="; param << xbox::services::uri::encode_uri(KeywordFilter); params.push_back(param.str()); } if (VisibilityFilter != XblMultiplayerSessionVisibility::Any) { Stringstream param; param << "visibility="; param << xbox::services::uri::encode_uri(Serializers::StringFromMultiplayerSessionVisibility(VisibilityFilter)); params.push_back(param.str()); } if (ContractVersionFilter != 0) { Stringstream param; param << "version="; param << ContractVersionFilter; params.push_back(param.str()); } if (IncludePrivateSessions) { params.push_back("private=true"); } if (IncludeReservations) { params.push_back("reservations=true"); } if (IncludeInactiveSessions) { params.push_back("inactive=true"); } if (MaxItems != 0) { Stringstream param; param << "take="; param << MaxItems; params.push_back(param.str()); } source << utils::get_query_from_params(params); return source.str(); } JsonDocument SessionQuery::RequestBody() const noexcept { JsonDocument requestBody{ rapidjson::kNullType }; if (m_xuidFilters.size() > 1) { requestBody.SetObject(); JsonValue xuidsArrayJson{ rapidjson::kArrayType }; JsonUtils::SerializeVector(JsonUtils::JsonXuidSerializer, m_xuidFilters, xuidsArrayJson, requestBody.GetAllocator()); requestBody.AddMember("xuids", xuidsArrayJson, requestBody.GetAllocator()); } return requestBody; } HRESULT MultiplayerService::GetSessions( _In_ const SessionQuery& getSessionsRequest, _In_ AsyncContext>> async ) const noexcept { RETURN_HR_INVALIDARGUMENT_IF_EMPTY_STRING(getSessionsRequest.Scid); RETURN_HR_INVALIDARGUMENT_IF((getSessionsRequest.XuidFilters == nullptr || getSessionsRequest.XuidFiltersCount == 0) && (getSessionsRequest.KeywordFilter == nullptr || getSessionsRequest.KeywordFilter[0] == 0)); RETURN_HR_INVALIDARGUMENT_IF(getSessionsRequest.IncludeReservations && (getSessionsRequest.XuidFilters == nullptr || getSessionsRequest.XuidFiltersCount == 0)); RETURN_HR_INVALIDARGUMENT_IF(getSessionsRequest.IncludeInactiveSessions && (getSessionsRequest.XuidFilters == nullptr || getSessionsRequest.XuidFiltersCount == 0)); Result userResult = m_user.Copy(); RETURN_HR_IF_FAILED(userResult.Hresult()); auto httpCall = MakeShared(userResult.ExtractPayload()); HRESULT hr = httpCall->Init( m_xboxLiveContextSettings, getSessionsRequest.XuidFiltersCount > 1 ? "POST" : "GET", XblHttpCall::BuildUrl("sessiondirectory", getSessionsRequest.PathAndQuery()), xbox_live_api::get_sessions ); RETURN_HR_IF_FAILED(hr); RETURN_HR_IF_FAILED(httpCall->SetXblServiceContractVersion(MULTIPLAYER_SERVICE_CONTRACT_VERSION)); auto requestBody{ getSessionsRequest.RequestBody() }; if (!requestBody.IsNull()) { RETURN_HR_IF_FAILED(httpCall->SetRequestBody(requestBody)); } return httpCall->Perform(AsyncContext{ async.Queue().DeriveWorkerQueue(), [ async ] (HttpResult httpResult) { if (FAILED(httpResult.Hresult())) { return async.Complete({ httpResult.Hresult(), "Http call failed" }); } auto hr = httpResult.Payload()->Result(); if (FAILED(hr)) { const char* errorMessagePtr{}; std::unique_ptr errorMessage{ errorMessagePtr }; httpResult.Payload()->GetErrorMessage(&errorMessagePtr); return async.Complete({ hr, errorMessagePtr }); } Vector sessionStates; hr = JsonUtils::ExtractJsonVector( Serializers::DeserializeMultiplayerSessionQueryResult, httpResult.Payload()->GetResponseBodyJson(), "results", sessionStates, true ); if (FAILED(hr)) { return async.Complete(hr); } return async.Complete(sessionStates); } }); } HRESULT MultiplayerService::SetActivity( _In_ const XblMultiplayerSessionReference& sessionReference, _In_ AsyncContext> async ) const noexcept { RETURN_HR_INVALIDARGUMENT_IF_EMPTY_STRING(sessionReference.Scid); MultiplayerActivityHandlePostRequest request{ sessionReference }; Result userResult = m_user.Copy(); RETURN_HR_IF_FAILED(userResult.Hresult()); auto httpCall = MakeShared(userResult.ExtractPayload()); HRESULT hr = httpCall->Init( m_xboxLiveContextSettings, "POST", XblHttpCall::BuildUrl("sessiondirectory", "/handles"), xbox_live_api::set_activity ); RETURN_HR_IF_FAILED(hr); RETURN_HR_IF_FAILED(httpCall->SetXblServiceContractVersion(MULTIPLAYER_SERVICE_CONTRACT_VERSION)); JsonDocument requestJson; request.Serialize(requestJson, requestJson.GetAllocator()); RETURN_HR_IF_FAILED(httpCall->SetRequestBody(JsonUtils::SerializeJson(requestJson))); return httpCall->Perform(AsyncContext{ async.Queue().DeriveWorkerQueue(), [async](HttpResult httpResult) { if (FAILED(httpResult.Hresult())) { async.Complete({ httpResult.Hresult(), "Http call failed" }); } else { const char* errorMessagePtr{}; std::unique_ptr errorMessage{ errorMessagePtr }; httpResult.Payload()->GetErrorMessage(&errorMessagePtr); async.Complete({ httpResult.Payload()->Result(), errorMessagePtr }); } } }); } HRESULT MultiplayerService::SetTransferHandle( _In_ const XblMultiplayerSessionReference& targetSessionReference, _In_ const XblMultiplayerSessionReference& originSessionReference, _In_ AsyncContext> async ) const noexcept { RETURN_HR_INVALIDARGUMENT_IF_EMPTY_STRING(targetSessionReference.Scid); RETURN_HR_INVALIDARGUMENT_IF_EMPTY_STRING(originSessionReference.Scid); MultiplayerTransferHandlePostRequest request{ targetSessionReference, originSessionReference }; Result userResult = m_user.Copy(); RETURN_HR_IF_FAILED(userResult.Hresult()); auto httpCall = MakeShared(userResult.ExtractPayload()); RETURN_HR_IF_FAILED(httpCall->Init( m_xboxLiveContextSettings, "POST", XblHttpCall::BuildUrl("sessiondirectory", "/handles"), xbox_live_api::set_transfer_handle )); RETURN_HR_IF_FAILED(httpCall->SetXblServiceContractVersion(MULTIPLAYER_SERVICE_CONTRACT_VERSION)); JsonDocument requestJson; request.Serialize(requestJson, requestJson.GetAllocator()); RETURN_HR_IF_FAILED(httpCall->SetRequestBody(JsonUtils::SerializeJson(requestJson))); return httpCall->Perform(AsyncContext{ async.Queue().DeriveWorkerQueue(), [async](HttpResult httpResult) { if (FAILED(httpResult.Hresult())) { return async.Complete({ httpResult.Hresult(), "Http call failed" }); } auto hr = httpResult.Payload()->Result(); if (FAILED(hr)) { const char* errorMessagePtr{}; std::unique_ptr errorMessage{ errorMessagePtr }; httpResult.Payload()->GetErrorMessage(&errorMessagePtr); return async.Complete({ hr, errorMessagePtr }); } auto result = Serializers::DeserializeMultiplayerInvite(httpResult.Payload()->GetResponseBodyJson()); auto multiplayerInvite = result.Payload(); if (Failed(result)) { return async.Complete(result.Hresult()); } else { return async.Complete(String{ multiplayerInvite.Data }); } }}); } HRESULT MultiplayerService::CreateSearchHandle( _In_ MultiplayerSearchHandleRequest searchHandleRequest, _In_ AsyncContext>> async ) const noexcept { Result userResult = m_user.Copy(); RETURN_HR_IF_FAILED(userResult.Hresult()); auto httpCall = MakeShared(userResult.ExtractPayload()); RETURN_HR_IF_FAILED(httpCall->Init( m_xboxLiveContextSettings, "POST", XblHttpCall::BuildUrl("sessiondirectory", "/handles"), xbox_live_api::set_search_handle )); RETURN_HR_IF_FAILED(httpCall->SetXblServiceContractVersion(MULTIPLAYER_SERVICE_CONTRACT_VERSION)); JsonDocument searchHandleRequestJson; searchHandleRequest.Serialize(searchHandleRequestJson, searchHandleRequestJson.GetAllocator()); RETURN_HR_IF_FAILED(httpCall->SetRequestBody(JsonUtils::SerializeJson(searchHandleRequestJson))); return httpCall->Perform(AsyncContext{ async.Queue().DeriveWorkerQueue(), [async](HttpResult httpResult) { if (FAILED(httpResult.Hresult())) { return async.Complete({ httpResult.Hresult(), "Http call failed" }); } auto hr = httpResult.Payload()->Result(); if (FAILED(hr)) { const char* errorMessagePtr{}; std::unique_ptr errorMessage{ errorMessagePtr }; httpResult.Payload()->GetErrorMessage(&errorMessagePtr); return async.Complete({ hr, errorMessagePtr }); } auto result = XblMultiplayerSearchHandleDetails::Deserialize(httpResult.Payload()->GetResponseBodyJson()); async.Complete(result); }}); } HRESULT MultiplayerService::ClearActivity( _In_ const String& scid, _In_ AsyncContext> async ) const noexcept { RETURN_HR_INVALIDARGUMENT_IF(scid.empty()); return GetActivitiesForUsers( scid, Vector{ m_user.Xuid() }, AsyncContext>>{ async.Queue().DeriveWorkerQueue(), [ sharedThis{ shared_from_this() }, this, async ] (Result> result) { if (FAILED(result.Hresult())) { return async.Complete(result.Hresult()); } auto& activityDetails = result.Payload(); size_t responseSize = activityDetails.size(); Stringstream subPath; if (responseSize == 0) { // There should be at least one activity per user // Don't want to change behavior, but I think we should treat it as a success if there is no // activity to clear. return async.Complete(utils::convert_xbox_live_error_code_to_hresult(xbl_error_code::invalid_argument)); } else if (responseSize > 1) { // There should only be one activity per user // Don't want to change behavior, but it seems like this is an unexpected service response, so // we should return E_UNEXPECTED rather than E_INVALIDARG return async.Complete(utils::convert_xbox_live_error_code_to_hresult(xbl_error_code::invalid_argument)); } else { subPath << "/handles/" << activityDetails.at(0).HandleId; } Result userResult = m_user.Copy(); if (FAILED(userResult.Hresult())) { return async.Complete(userResult.Hresult()); } auto httpCall = MakeShared(userResult.ExtractPayload()); HRESULT hr = httpCall->Init( m_xboxLiveContextSettings, "DELETE", XblHttpCall::BuildUrl("sessiondirectory", subPath.str()), xbox_live_api::clear_activity ); if (FAILED(hr)) { return async.Complete(hr); } hr = httpCall->SetXblServiceContractVersion(MULTIPLAYER_SERVICE_CONTRACT_VERSION); if (FAILED(hr)) { return async.Complete(hr); } hr = httpCall->Perform(AsyncContext{ async.Queue().DeriveWorkerQueue(), [async](HttpResult httpResult) { if (Failed(httpResult)) { async.Complete(httpResult.Hresult()); } else { async.Complete(httpResult.Payload()->Result()); } }}); if (FAILED(hr)) { async.Complete(hr); } } }); } HRESULT MultiplayerService::DeleteSearchHandle( _In_ const String& handleId, _In_ AsyncContext> async ) const noexcept { RETURN_HR_INVALIDARGUMENT_IF(handleId.empty()); String handleStr = "/handles/" + handleId; Result userResult = m_user.Copy(); RETURN_HR_IF_FAILED(userResult.Hresult()); auto httpCall = MakeShared(userResult.ExtractPayload()); RETURN_HR_IF_FAILED(httpCall->Init( m_xboxLiveContextSettings, "DELETE", XblHttpCall::BuildUrl("sessiondirectory", handleStr), xbox_live_api::delete_search_handle )); RETURN_HR_IF_FAILED(httpCall->SetXblServiceContractVersion(MULTIPLAYER_SERVICE_CONTRACT_VERSION)); return httpCall->Perform(AsyncContext{ async.Queue().DeriveWorkerQueue(), [async](HttpResult httpResult) { if (Failed(httpResult)) { async.Complete(httpResult.Hresult()); } else { async.Complete(httpResult.Payload()->Result()); } }}); } HRESULT MultiplayerService::SendInvites( _In_ XblMultiplayerSessionReference sessionReference, _In_ const Vector& xuids, _In_ uint32_t titleId, _In_ const String& contextStringId, _In_ const String& customActivationContext, _In_ AsyncContext>> async ) const noexcept { RETURN_HR_INVALIDARGUMENT_IF(!XblMultiplayerSessionReferenceIsValid(&sessionReference) || xuids.empty()); // MPSD only allows creating a single invite handle at a time. SendInvitesOperation attempts // to create an invite handle for each invited user. If creation of a single handle fails, the operation // will continue, attempting to create the remaining handles. The result will contain all handles which // were successfully created. struct SendInvitesOperation : public std::enable_shared_from_this { SendInvitesOperation( std::shared_ptr multiplayerService, const XblMultiplayerSessionReference& sessionReference, const Vector& xuidsToInvite, uint32_t titleId, const String& contextString, const String& customActivationContext, AsyncContext>> async ) noexcept : m_multiplayerService{ std::move(multiplayerService) }, m_requestBody{ sessionReference, 0, titleId, contextString, customActivationContext }, m_xuidsToInvite{ xuidsToInvite.rbegin(), xuidsToInvite.rend() }, m_async{ std::move(async) } { } void Run() noexcept { if (m_xuidsToInvite.empty()) { m_async.Complete(m_inviteHandles); } else { auto nextXuid{ m_xuidsToInvite.back() }; m_xuidsToInvite.pop_back(); HRESULT hr = SendInvite(nextXuid); if (FAILED(hr)) { LOGS_ERROR << __FUNCTION__ << " Invite failed for user[" << nextXuid << "], hr=" << hr; LOGS_ERROR << __FUNCTION__ << " Continuing with remaining invited users."; this->Run(); } } } private: HRESULT SendInvite(uint64_t xuid) noexcept { Result userResult = m_multiplayerService->m_user.Copy(); RETURN_HR_IF_FAILED(userResult.Hresult()); auto httpCall = MakeShared(userResult.ExtractPayload()); RETURN_HR_IF_FAILED(httpCall->Init( m_multiplayerService->m_xboxLiveContextSettings, "POST", XblHttpCall::BuildUrl("sessiondirectory", "/handles"), xbox_live_api::send_invites )); RETURN_HR_IF_FAILED(httpCall->SetRetryAllowed(false)); RETURN_HR_IF_FAILED(httpCall->SetXblServiceContractVersion(MULTIPLAYER_SERVICE_CONTRACT_VERSION)); m_requestBody.SetInvitedXuid(xuid); RETURN_HR_IF_FAILED(httpCall->SetRequestBody(m_requestBody.Json())); return httpCall->Perform(AsyncContext{ m_async.Queue().DeriveWorkerQueue(), [ sharedThis{ shared_from_this() }, this, xuid ] (HttpResult httpResult) { auto inviteHandleResult = HandleServiceResult(httpResult); if (Succeeded(inviteHandleResult)) { m_inviteHandles.push_back(inviteHandleResult.ExtractPayload()); } else { LOGS_ERROR << __FUNCTION__ << " Invite failed for user[" << xuid << "], hr=" << inviteHandleResult.Hresult(); LOGS_ERROR << __FUNCTION__ << " Continuing with remaining invited users."; } Run(); } }); } Result HandleServiceResult(HttpResult httpResult) noexcept { RETURN_HR_IF_FAILED(httpResult.Hresult()); RETURN_HR_IF_FAILED(httpResult.Payload()->Result()); auto deserializationResult = Serializers::DeserializeMultiplayerInvite(httpResult.Payload()->GetResponseBodyJson()); return Result{ deserializationResult.Payload().Data, deserializationResult.Hresult() }; } std::shared_ptr m_multiplayerService; MultiplayerInviteHandlePostRequest m_requestBody; Vector m_xuidsToInvite; Vector m_inviteHandles; AsyncContext>> m_async; }; auto operation = MakeShared( shared_from_this(), sessionReference, xuids, titleId, contextStringId, customActivationContext, std::move(async) ); operation->Run(); return S_OK; } HRESULT MultiplayerService::GetActivitiesForSocialGroup( _In_ const String& scid, _In_ uint64_t socialGroupOwnerXuid, _In_ const String& socialGroup, _In_ AsyncContext>> async ) const noexcept { RETURN_HR_INVALIDARGUMENT_IF(scid.empty() || socialGroupOwnerXuid == 0 || socialGroup.empty()); MultiplayerActivityQueryPostRequest request{ scid, socialGroup, socialGroupOwnerXuid }; Result userResult = m_user.Copy(); RETURN_HR_IF_FAILED(userResult.Hresult()); auto httpCall = MakeShared(userResult.ExtractPayload()); RETURN_HR_IF_FAILED(httpCall->Init( m_xboxLiveContextSettings, "POST", XblHttpCall::BuildUrl("sessiondirectory", GET_ACTIIVITIES_SUBPATH), xbox_live_api::get_activities_for_social_group )); RETURN_HR_IF_FAILED(httpCall->SetXblServiceContractVersion(MULTIPLAYER_SERVICE_CONTRACT_VERSION)); JsonDocument requestJson; request.Serialize(requestJson, requestJson.GetAllocator()); RETURN_HR_IF_FAILED(httpCall->SetRequestBody(JsonUtils::SerializeJson(requestJson))); return httpCall->Perform(AsyncContext{ async.Queue().DeriveWorkerQueue(), [async](HttpResult httpResult) { auto hr = httpResult.Hresult(); if (FAILED(hr)) { return async.Complete({ hr, "Http call failed" }); } hr = httpResult.Payload()->Result(); if (FAILED(hr)) { const char* errorMessagePtr{}; std::unique_ptr errorMessage{ errorMessagePtr }; httpResult.Payload()->GetErrorMessage(&errorMessagePtr); return async.Complete({ hr, errorMessagePtr }); } Vector activityDetails; hr = JsonUtils::ExtractJsonVector( Serializers::DeserializeMultiplayerActivityDetails, httpResult.Payload()->GetResponseBodyJson(), "results", activityDetails, true ); if (FAILED(hr)) { return async.Complete(hr); } async.Complete(activityDetails); }}); } HRESULT MultiplayerService::GetActivitiesForUsers( _In_ const String& scid, _In_ const Vector& xuids, _In_ AsyncContext>> async ) const noexcept { RETURN_HR_INVALIDARGUMENT_IF(scid.empty() || xuids.empty()); MultiplayerActivityQueryPostRequest request{ scid, xuids }; Result userResult = m_user.Copy(); RETURN_HR_IF_FAILED(userResult.Hresult()); auto httpCall = MakeShared(userResult.ExtractPayload()); RETURN_HR_IF_FAILED(httpCall->Init( m_xboxLiveContextSettings, "POST", XblHttpCall::BuildUrl("sessiondirectory", GET_ACTIIVITIES_SUBPATH), xbox_live_api::get_activities_for_users )); RETURN_HR_IF_FAILED(httpCall->SetXblServiceContractVersion(MULTIPLAYER_SERVICE_CONTRACT_VERSION)); JsonDocument requestJson; request.Serialize(requestJson, requestJson.GetAllocator()); RETURN_HR_IF_FAILED(httpCall->SetRequestBody(JsonUtils::SerializeJson(requestJson))); return httpCall->Perform(AsyncContext{ async.Queue().DeriveWorkerQueue(), [async](HttpResult httpResult) { if (FAILED(httpResult.Hresult())) { return async.Complete({ httpResult.Hresult(), "Http call failed" }); } auto hr = httpResult.Payload()->Result(); if (FAILED(hr)) { const char* errorMessagePtr{}; std::unique_ptr errorMessage{ errorMessagePtr }; httpResult.Payload()->GetErrorMessage(&errorMessagePtr); return async.Complete({ hr, errorMessagePtr }); } Vector activityDetails; hr = JsonUtils::ExtractJsonVector( Serializers::DeserializeMultiplayerActivityDetails, httpResult.Payload()->GetResponseBodyJson(), "results", activityDetails, true ); if (FAILED(hr)) { return async.Complete(hr); } async.Complete(activityDetails); }}); } HRESULT MultiplayerService::GetSearchHandles( _In_ const MultiplayerQuerySearchHandleRequest& searchHandleRequest, _In_ AsyncContext>>> async ) const noexcept { RETURN_HR_INVALIDARGUMENT_IF(searchHandleRequest.Scid().empty() || searchHandleRequest.SessionTemplateName().empty()); Result userResult = m_user.Copy(); RETURN_HR_IF_FAILED(userResult.Hresult()); auto httpCall = MakeShared(userResult.ExtractPayload()); RETURN_HR_IF_FAILED(httpCall->Init( m_xboxLiveContextSettings, "POST", XblHttpCall::BuildUrl("sessiondirectory", GET_SEARCH_HANDLES_SUBPATH), xbox_live_api::get_search_handles )); RETURN_HR_IF_FAILED(httpCall->SetXblServiceContractVersion(MULTIPLAYER_SERVICE_CONTRACT_VERSION)); JsonDocument searchHandleRequestJson; searchHandleRequest.Serialize(m_user.Xuid(), searchHandleRequestJson, searchHandleRequestJson.GetAllocator()); RETURN_HR_IF_FAILED(httpCall->SetRequestBody(JsonUtils::SerializeJson(searchHandleRequestJson))); return httpCall->Perform(AsyncContext{ async.Queue().DeriveWorkerQueue(), [async](HttpResult httpResult) { if (FAILED(httpResult.Hresult())) { return async.Complete({ httpResult.Hresult(), "Http call failed" }); } auto hr = httpResult.Payload()->Result(); if (FAILED(hr)) { const char* errorMessagePtr{}; std::unique_ptr errorMessage{ errorMessagePtr }; httpResult.Payload()->GetErrorMessage(&errorMessagePtr); return async.Complete({ hr, errorMessagePtr }); } Vector> searchHandleDetails; hr = JsonUtils::ExtractJsonVector>( XblMultiplayerSearchHandleDetails::Deserialize, httpResult.Payload()->GetResponseBodyJson(), "results", searchHandleDetails, true ); async.Complete(Result>>{ searchHandleDetails, hr }); }}); } HRESULT MultiplayerService::WriteSessionUsingSubpath( _In_ std::shared_ptr session, _In_ XblMultiplayerSessionWriteMode mode, _In_ const String& subpathAndQuery, _In_ AsyncContext>> async ) noexcept { RETURN_HR_INVALIDARGUMENT_IF(subpathAndQuery.empty()); Result userResult = m_user.Copy(); RETURN_HR_IF_FAILED(userResult.Hresult()); auto httpCall = MakeShared(userResult.ExtractPayload()); RETURN_HR_IF_FAILED(httpCall->Init( m_xboxLiveContextSettings, "PUT", XblHttpCall::BuildUrl("sessiondirectory", subpathAndQuery), xbox_live_api::write_session_using_subpath )); RETURN_HR_IF_FAILED(httpCall->SetRetryAllowed(false)); RETURN_HR_IF_FAILED(httpCall->SetXblServiceContractVersion(MULTIPLAYER_SERVICE_CONTRACT_VERSION)); switch (mode) { case XblMultiplayerSessionWriteMode::CreateNew: { RETURN_HR_IF_FAILED(httpCall->SetHeader("If-None-Match", "*")); break; } case XblMultiplayerSessionWriteMode::UpdateExisting: { RETURN_HR_IF_FAILED(httpCall->SetHeader("If-Match", "*")); break; } case XblMultiplayerSessionWriteMode::UpdateOrCreateNew: { // No match header break; } case XblMultiplayerSessionWriteMode::SynchronizedUpdate: { if (session->ETag().empty()) { RETURN_HR_IF_FAILED(httpCall->SetHeader("If-None-Match", "*")); } else { RETURN_HR_IF_FAILED(httpCall->SetHeader("If-Match", session->ETag())); } break; } default: { return E_INVALIDARG; } } // Set the ConnectionId for the session TaskQueue derivedQueue{ async.Queue().DeriveWorkerQueue() }; return SetRtaConnectionId(session, AsyncContext>{ derivedQueue, [ httpCall, xuid{ m_user.Xuid() }, sessionReference{ session->SessionReference() }, session, async{ std::move(async) } ] (Result setConnectionIdResult) { if (Failed(setConnectionIdResult)) { return async.Complete({ setConnectionIdResult.Hresult(), "Failed to establish MPSD RTA subscription" }); } JsonDocument requestBody{ rapidjson::kObjectType }; session->Serialize(requestBody, requestBody.GetAllocator()); HRESULT hr = httpCall->SetRequestBody(requestBody); if (FAILED(hr)) { return async.Complete(hr); } hr = httpCall->Perform(AsyncContext{ async.Queue().DeriveWorkerQueue(), [ xuid, sessionReference, async ] (HttpResult httpResult) { HRESULT hr = httpResult.Hresult(); if (FAILED(hr)) { return async.Complete({ hr, "Http call failed" }); } hr = httpResult.Payload()->Result(); auto statusCode = httpResult.Payload()->HttpStatus(); if (FAILED(hr) && statusCode != 412) { return async.Complete(hr); } else if (statusCode == 204) { // Consistent with XDK behavior, return success on 204 when writing session return async.Complete(S_OK); } auto responseJson = httpResult.Payload()->GetResponseBodyJson(); if (responseJson.IsNull()) { return async.Complete(hr); } XblMultiplayerSessionReference localSessionRef; if (sessionReference.Scid[0] == 0) { auto contentLocation = httpResult.Payload()->GetResponseHeader("Content-Location"); hr = XblMultiplayerSessionReferenceParseFromUriPath(contentLocation.c_str(), &localSessionRef); if (FAILED(hr)) { return async.Complete({ E_FAIL, "Failed to parse session reference from URI" }); } } else { localSessionRef = sessionReference; } auto session = MakeShared( xuid, localSessionRef, httpResult.Payload()->GetResponseHeader(ETAG_HEADER), httpResult.Payload()->GetResponseHeader(DATE_HEADER), httpResult.Payload()->GetResponseBodyJson() ); if (FAILED(session->DeserializationError()) && SUCCEEDED(hr)) { // WriteSession failed due to deserialization error hr = session->DeserializationError(); } session->SetWriteSessionStatus( statusCode ); return async.Complete(Result>(session, hr)); }}); if (FAILED(hr)) { return async.Complete(hr); } } }); } HRESULT MultiplayerService::SetRtaConnectionId( _In_ std::shared_ptr session, _In_ AsyncContext> async ) noexcept { std::lock_guard lock{ m_mutexMultiplayerService }; XblMultiplayerSessionReadLockGuard sessionSafe(session); if (!m_subscription || !sessionSafe.CurrentUserInternal()) { // If we don't have an active subscription or the current user is not in the session do nothing async.Complete(S_OK); } else if (!m_subscription->RtaConnectionId().empty()) { // If we already have a connectionId add it sessionSafe.CurrentUserInternal()->SetRtaConnectionId(m_subscription->RtaConnectionId()); async.Complete(S_OK); } else { // Wait for subscription to be finalized and add connectionId then m_sessionsAwaitingConnectionId.emplace_back(session, std::move(async)); } return S_OK; } HRESULT MultiplayerService::SubscribeToRta(std::unique_lock lock) noexcept { if (m_subscription == nullptr) { m_subscription = MakeShared(); m_subscription->AddConnectionIdChangedHandler( [ weakThis = std::weak_ptr{ shared_from_this() } ] (const String& connectionId) { if (auto sharedThis{ weakThis.lock() }) { std::unique_lock lock{ sharedThis->m_mutexMultiplayerService }; for (auto& pair : sharedThis->m_sessionsAwaitingConnectionId) { // Make sure the current user is still in the session XblMultiplayerSessionReadLockGuard pairSafe(pair.first); if (pairSafe.CurrentUserInternal()) { pairSafe.CurrentUserInternal()->SetRtaConnectionId(connectionId); } pair.second.Complete(S_OK); } sharedThis->m_sessionsAwaitingConnectionId.clear(); // Invoke client connectionId changed handlers as well auto clientHandlers{ sharedThis->m_connectionIdChangedHandlers }; lock.unlock(); for (auto& handler : clientHandlers) { handler.second(connectionId); } } }); // To support title subscription lost events, add an RTA connection state changed handler m_rtaConnectionStateChangedToken = m_rtaManager->AddStateChangedHandler(m_user, [ weakThis = std::weak_ptr{ shared_from_this() } ] (XblRealTimeActivityConnectionState state) { auto sharedThis{ weakThis.lock() }; if (state == XblRealTimeActivityConnectionState::Disconnected && sharedThis) { std::unique_lock lock{ sharedThis->m_mutexMultiplayerService }; auto handlers{ sharedThis->m_subscriptionLostHandlers }; lock.unlock(); for (auto& handler : handlers) { handler.second(); } // If there were sessions awaiting a connectionId, complete those AsyncContexts for (auto& pair : sharedThis->m_sessionsAwaitingConnectionId) { pair.second.Complete(S_OK); } sharedThis->m_sessionsAwaitingConnectionId.clear(); } }); // Unlock before adding subscription as it can synchronously call back into our ConnectionIdChanged handler lock.unlock(); return m_rtaManager->AddSubscription(m_user, m_subscription); } return S_OK; } HRESULT MultiplayerService::UnsubscribeFromRta() noexcept { if (m_subscription != nullptr) { m_rtaManager->RemoveStateChangedHandler(m_user, m_rtaConnectionStateChangedToken); m_rtaManager->RemoveSubscription(m_user, m_subscription); m_subscription.reset(); // If there were sessions awaiting a connectionId, complete those AsyncContexts for (auto& pair : m_sessionsAwaitingConnectionId) { pair.second.Complete(S_OK); } m_sessionsAwaitingConnectionId.clear(); } return S_OK; } HRESULT MultiplayerService::EnableMultiplayerSubscriptions() noexcept { std::unique_lock lock{ m_mutexMultiplayerService }; m_forceEnableRtaSubscription = true; return SubscribeToRta(std::move(lock)); } HRESULT MultiplayerService::DisableMultiplayerSubscriptions() noexcept { std::unique_lock lock{ m_mutexMultiplayerService }; m_forceEnableRtaSubscription = false; HRESULT hr = UnsubscribeFromRta(); // Maintain existing behavior and invoke subscription lost handler here auto handlers{ m_subscriptionLostHandlers }; lock.unlock(); for (auto& handler : handlers) { handler.second(); } return hr; } bool MultiplayerService::SubscriptionsEnabled() noexcept { std::lock_guard lock{ m_mutexMultiplayerService }; return m_subscription != nullptr; } XblFunctionContext MultiplayerService::AddMultiplayerSessionChangedHandler( _In_ MultiplayerSubscription::SessionChangedHandler handler ) noexcept { { std::unique_lock lock{ m_mutexMultiplayerService }; SubscribeToRta(std::move(lock)); } XblFunctionContext token{}; { std::unique_lock lock{ m_mutexMultiplayerService }; if (m_subscription) { token = m_subscription->AddSessionChangedHandler(std::move(handler)); } } return token; } void MultiplayerService::RemoveMultiplayerSessionChangedHandler( _In_ XblFunctionContext token ) noexcept { std::lock_guard lock{ m_mutexMultiplayerService }; if (m_subscription) { size_t remainingHandlers = m_subscription->RemoveSessionChangedHandler(token); // If that was the last handler and the title hasn't force enabled the RTA subscription then unsubscribe if (!remainingHandlers && !m_forceEnableRtaSubscription) { UnsubscribeFromRta(); } } } XblFunctionContext MultiplayerService::AddMultiplayerSubscriptionLostHandler( _In_ SubscriptionLostHandler handler ) noexcept { std::lock_guard lock{ m_mutexMultiplayerService }; m_subscriptionLostHandlers[m_nextClientToken] = std::move(handler); return m_nextClientToken++; } void MultiplayerService::RemoveMultiplayerSubscriptionLostHandler( _In_ XblFunctionContext token ) noexcept { std::lock_guard lock{ m_mutexMultiplayerService }; m_subscriptionLostHandlers.erase(token); } XblFunctionContext MultiplayerService::AddMultiplayerConnectionIdChangedHandler( _In_ MultiplayerSubscription::ConnectionIdChangedHandler handler ) noexcept { std::lock_guard lock{ m_mutexMultiplayerService }; // For legacy reasons, allow adding a connectionId changed handler even if subscriptions are not enabled m_connectionIdChangedHandlers[m_nextClientToken] = std::move(handler); return m_nextClientToken++; } void MultiplayerService::RemoveMultiplayerConnectionIdChangedHandler( _In_ XblFunctionContext token ) noexcept { std::lock_guard lock{ m_mutexMultiplayerService }; m_connectionIdChangedHandlers.erase(token); } String MultiplayerService::MultiplayerSessionDirectoryCreateOrUpdateSubpath( _In_ const String& serviceConfigurationId, _In_ const String& sessionTemplateName, _In_ const String& sessionName ) noexcept { Stringstream source; source << "/serviceconfigs/"; source << serviceConfigurationId; source << "/sessionTemplates/"; source << sessionTemplateName; source << "/sessions/"; source << sessionName; return source.str(); } String MultiplayerService::MultiplayerSessionDirectoryCreateOrUpdateByHandleSubpath( _In_ const String& handleId ) noexcept { Stringstream source; source << "/handles/"; source << handleId; source << "/session"; return source.str(); } NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_CPP_END STDAPI MultiplayerWriteSessionHelper( _In_ XblContextHandle xblContextHandle, _In_ XblMultiplayerSessionHandle multiplayerSession, _In_ XblMultiplayerSessionWriteMode writeMode, _In_opt_ const char* handleIdArg, _Inout_ XAsyncBlock* async ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(xblContextHandle == nullptr || multiplayerSession == nullptr || async == nullptr); return RunAsync(async, __FUNCTION__, [ xblContext{ xblContextHandle->shared_from_this() }, inputSession{ multiplayerSession->shared_from_this() }, writeMode, handleId{ String{handleIdArg ? handleIdArg : ""} }, outputSession{ std::shared_ptr() } ] (XAsyncOp op, const XAsyncProviderData* data) mutable { switch (op) { case XAsyncOp::DoWork: { AsyncContext>> async{ data->async->queue, [ &outputSession, async{ data->async } ] (Result> result) { outputSession = result.ExtractPayload(); auto hr = result.Hresult(); // Still must return latest session to allow retries if (hr == HTTP_E_STATUS_PRECOND_FAILED) { hr = S_OK; } XAsyncComplete(async, hr, sizeof(XblMultiplayerSessionHandle)); } }; if (handleId.empty()) { RETURN_HR_IF_FAILED(xblContext->MultiplayerService()->WriteSession( inputSession, writeMode, std::move(async) )); } else { RETURN_HR_IF_FAILED(xblContext->MultiplayerService()->WriteSessionByHandle( inputSession, writeMode, handleId, std::move(async) )); } return E_PENDING; } case XAsyncOp::GetResult: { auto handlePtr = static_cast(data->buffer); if (outputSession) { outputSession->AddRef(); *handlePtr = outputSession.get(); } else { *handlePtr = nullptr; } return S_OK; } default: { return S_OK; } } }); } CATCH_RETURN() STDAPI XblMultiplayerWriteSessionAsync( _In_ XblContextHandle xblContext, _In_ XblMultiplayerSessionHandle multiplayerSession, _In_ XblMultiplayerSessionWriteMode writeMode, _Inout_ XAsyncBlock* async ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(multiplayerSession); if (!XblMultiplayerSessionReferenceIsValid(&multiplayerSession->SessionReference())) { LOGS_DEBUG << "XblMultiplayerWriteSessionAsync cannot be called on a session without a valid session reference"; return E_XBL_RUNTIME_ERROR; } return MultiplayerWriteSessionHelper(xblContext, multiplayerSession, writeMode, nullptr, async); } CATCH_RETURN() STDAPI XblMultiplayerWriteSessionResult( _Inout_ XAsyncBlock* async, _Out_ XblMultiplayerSessionHandle* handle ) XBL_NOEXCEPT try { XblMultiplayerSessionHandle handleCopy = nullptr; auto hr = XAsyncGetResult(async, nullptr, sizeof(XblMultiplayerSessionHandle), &handleCopy, nullptr); if (handle != nullptr) { *handle = handleCopy; } else { XblMultiplayerSessionCloseHandle(handleCopy); } return hr; } CATCH_RETURN() STDAPI XblMultiplayerWriteSessionByHandleAsync( _In_ XblContextHandle xblContext, _In_ XblMultiplayerSessionHandle multiplayerSession, _In_ XblMultiplayerSessionWriteMode writeMode, _In_ const char* handleId, _In_ XAsyncBlock* async ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(handleId); return MultiplayerWriteSessionHelper(xblContext, multiplayerSession, writeMode, handleId, async); } CATCH_RETURN() STDAPI XblMultiplayerWriteSessionByHandleResult( _Inout_ XAsyncBlock* async, _Out_ XblMultiplayerSessionHandle* handle ) XBL_NOEXCEPT try { return XblMultiplayerWriteSessionResult(async, handle); } CATCH_RETURN() STDAPI MultiplayerGetSessionHelper( _In_ XblContextHandle xblContextHandle, _In_opt_ const XblMultiplayerSessionReference* sessionReferenceArg, _In_opt_ const char* handleIdArg, _In_ XAsyncBlock* async ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(xblContextHandle == nullptr || async == nullptr); return RunAsync(async, __FUNCTION__, [ xblContext{ xblContextHandle->shared_from_this() }, sessionReference = sessionReferenceArg ? XblMultiplayerSessionReference{ *sessionReferenceArg } : XblMultiplayerSessionReference{}, handleId = handleIdArg ? String{ handleIdArg } : String{}, session = std::shared_ptr{ nullptr } ] (XAsyncOp op, const XAsyncProviderData* data) mutable { switch (op) { case XAsyncOp::DoWork: { AsyncContext>> async{ data->async->queue, [ &session, async{ data->async } ] (Result> result) { session = result.ExtractPayload(); XAsyncComplete(async, result.Hresult(), sizeof(XblMultiplayerSessionHandle)); } }; if (handleId.empty()) { RETURN_HR_IF_FAILED(xblContext->MultiplayerService()->GetCurrentSession(sessionReference, std::move(async))); } else { RETURN_HR_IF_FAILED(xblContext->MultiplayerService()->GetCurrentSessionByHandle(handleId, std::move(async))); } return E_PENDING; } case XAsyncOp::GetResult: { auto handlePtr = static_cast(data->buffer); if (session) { session->AddRef(); *handlePtr = session.get(); } else { *handlePtr = nullptr; } return S_OK; } default: { return S_OK; } } }); } CATCH_RETURN() STDAPI XblMultiplayerGetSessionAsync( _In_ XblContextHandle xblContext, _In_ const XblMultiplayerSessionReference* sessionReference, _In_ XAsyncBlock* async ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(sessionReference); return MultiplayerGetSessionHelper(xblContext, sessionReference, nullptr, async); } CATCH_RETURN() STDAPI XblMultiplayerGetSessionResult( _In_ XAsyncBlock* async, _Out_ XblMultiplayerSessionHandle* handle ) XBL_NOEXCEPT try { return XAsyncGetResult(async, nullptr, sizeof(XblMultiplayerSessionHandle), handle, nullptr); } CATCH_RETURN() STDAPI XblMultiplayerGetSessionByHandleAsync( _In_ XblContextHandle xblContext, _In_ const char* handleId, _In_ XAsyncBlock* async ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(handleId); return MultiplayerGetSessionHelper(xblContext, nullptr, handleId, async); } CATCH_RETURN() STDAPI XblMultiplayerGetSessionByHandleResult( _In_ XAsyncBlock* async, _Out_ XblMultiplayerSessionHandle* handle ) XBL_NOEXCEPT try { return XAsyncGetResult(async, nullptr, sizeof(XblMultiplayerSessionHandle), handle, nullptr); } CATCH_RETURN() STDAPI XblMultiplayerQuerySessionsAsync( _In_ XblContextHandle xblContextHandle, _In_ const XblMultiplayerSessionQuery* sessionQuery, _In_ XAsyncBlock* async ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(xblContextHandle == nullptr || sessionQuery == nullptr || async == nullptr); RETURN_HR_INVALIDARGUMENT_IF_EMPTY_STRING(sessionQuery->Scid); RETURN_HR_INVALIDARGUMENT_IF((sessionQuery->XuidFilters == nullptr || sessionQuery->XuidFiltersCount == 0) && (sessionQuery->KeywordFilter == nullptr || sessionQuery->KeywordFilter[0] == 0)); RETURN_HR_INVALIDARGUMENT_IF(sessionQuery->IncludeReservations && (sessionQuery->XuidFilters == nullptr || sessionQuery->XuidFiltersCount == 0)); RETURN_HR_INVALIDARGUMENT_IF(sessionQuery->IncludeInactiveSessions && (sessionQuery->XuidFilters == nullptr || sessionQuery->XuidFiltersCount == 0)); RETURN_HR_INVALIDARGUMENT_IF(sessionQuery->VisibilityFilter == XblMultiplayerSessionVisibility::Unknown); return RunAsync(async, __FUNCTION__, [ xblContext{ xblContextHandle->shared_from_this() }, query = SessionQuery{ sessionQuery }, sessions = Vector{} ] (XAsyncOp op, const XAsyncProviderData* data) mutable { switch (op) { case XAsyncOp::DoWork: { RETURN_HR_IF_FAILED(xblContext->MultiplayerService()->GetSessions( query, AsyncContext>>{ data->async->queue, [ &sessions, async{ data->async } ] (Result> result) { sessions = result.ExtractPayload(); XAsyncComplete(async, result.Hresult(), sizeof(XblMultiplayerSessionQueryResult) * sessions.size()); } })); return E_PENDING; } case XAsyncOp::GetResult: { memcpy(data->buffer, sessions.data(), data->bufferSize); return S_OK; } default: { return S_OK; } } }); } CATCH_RETURN() STDAPI XblMultiplayerQuerySessionsResultCount( _In_ XAsyncBlock* async, _Out_ size_t* sessionCount ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(async == nullptr || sessionCount == nullptr); size_t sizeInBytes; auto hr = XAsyncGetResultSize(async, &sizeInBytes); *sessionCount = sizeInBytes / sizeof(XblMultiplayerSessionQueryResult); return hr; } CATCH_RETURN() STDAPI XblMultiplayerQuerySessionsResult( _In_ XAsyncBlock* async, _In_ size_t sessionCount, _Out_writes_(sessionCount) XblMultiplayerSessionQueryResult* sessions ) XBL_NOEXCEPT try { RETURN_HR_IF(sessionCount == 0, S_OK); return XAsyncGetResult(async, nullptr, sessionCount * sizeof(XblMultiplayerSessionQueryResult), sessions, nullptr); } CATCH_RETURN() STDAPI XblMultiplayerSetActivityAsync( _In_ XblContextHandle xblContextHandle, _In_ const XblMultiplayerSessionReference* sessionReferenceArg, _In_ XAsyncBlock* async ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(xblContextHandle == nullptr || sessionReferenceArg == nullptr || async == nullptr); return RunAsync(async, __FUNCTION__, [ xblContext{ xblContextHandle->shared_from_this() }, sessionReference{ *sessionReferenceArg } ] (XAsyncOp op, const XAsyncProviderData* data) { switch (op) { case XAsyncOp::DoWork: { RETURN_HR_IF_FAILED(xblContext->MultiplayerService()->SetActivity(sessionReference, data->async)); return E_PENDING; } default: { return S_OK; } } }); } CATCH_RETURN() STDAPI XblMultiplayerClearActivityAsync( _In_ XblContextHandle xblContextHandle, _In_z_ const char* scidArg, _In_ XAsyncBlock* async ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(xblContextHandle == nullptr || scidArg == nullptr || async == nullptr); return RunAsync(async, __FUNCTION__, [ xblContext{ xblContextHandle->shared_from_this() }, scid = String{ scidArg } ] (XAsyncOp op, const XAsyncProviderData* data) { switch (op) { case XAsyncOp::DoWork: { RETURN_HR_IF_FAILED(xblContext->MultiplayerService()->ClearActivity(scid, data->async)); return E_PENDING; } default: { return S_OK; } } }); } CATCH_RETURN() STDAPI XblMultiplayerSendInvitesAsync( _In_ XblContextHandle xblContextHandle, _In_ const XblMultiplayerSessionReference* sessionReference, _In_ const uint64_t* xuids, _In_ size_t xuidsCount, _In_ uint32_t titleId, _In_opt_z_ const char* contextStringId, _In_opt_z_ const char* customActivationContext, _In_ XAsyncBlock* async ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(xblContextHandle == nullptr || sessionReference == nullptr || xuids == nullptr || xuidsCount == 0 || async == nullptr); return RunAsync(async, __FUNCTION__, [ xblContext{ xblContextHandle->shared_from_this() }, sessionRef{ *sessionReference }, xuidsVector{ Vector(xuids, xuids + xuidsCount) }, titleId, contextString{ String{ contextStringId ? contextStringId : "" } }, activiationContext{ String{customActivationContext ? customActivationContext : ""} }, inviteHandles{ Vector() } ] (XAsyncOp op, const XAsyncProviderData* data) mutable { switch (op) { case XAsyncOp::DoWork: { RETURN_HR_IF_FAILED(xblContext->MultiplayerService()->SendInvites( sessionRef, xuidsVector, titleId, contextString, activiationContext, AsyncContext>>{ data->async->queue, [ &inviteHandles, async{ data->async } ] (Result> result) { if (Succeeded(result)) { inviteHandles = result.ExtractPayload(); } XAsyncComplete(async, result.Hresult(), sizeof(XblMultiplayerInviteHandle) * inviteHandles.size()); } })); return E_PENDING; } case XAsyncOp::GetResult: { auto handlesArray = static_cast(data->buffer); for (uint32_t i = 0; i < inviteHandles.size(); ++i) { utils::strcpy(handlesArray[i].Data, sizeof(handlesArray[i].Data), inviteHandles[i].data()); } return S_OK; } default: { return S_OK; } } }); } CATCH_RETURN() STDAPI XblMultiplayerSendInvitesResult( _In_ XAsyncBlock* async, _In_ size_t handlesCount, _Out_writes_(handlesCount) XblMultiplayerInviteHandle* handles ) XBL_NOEXCEPT try { RETURN_HR_IF(handlesCount == 0, S_OK); return XAsyncGetResult(async, nullptr, sizeof(XblMultiplayerInviteHandle) * handlesCount, handles, nullptr); } CATCH_RETURN() STDAPI XblMultiplayerGetActivitiesForSocialGroupAsync( _In_ XblContextHandle xblContextHandle, _In_ const char* scidArg, _In_ uint64_t socialGroupOwnerXuid, _In_ const char* socialGroupArg, _In_ XAsyncBlock* async ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(xblContextHandle == nullptr || scidArg == nullptr || socialGroupOwnerXuid == 0 || socialGroupArg == nullptr || async == nullptr); return RunAsync(async, __FUNCTION__, [ xblContext{ xblContextHandle->shared_from_this() }, scid = String{ scidArg }, socialGroupOwnerXuid, socialGroup = String{ socialGroupArg }, activityDetails = Vector{} ] (XAsyncOp op, const XAsyncProviderData* data) mutable { switch (op) { case XAsyncOp::DoWork: { RETURN_HR_IF_FAILED(xblContext->MultiplayerService()->GetActivitiesForSocialGroup( scid, socialGroupOwnerXuid, socialGroup, AsyncContext>>{ data->async->queue, [ &activityDetails, async{ data->async } ] (Result> result) { activityDetails = result.ExtractPayload(); XAsyncComplete(async, result.Hresult(), sizeof(XblMultiplayerActivityDetails) * activityDetails.size()); } })); return E_PENDING; } case XAsyncOp::GetResult: { for (auto& activity : activityDetails) { Delete(activity.CustomSessionPropertiesJson); activity.CustomSessionPropertiesJson = nullptr; } memcpy(data->buffer, activityDetails.data(), data->bufferSize); return S_OK; } default: { return S_OK; } } }); } CATCH_RETURN() STDAPI XblMultiplayerGetActivitiesWithPropertiesForSocialGroupAsync( _In_ XblContextHandle xblContextHandle, _In_ const char* scidArg, _In_ uint64_t socialGroupOwnerXuid, _In_ const char* socialGroupArg, _In_ XAsyncBlock* async ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(xblContextHandle == nullptr || scidArg == nullptr || socialGroupOwnerXuid == 0 || socialGroupArg == nullptr || async == nullptr); return RunAsync(async, __FUNCTION__, [ xblContext{ xblContextHandle->shared_from_this() }, scid = String{ scidArg }, socialGroupOwnerXuid, socialGroup = String{ socialGroupArg }, activityDetails = Vector{} ] (XAsyncOp op, const XAsyncProviderData* data) mutable { switch (op) { case XAsyncOp::DoWork: { RETURN_HR_IF_FAILED(xblContext->MultiplayerService()->GetActivitiesForSocialGroup( scid, socialGroupOwnerXuid, socialGroup, AsyncContext>>{ data->async->queue, [ &activityDetails, async{ data->async } ] (Result> result) { activityDetails = result.ExtractPayload(); auto hr = result.Hresult(); size_t jsonSize{}; for (auto& activity : activityDetails) { jsonSize += strlen(activity.CustomSessionPropertiesJson) + 1; } size_t bufferSize = jsonSize + sizeof(XblMultiplayerActivityDetails) * activityDetails.size(); bufferSize = static_cast((bufferSize + XBL_ALIGN_SIZE - 1) / XBL_ALIGN_SIZE) * XBL_ALIGN_SIZE; XAsyncComplete(async, hr, bufferSize); } })); return E_PENDING; } case XAsyncOp::GetResult: { auto activityPtr = reinterpret_cast(data->buffer); auto jsonPtr = reinterpret_cast(data->buffer) + sizeof(XblMultiplayerActivityDetails) * activityDetails.size(); for (auto& activity : activityDetails) { size_t len = strlen(activity.CustomSessionPropertiesJson) + 1; *activityPtr = activity; utils::strcpy(jsonPtr, len, activity.CustomSessionPropertiesJson); activityPtr->CustomSessionPropertiesJson = jsonPtr; Delete(activity.CustomSessionPropertiesJson); jsonPtr += len; ++activityPtr; } return S_OK; } default: { return S_OK; } } }); } CATCH_RETURN() STDAPI XblMultiplayerGetActivitiesForSocialGroupResultCount( _In_ XAsyncBlock* async, _Out_ size_t* activityCount ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(activityCount); size_t sizeInBytes; auto hr = XAsyncGetResultSize(async, &sizeInBytes); *activityCount = sizeInBytes / sizeof(XblMultiplayerActivityDetails); return hr; } CATCH_RETURN() STDAPI XblMultiplayerGetActivitiesForSocialGroupResult( _In_ XAsyncBlock* async, _In_ size_t activityCount, _Out_writes_(activityCount) XblMultiplayerActivityDetails* activities ) XBL_NOEXCEPT try { RETURN_HR_IF(activityCount == 0, S_OK); return XAsyncGetResult(async, nullptr, activityCount * sizeof(XblMultiplayerActivityDetails), activities, nullptr); } CATCH_RETURN() STDAPI XblMultiplayerGetActivitiesWithPropertiesForSocialGroupResultSize( _In_ XAsyncBlock* async, _Out_ size_t* resultSizeInBytes ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(resultSizeInBytes); return XAsyncGetResultSize(async, resultSizeInBytes); } CATCH_RETURN() STDAPI XblMultiplayerGetActivitiesWithPropertiesForSocialGroupResult( _In_ XAsyncBlock* async, _In_ size_t bufferSize, _Out_writes_bytes_to_(bufferSize, *bufferUsed) void* buffer, _Outptr_ XblMultiplayerActivityDetails** ptrToBuffer, _Out_ size_t* ptrToBufferCount, _Out_opt_ size_t* bufferUsed ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(buffer == nullptr || ptrToBuffer == nullptr || ptrToBufferCount == nullptr); size_t bufferUsedTemp{}; if (bufferUsed == nullptr) { bufferUsed = &bufferUsedTemp; } auto hr = XAsyncGetResult(async, nullptr, bufferSize, buffer, bufferUsed); if (SUCCEEDED(hr)) { *ptrToBuffer = static_cast(buffer); size_t count{ 0 }; size_t verifiedSize{ 0 }; for (; *bufferUsed > 0 && verifiedSize < *bufferUsed - XBL_ALIGN_SIZE; ++count) { verifiedSize += sizeof(XblMultiplayerActivityDetails); verifiedSize += strlen((*ptrToBuffer)[count].CustomSessionPropertiesJson) + 1; } verifiedSize = static_cast((verifiedSize + XBL_ALIGN_SIZE - 1) / XBL_ALIGN_SIZE) * XBL_ALIGN_SIZE; assert(verifiedSize == *bufferUsed); *ptrToBufferCount = count; } return hr; } CATCH_RETURN() STDAPI XblMultiplayerGetActivitiesForUsersAsync( _In_ XblContextHandle xblContextHandle, _In_ const char* scidArg, _In_reads_(xuidsCount) const uint64_t* xuidsArg, _In_ size_t xuidsCount, _In_ XAsyncBlock* async ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(xblContextHandle == nullptr || scidArg == nullptr || xuidsArg == nullptr || xuidsCount == 0 || async == nullptr); return RunAsync(async, __FUNCTION__, [ xblContext{ xblContextHandle->shared_from_this() }, scid = String{ scidArg }, xuids = Vector(xuidsArg, xuidsArg + xuidsCount), activityDetails = Vector{} ] (XAsyncOp op, const XAsyncProviderData* data) mutable { switch (op) { case XAsyncOp::DoWork: { RETURN_HR_IF_FAILED(xblContext->MultiplayerService()->GetActivitiesForUsers( scid, xuids, AsyncContext>>{ data->async->queue, [ &activityDetails, async{ data->async } ] (Result> result) { activityDetails = result.ExtractPayload(); XAsyncComplete(async, result.Hresult(), sizeof(XblMultiplayerActivityDetails) * activityDetails.size()); } })); return E_PENDING; } case XAsyncOp::GetResult: { for (auto& activity : activityDetails) { Delete(activity.CustomSessionPropertiesJson); activity.CustomSessionPropertiesJson = nullptr; } memcpy(data->buffer, activityDetails.data(), data->bufferSize); return S_OK; } default: { return S_OK; } } }); } CATCH_RETURN() STDAPI XblMultiplayerGetActivitiesWithPropertiesForUsersAsync( _In_ XblContextHandle xblContextHandle, _In_ const char* scidArg, _In_reads_(xuidsCount) const uint64_t* xuidsArg, _In_ size_t xuidsCount, _In_ XAsyncBlock* async ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(xblContextHandle == nullptr || scidArg == nullptr || xuidsArg == nullptr || xuidsCount == 0 || async == nullptr); return RunAsync(async, __FUNCTION__, [ xblContext{ xblContextHandle->shared_from_this() }, scid = String{ scidArg }, xuids = Vector(xuidsArg, xuidsArg + xuidsCount), activityDetails = Vector{} ] (XAsyncOp op, const XAsyncProviderData* data) mutable { switch (op) { case XAsyncOp::DoWork: { RETURN_HR_IF_FAILED(xblContext->MultiplayerService()->GetActivitiesForUsers( scid, xuids, AsyncContext>>{ data->async->queue, [ &activityDetails, async{ data->async } ] (Result> result) { activityDetails = result.ExtractPayload(); auto hr = result.Hresult(); size_t jsonSize{}; for (auto& activity : activityDetails) { jsonSize += strlen(activity.CustomSessionPropertiesJson) + 1; } size_t bufferSize = jsonSize + sizeof(XblMultiplayerActivityDetails) * activityDetails.size(); bufferSize = static_cast((bufferSize + XBL_ALIGN_SIZE - 1) / XBL_ALIGN_SIZE) * XBL_ALIGN_SIZE; XAsyncComplete(async, hr, bufferSize); } })); return E_PENDING; } case XAsyncOp::GetResult: { auto activityPtr = reinterpret_cast(data->buffer); auto jsonPtr = reinterpret_cast(data->buffer) + sizeof(XblMultiplayerActivityDetails) * activityDetails.size(); for (auto& activity : activityDetails) { size_t len = strlen(activity.CustomSessionPropertiesJson) + 1; *activityPtr = activity; utils::strcpy(jsonPtr, len, activity.CustomSessionPropertiesJson); activityPtr->CustomSessionPropertiesJson = jsonPtr; Delete(activity.CustomSessionPropertiesJson); jsonPtr += len; ++activityPtr; } return S_OK; } default: { return S_OK; } } }); } CATCH_RETURN() STDAPI XblMultiplayerGetActivitiesForUsersResultCount( _In_ XAsyncBlock* async, _Out_ size_t* activityCount ) XBL_NOEXCEPT try { return XblMultiplayerGetActivitiesForSocialGroupResultCount(async, activityCount); } CATCH_RETURN() STDAPI XblMultiplayerGetActivitiesForUsersResult( _In_ XAsyncBlock* async, _In_ size_t activityCount, _Out_writes_(activityCount) XblMultiplayerActivityDetails* activities ) XBL_NOEXCEPT try { return XAsyncGetResult(async, nullptr, activityCount * sizeof(XblMultiplayerActivityDetails), activities, nullptr); } CATCH_RETURN() STDAPI XblMultiplayerGetActivitiesWithPropertiesForUsersResultSize( _In_ XAsyncBlock* async, _Out_ size_t* resultSizeInBytes ) XBL_NOEXCEPT try { return XblMultiplayerGetActivitiesWithPropertiesForSocialGroupResultSize(async, resultSizeInBytes); } CATCH_RETURN() STDAPI XblMultiplayerGetActivitiesWithPropertiesForUsersResult( _In_ XAsyncBlock* async, _In_ size_t bufferSize, _Out_writes_bytes_to_(bufferSize, *bufferUsed) void* buffer, _Outptr_ XblMultiplayerActivityDetails** ptrToBuffer, _Out_ size_t* ptrToBufferCount, _Out_opt_ size_t* bufferUsed ) XBL_NOEXCEPT try { return XblMultiplayerGetActivitiesWithPropertiesForSocialGroupResult(async, bufferSize, buffer, ptrToBuffer, ptrToBufferCount, bufferUsed); } CATCH_RETURN() STDAPI XblMultiplayerSetSubscriptionsEnabled( _In_ XblContextHandle xblContext, _In_ bool subscriptionsEnabled ) XBL_NOEXCEPT try { if (subscriptionsEnabled) { return xblContext->MultiplayerService()->EnableMultiplayerSubscriptions(); } else { return xblContext->MultiplayerService()->DisableMultiplayerSubscriptions(); } } CATCH_RETURN() STDAPI_(bool) XblMultiplayerSubscriptionsEnabled( _In_ XblContextHandle xblContext ) XBL_NOEXCEPT try { return xblContext->MultiplayerService()->SubscriptionsEnabled(); } CATCH_RETURN() STDAPI_(XblFunctionContext) XblMultiplayerAddSessionChangedHandler( _In_ XblContextHandle xblContext, _In_ XblMultiplayerSessionChangedHandler* handler, _In_opt_ void* context ) XBL_NOEXCEPT try { return xblContext->MultiplayerService()->AddMultiplayerSessionChangedHandler( [ handler, context ] (const XblMultiplayerSessionChangeEventArgs& args) { try { handler(context, args); } catch (...) { LOGS_ERROR << __FUNCTION__ << ": exception in client handler!"; } }); } CATCH_RETURN() STDAPI_(void) XblMultiplayerRemoveSessionChangedHandler( _In_ XblContextHandle xblContext, _In_ XblFunctionContext token ) XBL_NOEXCEPT try { xblContext->MultiplayerService()->RemoveMultiplayerSessionChangedHandler(token); } CATCH_RETURN_WITH(;) STDAPI_(XblFunctionContext) XblMultiplayerAddSubscriptionLostHandler( _In_ XblContextHandle xblContext, _In_ XblMultiplayerSessionSubscriptionLostHandler* handler, _In_opt_ void* context ) XBL_NOEXCEPT try { return xblContext->MultiplayerService()->AddMultiplayerSubscriptionLostHandler( [ handler, context ] { try { handler(context); } catch (...) { LOGS_ERROR << __FUNCTION__ << ": exception in client handler!"; } }); } CATCH_RETURN() STDAPI_(void) XblMultiplayerRemoveSubscriptionLostHandler( _In_ XblContextHandle xblContext, _In_ XblFunctionContext token ) XBL_NOEXCEPT try { xblContext->MultiplayerService()->RemoveMultiplayerSubscriptionLostHandler(token); } CATCH_RETURN_WITH(;) STDAPI_(XblFunctionContext) XblMultiplayerAddConnectionIdChangedHandler( _In_ XblContextHandle xblContext, _In_ XblMultiplayerConnectionIdChangedHandler* handler, _In_opt_ void* context ) XBL_NOEXCEPT try { return xblContext->MultiplayerService()->AddMultiplayerConnectionIdChangedHandler( [ handler, context ] (const String&) { try { handler(context); } catch (...) { LOGS_ERROR << __FUNCTION__ << ": exception in client handler!"; } }); } CATCH_RETURN() STDAPI_(void) XblMultiplayerRemoveConnectionIdChangedHandler( _In_ XblContextHandle xblContext, _In_ XblFunctionContext token ) XBL_NOEXCEPT try { xblContext->MultiplayerService()->RemoveMultiplayerConnectionIdChangedHandler(token); } CATCH_RETURN_WITH(;) ================================================ FILE: Source/Services/Multiplayer/multiplayer_session.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "multiplayer_internal.h" using namespace xbox::services; using namespace xbox::services::legacy; using namespace xbox::services::multiplayer; #define MULTIPLAYER_SESSION_VERSION 1 XblMultiplayerSession::XblMultiplayerSession( _In_ uint64_t xuid, _In_ XblMultiplayerSessionReference sessionReference, _In_ const xsapi_internal_string& eTag, _In_ const xsapi_internal_string& responseDate, _In_ const JsonValue& json ) : m_xuid(xuid), m_eTag(eTag), m_sessionRetrievedTime(), m_info(), m_sessionReference(sessionReference), m_sessionConstants{}, m_memberInitialization(), m_sessionProperties(), m_memberCurrentUser(nullptr), m_membersAccepted(0), m_writeSessionStatus(), m_joiningSession(false), m_newSession(false), m_deserializationError(), m_writePropertiesKeywords(false), m_writePropertiesTurns(false), m_writeInitializationStatus(false), m_initializationSucceeded(false), m_writeHostDeviceToken(false), m_writeMatchmakingServerConnectionPath(false), m_writeMatchmakingResubmit(false), m_writeServerConnectionStringCandidates(false), m_leaveSession(false), m_writeClosed(false), m_writeLocked(false), m_writeAllocateCloudCompute(false), m_writeRoleTypes(false), m_writeTimeouts(false), m_writeQosConnectivityMetrics(false), m_writeMemberInitialization(false), m_writePeerToPeerRequirements(false), m_writePeerToHostRequirements(false), m_writeMeasurementServerAddresses(false), m_writeJoinRestriction(false), m_writeReadRestriction(false), m_writeServersJson(false), m_writeMatchmakingTargetSessionConstants(false), m_writeSessionCustomPropertiesJson(false), m_writeConstants(false), m_memberRequestIndex(0) { auto sessionDatetime = xbox::services::datetime::from_string(responseDate, xbox::services::datetime::date_format::RFC_1123); m_sessionRetrievedTime = utils::time_t_from_datetime(sessionDatetime); Initialize(); Deserialize(json); } XblMultiplayerSession::XblMultiplayerSession( _In_ uint64_t xuid, _In_opt_ const XblMultiplayerSessionReference* sessionReference, _In_opt_ const XblMultiplayerSessionInitArgs* initArgs ) : m_xuid(xuid), m_sessionReference{}, m_sessionConstants{}, m_memberCurrentUser(nullptr), m_membersAccepted(0), m_joiningSession(false), m_newSession(true), m_writePropertiesKeywords(false), m_writePropertiesTurns(false), m_writeInitializationStatus(false), m_initializationSucceeded(false), m_writeHostDeviceToken(false), m_writeMatchmakingServerConnectionPath(false), m_writeMatchmakingResubmit(false), m_writeServerConnectionStringCandidates(false), m_leaveSession(false), m_writeClosed(false), m_writeLocked(false), m_writeAllocateCloudCompute(false), m_writeRoleTypes(false), m_writeTimeouts(false), m_writeQosConnectivityMetrics(false), m_writeMemberInitialization(false), m_writePeerToPeerRequirements(false), m_writePeerToHostRequirements(false), m_writeMeasurementServerAddresses(false), m_writeJoinRestriction(false), m_writeReadRestriction(false), m_writeServersJson(false), m_writeMatchmakingTargetSessionConstants(false), m_writeSessionCustomPropertiesJson(false), m_writeConstants(false), m_memberRequestIndex(0) { if (sessionReference != nullptr) { m_sessionReference = *sessionReference; } Initialize(); if (initArgs != nullptr) { if (initArgs->CustomJson) { m_constantsCustomJson = initArgs->CustomJson; m_sessionConstants.CustomJson = m_constantsCustomJson.data(); } m_sessionConstants.MaxMembersInSession = initArgs->MaxMembersInSession; m_sessionConstants.Visibility = initArgs->Visibility; if (initArgs->InitiatorXuids && initArgs->InitiatorXuidsCount > 0) { m_initiatorXuids = xsapi_internal_vector(initArgs->InitiatorXuids, initArgs->InitiatorXuids + initArgs->InitiatorXuidsCount); m_sessionConstants.InitiatorXuids = m_initiatorXuids.data(); m_sessionConstants.InitiatorXuidsCount = m_initiatorXuids.size(); } m_writeConstants = true; } } XblMultiplayerSession::XblMultiplayerSession(const XblMultiplayerSession& other) : m_xuid(other.m_xuid), m_eTag(other.m_eTag), m_sessionRetrievedTime(other.m_sessionRetrievedTime), m_info(other.m_info), m_initialization(other.m_initialization), m_sessionReference(other.m_sessionReference), m_hostCandidates(other.m_hostCandidates), m_sessionConstants(other.m_sessionConstants), m_initiatorXuids(other.m_initiatorXuids), m_memberInitialization(other.m_memberInitialization), m_constantsCustomJson(other.m_constantsCustomJson), m_constantsCloudComputePackageJson(other.m_constantsCloudComputePackageJson), m_constantsMeasurementServerAddressesJson(other.m_constantsMeasurementServerAddressesJson), m_sessionProperties(other.m_sessionProperties), m_keywords(other.m_keywords), m_sessionOwnerIndices(other.m_sessionOwnerIndices), m_turnCollection(other.m_turnCollection), m_serverConnectionStringCandidates(other.m_serverConnectionStringCandidates), m_matchmakingServerConnectionString(other.m_matchmakingServerConnectionString), m_matchmakingTargetSessionConstantsJson(other.m_matchmakingTargetSessionConstantsJson), m_sessionCustomPropertiesJson(other.m_sessionCustomPropertiesJson), m_roleTypes(other.m_roleTypes), m_members(other.m_members), m_membersAccepted(other.m_membersAccepted), m_serversJson(other.m_serversJson), m_matchmakingStatusDetails(other.m_matchmakingStatusDetails), m_lastTeamResultTeam(other.m_lastTeamResultTeam), m_writeSessionStatus(other.m_writeSessionStatus), m_newSession(other.m_newSession), m_deserializationError(other.m_deserializationError), m_sessionSubscriptionGuid(other.m_sessionSubscriptionGuid), m_writePropertiesKeywords(other.m_writePropertiesKeywords), m_writePropertiesTurns(other.m_writePropertiesTurns), m_writeInitializationStatus(other.m_writeInitializationStatus), m_initializationSucceeded(other.m_initializationSucceeded), m_writeHostDeviceToken(other.m_writeHostDeviceToken), m_writeMatchmakingServerConnectionPath(other.m_writeMatchmakingServerConnectionPath), m_writeMatchmakingResubmit(other.m_writeMatchmakingResubmit), m_writeServerConnectionStringCandidates(other.m_writeServerConnectionStringCandidates), m_leaveSession(other.m_leaveSession), m_writeClosed(other.m_writeClosed), m_writeLocked(other.m_writeLocked), m_writeAllocateCloudCompute(other.m_writeAllocateCloudCompute), m_writeRoleTypes(other.m_writeRoleTypes), m_writeTimeouts(other.m_writeTimeouts), m_writeQosConnectivityMetrics(other.m_writeQosConnectivityMetrics), m_writeMemberInitialization(other.m_writeMemberInitialization), m_writePeerToPeerRequirements(other.m_writePeerToPeerRequirements), m_writePeerToHostRequirements(other.m_writePeerToHostRequirements), m_writeMeasurementServerAddresses(other.m_writeMeasurementServerAddresses), m_writeJoinRestriction(other.m_writeJoinRestriction), m_writeReadRestriction(other.m_writeReadRestriction), m_writeServersJson(other.m_writeServersJson), m_writeMatchmakingTargetSessionConstants(other.m_writeMatchmakingTargetSessionConstants), m_writeSessionCustomPropertiesJson(other.m_writeSessionCustomPropertiesJson), m_writeConstants(other.m_writeConstants), m_memberRequestIndex(other.m_memberRequestIndex) { m_joiningSession.exchange(other.m_joiningSession); for (auto& candidate : m_serverConnectionStringCandidates) { candidate = Make(candidate); } for (auto& keyword : m_keywords) { keyword = Make(keyword); } m_sessionProperties.Keywords = m_keywords.data(); m_sessionProperties.SessionOwnerMemberIds = m_sessionOwnerIndices.data(); m_sessionProperties.TurnCollection = m_turnCollection.data(); m_sessionProperties.ServerConnectionStringCandidates = m_serverConnectionStringCandidates.data(); m_sessionProperties.MatchmakingServerConnectionString = m_matchmakingServerConnectionString.data(); m_sessionProperties.MatchmakingTargetSessionConstantsJson = m_matchmakingTargetSessionConstantsJson.data(); m_sessionProperties.SessionCustomPropertiesJson = m_sessionCustomPropertiesJson.data(); for (auto& member : m_members) { auto internalMember = Make(*static_cast(member.Internal)); member.Internal = internalMember; MultiplayerSessionMember::SetExternalMemberPointer(member); if (member.Xuid == m_xuid) { m_memberCurrentUser = &member; } } if (other.m_matchmakingServer != nullptr) { m_matchmakingServer = MakeShared(*other.m_matchmakingServer); m_matchmakingServer->StatusDetails = m_matchmakingStatusDetails.data(); } m_sessionConstants.InitiatorXuids = m_initiatorXuids.data(); m_sessionConstants.InitiatorXuidsCount = static_cast(m_initiatorXuids.size()); if (m_sessionConstants.MemberInitialization != nullptr) { m_sessionConstants.MemberInitialization = &m_memberInitialization; } m_sessionConstants.CustomJson = m_constantsCustomJson.data(); m_sessionConstants.SessionCloudComputePackageConstantsJson = m_constantsCloudComputePackageJson.data(); m_sessionConstants.MeasurementServerAddressesJson = m_constantsMeasurementServerAddressesJson.data(); } XblMultiplayerSession::~XblMultiplayerSession() { for (auto candidate : m_serverConnectionStringCandidates) { Delete(candidate); } for (auto keyword : m_keywords) { Delete(keyword); } for (auto& member : m_members) { Delete(static_cast(member.Internal)); } } std::shared_ptr XblMultiplayerSession::GetSharedThis() { return shared_from_this(); } void XblMultiplayerSession::Initialize() { m_info = XblMultiplayerSessionInfo{}; m_initialization = XblMultiplayerSessionInitializationInfo{}; m_sessionProperties = XblMultiplayerSessionProperties{}; m_sessionSubscriptionGuid = utils::create_guid(true); m_sessionCustomPropertiesJson = "{}"; m_sessionProperties.SessionCustomPropertiesJson = m_sessionCustomPropertiesJson.data(); m_constantsCustomJson = ""; m_sessionConstants.CustomJson = m_constantsCustomJson.data(); m_constantsCloudComputePackageJson = ""; m_sessionConstants.SessionCloudComputePackageConstantsJson = m_constantsCloudComputePackageJson.data(); m_constantsMeasurementServerAddressesJson = ""; m_sessionConstants.MeasurementServerAddressesJson = m_constantsMeasurementServerAddressesJson.data(); m_matchmakingTargetSessionConstantsJson = ""; m_sessionProperties.MatchmakingTargetSessionConstantsJson = m_matchmakingTargetSessionConstantsJson.data(); m_matchmakingServerConnectionString = ""; m_sessionProperties.MatchmakingServerConnectionString = m_matchmakingServerConnectionString.data(); // Skipping this one cause m_matchmakingServer is optional and nullptr // m_matchmakingServer->StatusDetails = m_matchmakingStatusDetails.data(); // TODO remove this after fixing member management m_members.reserve(100); } const xsapi_internal_string XblMultiplayerSession::ETag() const { return m_eTag; } const xsapi_internal_string& XblMultiplayerSession::ETagUnsafe() const { return m_eTag; } const XblMultiplayerSessionInfo& XblMultiplayerSession::SessionInfo() const { return m_info; } const XblMultiplayerSessionInitializationInfo& XblMultiplayerSession::InitializationInfo() const { return m_initialization; } time_t XblMultiplayerSession::TimeOfSession() const { return m_sessionRetrievedTime; } const XblMultiplayerSessionReference& XblMultiplayerSession::SessionReference() const { return m_sessionReference; } const xsapi_internal_vector& XblMultiplayerSession::HostCandidates() const { return m_hostCandidates; } const XblMultiplayerSessionConstants& XblMultiplayerSession::SessionConstantsUnsafe() const { return m_sessionConstants; } const XblMultiplayerSessionProperties& XblMultiplayerSession::SessionPropertiesUnsafe() const { return m_sessionProperties; } void XblMultiplayerSession::StateLock() const { m_lockSession.lock(); } void XblMultiplayerSession::StateUnlock() const { m_lockSession.unlock(); } const RoleTypes& XblMultiplayerSession::RoleTypesUnsafe() const { return m_roleTypes; } const xsapi_internal_vector& XblMultiplayerSession::MembersUnsafe() const { return m_members; } const xsapi_internal_vector& XblMultiplayerSession::ServerConnectionStringCandidatesUnsafe() const { return m_serverConnectionStringCandidates; } const xsapi_internal_vector& XblMultiplayerSession::TurnCollectionUnsafe() const { return m_turnCollection; } const xsapi_internal_vector& XblMultiplayerSession::KeywordsUnsafe() const { return m_keywords; } const XblMultiplayerSessionMember* XblMultiplayerSession::CurrentUserUnsafe() const { return m_memberCurrentUser; } MultiplayerSessionMember* XblMultiplayerSession::CurrentUserInternalUnsafe() const { if (m_memberCurrentUser != nullptr) { return MultiplayerSessionMember::Get(m_memberCurrentUser); } return nullptr; } const XblMultiplayerSessionMember* XblMultiplayerSession::GetMemberUnsafe(uint32_t memberId) const { const XblMultiplayerSessionMember* out = nullptr; for (const auto& member : m_members) { if (member.MemberId == memberId) { out = &member; } } return out; } uint32_t XblMultiplayerSession::MembersAccepted() const { return m_membersAccepted; } const xsapi_internal_string XblMultiplayerSession::RawServersJson() const { return m_serversJson; } const xsapi_internal_string& XblMultiplayerSession::RawServersJsonUnsafe() const { return m_serversJson; } std::shared_ptr XblMultiplayerSession::MatchmakingServer() const { return m_matchmakingServer; } XblWriteSessionStatus XblMultiplayerSession::WriteStatus() const { return m_writeSessionStatus; } HRESULT XblMultiplayerSession::DeserializationError() const { return m_deserializationError; } HRESULT XblMultiplayerSession::SetServersJson( _In_ const xsapi_internal_string& serversJson ) { std::lock_guard lock{ m_lockSession }; auto hr = JsonUtils::ValidateJson(serversJson.data()); if (SUCCEEDED(hr)) { m_serversJson = serversJson; m_writeServersJson = true; } return hr; } void XblMultiplayerSession::SetWriteSessionStatus( int32_t httpStatusCode ) { std::lock_guard lock{ m_lockSession }; m_writeSessionStatus = ConvertHttpStatusToWriteSessionStatus(httpStatusCode); } HRESULT XblMultiplayerSession::AddMemberReservation( _In_ uint64_t xuid, _In_opt_z_ const char* memberCustomConstantsJson, _In_ bool initializeRequested ) { if (memberCustomConstantsJson) { auto hr = JsonUtils::ValidateJson(memberCustomConstantsJson); if (FAILED(hr)) { return hr; } } std::lock_guard lock{ m_lockSession }; xsapi_internal_stringstream memberId; memberId << "reserve_"; memberId << m_memberRequestIndex++; m_members.push_back(MultiplayerSessionMember::Construct(false, memberId.str(), xuid, memberCustomConstantsJson, initializeRequested)); MultiplayerSessionMember::SetExternalMemberPointer(m_members.back()); return S_OK; } HRESULT XblMultiplayerSession::Join( _In_opt_z_ const char* memberCustomConstantsJson, _In_ bool initializeRequested, _In_ bool joinWithActiveStatus ) { auto alreadyJoined = m_joiningSession.exchange(true); if (alreadyJoined) { return E_UNEXPECTED; } if (memberCustomConstantsJson) { auto hr = JsonUtils::ValidateJson(memberCustomConstantsJson); if (FAILED(hr)) { return hr; } } std::lock_guard lock{ m_lockSession }; m_members.push_back(MultiplayerSessionMember::Construct(true, "me", m_xuid, memberCustomConstantsJson, initializeRequested)); m_memberCurrentUser = &m_members.back(); MultiplayerSessionMember::SetExternalMemberPointer(m_members.back()); if (joinWithActiveStatus) { MultiplayerSessionMember::Get(m_memberCurrentUser)->SetStatus(XblMultiplayerSessionMemberStatus::Active); } return S_OK; } void XblMultiplayerSession::SetVisibility( _In_ XblMultiplayerSessionVisibility visibility ) { std::lock_guard lock{ m_lockSession }; m_sessionConstants.Visibility = visibility; m_writeConstants = true; } void XblMultiplayerSession::SetMaxMembersInSession( _In_ uint32_t maxMembersInSession ) { std::lock_guard lock{ m_lockSession }; m_sessionConstants.MaxMembersInSession = maxMembersInSession; m_writeConstants = true; } HRESULT XblMultiplayerSession::SetTimeouts( _In_ uint64_t memberReservedTimeout, _In_ uint64_t memberInactiveTimeout, _In_ uint64_t memberReadyTimeout, _In_ uint64_t sessionEmptyTimeout ) { // Call set_timeouts/SetTimeouts before writing a new session to the service if (!m_newSession) { return E_UNEXPECTED; } std::lock_guard lock{ m_lockSession }; m_sessionConstants.MemberReservedTimeout = memberReservedTimeout; m_sessionConstants.MemberInactiveTimeout = memberInactiveTimeout; m_sessionConstants.MemberReadyTimeout = memberReadyTimeout; m_sessionConstants.SessionEmptyTimeout = sessionEmptyTimeout; m_writeTimeouts = true; m_writeConstants = true; return S_OK; } HRESULT XblMultiplayerSession::SetQosConnectivityMetrics( _In_ bool enableLatencyMetric, _In_ bool enableBandwidthDownMetric, _In_ bool enableBandwidthUpMetric, _In_ bool enableCustomMetric ) { // Call set_quality_of_service_connectivity_metrics/SetQualityOfServiceConnectivityMetrics // before writing a new session to the service if (!m_newSession) { return E_UNEXPECTED; } std::lock_guard lock{ m_lockSession }; m_sessionConstants.EnableMetricsLatency = enableLatencyMetric; m_sessionConstants.EnableMetricsBandwidthUp = enableBandwidthUpMetric; m_sessionConstants.EnableMetricsBandwidthDown = enableBandwidthDownMetric; m_sessionConstants.EnableMetricsCustom = enableCustomMetric; m_writeQosConnectivityMetrics = true; m_writeConstants = true; return S_OK; } HRESULT XblMultiplayerSession::SetMemberInitialization( _In_ const XblMultiplayerMemberInitialization& memberInitialization ) { // Call set_member_initialization/SetMemberInitialization before writing a new session to the service if (!m_newSession) { return E_UNEXPECTED; } std::lock_guard lock{ m_lockSession }; m_memberInitialization = memberInitialization; m_sessionConstants.MemberInitialization = &m_memberInitialization; m_writeMemberInitialization = true; m_writeConstants = true; return S_OK; } HRESULT XblMultiplayerSession::SetPeerToPeerRequirements( _In_ const XblMultiplayerPeerToPeerRequirements& requirements ) { // Call set_peer_to_peer_requirements/SetPeerToPeerRequirements before writing a new session to the service if (!m_newSession) { return E_UNEXPECTED; } std::lock_guard lock{ m_lockSession }; m_sessionConstants.PeerToPeerRequirements = requirements; m_writePeerToPeerRequirements = true; m_writeConstants = true; return S_OK; } HRESULT XblMultiplayerSession::SetPeerToHostRequirements( _In_ const XblMultiplayerPeerToHostRequirements& requirements ) { // Call set_peer_to_host_requirements/SetPeerToHostRequirements before writing a new session to the service if (!m_newSession) { return E_UNEXPECTED; } std::lock_guard lock{ m_lockSession }; m_sessionConstants.PeerToHostRequirements = requirements; m_writePeerToHostRequirements = true; m_writeConstants = true; return S_OK; } HRESULT XblMultiplayerSession::SetMeasurementServerAddresses( _In_ const xsapi_internal_string& measurementServerAddresses ) { if (!m_newSession) { return E_UNEXPECTED; } auto hr = JsonUtils::ValidateJson(measurementServerAddresses.data()); if (SUCCEEDED(hr)) { std::lock_guard lock{ m_lockSession }; m_constantsMeasurementServerAddressesJson = measurementServerAddresses; m_sessionConstants.MeasurementServerAddressesJson = m_constantsMeasurementServerAddressesJson.data(); m_writeMeasurementServerAddresses = true; m_writeConstants = true; } return hr; } HRESULT XblMultiplayerSession::SetSessionCapabilities( _In_ const XblMultiplayerSessionCapabilities& capabilities ) { // Call set_session_capabilities/SetSessionCapabilities before writing a new session to the service if (!m_newSession) { return E_UNEXPECTED; } std::lock_guard lock{ m_lockSession }; m_sessionConstants.SessionCapabilities = capabilities; m_writeConstants = true; return S_OK; } HRESULT XblMultiplayerSession::SetCloudComputePackageJson( _In_ const xsapi_internal_string& sessionCloudComputePackageConstantsJson ) { // Call set_cloud_compute_package_json/SetCloudComputePackageJson before writing a new session to the service if (!m_newSession) { return E_UNEXPECTED; } std::lock_guard lock{ m_lockSession }; m_constantsCloudComputePackageJson = sessionCloudComputePackageConstantsJson; m_sessionConstants.SessionCloudComputePackageConstantsJson = m_constantsCloudComputePackageJson.data(); m_writeConstants = true; return S_OK; } void XblMultiplayerSession::SetInitializationStatus( _In_ bool initializationSucceeded ) { std::lock_guard lock{ m_lockSession }; m_writeInitializationStatus = true; m_initializationSucceeded = initializationSucceeded; } void XblMultiplayerSession::SetHostDeviceToken( _In_ const XblDeviceToken hostDeviceToken ) { std::lock_guard lock{ m_lockSession }; utils::strcpy(m_sessionProperties.HostDeviceToken.Value, sizeof(m_sessionProperties.HostDeviceToken.Value), hostDeviceToken.Value); m_writeHostDeviceToken = true; } void XblMultiplayerSession::SetHostDeviceToken( _In_ const xsapi_internal_string& hostDeviceToken ) { std::lock_guard lock{ m_lockSession }; utils::strcpy(m_sessionProperties.HostDeviceToken.Value, sizeof(m_sessionProperties.HostDeviceToken.Value), hostDeviceToken.data()); m_writeHostDeviceToken = true; } void XblMultiplayerSession::SetMatchmakingServerConnectionPath( _In_ const xsapi_internal_string& serverConnectionPath ) { std::lock_guard lock{ m_lockSession }; m_matchmakingServerConnectionString = serverConnectionPath; m_sessionProperties.MatchmakingServerConnectionString = m_matchmakingServerConnectionString.data(); m_writeMatchmakingServerConnectionPath = true; } void XblMultiplayerSession::SetMatchmakingResubmit( _In_ bool matchResubmit ) { std::lock_guard lock{ m_lockSession }; m_sessionProperties.MatchmakingResubmit = matchResubmit; m_writeMatchmakingResubmit = true; } void XblMultiplayerSession::SetClosed( _In_ bool closed ) { std::lock_guard lock{ m_lockSession }; m_sessionProperties.Closed = closed; m_writeClosed = true; } void XblMultiplayerSession::SetLocked( _In_ bool locked ) { std::lock_guard lock{ m_lockSession }; m_sessionProperties.Locked = locked; m_writeLocked = true; } void XblMultiplayerSession::SetAllocateCloudCompute( _In_ bool allocateCloudCompute ) { std::lock_guard lock{ m_lockSession }; m_sessionProperties.AllocateCloudCompute = allocateCloudCompute; m_writeAllocateCloudCompute = true; } HRESULT XblMultiplayerSession::SetServerConnectionStringCandidates( _In_reads_(serverConnectionStringCandidatesCount) const char** serverConnectionStringCandidates, _In_ size_t serverConnectionStringCandidatesCount ) { RETURN_HR_INVALIDARGUMENT_IF(serverConnectionStringCandidates == nullptr) std::lock_guard lock{ m_lockSession }; for (auto candidate : m_serverConnectionStringCandidates) { Delete(candidate); } m_serverConnectionStringCandidates.clear(); for (uint32_t i = 0; i < serverConnectionStringCandidatesCount; ++i) { m_serverConnectionStringCandidates.push_back(Make(serverConnectionStringCandidates[i])); } m_sessionProperties.ServerConnectionStringCandidates = m_serverConnectionStringCandidates.data(); m_sessionProperties.ServerConnectionStringCandidatesCount = m_serverConnectionStringCandidates.size(); m_writeServerConnectionStringCandidates = true; return S_OK; } HRESULT XblMultiplayerSession::SetSessionChangeSubscription( _In_ XblMultiplayerSessionChangeTypes changeTypes ) { if(m_memberCurrentUser == nullptr) { return E_UNEXPECTED; } std::lock_guard lock{ m_lockSession }; MultiplayerSessionMember::Get(m_memberCurrentUser)->SetSessionChangeSubscription(changeTypes, m_sessionSubscriptionGuid); return S_OK; } HRESULT XblMultiplayerSession::Leave() { std::lock_guard lock{ m_lockSession }; // Failed trying to leave and join the session at the same time if (m_joiningSession) { return E_UNEXPECTED; } for (auto iter = m_members.begin(); iter != m_members.end(); iter++) { if (iter->IsCurrentUser) { Delete(static_cast(iter->Internal)); m_members.erase(iter); m_memberCurrentUser = nullptr; break; } } m_leaveSession = true; return S_OK; } HRESULT XblMultiplayerSession::SetCurrentUserStatus( _In_ XblMultiplayerSessionMemberStatus status ) { std::lock_guard lock{ m_lockSession }; // Must join the session first before calling SetCurrentUserStatus // Can not set member to ready // Can not set member to reserved. Use AddMemberReservation instead if (m_memberCurrentUser == nullptr || status == XblMultiplayerSessionMemberStatus::Ready || status == XblMultiplayerSessionMemberStatus::Reserved) { return E_UNEXPECTED; } return MultiplayerSessionMember::Get(m_memberCurrentUser)->SetStatus(status); } HRESULT XblMultiplayerSession::SetCurrentUserSecureDeviceAddressBase64( _In_ const xsapi_internal_string& value ) { std::lock_guard lock{ m_lockSession }; // Must join the session first before calling SetCurrentUserSecureDeviceAddressBase64 if (m_memberCurrentUser == nullptr) { return E_UNEXPECTED; } MultiplayerSessionMember::Get(m_memberCurrentUser)->SetSecureDeviceBaseAddress64(value); return S_OK; } HRESULT XblMultiplayerSession::SetCurrentUserRoleInfo( _In_ const xsapi_internal_vector& roles ) { std::lock_guard lock{ m_lockSession }; // Must join the session first before calling SetCurrentUserRoleInfo if (m_memberCurrentUser == nullptr) { return E_UNEXPECTED; } MultiplayerSessionMember::Get(m_memberCurrentUser)->SetRoles(roles); return S_OK; } HRESULT XblMultiplayerSession::SetMutableRoleSettings( _In_ String&& roleTypeName, _In_ String&& roleName, _In_opt_ uint32_t* maxCount, _In_opt_ uint32_t* targetCount ) { std::lock_guard lock{ m_lockSession }; auto hr = m_roleTypes.SetRoleSettings(std::move(roleTypeName), std::move(roleName), maxCount, targetCount); if (SUCCEEDED(hr)) { m_writeRoleTypes = true; } return hr; } HRESULT XblMultiplayerSession::SetCurrentUserMembersInGroup( _In_ const xsapi_internal_vector& membersInGroup ) { std::lock_guard lock{ m_lockSession }; // Must join the session first before calling SetCurrentUserMembersInGroup if (m_memberCurrentUser == nullptr) { return E_UNEXPECTED; } MultiplayerSessionMember::Get(m_memberCurrentUser)->SetMembersInGroup(membersInGroup); return S_OK; } HRESULT XblMultiplayerSession::SetCurrentUserGroups( _In_reads_(groupsCount) const char** groups, _In_ size_t groupsCount ) { std::lock_guard lock{ m_lockSession }; // Must join the session first before calling SetCurrentUserGroups if (m_memberCurrentUser == nullptr) { return E_UNEXPECTED; } MultiplayerSessionMember::Get(m_memberCurrentUser)->SetGroups(groups, groupsCount); return S_OK; } HRESULT XblMultiplayerSession::SetCurrentUserEncounters( _In_reads_(encountersCount) const char** encounters, _In_ size_t encountersCount ) { std::lock_guard lock{ m_lockSession }; // Must join the session first before calling SetCurrentUserEncounters if (m_memberCurrentUser == nullptr) { return E_UNEXPECTED; } MultiplayerSessionMember::Get(m_memberCurrentUser)->SetEncounters(encounters, encountersCount); return S_OK; } HRESULT XblMultiplayerSession::SetCurrentUserServerMeasurementsJson( _In_ const xsapi_internal_string& serverMeasurementsJson ) { std::lock_guard lock{ m_lockSession }; // Must join the session first before calling SetCurrentUserServerMeasurementsJson if (m_memberCurrentUser == nullptr) { return E_UNEXPECTED; } return MultiplayerSessionMember::Get(m_memberCurrentUser)->SetServerMeasurementsJson(serverMeasurementsJson); } HRESULT XblMultiplayerSession::SetCurrentUserQosMeasurementsJson( _In_ const xsapi_internal_string& serverMeasurementsJson ) { std::lock_guard lock{ m_lockSession }; // Must join the session first before calling SetCurrentUserQosMeasurementsJson if (m_memberCurrentUser == nullptr) { return E_UNEXPECTED; } return MultiplayerSessionMember::Get(m_memberCurrentUser)->SetQosMeasurementsJson(serverMeasurementsJson); } HRESULT XblMultiplayerSession::SetCurrentUserMemberCustomPropertyJson( _In_ const xsapi_internal_string& name, _In_ const JsonValue& valueJson ) { if (name.empty()) { return E_INVALIDARG; } std::lock_guard lock{ m_lockSession }; // Must join the session first before calling SetCurrentUserMemberCustomPropertyJson if (m_memberCurrentUser == nullptr) { return E_UNEXPECTED; } return MultiplayerSessionMember::Get(m_memberCurrentUser)->SetCustomPropertyJson(name, valueJson); } HRESULT XblMultiplayerSession::DeleteCurrentUserMemberCustomPropertyJson( _In_ const xsapi_internal_string& name ) { if (name.empty()) { return E_INVALIDARG; } std::lock_guard lock{ m_lockSession }; // Must join the session first before calling DeleteCurrentUserMemberCustomPropertyJson if (m_memberCurrentUser == nullptr) { return E_UNEXPECTED; } MultiplayerSessionMember::Get(m_memberCurrentUser)->DeleteCustomPropertyJson(name); return S_OK; } HRESULT XblMultiplayerSession::SetMatchmakingTargetSessionConstantsJson( _In_ const xsapi_internal_string& matchmakingTargetSessionConstantsJson ) { std::lock_guard lock{ m_lockSession }; auto hr = JsonUtils::ValidateJson(matchmakingTargetSessionConstantsJson.data()); if (SUCCEEDED(hr)) { m_matchmakingTargetSessionConstantsJson = matchmakingTargetSessionConstantsJson; m_sessionProperties.MatchmakingTargetSessionConstantsJson = m_matchmakingTargetSessionConstantsJson.data(); m_writeMatchmakingTargetSessionConstants = true; } return hr; } HRESULT XblMultiplayerSession::SetSessionCustomPropertyJson( _In_ const xsapi_internal_string& name, _In_ const JsonValue& customProperty ) { if (name.empty()) { return E_INVALIDARG; } std::lock_guard lock{ m_lockSession }; JsonDocument customProperties; customProperties.Parse(m_sessionCustomPropertiesJson.data()); auto hr = JsonUtils::SetMember(customProperties, name, customProperty); XSAPI_ASSERT(SUCCEEDED(hr)); if (SUCCEEDED(hr)) { m_sessionCustomPropertiesJson = JsonUtils::SerializeJson(customProperties); m_sessionProperties.SessionCustomPropertiesJson = m_sessionCustomPropertiesJson.data(); m_writeSessionCustomPropertiesJson = true; } return hr; } HRESULT XblMultiplayerSession::DeleteSessionCustomPropertyJson( _In_ const xsapi_internal_string& name ) { return SetSessionCustomPropertyJson(name, JsonValue()); } HRESULT XblMultiplayerSession::SetKeywords( _In_ const char** keywords, _In_ size_t keywordsCount ) { RETURN_HR_INVALIDARGUMENT_IF(keywords == nullptr) std::lock_guard lock{ m_lockSession }; for (auto keyword : m_keywords) { Delete(keyword); } m_keywords.clear(); for (uint32_t i = 0; i < keywordsCount; ++i) { m_keywords.push_back(Make(keywords[i])); } m_sessionProperties.Keywords = m_keywords.data(); m_sessionProperties.KeywordCount = m_keywords.size(); m_writePropertiesKeywords = true; return S_OK; } void XblMultiplayerSession::SetJoinRestriction( _In_ XblMultiplayerSessionRestriction joinRestriction ) { std::lock_guard lock{ m_lockSession }; m_sessionProperties.JoinRestriction = joinRestriction; m_writeJoinRestriction = true; } void XblMultiplayerSession::SetReadRestriction( _In_ XblMultiplayerSessionRestriction readRestriction ) { std::lock_guard lock{ m_lockSession }; m_sessionProperties.ReadRestriction = readRestriction; m_writeReadRestriction = true; } XblMultiplayerMetrics XblMultiplayerSession::ConvertStringToMultiplayerHostSelectionMetric( _In_ const xsapi_internal_string& value ) { if (value.empty()) { return XblMultiplayerMetrics::Latency; } else if (utils::str_icmp_internal(value, "bandwidthUp") == 0) { return XblMultiplayerMetrics::BandwidthUp; } else if (utils::str_icmp_internal(value, "bandwidthDown") == 0) { return XblMultiplayerMetrics::BandwidthDown; } else if (utils::str_icmp_internal(value, "bandwidth") == 0) { return XblMultiplayerMetrics::Bandwidth; } else if (utils::str_icmp_internal(value, "latency") == 0) { return XblMultiplayerMetrics::Latency; } return XblMultiplayerMetrics::Unknown; } xsapi_internal_string XblMultiplayerSession::ConvertMultiplayerHostSelectionMetricToString( _In_ XblMultiplayerMetrics multiplayMetric ) { switch (multiplayMetric) { case XblMultiplayerMetrics::Unknown: return "unknown"; case XblMultiplayerMetrics::BandwidthUp: return "bandwidthUp"; case XblMultiplayerMetrics::BandwidthDown: return "bandwidthDown"; case XblMultiplayerMetrics::Bandwidth: return "bandwidth"; case XblMultiplayerMetrics::Latency: return "latency"; default: { XSAPI_ASSERT(false); return "unknown"; }; } } XblMultiplayerInitializationStage XblMultiplayerSession::ConvertStringToMultiplayerInitializationStage( _In_ const xsapi_internal_string& value ) { if (value.empty()) { return XblMultiplayerInitializationStage::None; } else if (utils::str_icmp_internal(value, "joining") == 0) { return XblMultiplayerInitializationStage::Joining; } else if (utils::str_icmp_internal(value, "failed") == 0) { return XblMultiplayerInitializationStage::Failed; } else if (utils::str_icmp_internal(value, "evaluating") == 0) { return XblMultiplayerInitializationStage::Evaluating; } else if (utils::str_icmp_internal(value, "measuring") == 0) { return XblMultiplayerInitializationStage::Measuring; } return XblMultiplayerInitializationStage::Unknown; } XblMatchmakingStatus XblMultiplayerSession::ConvertStringToMatchmakingStatus( _In_ const xsapi_internal_string& value ) { XSAPI_ASSERT(!value.empty()); if (utils::str_icmp_internal(value, "searching") == 0) { return XblMatchmakingStatus::Searching; } else if (utils::str_icmp_internal(value, "expired") == 0) { return XblMatchmakingStatus::Expired; } else if (utils::str_icmp_internal(value, "found") == 0) { return XblMatchmakingStatus::Found; } else if (utils::str_icmp_internal(value, "canceled") == 0) { return XblMatchmakingStatus::Canceled; } return XblMatchmakingStatus::Unknown; } xsapi_internal_string XblMultiplayerSession::ConvertMatchmakingStatusToString( _In_ XblMatchmakingStatus matchmakingStatus ) { switch (matchmakingStatus) { case XblMatchmakingStatus::Unknown: return "unknown"; case XblMatchmakingStatus::Searching: return "searching"; case XblMatchmakingStatus::Expired: return "expired"; case XblMatchmakingStatus::Found: return "found"; case XblMatchmakingStatus::Canceled: return "canceled"; default: { XSAPI_ASSERT(false); return "unknown"; } } } XblWriteSessionStatus XblMultiplayerSession::ConvertHttpStatusToWriteSessionStatus( _In_ int32_t httpStatusCode ) { switch (httpStatusCode) { case 200: return XblWriteSessionStatus::Updated; case 201: return XblWriteSessionStatus::Created; case 204: return XblWriteSessionStatus::SessionDeleted; case 401: return XblWriteSessionStatus::AccessDenied; case 404: return XblWriteSessionStatus::HandleNotFound; case 409: return XblWriteSessionStatus::Conflict; case 412: return XblWriteSessionStatus::OutOfSync; default: return XblWriteSessionStatus::Unknown; } } HRESULT XblMultiplayerSession::DeserializeMembers( _In_ const JsonValue& json ) { if (!json.IsNull() && json.IsObject()) { uint32_t first = 0; uint32_t last = 0; if (json.HasMember("membersInfo")) { const JsonValue& membersInfo = json["membersInfo"]; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonInt(membersInfo, "first", first, true)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonInt(membersInfo, "next", last, true)); } else { //required return WEB_E_INVALID_JSON_STRING; } for (uint32_t current = first; current != last;) { XblMultiplayerSessionMember member; if (json.HasMember("members")) { const JsonValue& membersJson = json["members"]; // In large sessions the member json only contains "me" member auto currentIdString = m_sessionConstants.SessionCapabilities.Large ? "me" : utils::uint32_to_internal_string(current); if (membersJson.IsObject() && membersJson.HasMember(currentIdString.c_str())) { const JsonValue& memberJson = membersJson[currentIdString.c_str()]; member = MultiplayerSessionMember::Deserialize(memberJson).Payload(); member.MemberId = current; if (member.Xuid == m_xuid) { member.IsCurrentUser = true; } m_members.push_back(std::move(member)); if (m_members.back().IsCurrentUser) { m_memberCurrentUser = &m_members.back(); } MultiplayerSessionMember::SetExternalMemberPointer(m_members.back()); if (m_sessionConstants.SessionCapabilities.Large) { break; } current = last; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonInt(memberJson, "next", current, false)); } else { //required return WEB_E_INVALID_JSON_STRING; } } } } return S_OK; } XblMultiplayerSessionChangeTypes XblMultiplayerSession::CompareMultiplayerSessions( _In_ std::shared_ptr other ) { XblMultiplayerSessionChangeTypes currentType = XblMultiplayerSessionChangeTypes::None; std::lock_guard lock{ m_lockSession }; if (utils::str_icmp_internal(m_sessionProperties.HostDeviceToken.Value, other->m_sessionProperties.HostDeviceToken.Value) != 0) { currentType |= XblMultiplayerSessionChangeTypes::HostDeviceTokenChange; } if (m_initialization.Stage != other->m_initialization.Stage) { currentType |= XblMultiplayerSessionChangeTypes::InitializationStateChange; } if ((m_matchmakingServer == nullptr) != (other->m_matchmakingServer == nullptr)) { currentType |= XblMultiplayerSessionChangeTypes::MatchmakingStatusChange; } else if (m_matchmakingServer != nullptr && ((m_matchmakingServer->Status != other->m_matchmakingServer->Status) || !(m_matchmakingServer->TargetSessionRef == other->m_matchmakingServer->TargetSessionRef)) ) { currentType |= XblMultiplayerSessionChangeTypes::MatchmakingStatusChange; } bool hasMemberChanged = false; bool memberStatusChanged = false; bool memberCustomPropertyChanged = false; if (m_members.size() != other->m_members.size()) { hasMemberChanged = true; } for (const auto& currentMember : m_members) { bool isMemberFound = false; for (const auto& olderSessionMember : other->m_members) { if (currentMember.Xuid == olderSessionMember.Xuid) { isMemberFound = true; if (currentMember.Status != olderSessionMember.Status) { memberStatusChanged = true; } MultiplayerSessionMemberReadLockGuard memberSafe(MultiplayerSessionMember::Get(¤tMember)); MultiplayerSessionMemberReadLockGuard olderSessionMemberSafe(MultiplayerSessionMember::Get(&olderSessionMember)); if (utils::str_icmp(JsonUtils::SerializeJson(memberSafe.CustomPropertiesJson()).c_str(), JsonUtils::SerializeJson(olderSessionMemberSafe.CustomPropertiesJson()).c_str()) != 0) { memberCustomPropertyChanged = true; } } } if (!isMemberFound) { hasMemberChanged = true; } if (memberStatusChanged && hasMemberChanged && memberCustomPropertyChanged) { break; } } if (hasMemberChanged) { currentType |= XblMultiplayerSessionChangeTypes::MemberListChange; } if (memberStatusChanged) { currentType |= XblMultiplayerSessionChangeTypes::MemberStatusChange; } if (memberCustomPropertyChanged) { currentType |= XblMultiplayerSessionChangeTypes::MemberCustomPropertyChange; } XblMultiplayerSessionReadLockGuard otherSafe(other); if (m_sessionProperties.Closed != other->m_sessionProperties.Closed || m_sessionProperties.Locked != other->m_sessionProperties.Locked || m_sessionProperties.JoinRestriction != other->m_sessionProperties.JoinRestriction || (m_members.size() == m_sessionConstants.MaxMembersInSession) != (otherSafe.Members().size() == other->m_sessionConstants.MaxMembersInSession) // if the session is open again or closed again because the max member has changed ) { currentType |= XblMultiplayerSessionChangeTypes::SessionJoinabilityChange; } if (utils::str_icmp_internal(m_sessionCustomPropertiesJson, other->m_sessionCustomPropertiesJson) != 0) { currentType |= XblMultiplayerSessionChangeTypes::CustomPropertyChange; } return static_cast(currentType); } HRESULT XblMultiplayerSession::Deserialize( _In_ const JsonValue& json ) { RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonInt(json, "contractVersion", m_info.ContractVersion)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonStringToCharArray(json, "correlationId", m_info.CorrelationId, sizeof(m_info.CorrelationId))); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonStringToCharArray(json, "searchHandle", m_info.SearchHandleId, sizeof(m_info.SearchHandleId))); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonStringToCharArray(json, "branch", m_info.Branch, sizeof(m_info.Branch))); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonUInt64(json, "changeNumber", m_info.ChangeNumber, false)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonTimeT(json, "startTime", m_info.StartTime)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonTimeT(json, "nextTimer", m_info.NextTimer)); if (json.IsObject() && json.HasMember("constants")) { DeserializeSessionConstants(json["constants"]); } else { //required DeserializeSessionConstants(JsonValue()); return WEB_E_INVALID_JSON_STRING; } if (json.IsObject() && json.HasMember("initializing")) { const JsonValue& initializingJson = json["initializing"]; xsapi_internal_string stage; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(initializingJson, "stage", stage)); m_initialization.Stage = ConvertStringToMultiplayerInitializationStage(stage); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonTimeT(initializingJson, "stageStartTime", m_initialization.StageStartTime)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonInt(initializingJson, "episode", m_initialization.Episode)); } else { m_initialization.Stage = XblMultiplayerInitializationStage::None; m_initialization.StageStartTime = utils::time_t_from_datetime(xbox::services::datetime()); m_initialization.Episode = 0; } RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonVector([](const JsonValue& json) { if (!json.IsString()) { return Result(WEB_E_INVALID_JSON_STRING); } XblDeviceToken token{}; utils::strcpy(token.Value, sizeof(token.Value), json.GetString()); return Result(token); }, json, "hostCandidates", m_hostCandidates, false)); if (json.IsObject() && json.HasMember("roleTypes")) { auto roleTypesResult{ RoleTypes::Deserialize(json["roleTypes"]) }; RETURN_HR_IF_FAILED(roleTypesResult.Hresult()); m_roleTypes = roleTypesResult.ExtractPayload(); } DeserializeMembers(json); if (json.IsObject() && json.HasMember("properties")) { DeserializeSessionProperties(json["properties"]); } else { //required DeserializeSessionProperties(JsonValue()); return WEB_E_INVALID_JSON_STRING; } if (json.IsObject() && json.HasMember("membersInfo")) { const JsonValue& memberInfoJson = json["membersInfo"]; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonInt(memberInfoJson, "accepted", m_membersAccepted)); } else { m_membersAccepted = 0; } if (json.IsObject() && json.HasMember("servers")) { const JsonValue& serversJson = json["servers"]; if (serversJson.IsObject()) { if (serversJson.HasMember("matchmaking")) { const JsonValue& serversMatchmakingJson = serversJson["matchmaking"]; if (!serversMatchmakingJson.IsNull()) { DeserializeMatchmakingServer(serversMatchmakingJson); } } } m_serversJson = JsonUtils::SerializeJson(serversJson); } else { m_serversJson = ""; } return S_OK; } void XblMultiplayerSession::SerializeSessionProperties(_Out_ JsonValue& jsonProperties, _In_ JsonDocument::AllocatorType& allocator) { jsonProperties.SetObject(); JsonValue jsonPropertiesSystem(rapidjson::kObjectType); if (m_writePropertiesKeywords) { JsonValue keywordsJson; JsonUtils::SerializeVector(JsonUtils::JsonUtf8Serializer, m_keywords, keywordsJson, allocator); jsonPropertiesSystem.AddMember("keywords", keywordsJson, allocator); } if (m_writePropertiesTurns) { JsonValue turnJson; JsonUtils::SerializeVector(JsonUtils::JsonIntSerializer, m_turnCollection, turnJson, allocator); jsonPropertiesSystem.AddMember("turn", turnJson, allocator); } if (m_writeJoinRestriction && m_sessionProperties.JoinRestriction != XblMultiplayerSessionRestriction::Unknown) { auto joinRestrictionToString = Serializers::StringFromMultiplayerSessionRestriction(m_sessionProperties.JoinRestriction); jsonPropertiesSystem.AddMember("joinRestriction", JsonValue(joinRestrictionToString.c_str(), allocator).Move(), allocator); } if (m_writeReadRestriction && m_sessionProperties.ReadRestriction != XblMultiplayerSessionRestriction::Unknown) { auto readRestrictionToString = Serializers::StringFromMultiplayerSessionRestriction(m_sessionProperties.ReadRestriction); jsonPropertiesSystem.AddMember("readRestriction", JsonValue(readRestrictionToString.c_str(), allocator).Move(), allocator); } if (m_writeClosed) { jsonPropertiesSystem.AddMember("closed", m_sessionProperties.Closed, allocator); } if (m_writeLocked) { jsonPropertiesSystem.AddMember("locked", m_sessionProperties.Locked, allocator); } if (m_writeAllocateCloudCompute) { jsonPropertiesSystem.AddMember("allocateCloudCompute", m_sessionProperties.AllocateCloudCompute, allocator); } if (m_writeMatchmakingTargetSessionConstants || m_writeMatchmakingServerConnectionPath) { JsonValue jsonMatchmaking(rapidjson::kObjectType); if (m_writeMatchmakingTargetSessionConstants) { JsonDocument targetSessionConstraintsJson{ &allocator }; targetSessionConstraintsJson.Parse(m_matchmakingTargetSessionConstantsJson.c_str()); jsonMatchmaking.AddMember("targetSessionConstants", targetSessionConstraintsJson, allocator); } if (m_writeMatchmakingServerConnectionPath) { jsonMatchmaking.AddMember("serverConnectionString", JsonValue(m_sessionProperties.MatchmakingServerConnectionString, allocator).Move(), allocator); } jsonPropertiesSystem.AddMember("matchmaking", jsonMatchmaking, allocator); } if (m_writeMatchmakingResubmit) { jsonPropertiesSystem.AddMember("matchmakingResubmit", m_sessionProperties.MatchmakingResubmit, allocator); } if (m_writeInitializationStatus) { jsonPropertiesSystem.AddMember("initializationSucceeded", m_initializationSucceeded, allocator); } if (m_writeHostDeviceToken) { jsonPropertiesSystem.AddMember("host", JsonValue(m_sessionProperties.HostDeviceToken.Value, allocator).Move(), allocator); } if (m_writeServerConnectionStringCandidates) { JsonValue serverConnectionStringCandidatesJson; JsonUtils::SerializeVector(JsonUtils::JsonUtf8Serializer, m_serverConnectionStringCandidates, serverConnectionStringCandidatesJson, allocator); jsonPropertiesSystem.AddMember("serverConnectionStringCandidates", serverConnectionStringCandidatesJson, allocator); } if (!jsonPropertiesSystem.IsNull() && !jsonPropertiesSystem.ObjectEmpty()) { jsonProperties.AddMember("system", jsonPropertiesSystem, allocator); } if (m_writeSessionCustomPropertiesJson) { JsonDocument customJson{ &allocator }; customJson.Parse(m_sessionCustomPropertiesJson.c_str()); jsonProperties.AddMember("custom", customJson, allocator); } } void XblMultiplayerSession::SerializeSessionConstants(_Out_ JsonValue& json, _In_ JsonDocument::AllocatorType& allocator) { if (!m_writeConstants) { return; } json.SetObject(); JsonValue systemJson(rapidjson::kObjectType); systemJson.AddMember("version", MULTIPLAYER_SESSION_VERSION, allocator); if (m_sessionConstants.MaxMembersInSession > 0) { systemJson.AddMember("maxMembersCount", m_sessionConstants.MaxMembersInSession, allocator); } JsonValue systemCapabilitiesJson(rapidjson::kObjectType); if (m_sessionConstants.SessionCapabilities.Connectivity) { systemCapabilitiesJson.AddMember("connectivity", true, allocator); } if (m_sessionConstants.SessionCapabilities.SuppressPresenceActivityCheck) { systemCapabilitiesJson.AddMember("suppressPresenceActivityCheck", true, allocator); } if (m_sessionConstants.SessionCapabilities.Gameplay) { systemCapabilitiesJson.AddMember("gameplay", true, allocator); } if (m_sessionConstants.SessionCapabilities.Large) { systemCapabilitiesJson.AddMember("large", true, allocator); } if (m_sessionConstants.SessionCapabilities.UserAuthorizationStyle) { systemCapabilitiesJson.AddMember("userAuthorizationStyle", true, allocator); } if (m_sessionConstants.SessionCapabilities.ConnectionRequiredForActiveMembers) { systemCapabilitiesJson.AddMember("connectionRequiredForActiveMembers", true, allocator); } if (m_sessionConstants.SessionCapabilities.Crossplay) { systemCapabilitiesJson.AddMember("crossPlay", true, allocator); } if (m_sessionConstants.SessionCapabilities.Searchable) { systemCapabilitiesJson.AddMember("searchable", true, allocator); } if (m_sessionConstants.SessionCapabilities.HasOwners) { systemCapabilitiesJson.AddMember("hasOwners", true, allocator); } if (!systemCapabilitiesJson.IsNull() && !systemCapabilitiesJson.ObjectEmpty()) { systemJson.AddMember("capabilities", systemCapabilitiesJson, allocator); } if (m_sessionConstants.Visibility != XblMultiplayerSessionVisibility::Any && m_sessionConstants.Visibility != XblMultiplayerSessionVisibility::Unknown) { auto visibilityString = Serializers::StringFromMultiplayerSessionVisibility(m_sessionConstants.Visibility); systemJson.AddMember("visibility", JsonValue(visibilityString.c_str(), allocator).Move(), allocator); } if (m_sessionConstants.InitiatorXuids != nullptr && m_sessionConstants.InitiatorXuidsCount > 0) { std::sort(m_sessionConstants.InitiatorXuids, m_sessionConstants.InitiatorXuids + m_sessionConstants.InitiatorXuidsCount); JsonValue jsonArray(rapidjson::kArrayType); for (uint32_t i = 0; i < m_sessionConstants.InitiatorXuidsCount; ++i) { jsonArray.PushBack(JsonValue(utils::uint64_to_internal_string(m_sessionConstants.InitiatorXuids[i]).c_str(), allocator).Move(), allocator); } systemJson.AddMember("initiators", jsonArray, allocator); } if (m_writeTimeouts) { JsonValue reservedRemovalTimeoutJson; JsonUtils::SerializeUInt52ToJson(m_sessionConstants.MemberReservedTimeout, reservedRemovalTimeoutJson); systemJson.AddMember("reservedRemovalTimeout", reservedRemovalTimeoutJson, allocator); JsonValue inactiveRemovalTimeoutJson; JsonUtils::SerializeUInt52ToJson(m_sessionConstants.MemberInactiveTimeout, inactiveRemovalTimeoutJson); systemJson.AddMember("inactiveRemovalTimeout", inactiveRemovalTimeoutJson, allocator); JsonValue readyRemovalTimeoutJson; JsonUtils::SerializeUInt52ToJson(m_sessionConstants.MemberReadyTimeout, readyRemovalTimeoutJson); systemJson.AddMember("readyRemovalTimeout", readyRemovalTimeoutJson, allocator); JsonValue sessionEmptyTimeoutJson; JsonUtils::SerializeUInt52ToJson(m_sessionConstants.SessionEmptyTimeout, sessionEmptyTimeoutJson); systemJson.AddMember("sessionEmptyTimeout", sessionEmptyTimeoutJson, allocator); } if (m_writeQosConnectivityMetrics) { JsonValue systemMetricsJson(rapidjson::kObjectType); systemMetricsJson.AddMember("latency", m_sessionConstants.EnableMetricsLatency, allocator); systemMetricsJson.AddMember("bandwidthDown", m_sessionConstants.EnableMetricsBandwidthDown, allocator); systemMetricsJson.AddMember("bandwidthUp", m_sessionConstants.EnableMetricsBandwidthUp, allocator); systemMetricsJson.AddMember("custom", m_sessionConstants.EnableMetricsCustom, allocator); systemJson.AddMember("metrics", systemMetricsJson, allocator); } if (m_writeMemberInitialization) { JsonValue memberInitializationJson{ rapidjson::kObjectType }; JsonValue joinTimeoutJson; JsonUtils::SerializeUInt52ToJson(m_sessionConstants.MemberInitialization->JoinTimeout, joinTimeoutJson); memberInitializationJson.AddMember("joinTimeout", joinTimeoutJson, allocator); JsonValue measurementTimeoutJson; JsonUtils::SerializeUInt52ToJson(m_sessionConstants.MemberInitialization->MeasurementTimeout, measurementTimeoutJson); memberInitializationJson.AddMember("measurementTimeout", measurementTimeoutJson, allocator); if (m_sessionConstants.MemberInitialization->ExternalEvaluation) { JsonValue evaluationTimeoutJson; JsonUtils::SerializeUInt52ToJson(m_sessionConstants.MemberInitialization->EvaluationTimeout, evaluationTimeoutJson); memberInitializationJson.AddMember("evaluationTimeout", evaluationTimeoutJson, allocator); } memberInitializationJson.AddMember("externalEvaluation", m_sessionConstants.MemberInitialization->ExternalEvaluation, allocator); memberInitializationJson.AddMember("membersNeededToStart", m_sessionConstants.MemberInitialization->MembersNeededToStart, allocator); systemJson.AddMember("memberInitialization", memberInitializationJson, allocator); } if (m_writePeerToPeerRequirements) { JsonValue peerToPeerRequirementsJson(rapidjson::kObjectType); JsonValue latencyMaxJson; JsonUtils::SerializeUInt52ToJson(m_sessionConstants.PeerToPeerRequirements.LatencyMaximum, latencyMaxJson); peerToPeerRequirementsJson.AddMember("latencyMaximum", latencyMaxJson, allocator); peerToPeerRequirementsJson.AddMember("bandwidthMinimum", m_sessionConstants.PeerToPeerRequirements.BandwidthMinimumInKbps, allocator); systemJson.AddMember("peerToPeerRequirements", peerToPeerRequirementsJson, allocator); } if (m_writePeerToHostRequirements) { JsonValue peerToHostRequirementsJson(rapidjson::kObjectType); JsonValue latencyMaxJson; JsonUtils::SerializeUInt52ToJson(m_sessionConstants.PeerToHostRequirements.LatencyMaximum, latencyMaxJson); peerToHostRequirementsJson.AddMember("latencyMaximum",latencyMaxJson, allocator); peerToHostRequirementsJson.AddMember("bandwidthDownMinimum", m_sessionConstants.PeerToHostRequirements.BandwidthDownMinimumInKbps, allocator); peerToHostRequirementsJson.AddMember("bandwidthUpMinimum", m_sessionConstants.PeerToHostRequirements.BandwidthUpMinimumInKbps, allocator); peerToHostRequirementsJson.AddMember( "hostSelectionMetric", JsonValue(XblMultiplayerSession::ConvertMultiplayerHostSelectionMetricToString(m_sessionConstants.PeerToHostRequirements.HostSelectionMetric).c_str(), allocator).Move(), allocator ); systemJson.AddMember("peerToHostRequirements", peerToHostRequirementsJson, allocator); } if (m_writeMeasurementServerAddresses) { JsonDocument measurementServerAddressesJson; measurementServerAddressesJson.Parse(m_sessionConstants.MeasurementServerAddressesJson); systemJson.AddMember("measurementServerAddresses", measurementServerAddressesJson, allocator); } if (m_sessionConstants.SessionCloudComputePackageConstantsJson != nullptr && m_sessionConstants.SessionCloudComputePackageConstantsJson[0] != 0) { JsonDocument cloudComputePackage{ &allocator }; cloudComputePackage.Parse(m_sessionConstants.SessionCloudComputePackageConstantsJson); systemJson.AddMember("cloudComputePackage", cloudComputePackage, allocator); } json.AddMember("system", systemJson, allocator); if (m_sessionConstants.CustomJson != nullptr && m_sessionConstants.CustomJson[0] != 0) { JsonDocument customJson{ &allocator }; customJson.Parse(m_sessionConstants.CustomJson); json.AddMember("custom", customJson, allocator); } else { json.AddMember("custom", JsonValue(rapidjson::kObjectType), allocator); } } void XblMultiplayerSession::Serialize(_Out_ JsonValue& json, _In_ JsonDocument::AllocatorType& allocator) { json.SetObject(); std::lock_guard lock{ m_lockSession }; if (m_newSession && m_writeConstants) { JsonValue serializedSessionConstants; SerializeSessionConstants(serializedSessionConstants, allocator); json.AddMember("constants", serializedSessionConstants, allocator); } if (m_writeRoleTypes) { json.AddMember("roleTypes", m_roleTypes.Serialize(allocator), allocator); } JsonValue serializedSessionProperties; SerializeSessionProperties(serializedSessionProperties, allocator); if (!serializedSessionProperties.IsNull() && !serializedSessionProperties.ObjectEmpty()) { json.AddMember("properties", serializedSessionProperties, allocator); } if (!m_members.empty() || m_leaveSession) { JsonValue memberListJson(rapidjson::kObjectType); for (const auto& member : m_members) { auto internalMember = MultiplayerSessionMember::Get(&member); JsonValue memberJson{ rapidjson::kObjectType }; internalMember->Serialize(memberJson, allocator); if (memberJson.MemberCount()) { memberListJson.AddMember(JsonValue(internalMember->MemberId().c_str(), allocator).Move(), memberJson, allocator); } } if (m_leaveSession) { // Write "me" : null to leave session. memberListJson.AddMember("me", JsonValue{ rapidjson::kNullType }, allocator); } if (memberListJson.MemberCount()) { json.AddMember("members", memberListJson, allocator); } } if (m_writeServersJson) { JsonDocument serversJson{}; serversJson.Parse(m_serversJson.c_str()); JsonUtils::SetMember(json, allocator, "servers", serversJson); } } bool XblMultiplayerSession::operator==(const XblMultiplayerSession& rhs) const { return m_sessionReference == rhs.m_sessionReference; } bool XblMultiplayerSession::IsHost( _In_ const xsapi_internal_string& memberDeviceToken, _In_ const std::shared_ptr& session ) { if (memberDeviceToken.empty() || session == nullptr) { return false; } return utils::str_icmp(memberDeviceToken.data(), session->m_sessionProperties.HostDeviceToken.Value) == 0; } bool XblMultiplayerSession::IsPlayerInSession( _In_ uint64_t xboxUserId, _In_ const std::shared_ptr& session ) { if (session == nullptr) { return false; } for (const auto& member : session->m_members) { if (xboxUserId == member.Xuid) { return true; } } return false; } const XblMultiplayerSessionMember* XblMultiplayerSession::GetPlayerInSession( _In_ uint64_t xboxUserId, _In_ std::shared_ptr session ) { if (session == nullptr) { return nullptr; } for (const auto& member : session->m_members) { if (xboxUserId == member.Xuid) { return &member; } } return nullptr; } const XblMultiplayerSessionMember* XblMultiplayerSession::HostMember( _In_ std::shared_ptr session ) { if (session == nullptr) { return nullptr; } for (const auto& member : session->m_members) { if (utils::str_icmp(member.DeviceToken.Value, session->m_sessionProperties.HostDeviceToken.Value) == 0) { return &member; } } return nullptr; } HRESULT XblMultiplayerSession::SetTurnCollection(_In_ const xsapi_internal_vector& turnCollection) { std::lock_guard lock{ m_lockSession }; if (turnCollection.empty()) { return E_INVALIDARG; } m_turnCollection = turnCollection; m_sessionProperties.TurnCollection = m_turnCollection.data(); m_sessionProperties.TurnCollectionCount = m_turnCollection.size(); m_writePropertiesTurns = true; return S_OK; } bool XblMultiplayerSession::HasSessionPropertyChanged( _In_ const std::shared_ptr& session1, _In_ const std::shared_ptr& session2, _In_ const xsapi_internal_string& _propertyName ) { if (session1 == nullptr && session2 == nullptr) { return true; } if (session1 == nullptr || session2 == nullptr) { return false; } auto propertyName = _propertyName; JsonDocument customProp1; JsonDocument customProp2; customProp1.Parse(session1->m_sessionCustomPropertiesJson.data()); customProp2.Parse(session2->m_sessionCustomPropertiesJson.data()); if (!customProp1.HasParseError() && !customProp2.HasParseError()) { xsapi_internal_string prop1; xsapi_internal_string prop2; bool isInProp1 = SUCCEEDED(JsonUtils::ExtractJsonString(customProp1, propertyName, prop1, true)); bool isInProp2 = SUCCEEDED(JsonUtils::ExtractJsonString(customProp2, propertyName, prop2, true)); if ((isInProp1 && !isInProp2) || (!isInProp1 && isInProp2)) { return true; } if (isInProp1 && isInProp2) { return utils::str_icmp(prop1.c_str(), prop2.c_str()) != 0; } } return false; } bool XblMultiplayerSession::DoSessionsMatch( _In_ std::shared_ptr lhs, _In_ std::shared_ptr rhs ) { return lhs != nullptr && rhs != nullptr && *rhs == *lhs; } HRESULT XblMultiplayerSession::DeserializeMatchmakingServer( _In_ const JsonValue& serversMatchmakingJson ) { if (serversMatchmakingJson.IsObject() && serversMatchmakingJson.HasMember("properties")) { const JsonValue& serversMatchmakingPropertiesJson = serversMatchmakingJson["properties"]; if (serversMatchmakingPropertiesJson.IsObject() && serversMatchmakingPropertiesJson.HasMember("system")) { const JsonValue& json = serversMatchmakingPropertiesJson["system"]; if (!json.IsNull()) { m_matchmakingServer = MakeShared(); xsapi_internal_string status; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "status", status)); m_matchmakingServer->Status = XblMultiplayerSession::ConvertStringToMatchmakingStatus(status); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "statusDetails", m_matchmakingStatusDetails)); m_matchmakingServer->StatusDetails = m_matchmakingStatusDetails.data(); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonInt(json, "typicalWait", m_matchmakingServer->TypicalWaitInSeconds)); if (json.IsObject() && json.HasMember("targetSessionRef")) { m_matchmakingServer->TargetSessionRef = Serializers::DeserializeSessionReference(json["targetSessionRef"]).Payload(); } else { m_matchmakingServer->TargetSessionRef = XblMultiplayerSessionReference(); } } } } return S_OK; } HRESULT XblMultiplayerSession::DeserializeSessionProperties( _In_ const JsonValue& json ) { if (json.IsObject() && json.HasMember("system")) { const JsonValue& systemJson = json["system"]; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonVector(JsonUtils::JsonUtf8Extractor, systemJson, "keywords", m_keywords, false)); m_sessionProperties.KeywordCount = m_keywords.size(); m_sessionProperties.Keywords = m_keywords.data(); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonVector(JsonUtils::JsonIntExtractor, systemJson, "owners", m_sessionOwnerIndices, false)); m_sessionProperties.SessionOwnerMemberIdsCount = m_sessionOwnerIndices.size(); m_sessionProperties.SessionOwnerMemberIds = m_sessionOwnerIndices.data(); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonVector(JsonUtils::JsonIntExtractor, systemJson, "turn", m_turnCollection, false)); m_sessionProperties.TurnCollectionCount = static_cast(m_turnCollection.size()); m_sessionProperties.TurnCollection = m_turnCollection.data(); xsapi_internal_string joinRestrictionString; xsapi_internal_string readRestrictionString; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(systemJson, "joinRestriction", joinRestrictionString)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(systemJson, "readRestriction", readRestrictionString)); if (!joinRestrictionString.empty()) { m_sessionProperties.JoinRestriction = Serializers::MultiplayerSessionRestrictionFromString(joinRestrictionString); } if (!readRestrictionString.empty()) { m_sessionProperties.ReadRestriction = Serializers::MultiplayerSessionRestrictionFromString(readRestrictionString); } RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonBool(systemJson, "closed", m_sessionProperties.Closed)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonBool(systemJson, "locked", m_sessionProperties.Locked)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonBool(systemJson, "allocateCloudCompute", m_sessionProperties.AllocateCloudCompute)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonBool(systemJson, "matchmakingResubmit", m_sessionProperties.MatchmakingResubmit)); if (systemJson.IsObject() && systemJson.HasMember("matchmaking")) { const JsonValue& systemMatchmakingJson = systemJson["matchmaking"]; if (systemMatchmakingJson.IsObject() && systemMatchmakingJson.HasMember("targetSessionConstants")) { m_matchmakingTargetSessionConstantsJson = JsonUtils::SerializeJson( systemMatchmakingJson["targetSessionConstants"] ); } RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(systemMatchmakingJson, "serverConnectionString", m_matchmakingServerConnectionString)); m_sessionProperties.MatchmakingServerConnectionString = m_matchmakingServerConnectionString.data(); } m_sessionProperties.MatchmakingTargetSessionConstantsJson = m_matchmakingTargetSessionConstantsJson.data(); xsapi_internal_string host; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(systemJson, "host", host)); utils::strcpy( m_sessionProperties.HostDeviceToken.Value, sizeof(m_sessionProperties.HostDeviceToken), host.c_str() ); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonVector(JsonUtils::JsonUtf8Extractor, systemJson, "serverConnectionStringCandidates", m_serverConnectionStringCandidates, false)); m_sessionProperties.ServerConnectionStringCandidatesCount = static_cast(m_serverConnectionStringCandidates.size()); m_sessionProperties.ServerConnectionStringCandidates = m_serverConnectionStringCandidates.data(); } else { //required return WEB_E_INVALID_JSON_STRING; } if (json.IsObject() && json.HasMember("custom")) { m_sessionCustomPropertiesJson = JsonUtils::SerializeJson(json["custom"]); } m_sessionProperties.SessionCustomPropertiesJson = m_sessionCustomPropertiesJson.data(); return S_OK; } HRESULT XblMultiplayerSession::DeserializeSessionConstants( _In_ const JsonValue& json ) { if (!json.IsObject()) { return WEB_E_INVALID_JSON_STRING; } if (json.HasMember("system")) { const JsonValue& systemJson = json["system"]; if (systemJson.IsObject()) { if (systemJson.HasMember("cloudComputePackage")) { m_constantsCloudComputePackageJson = JsonUtils::SerializeJson(systemJson["cloudComputePackage"]); m_sessionConstants.SessionCloudComputePackageConstantsJson = m_constantsCloudComputePackageJson.data(); } RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonInt(systemJson, "maxMembersCount", m_sessionConstants.MaxMembersInSession)); xsapi_internal_string visibility; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(systemJson, "visibility", visibility)); m_sessionConstants.Visibility = Serializers::MultiplayerSessionVisibilityFromString(visibility); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonVector(JsonUtils::JsonXuidExtractor, systemJson, "initiators", m_initiatorXuids, false)); m_sessionConstants.InitiatorXuids = m_initiatorXuids.data(); m_sessionConstants.InitiatorXuidsCount = m_initiatorXuids.size(); if (systemJson.HasMember("capabilities")) { const JsonValue& systemCapabilitiesJson = systemJson["capabilities"]; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonBool(systemCapabilitiesJson, "connectivity", m_sessionConstants.SessionCapabilities.Connectivity, false)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonBool(systemCapabilitiesJson, "suppressPresenceActivityCheck", m_sessionConstants.SessionCapabilities.SuppressPresenceActivityCheck, false)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonBool(systemCapabilitiesJson, "gameplay", m_sessionConstants.SessionCapabilities.Gameplay, false)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonBool(systemCapabilitiesJson, "large", m_sessionConstants.SessionCapabilities.Large, false)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonBool(systemCapabilitiesJson, "connectionRequiredForActiveMembers", m_sessionConstants.SessionCapabilities.ConnectionRequiredForActiveMembers, false)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonBool(systemCapabilitiesJson, "userAuthorizationStyle", m_sessionConstants.SessionCapabilities.UserAuthorizationStyle, false)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonBool(systemCapabilitiesJson, "crossPlay", m_sessionConstants.SessionCapabilities.Crossplay, false)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonBool(systemCapabilitiesJson, "hasOwners", m_sessionConstants.SessionCapabilities.HasOwners, false)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonBool(systemCapabilitiesJson, "searchable", m_sessionConstants.SessionCapabilities.Searchable, false)); } else { m_sessionConstants.SessionCapabilities.Connectivity = false; m_sessionConstants.SessionCapabilities.SuppressPresenceActivityCheck = false; m_sessionConstants.SessionCapabilities.Gameplay = false; m_sessionConstants.SessionCapabilities.Large = false; m_sessionConstants.SessionCapabilities.ConnectionRequiredForActiveMembers = false; m_sessionConstants.SessionCapabilities.UserAuthorizationStyle = false; m_sessionConstants.SessionCapabilities.Crossplay = false; m_sessionConstants.SessionCapabilities.HasOwners = false; m_sessionConstants.SessionCapabilities.Searchable = false; } RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonUInt64(systemJson, "reservedRemovalTimeout", m_sessionConstants.MemberReservedTimeout, false)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonUInt64(systemJson, "inactiveRemovalTimeout", m_sessionConstants.MemberInactiveTimeout, false)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonUInt64(systemJson, "readyRemovalTimeout", m_sessionConstants.MemberReadyTimeout, false)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonUInt64(systemJson, "sessionEmptyTimeout", m_sessionConstants.SessionEmptyTimeout, false)); if (systemJson.HasMember("metrics")) { const JsonValue& systemMetricsJson = systemJson["metrics"]; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonBool(systemMetricsJson, "latency", m_sessionConstants.EnableMetricsLatency, false)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonBool(systemMetricsJson, "bandwidthDown", m_sessionConstants.EnableMetricsBandwidthDown, false)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonBool(systemMetricsJson, "bandwidthUp", m_sessionConstants.EnableMetricsBandwidthUp, false)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonBool(systemMetricsJson, "custom", m_sessionConstants.EnableMetricsCustom, false)); } else { m_sessionConstants.EnableMetricsLatency = false; m_sessionConstants.EnableMetricsBandwidthDown = false; m_sessionConstants.EnableMetricsBandwidthUp = false; m_sessionConstants.EnableMetricsCustom = false; } if (systemJson.HasMember("memberInitialization")) { const JsonValue& memberInitializationJson = systemJson["memberInitialization"]; if (!memberInitializationJson.IsNull()) { RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonUInt64(memberInitializationJson, "joinTimeout", m_memberInitialization.JoinTimeout)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonUInt64(memberInitializationJson, "measurementTimeout", m_memberInitialization.MeasurementTimeout)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonUInt64(memberInitializationJson, "evaluationTimeout", m_memberInitialization.EvaluationTimeout)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonBool(memberInitializationJson, "externalEvaluation", m_memberInitialization.ExternalEvaluation)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonInt(memberInitializationJson, "membersNeededToStart", m_memberInitialization.MembersNeededToStart)); m_sessionConstants.MemberInitialization = &m_memberInitialization; } } if (systemJson.HasMember("peerToHostRequirements")) { const JsonValue& peerToHostRequirementsJson = systemJson["peerToHostRequirements"]; if (!peerToHostRequirementsJson.IsNull()) { RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonUInt64(peerToHostRequirementsJson, "latencyMaximum", m_sessionConstants.PeerToHostRequirements.LatencyMaximum)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonInt(peerToHostRequirementsJson, "bandwidthDownMinimum", m_sessionConstants.PeerToHostRequirements.BandwidthDownMinimumInKbps)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonInt(peerToHostRequirementsJson, "bandwidthUpMinimum", m_sessionConstants.PeerToHostRequirements.BandwidthUpMinimumInKbps)); xsapi_internal_string hostSelectionMetric; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "hostSelectionMetric", hostSelectionMetric)); m_sessionConstants.PeerToHostRequirements.HostSelectionMetric = XblMultiplayerSession::ConvertStringToMultiplayerHostSelectionMetric(hostSelectionMetric); } } if (systemJson.HasMember("peerToPeerRequirements")) { const JsonValue& peerToPeerRequirementsJson = systemJson["peerToPeerRequirements"]; if (!peerToPeerRequirementsJson.IsNull()) { RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonUInt64(peerToPeerRequirementsJson, "latencyMaximum", m_sessionConstants.PeerToPeerRequirements.LatencyMaximum)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonInt(peerToPeerRequirementsJson, "bandwidthMinimum", m_sessionConstants.PeerToPeerRequirements.BandwidthMinimumInKbps)); } } if (systemJson.HasMember("measurementServerAddresses")) { m_constantsMeasurementServerAddressesJson = JsonUtils::SerializeJson(systemJson["measurementServerAddresses"]); m_sessionConstants.MeasurementServerAddressesJson = m_constantsMeasurementServerAddressesJson.data(); } } } else { //required return WEB_E_INVALID_JSON_STRING; } if (json.HasMember("custom")) { m_constantsCustomJson = JsonUtils::SerializeJson(json["custom"]); m_sessionConstants.CustomJson = m_constantsCustomJson.data(); } return S_OK; } NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_CPP_BEGIN RoleTypes::RoleTypes(const RoleTypes& other) noexcept : m_values{ other.m_values } { for (auto& roleType : m_values) { roleType.Name = Make(roleType.Name); roleType.Roles = MakeArray(roleType.Roles, roleType.RoleCount); for (size_t i = 0; i < roleType.RoleCount; ++i) { roleType.Roles[i].RoleType = &roleType; roleType.Roles[i].Name = Make(roleType.Roles[i].Name); roleType.Roles[i].MemberXuids = MakeArray(roleType.Roles[i].MemberXuids, roleType.Roles[i].MemberCount); } } } RoleTypes& RoleTypes::operator=(RoleTypes other) noexcept { std::swap(other.m_values, m_values); return *this; } RoleTypes::~RoleTypes() noexcept { for (auto& roleType : m_values) { Delete(roleType.Name); for (size_t i = 0; i < roleType.RoleCount; ++i) { Delete(roleType.Roles[i].Name); Delete(roleType.Roles[i].MemberXuids); } Delete(roleType.Roles); } } Result RoleTypes::Deserialize(const JsonValue& json) noexcept { if (json.IsNull()) { return WEB_E_INVALID_JSON_STRING; } RoleTypes roleTypes{}; roleTypes.m_values.reserve(json.MemberCount()); for (const auto& roleTypeJson : json.GetObject()) { roleTypes.m_values.push_back(XblMultiplayerRoleType{}); auto& roleType{ roleTypes.m_values.back() }; roleType.Name = Make(roleTypeJson.name.GetString()); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonBool(roleTypeJson.value, "ownerManaged", roleType.OwnerManaged, false)); Vector mutableSettingsVector; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonVector(JsonUtils::JsonStringExtractor, roleTypeJson.value, "mutableRoleSettings", mutableSettingsVector, false)); for (auto& setting : mutableSettingsVector) { if (utils::str_icmp_internal(setting, "max") == 0) { roleType.MutableRoleSettings |= XblMutableRoleSettings::Max; } else if (utils::str_icmp_internal(setting, "target") == 0) { roleType.MutableRoleSettings |= XblMutableRoleSettings::Target; } } if (roleTypeJson.value.HasMember("roles")) { const auto& rolesJson = roleTypeJson.value["roles"]; if (rolesJson.IsObject()) { roleType.Roles = MakeArray(rolesJson.MemberCount()); for (const auto& roleJson : rolesJson.GetObject()) { auto& role{ roleType.Roles[roleType.RoleCount++] }; role.RoleType = &roleType; role.Name = Make(roleJson.name.GetString()); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonInt(roleJson.value, "count", role.MemberCount, false)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonInt(roleJson.value, "max", role.MaxMemberCount, false)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonInt(roleJson.value, "target", role.TargetCount, false)); if (role.MemberCount > 0) { Vector memberXuidsVector; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonVector(JsonUtils::JsonXuidExtractor, roleJson.value, "memberXuids", memberXuidsVector, true)); role.MemberXuids = MakeArray(memberXuidsVector); } } } } } return roleTypes; } JsonValue RoleTypes::Serialize(JsonDocument::AllocatorType& a) const noexcept { JsonValue roleTypesJson{ rapidjson::Type::kObjectType }; for (const auto& roleType : m_values) { JsonValue roleTypeJson{ rapidjson::kObjectType }; JsonValue rolesJson{ rapidjson::kObjectType }; for (size_t i = 0; i < roleType.RoleCount; ++i) { auto& role = roleType.Roles[i]; JsonValue roleJson{ rapidjson::kObjectType }; if (role.MaxMemberCount) { roleJson.AddMember("max", role.MaxMemberCount, a); } if (role.TargetCount > 0) { roleJson.AddMember("target", role.TargetCount, a); } rolesJson.AddMember(JsonValue{ role.Name, a }.Move(), roleJson, a); } roleTypeJson.AddMember("roles", rolesJson, a); roleTypesJson.AddMember(JsonValue{ roleType.Name, a }.Move(), roleTypeJson, a); } return roleTypesJson; } const Vector& RoleTypes::Values() const noexcept { return m_values; } HRESULT RoleTypes::SetRoleSettings( String&& roleTypeName, String&& roleName, uint32_t* maxCount, uint32_t* targetCount ) noexcept { auto role{ GetRole(std::move(roleTypeName), std::move(roleName)) }; RETURN_HR_INVALIDARGUMENT_IF_NULL(role); bool maxMutable{ (role->RoleType->MutableRoleSettings | XblMutableRoleSettings::Max) != XblMutableRoleSettings::None }; bool targetMutable{ (role->RoleType->MutableRoleSettings | XblMutableRoleSettings::Target) != XblMutableRoleSettings::None }; RETURN_HR_IF((maxCount && !maxMutable) || (targetCount && !targetMutable), E_UNEXPECTED); if (maxCount) { role->MaxMemberCount = *maxCount; } if (targetCount) { role->TargetCount = *targetCount; } return S_OK; } XblMultiplayerRole* RoleTypes::GetRole( String&& roleTypeName, String&& roleName ) const noexcept { // Could store these in a map to make lookup quicker but the collection of roles should be small for (const auto& roleType : m_values) { if (Stricmp(roleType.Name, roleTypeName.data()) == 0) { for (size_t i = 0; i < roleType.RoleCount; ++i) { if (Stricmp(roleType.Roles[i].Name, roleName.data()) == 0) { return &roleType.Roles[i]; } } break; } } return nullptr; } NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_CPP_END STDAPI_(XblMultiplayerSessionHandle) XblMultiplayerSessionCreateHandle( _In_ uint64_t xuid, _In_opt_ const XblMultiplayerSessionReference* sessionReference, _In_opt_ const XblMultiplayerSessionInitArgs* initArgs ) XBL_NOEXCEPT try { auto session = MakeShared(xuid, sessionReference, initArgs); session->AddRef(); return session.get(); } CATCH_RETURN_WITH(nullptr) STDAPI XblMultiplayerSessionDuplicateHandle( _In_ XblMultiplayerSessionHandle session, _Out_ XblMultiplayerSessionHandle* duplicatedHandle ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(session == nullptr || duplicatedHandle == nullptr); session->AddRef(); *duplicatedHandle = session; return S_OK; } CATCH_RETURN() STDAPI_(void) XblMultiplayerSessionCloseHandle( _In_ XblMultiplayerSessionHandle session ) XBL_NOEXCEPT try { if (session) { session->DecRef(); } } CATCH_RETURN_WITH(;) STDAPI_(XblTournamentArbitrationStatus) XblMultiplayerSessionArbitrationStatus( _In_ XblMultiplayerSessionHandle session ) XBL_NOEXCEPT try { UNREFERENCED_PARAMETER(session); return XblTournamentArbitrationStatus::Incomplete; } CATCH_RETURN_WITH(XblTournamentArbitrationStatus::Incomplete) STDAPI_(time_t) XblMultiplayerSessionTimeOfSession( _In_ XblMultiplayerSessionHandle session ) XBL_NOEXCEPT try { if (session == nullptr) { return 0; } return session->TimeOfSession(); } CATCH_RETURN() STDAPI_(const XblMultiplayerSessionInitializationInfo*) XblMultiplayerSessionGetInitializationInfo( _In_ XblMultiplayerSessionHandle session ) XBL_NOEXCEPT try { if (session == nullptr) { return nullptr; } return &session->InitializationInfo(); } CATCH_RETURN_WITH(nullptr) STDAPI_(XblMultiplayerSessionChangeTypes) XblMultiplayerSessionSubscribedChangeTypes( _In_ XblMultiplayerSessionHandle session ) XBL_NOEXCEPT try { if (session == nullptr) { return XblMultiplayerSessionChangeTypes::None; } if (session->CurrentUserUnsafe() == nullptr) { return XblMultiplayerSessionChangeTypes::None; } return MultiplayerSessionMember::Get(session->CurrentUserUnsafe())->SubscribedChangeTypes(); } CATCH_RETURN_WITH(XblMultiplayerSessionChangeTypes::None) STDAPI XblMultiplayerSessionHostCandidates( _In_ XblMultiplayerSessionHandle session, _Out_ const XblDeviceToken** deviceTokens, _Out_ size_t* deviceTokensCount ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(deviceTokens == nullptr || deviceTokensCount == nullptr); RETURN_HR_INVALIDARGUMENT_IF_NULL(session); *deviceTokens = session->HostCandidates().data(); *deviceTokensCount = session->HostCandidates().size(); return S_OK; } CATCH_RETURN() STDAPI_(const XblMultiplayerSessionReference*) XblMultiplayerSessionSessionReference( _In_ XblMultiplayerSessionHandle session ) XBL_NOEXCEPT try { if (session == nullptr) { return nullptr; } return &session->SessionReference(); } CATCH_RETURN_WITH(nullptr) STDAPI_(const XblMultiplayerSessionConstants*) XblMultiplayerSessionSessionConstants( _In_ XblMultiplayerSessionHandle session ) XBL_NOEXCEPT try { if (session == nullptr) { return nullptr; } return &session->SessionConstantsUnsafe(); } CATCH_RETURN_WITH(nullptr) STDAPI_(void) XblMultiplayerSessionConstantsSetMaxMembersInSession( _In_ XblMultiplayerSessionHandle session, uint32_t maxMembersInSession ) XBL_NOEXCEPT try { if (session == nullptr) { return; } session->SetMaxMembersInSession(maxMembersInSession); } CATCH_RETURN_WITH(;) STDAPI_(void) XblMultiplayerSessionConstantsSetVisibility( _In_ XblMultiplayerSessionHandle session, _In_ XblMultiplayerSessionVisibility visibility ) XBL_NOEXCEPT try { if (session == nullptr) { return; } session->SetVisibility(visibility); } CATCH_RETURN_WITH(;) STDAPI XblMultiplayerSessionConstantsSetTimeouts( _In_ XblMultiplayerSessionHandle session, _In_ uint64_t memberReservedTimeout, _In_ uint64_t memberInactiveTimeout, _In_ uint64_t memberReadyTimeout, _In_ uint64_t sessionEmptyTimeout ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(session); return session->SetTimeouts(memberReservedTimeout, memberInactiveTimeout, memberReadyTimeout, sessionEmptyTimeout); } CATCH_RETURN() STDAPI XblMultiplayerSessionConstantsSetArbitrationTimeouts( _In_ XblMultiplayerSessionHandle session, _In_ uint64_t arbitrationTimeout, _In_ uint64_t forfeitTimeout ) XBL_NOEXCEPT try { UNREFERENCED_PARAMETER(session); UNREFERENCED_PARAMETER(arbitrationTimeout); UNREFERENCED_PARAMETER(forfeitTimeout); return E_NOTIMPL; } CATCH_RETURN() STDAPI XblMultiplayerSessionConstantsSetQosConnectivityMetrics( _In_ XblMultiplayerSessionHandle session, _In_ bool enableLatencyMetric, _In_ bool enableBandwidthDownMetric, _In_ bool enableBandwidthUpMetric, _In_ bool enableCustomMetric ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(session); return session->SetQosConnectivityMetrics(enableLatencyMetric, enableBandwidthDownMetric, enableBandwidthUpMetric, enableCustomMetric); } CATCH_RETURN() STDAPI XblMultiplayerSessionConstantsSetMemberInitialization( _In_ XblMultiplayerSessionHandle session, _In_ XblMultiplayerMemberInitialization memberInitialization ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(session); return session->SetMemberInitialization(std::move(memberInitialization)); } CATCH_RETURN() STDAPI XblMultiplayerSessionConstantsSetPeerToPeerRequirements( _In_ XblMultiplayerSessionHandle session, _In_ XblMultiplayerPeerToPeerRequirements requirements ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(session); return session->SetPeerToPeerRequirements(std::move(requirements)); } CATCH_RETURN() STDAPI XblMultiplayerSessionConstantsSetPeerToHostRequirements( _In_ XblMultiplayerSessionHandle session, _In_ XblMultiplayerPeerToHostRequirements requirements ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(session); return session->SetPeerToHostRequirements(std::move(requirements)); } CATCH_RETURN() STDAPI XblMultiplayerSessionConstantsSetMeasurementServerAddressesJson( _In_ XblMultiplayerSessionHandle session, _In_ const char* measurementServerAddressesJson ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(session == nullptr || measurementServerAddressesJson == nullptr); return session->SetMeasurementServerAddresses(measurementServerAddressesJson); } CATCH_RETURN() STDAPI XblMultiplayerSessionConstantsSetCapabilities( _In_ XblMultiplayerSessionHandle session, _In_ XblMultiplayerSessionCapabilities capabilities ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(session); return session->SetSessionCapabilities(std::move(capabilities)); } CATCH_RETURN() STDAPI XblMultiplayerSessionConstantsSetCloudComputePackageJson( _In_ XblMultiplayerSessionHandle session, _In_ const char* sessionCloudComputePackageConstantsJson ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(session); return session->SetCloudComputePackageJson(sessionCloudComputePackageConstantsJson); } CATCH_RETURN() STDAPI_(const XblMultiplayerSessionProperties*) XblMultiplayerSessionSessionProperties( _In_ XblMultiplayerSessionHandle session ) XBL_NOEXCEPT try { if (session == nullptr) { return nullptr; } return &session->SessionPropertiesUnsafe(); } CATCH_RETURN_WITH(nullptr) STDAPI XblMultiplayerSessionPropertiesSetKeywords( _In_ XblMultiplayerSessionHandle session, _In_ const char** keywords, _In_ size_t keywordsCount ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(session); return session->SetKeywords(keywords, keywordsCount); } CATCH_RETURN() STDAPI_(void) XblMultiplayerSessionPropertiesSetJoinRestriction( _In_ XblMultiplayerSessionHandle session, _In_ XblMultiplayerSessionRestriction joinRestriction ) XBL_NOEXCEPT try { if (session == nullptr) { return; } session->SetJoinRestriction(joinRestriction); } CATCH_RETURN_WITH(;) STDAPI_(void) XblMultiplayerSessionPropertiesSetReadRestriction( _In_ XblMultiplayerSessionHandle session, _In_ XblMultiplayerSessionRestriction readRestriction ) XBL_NOEXCEPT try { if (session == nullptr) { return; } session->SetReadRestriction(readRestriction); } CATCH_RETURN_WITH(;) STDAPI XblMultiplayerSessionPropertiesSetTurnCollection( _In_ XblMultiplayerSessionHandle session, _In_ const uint32_t* turnCollectionMemberIds, _In_ size_t turnCollectionMemberIdsCount ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(session); return session->SetTurnCollection(xsapi_internal_vector(turnCollectionMemberIds, turnCollectionMemberIds + turnCollectionMemberIdsCount)); } CATCH_RETURN() STDAPI XblMultiplayerSessionRoleTypes( _In_ XblMultiplayerSessionHandle session, _Out_ const XblMultiplayerRoleType** roleTypes, _Out_ size_t* roleTypesCount ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(session); RETURN_HR_INVALIDARGUMENT_IF(roleTypes == nullptr || roleTypesCount == nullptr); *roleTypes = session->RoleTypesUnsafe().Values().data(); *roleTypesCount = session->RoleTypesUnsafe().Values().size(); return S_OK; } CATCH_RETURN() STDAPI XblMultiplayerSessionGetRoleByName( _In_ XblMultiplayerSessionHandle session, _In_z_ const char* roleTypeName, _In_z_ const char* roleName, _Out_ const XblMultiplayerRole** role ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(session); RETURN_HR_INVALIDARGUMENT_IF(roleTypeName == nullptr || roleName == nullptr || role == nullptr); *role = session->RoleTypesUnsafe().GetRole(roleTypeName, roleName); return S_OK; } CATCH_RETURN() STDAPI XblMultiplayerSessionSetMutableRoleSettings( _In_ XblMultiplayerSessionHandle session, _In_z_ const char* roleTypeName, _In_z_ const char* roleName, _In_opt_ uint32_t* maxMemberCount, _In_opt_ uint32_t* targetMemberCount ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(session); RETURN_HR_INVALIDARGUMENT_IF(roleTypeName == nullptr || roleName == nullptr); return session->SetMutableRoleSettings(roleTypeName, roleName, maxMemberCount, targetMemberCount); } CATCH_RETURN() STDAPI XblMultiplayerSessionMembers( _In_ XblMultiplayerSessionHandle session, _Out_ const XblMultiplayerSessionMember** members, _Out_ size_t* membersCount ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(session); RETURN_HR_INVALIDARGUMENT_IF(members == nullptr || membersCount == nullptr); *members = session->MembersUnsafe().data(); *membersCount = session->MembersUnsafe().size(); return S_OK; } CATCH_RETURN() STDAPI_(const XblMultiplayerSessionMember*) XblMultiplayerSessionGetMember( _In_ XblMultiplayerSessionHandle session, _In_ uint32_t memberId ) XBL_NOEXCEPT try { if (session == nullptr) { return nullptr; } // Could store these in a map to improve lookup but member count should not be large auto& members = session->MembersUnsafe(); for (const auto& member : members) { if (member.MemberId == memberId) { return &member; } } return nullptr; } CATCH_RETURN_WITH(nullptr) STDAPI_(const XblMultiplayerMatchmakingServer*) XblMultiplayerSessionMatchmakingServer( _In_ XblMultiplayerSessionHandle session ) XBL_NOEXCEPT try { if (session == nullptr) { return nullptr; } return session->MatchmakingServer().get(); } CATCH_RETURN_WITH(nullptr) XBL_WARNING_PUSH XBL_WARNING_DISABLE_DEPRECATED STDAPI_(const XblMultiplayerTournamentsServer*) XblMultiplayerSessionTournamentsServer( _In_ XblMultiplayerSessionHandle session ) XBL_NOEXCEPT try { UNREFERENCED_PARAMETER(session); return nullptr; } CATCH_RETURN_WITH(nullptr) XBL_WARNING_POP XBL_WARNING_PUSH XBL_WARNING_DISABLE_DEPRECATED STDAPI_(const XblMultiplayerArbitrationServer*) XblMultiplayerSessionArbitrationServer( _In_ XblMultiplayerSessionHandle session ) XBL_NOEXCEPT try { UNREFERENCED_PARAMETER(session); return nullptr; } CATCH_RETURN_WITH(nullptr) XBL_WARNING_POP STDAPI_(uint32_t) XblMultiplayerSessionMembersAccepted( _In_ XblMultiplayerSessionHandle session ) XBL_NOEXCEPT try { if (session == nullptr) { return 0; } return session->MembersAccepted(); } CATCH_RETURN() STDAPI_(const char*) XblMultiplayerSessionRawServersJson( _In_ XblMultiplayerSessionHandle session ) XBL_NOEXCEPT try { if (session == nullptr) { return nullptr; } return session->RawServersJsonUnsafe().data(); } CATCH_RETURN_WITH(nullptr) STDAPI XblMultiplayerSessionSetRawServersJson( _In_ XblMultiplayerSessionHandle session, _In_z_ const char* rawServersJson ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(session); return session->SetServersJson(rawServersJson); } CATCH_RETURN() STDAPI_(const char*) XblMultiplayerSessionEtag( _In_ XblMultiplayerSessionHandle session ) XBL_NOEXCEPT try { if (session == nullptr) { return nullptr; } return session->ETagUnsafe().data(); } CATCH_RETURN_WITH(nullptr) STDAPI_(const XblMultiplayerSessionMember*) XblMultiplayerSessionCurrentUser( _In_ XblMultiplayerSessionHandle session ) XBL_NOEXCEPT try { if (session == nullptr) { return nullptr; } return session->CurrentUserUnsafe(); } CATCH_RETURN_WITH(nullptr) STDAPI_(const XblMultiplayerSessionInfo*) XblMultiplayerSessionGetInfo( _In_ XblMultiplayerSessionHandle session ) XBL_NOEXCEPT try { if (session == nullptr) { return nullptr; } return &session->SessionInfo(); } CATCH_RETURN_WITH(nullptr) STDAPI_(XblWriteSessionStatus) XblMultiplayerSessionWriteStatus( _In_ XblMultiplayerSessionHandle session ) XBL_NOEXCEPT try { if (session == nullptr) { // TODO: is this correct? return XblWriteSessionStatus::Unknown; } else { return session->WriteStatus(); } } CATCH_RETURN_WITH(XblWriteSessionStatus::Unknown) STDAPI XblMultiplayerSessionAddMemberReservation( _In_ XblMultiplayerSessionHandle session, _In_ uint64_t xuid, _In_opt_z_ const char* memberCustomConstantsJson, _In_ bool initializeRequested ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(session); return session->AddMemberReservation(xuid, memberCustomConstantsJson, initializeRequested); } CATCH_RETURN() STDAPI XblMultiplayerSessionJoin( _In_ XblMultiplayerSessionHandle session, _In_opt_z_ const char* memberCustomConstantsJson, _In_ bool initializeRequested, _In_ bool joinWithActiveStatus ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(session); return session->Join(memberCustomConstantsJson, initializeRequested, joinWithActiveStatus); } CATCH_RETURN() STDAPI_(void) XblMultiplayerSessionSetInitializationSucceeded( _In_ XblMultiplayerSessionHandle session, _In_ bool initializationSucceeded ) XBL_NOEXCEPT try { if (session == nullptr) { return; } session->SetInitializationStatus(initializationSucceeded); } CATCH_RETURN_WITH(;) STDAPI_(void) XblMultiplayerSessionSetHostDeviceToken( _In_ XblMultiplayerSessionHandle session, _In_ XblDeviceToken hostDeviceToken ) XBL_NOEXCEPT try { if (session == nullptr) { return; } session->SetHostDeviceToken(hostDeviceToken); } CATCH_RETURN_WITH(;) STDAPI_(void) XblMultiplayerSessionSetMatchmakingServerConnectionPath( _In_ XblMultiplayerSessionHandle session, _In_z_ const char* serverConnectionPath ) XBL_NOEXCEPT try { if (session == nullptr) { return; } return session->SetMatchmakingServerConnectionPath(serverConnectionPath); } CATCH_RETURN_WITH(;) STDAPI_(void) XblMultiplayerSessionSetClosed( _In_ XblMultiplayerSessionHandle session, _In_ bool closed ) XBL_NOEXCEPT try { if (session == nullptr) { return; } session->SetClosed(closed); } CATCH_RETURN_WITH(;) STDAPI_(void) XblMultiplayerSessionSetLocked( _In_ XblMultiplayerSessionHandle session, _In_ bool locked ) XBL_NOEXCEPT try { if (session == nullptr) { return; } session->SetLocked(locked); } CATCH_RETURN_WITH(;) STDAPI_(void) XblMultiplayerSessionSetAllocateCloudCompute( _In_ XblMultiplayerSessionHandle session, _In_ bool allocateCloudCompute ) XBL_NOEXCEPT try { if (session == nullptr) { return; } session->SetAllocateCloudCompute(allocateCloudCompute); } CATCH_RETURN_WITH(;) STDAPI_(void) XblMultiplayerSessionSetMatchmakingResubmit( _In_ XblMultiplayerSessionHandle session, _In_ bool matchResubmit ) XBL_NOEXCEPT try { if (session == nullptr) { return; } session->SetMatchmakingResubmit(matchResubmit); } CATCH_RETURN_WITH(;) STDAPI XblMultiplayerSessionSetServerConnectionStringCandidates( _In_ XblMultiplayerSessionHandle session, _In_reads_(serverConnectionStringCandidatesCount) const char** serverConnectionStringCandidates, _In_ size_t serverConnectionStringCandidatesCount ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(session); return session->SetServerConnectionStringCandidates(serverConnectionStringCandidates, serverConnectionStringCandidatesCount); } CATCH_RETURN() STDAPI XblMultiplayerSessionSetSessionChangeSubscription( _In_ XblMultiplayerSessionHandle session, _In_ XblMultiplayerSessionChangeTypes changeTypes ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(session); return session->SetSessionChangeSubscription(changeTypes); } CATCH_RETURN() STDAPI XblMultiplayerSessionLeave( _In_ XblMultiplayerSessionHandle session ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(session); return session->Leave(); } CATCH_RETURN() STDAPI XblMultiplayerSessionCurrentUserSetStatus( _In_ XblMultiplayerSessionHandle session, _In_ XblMultiplayerSessionMemberStatus status ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(session); return session->SetCurrentUserStatus(status); } CATCH_RETURN() STDAPI XblMultiplayerSessionCurrentUserSetSecureDeviceAddressBase64( _In_ XblMultiplayerSessionHandle session, _In_ const char* value ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(session); return session->SetCurrentUserSecureDeviceAddressBase64(value); } CATCH_RETURN() #if HC_PLATFORM != HC_PLATFORM_XDK && HC_PLATFORM != HC_PLATFORM_UWP STDAPI XblFormatSecureDeviceAddress( _In_ const char* deviceId, _Inout_ XblFormattedSecureDeviceAddress* address ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(address); RETURN_HR_INVALIDARGUMENT_IF_NULL(deviceId); RETURN_HR_INVALIDARGUMENT_IF_EMPTY_STRING(deviceId); auto sda = utils::format_secure_device_address(deviceId); utils::strcpy(address->value, sizeof(address->value), sda.c_str()); return S_OK; } CATCH_RETURN() #endif STDAPI XblMultiplayerSessionCurrentUserSetRoles( _In_ XblMultiplayerSessionHandle session, _In_ const XblMultiplayerSessionMemberRole* roles, _In_ size_t rolesCount ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(session); RETURN_HR_INVALIDARGUMENT_IF_NULL(roles); return session->SetCurrentUserRoleInfo(xsapi_internal_vector(roles, roles + rolesCount)); } CATCH_RETURN() STDAPI XblMultiplayerSessionCurrentUserSetMembersInGroup( _In_ XblMultiplayerSessionHandle session, _In_reads_(memberIdsCount) uint32_t* memberIds, _In_ size_t memberIdsCount ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(session); RETURN_HR_INVALIDARGUMENT_IF_NULL(memberIds); return session->SetCurrentUserMembersInGroup(xsapi_internal_vector(memberIds, memberIds + memberIdsCount)); } CATCH_RETURN() STDAPI XblMultiplayerSessionCurrentUserSetGroups( _In_ XblMultiplayerSessionHandle session, _In_reads_(groupsCount) const char** groups, _In_ size_t groupsCount ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(session); RETURN_HR_INVALIDARGUMENT_IF_NULL(groups); return session->SetCurrentUserGroups(groups, groupsCount); } CATCH_RETURN() STDAPI XblMultiplayerSessionCurrentUserSetEncounters( _In_ XblMultiplayerSessionHandle session, _In_reads_(encountersCount) const char** encounters, _In_ size_t encountersCount ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(session); RETURN_HR_INVALIDARGUMENT_IF_NULL(encounters); return session->SetCurrentUserEncounters(encounters, encountersCount); } CATCH_RETURN() STDAPI XblMultiplayerSessionCurrentUserSetQosMeasurements( _In_ XblMultiplayerSessionHandle session, _In_z_ const char* measurements ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(session); RETURN_HR_INVALIDARGUMENT_IF_NULL(measurements); return session->SetCurrentUserQosMeasurementsJson(measurements); } CATCH_RETURN() STDAPI XblMultiplayerSessionCurrentUserSetServerQosMeasurements( _In_ XblMultiplayerSessionHandle session, _In_z_ const char* measurements ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(session); RETURN_HR_INVALIDARGUMENT_IF_NULL(measurements); return session->SetCurrentUserServerMeasurementsJson(measurements); } CATCH_RETURN() STDAPI XblMultiplayerSessionCurrentUserSetCustomPropertyJson( _In_ XblMultiplayerSessionHandle session, _In_z_ const char* name, _In_z_ const char* valueJson ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(session); RETURN_HR_INVALIDARGUMENT_IF(name == nullptr || valueJson == nullptr); JsonDocument valueJsonObject; auto hr = JsonUtils::ValidateJson(valueJson, valueJsonObject); if (SUCCEEDED(hr)) { hr = session->SetCurrentUserMemberCustomPropertyJson(name, valueJsonObject); } return hr; } CATCH_RETURN() STDAPI XblMultiplayerSessionCurrentUserDeleteCustomPropertyJson( _In_ XblMultiplayerSessionHandle session, _In_z_ const char* name ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(session); RETURN_HR_INVALIDARGUMENT_IF_NULL(name); return session->DeleteCurrentUserMemberCustomPropertyJson(name); } CATCH_RETURN() STDAPI XblMultiplayerSessionSetMatchmakingTargetSessionConstantsJson( _In_ XblMultiplayerSessionHandle session, _In_ const char* matchmakingTargetSessionConstantsJson ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(session == nullptr || matchmakingTargetSessionConstantsJson == nullptr); return session->SetMatchmakingTargetSessionConstantsJson(matchmakingTargetSessionConstantsJson); } CATCH_RETURN() STDAPI XblMultiplayerSessionSetCustomPropertyJson( _In_ XblMultiplayerSessionHandle session, _In_z_ const char* name, _In_z_ const char* valueJson ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(session); RETURN_HR_INVALIDARGUMENT_IF(name == nullptr || valueJson == nullptr); JsonDocument valueJsonObject; auto hr = JsonUtils::ValidateJson(valueJson, valueJsonObject); if (SUCCEEDED(hr)) { hr = session->SetSessionCustomPropertyJson(name, valueJsonObject); } return hr; } CATCH_RETURN() STDAPI XblMultiplayerSessionDeleteCustomPropertyJson( _In_ XblMultiplayerSessionHandle session, _In_z_ const char* name ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(session); RETURN_HR_INVALIDARGUMENT_IF_NULL(name); return session->DeleteSessionCustomPropertyJson(name); } CATCH_RETURN() STDAPI_(XblMultiplayerSessionChangeTypes) XblMultiplayerSessionCompare( _In_ XblMultiplayerSessionHandle currentSessionHandle, _In_ XblMultiplayerSessionHandle oldSessionHandle ) XBL_NOEXCEPT try { if (currentSessionHandle == nullptr || oldSessionHandle == nullptr) { return XblMultiplayerSessionChangeTypes::None; } return currentSessionHandle->CompareMultiplayerSessions(oldSessionHandle->shared_from_this()); } CATCH_RETURN_WITH(XblMultiplayerSessionChangeTypes::None) ================================================ FILE: Source/Services/Multiplayer/multiplayer_session_member.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "multiplayer_internal.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_CPP_BEGIN XblMultiplayerSessionMember MultiplayerSessionMember::Construct( _In_ bool isCurrentUser, _In_ const xsapi_internal_string& memberIdToWrite, _In_ uint64_t xuid, _In_opt_z_ const char* customConstantsJson, _In_ bool initializeRequested ) { XblMultiplayerSessionMember member{}; auto memberInternal = Make(memberIdToWrite); member.Internal = memberInternal; member.Xuid = xuid; member.IsCurrentUser = isCurrentUser; if (customConstantsJson) { memberInternal->m_customConstantsJson = customConstantsJson; member.CustomConstantsJson = memberInternal->m_customConstantsJson.data(); } member.Nat = XblNetworkAddressTranslationSetting::Unknown; member.InitializationFailureCause = XblMultiplayerMeasurementFailure::None; memberInternal->m_subscribedChangeTypes = XblMultiplayerSessionChangeTypes::None; member.InitializeRequested = initializeRequested; return member; } MultiplayerSessionMember::MultiplayerSessionMember(const xsapi_internal_string& memberIdToWrite) : MultiplayerSessionMember() { m_memberIdToWrite = memberIdToWrite; m_newMember = true; } MultiplayerSessionMember::MultiplayerSessionMember(const MultiplayerSessionMember& other) : m_customConstantsJson(other.m_customConstantsJson), m_customPropertiesString(other.m_customPropertiesString), m_secureDeviceAddressBase64(other.m_secureDeviceAddressBase64), m_roles(other.m_roles), m_teamId(other.m_teamId), m_initialTeam(other.m_initialTeam), m_membersInGroupIds(other.m_membersInGroupIds), m_subscribedChangeTypes(other.m_subscribedChangeTypes), m_matchmakingResultServerMeasurementsJson(other.m_matchmakingResultServerMeasurementsJson), m_serverMeasurementsJson(other.m_serverMeasurementsJson), m_qosMeasurementsJson(other.m_qosMeasurementsJson), m_subscriptionId(other.m_subscriptionId), m_rtaConnectionId(other.m_rtaConnectionId), m_memberIdToWrite(other.m_memberIdToWrite), m_newMember(other.m_newMember), m_writeConstants(other.m_writeConstants), m_writeIsActive(other.m_writeIsActive), m_writeRoleInfo(other.m_writeRoleInfo), m_writeSecureDeviceAddressBase64(other.m_writeSecureDeviceAddressBase64), m_writeQoSMeasurementsJson(other.m_writeQoSMeasurementsJson), m_writeServerMeasurementsJson(other.m_writeServerMeasurementsJson), m_writeMembersInGroup(other.m_writeMembersInGroup), m_writeGroups(other.m_writeGroups), m_writeEncounters(other.m_writeEncounters), m_writeSubscribedChangeTypes(other.m_writeSubscribedChangeTypes), m_writeResults(other.m_writeResults), m_writeCustomPropertiesJson(other.m_writeCustomPropertiesJson) { for (auto group : other.m_groups) { m_groups.push_back(Make(group)); } for (auto encounter : other.m_encounters) { m_encounters.push_back(Make(encounter)); } for (auto& role : m_roles) { role.roleName = Make(role.roleName); role.roleTypeName = Make(role.roleTypeName); } JsonUtils::CopyFrom(m_customPropertiesJson, other.m_customPropertiesJson); JsonUtils::CopyFrom(m_resultsJson, other.m_resultsJson); } MultiplayerSessionMember::~MultiplayerSessionMember() { for (auto group : m_groups) { Delete(group); } for (auto encounter : m_encounters) { Delete(encounter); } for (auto& role : m_roles) { Delete(role.roleName); Delete(role.roleTypeName); } } xsapi_internal_string MultiplayerSessionMember::MemberId() const { return m_member->IsCurrentUser ? "me" : m_memberIdToWrite; } void MultiplayerSessionMember::SetSecureDeviceBaseAddress64(_In_ const xsapi_internal_string& deviceBaseAddress) { std::lock_guard lock{ m_lockMember }; m_secureDeviceAddressBase64 = deviceBaseAddress; m_member->SecureDeviceBaseAddress64 = m_secureDeviceAddressBase64.data(); m_writeSecureDeviceAddressBase64 = true; } void MultiplayerSessionMember::SetRoles(_In_ const xsapi_internal_vector& roles) { std::lock_guard lock{ m_lockMember }; for (auto& role : m_roles) { Delete(role.roleName); Delete(role.roleTypeName); } m_roles = roles; for (auto& role : m_roles) { role.roleName = Make(role.roleName); role.roleTypeName = Make(role.roleTypeName); } m_member->Roles = m_roles.data(); m_member->RolesCount = m_roles.size(); m_writeRoleInfo = true; } const xsapi_internal_vector& MultiplayerSessionMember::GroupsUnsafe() const { return m_groups; } void MultiplayerSessionMember::SetGroups( _In_reads_(groupsCount) const char** groups, _In_ size_t groupsCount ) { std::lock_guard lock{ m_lockMember }; for (auto& group : m_groups) { Delete(group); } m_groups.clear(); for (size_t i = 0; i < groupsCount; ++i) { m_groups.push_back(Make(groups[i])); } m_member->Groups = m_groups.data(); m_member->GroupsCount = m_groups.size(); m_writeGroups = true; } const xsapi_internal_vector& MultiplayerSessionMember::EncountersUnSafe() const { return m_encounters; } void MultiplayerSessionMember::SetEncounters( _In_reads_(encountersCount) const char** encounters, _In_ size_t encountersCount ) { std::lock_guard lock{ m_lockMember }; for (auto& encounter : m_encounters) { Delete(encounter); } m_encounters.clear(); for (uint32_t i = 0; i < encountersCount; ++i) { m_encounters.push_back(Make(encounters[i])); } m_member->Encounters = m_encounters.data(); m_member->EncountersCount = m_encounters.size(); m_writeEncounters = true; } HRESULT MultiplayerSessionMember::SetStatus( _In_ XblMultiplayerSessionMemberStatus status ) { std::lock_guard lock{ m_lockMember }; XSAPI_ASSERT(m_member->IsCurrentUser); if (status != XblMultiplayerSessionMemberStatus::Active && status != XblMultiplayerSessionMemberStatus::Inactive) { return E_INVALIDARG; } m_member->Status = status; m_writeIsActive = true; return S_OK; } void MultiplayerSessionMember::StateLock() const { m_lockMember.lock(); } void MultiplayerSessionMember::StateUnlock() const { m_lockMember.unlock(); } const xsapi_internal_vector& MultiplayerSessionMember::MembersInGroupUnsafe() const { return m_membersInGroupIds; } void MultiplayerSessionMember::SetMembersInGroup( _In_ const xsapi_internal_vector& membersInGroup ) { std::lock_guard lock{ m_lockMember }; m_membersInGroupIds = membersInGroup; m_member->MembersInGroupIds = m_membersInGroupIds.data(); m_member->MembersInGroupCount = static_cast(m_membersInGroupIds.size()); m_writeMembersInGroup = true; } HRESULT MultiplayerSessionMember::SetCustomPropertyJson( _In_ const xsapi_internal_string& name, _In_ const JsonValue& valueJson ) { if (name.empty()) { return E_INVALIDARG; } if (!m_member->IsCurrentUser) { return E_UNEXPECTED; } std::lock_guard lock{ m_lockMember }; auto hr = JsonUtils::SetMember(m_customPropertiesJson, name, valueJson); if (SUCCEEDED(hr)) { m_customPropertiesString = JsonUtils::SerializeJson(m_customPropertiesJson); m_member->CustomPropertiesJson = m_customPropertiesString.data(); m_writeCustomPropertiesJson = true; } return hr; } void MultiplayerSessionMember::DeleteCustomPropertyJson( _In_ const xsapi_internal_string& name ) { SetCustomPropertyJson(name, JsonValue()); } HRESULT MultiplayerSessionMember::SetQosMeasurementsJson( _In_ const xsapi_internal_string& qosMeasurementsJson ) { auto hr = JsonUtils::ValidateJson(qosMeasurementsJson.data()); if (SUCCEEDED(hr)) { std::lock_guard lock{ m_lockMember }; m_qosMeasurementsJson = qosMeasurementsJson; m_member->QosMeasurementsJson = m_qosMeasurementsJson.data(); m_writeQoSMeasurementsJson = true; } return hr; } HRESULT MultiplayerSessionMember::SetServerMeasurementsJson( _In_ const xsapi_internal_string& serverMeasurementsJson ) { auto hr = JsonUtils::ValidateJson(serverMeasurementsJson.data()); if (SUCCEEDED(hr)) { std::lock_guard lock{ m_lockMember }; m_serverMeasurementsJson = serverMeasurementsJson; m_member->ServerMeasurementsJson = m_serverMeasurementsJson.data(); m_writeServerMeasurementsJson = true; } return hr; } XblMultiplayerSessionChangeTypes MultiplayerSessionMember::SubscribedChangeTypes() const { return m_subscribedChangeTypes; } void MultiplayerSessionMember::SetSessionChangeSubscription( _In_ XblMultiplayerSessionChangeTypes changeTypes, _In_ const xsapi_internal_string& subscriptionId ) { std::lock_guard lock{ m_lockMember }; m_subscribedChangeTypes = changeTypes; m_subscriptionId = subscriptionId; m_writeSubscribedChangeTypes = true; } const JsonValue& MultiplayerSessionMember::CustomPropertiesJsonUnsafe() const { return m_customPropertiesJson; } void MultiplayerSessionMember::SetRtaConnectionId( _In_ const xsapi_internal_string& rtaConnectionId ) { std::lock_guard lock{ m_lockMember }; LOGS_DEBUG << "MultiplayerSessionMember::SetRtaConnectionId " << rtaConnectionId; m_rtaConnectionId = rtaConnectionId; } Result MultiplayerSessionMember::Deserialize( _In_ const JsonValue& json ) { XblMultiplayerSessionMember returnResult{}; if (json.IsNull()) { return returnResult; } auto returnResultInternal = Make(); returnResult.Internal = returnResultInternal; bool reserved = false; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonBool(json, "reserved", reserved)); bool active = false; bool ready = false; returnResultInternal->m_customConstantsJson = ""; returnResult.Xuid = 0; returnResult.InitializeRequested = false; returnResultInternal->m_teamId = ""; returnResultInternal->m_initialTeam = ""; returnResultInternal->m_matchmakingResultServerMeasurementsJson = ""; if (json.IsObject() && json.HasMember("constants")) { const JsonValue& constantsJson = json["constants"]; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonFieldAsString(constantsJson, "custom", returnResultInternal->m_customConstantsJson, false)); if (constantsJson.IsObject() && constantsJson.HasMember("custom")) { const JsonValue& constantsCustomJson = constantsJson["custom"]; if (constantsCustomJson.IsObject() && constantsCustomJson.HasMember("matchmakingResult")) { const JsonValue& constantsCustomMatchmakingResultJson = constantsCustomJson["matchmakingResult"]; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(constantsCustomMatchmakingResultJson, "initialTeam", returnResultInternal->m_initialTeam, false)); } } if (constantsJson.IsObject() && constantsJson.HasMember("system")) { const JsonValue& constantsSystemJson = constantsJson["system"]; xsapi_internal_string xuid; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(constantsSystemJson, "xuid", xuid)); returnResult.Xuid = utils::internal_string_to_uint64(xuid); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonBool(constantsSystemJson, "initialize", returnResult.InitializeRequested)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(constantsSystemJson, "team", returnResultInternal->m_teamId)); if (constantsSystemJson.IsObject() && constantsSystemJson.HasMember("matchmakingResult")) { const JsonValue& constantsSystemMatchmakingResultJson = constantsSystemJson["matchmakingResult"]; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonFieldAsString(constantsSystemMatchmakingResultJson, "serverMeasurements", returnResultInternal->m_matchmakingResultServerMeasurementsJson, false)); } } else { //required return WEB_E_INVALID_JSON_STRING; } } else { //required return WEB_E_INVALID_JSON_STRING; } returnResult.CustomConstantsJson = returnResultInternal->m_customConstantsJson.data(); returnResult.InitialTeam = returnResultInternal->m_initialTeam.data(); returnResult.MatchmakingResultServerMeasurementsJson = returnResultInternal->m_matchmakingResultServerMeasurementsJson.data(); returnResultInternal->m_secureDeviceAddressBase64 = ""; returnResultInternal->m_serverMeasurementsJson = ""; returnResultInternal->m_qosMeasurementsJson = ""; returnResultInternal->m_customPropertiesString = ""; if (json.IsObject() && json.HasMember("properties")) { const JsonValue& propertiesJson = json["properties"]; if (propertiesJson.IsObject() && propertiesJson.HasMember("custom")) { JsonUtils::CopyFrom(returnResultInternal->m_customPropertiesJson, propertiesJson["custom"]); returnResultInternal->m_customPropertiesString = JsonUtils::SerializeJson(returnResultInternal->m_customPropertiesJson); } if (propertiesJson.IsObject() && propertiesJson.HasMember("system")) { const JsonValue& propertiesSystemJson = propertiesJson["system"]; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(propertiesSystemJson, "secureDeviceAddress", returnResultInternal->m_secureDeviceAddressBase64)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonFieldAsString(propertiesSystemJson, "serverMeasurements", returnResultInternal->m_serverMeasurementsJson, false)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonFieldAsString(propertiesSystemJson, "measurements", returnResultInternal->m_qosMeasurementsJson, false)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonVector(JsonUtils::JsonIntExtractor, propertiesSystemJson, "initializationGroup", returnResultInternal->m_membersInGroupIds, false)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonBool(propertiesSystemJson, "active", active)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonBool(propertiesSystemJson, "ready", ready)); if (propertiesSystemJson.IsObject() && propertiesSystemJson.HasMember("subscription")) { const JsonValue& propertiesSystemSubscriptionJson = propertiesSystemJson["subscription"]; xsapi_internal_vector changeTypes; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonVector(JsonUtils::JsonStringExtractor, propertiesSystemSubscriptionJson, "changeTypes", changeTypes, false)); returnResultInternal->m_subscribedChangeTypes = Serializers::MultiplayerSessionChangeTypesFromStringVector(changeTypes); } if (propertiesSystemJson.IsObject() && propertiesSystemJson.HasMember("groups")) { RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonVector(JsonUtils::JsonUtf8Extractor, propertiesSystemJson, "groups", returnResultInternal->m_groups, false)); } if (propertiesSystemJson.IsObject() && propertiesSystemJson.HasMember("encounters")) { RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonVector(JsonUtils::JsonUtf8Extractor, propertiesSystemJson, "encounters", returnResultInternal->m_encounters, false)); } } else { //required return WEB_E_INVALID_JSON_STRING; } } else { //required return WEB_E_INVALID_JSON_STRING; } returnResult.SecureDeviceBaseAddress64 = returnResultInternal->m_secureDeviceAddressBase64.data(); returnResult.ServerMeasurementsJson = returnResultInternal->m_serverMeasurementsJson.data(); returnResult.QosMeasurementsJson = returnResultInternal->m_qosMeasurementsJson.data(); returnResult.MembersInGroupIds = returnResultInternal->m_membersInGroupIds.data(); returnResult.MembersInGroupCount = static_cast(returnResultInternal->m_membersInGroupIds.size()); returnResult.Groups = returnResultInternal->m_groups.data(); returnResult.GroupsCount = static_cast(returnResultInternal->m_groups.size()); returnResult.Encounters = returnResultInternal->m_encounters.data(); returnResult.EncountersCount = static_cast(returnResultInternal->m_encounters.size()); returnResult.CustomPropertiesJson = returnResultInternal->m_customPropertiesString.data(); if (active) { returnResult.Status = XblMultiplayerSessionMemberStatus::Active; } else if (ready) { returnResult.Status = XblMultiplayerSessionMemberStatus::Ready; } else if (reserved) { returnResult.Status = XblMultiplayerSessionMemberStatus::Reserved; } else { returnResult.Status = XblMultiplayerSessionMemberStatus::Inactive; } xsapi_internal_string gamertag, deviceToken, nat; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "gamertag", gamertag)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "deviceToken", deviceToken)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "nat", nat)); utils::strcpy(returnResult.Gamertag, sizeof(returnResult.Gamertag), gamertag.c_str()); utils::strcpy(returnResult.DeviceToken.Value, sizeof(returnResult.DeviceToken.Value), deviceToken.c_str()); returnResult.Nat = Serializers::MultiplayerNatSettingFromString(nat); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonBool(json, "turn", returnResult.IsTurnAvailable)); if (json.IsObject() && json.HasMember("roles")) { const JsonValue& rolesJson = json["roles"]; if (!rolesJson.IsNull() && rolesJson.IsObject()) { for (const auto& rolePair : rolesJson.GetObject()) { XblMultiplayerSessionMemberRole role{}; role.roleTypeName = Make(rolePair.name.GetString()); role.roleName = Make(rolePair.value.GetString()); returnResultInternal->m_roles.push_back(std::move(role)); } returnResult.Roles = returnResultInternal->m_roles.data(); returnResult.RolesCount = returnResultInternal->m_roles.size(); } } xsapi_internal_string titleIdString; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "activeTitleId", titleIdString)); if (!titleIdString.empty()) { returnResult.ActiveTitleId = utils::internal_string_to_uint32(titleIdString); } RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonTimeT(json, "joinTime", returnResult.JoinTime)); xsapi_internal_string initializationFailure; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "initializationFailure", initializationFailure)) returnResult.InitializationFailureCause = Serializers::MultiplayerMeasurementFailureFromString(initializationFailure); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonInt(json, "initializationEpisode", returnResult.InitializationEpisode)); return returnResult; } xsapi_internal_vector MultiplayerSessionMember::GetVectorViewForChangeTypes( _In_ XblMultiplayerSessionChangeTypes changeTypes ) { xsapi_internal_vector resultVector; if ((changeTypes & XblMultiplayerSessionChangeTypes::Everything) == XblMultiplayerSessionChangeTypes::Everything) { resultVector.push_back("everything"); } if ((changeTypes & XblMultiplayerSessionChangeTypes::HostDeviceTokenChange) == XblMultiplayerSessionChangeTypes::HostDeviceTokenChange) { resultVector.push_back("host"); } if ((changeTypes & XblMultiplayerSessionChangeTypes::InitializationStateChange) == XblMultiplayerSessionChangeTypes::InitializationStateChange) { resultVector.push_back("initialization"); } if ((changeTypes & XblMultiplayerSessionChangeTypes::MatchmakingStatusChange) == XblMultiplayerSessionChangeTypes::MatchmakingStatusChange) { resultVector.push_back("matchmakingStatus"); } if ((changeTypes & XblMultiplayerSessionChangeTypes::MemberListChange) == XblMultiplayerSessionChangeTypes::MemberListChange) { resultVector.push_back("membersList"); } if ((changeTypes & XblMultiplayerSessionChangeTypes::MemberStatusChange) == XblMultiplayerSessionChangeTypes::MemberStatusChange) { resultVector.push_back("membersStatus"); } if ((changeTypes & XblMultiplayerSessionChangeTypes::SessionJoinabilityChange) == XblMultiplayerSessionChangeTypes::SessionJoinabilityChange) { resultVector.push_back("joinability"); } if ((changeTypes & XblMultiplayerSessionChangeTypes::CustomPropertyChange) == XblMultiplayerSessionChangeTypes::CustomPropertyChange) { resultVector.push_back("customProperty"); } if ((changeTypes & XblMultiplayerSessionChangeTypes::MemberCustomPropertyChange) == XblMultiplayerSessionChangeTypes::MemberCustomPropertyChange) { resultVector.push_back("membersCustomProperty"); } return resultVector; } void MultiplayerSessionMember::Serialize(_Out_ JsonValue& json, _In_ JsonDocument::AllocatorType& allocator) { std::lock_guard lock{ m_lockMember }; json.SetObject(); if (m_newMember) { JsonValue systemConstantsJson(rapidjson::kObjectType); systemConstantsJson.AddMember("xuid", JsonValue(utils::uint64_to_internal_string(m_member->Xuid).c_str(), allocator).Move(), allocator); if (m_member->InitializeRequested) { systemConstantsJson.AddMember("initialize", m_member->InitializeRequested, allocator); } JsonValue constantsJson(rapidjson::kObjectType); constantsJson.AddMember("system", systemConstantsJson, allocator); if (!m_customConstantsJson.empty()) { JsonDocument customJson{ &allocator }; customJson.Parse(m_customConstantsJson.data()); if (!customJson.IsNull()) { constantsJson.AddMember("custom", customJson, allocator); } } json.AddMember("constants", constantsJson, allocator); } if (m_newMember || m_member->IsCurrentUser) { JsonValue propertiesJson(rapidjson::kObjectType); JsonValue systemPropertiesJson(rapidjson::kObjectType); if (m_writeIsActive) { bool isActive = m_member->Status == XblMultiplayerSessionMemberStatus::Active; systemPropertiesJson.AddMember("active", isActive, allocator); if (!isActive) { systemPropertiesJson.AddMember("ready", isActive, allocator); } else { if (!m_rtaConnectionId.empty()) { systemPropertiesJson.AddMember("connection", JsonValue(m_rtaConnectionId.c_str(), allocator).Move(), allocator); LOGS_DEBUG << "MultiplayerSessionMember::Serialize " << m_rtaConnectionId << " for " << m_member->Xuid; } } } if (m_writeRoleInfo && m_member->RolesCount > 0) { JsonValue rolesJson(rapidjson::kObjectType); for (uint32_t i = 0; i < m_member->RolesCount; ++i) { rolesJson.AddMember(JsonValue(m_member->Roles[i].roleTypeName, allocator).Move(), JsonValue(m_member->Roles[i].roleName, allocator).Move(), allocator); } json.AddMember("roles", rolesJson, allocator); } if (m_writeSubscribedChangeTypes) { JsonValue subscriptionJson(rapidjson::kObjectType); subscriptionJson.AddMember("id", JsonValue(m_subscriptionId.c_str(), allocator).Move(), allocator); JsonValue changeTypesJson; JsonUtils::SerializeVector(JsonUtils::JsonStringSerializer, GetVectorViewForChangeTypes(m_subscribedChangeTypes), changeTypesJson, allocator); subscriptionJson.AddMember("changeTypes", changeTypesJson, allocator); systemPropertiesJson.AddMember("subscription", subscriptionJson, allocator); } if (m_writeSecureDeviceAddressBase64) { systemPropertiesJson.AddMember("secureDeviceAddress", JsonValue(m_member->SecureDeviceBaseAddress64, allocator).Move(), allocator); } if (m_writeMembersInGroup) { JsonValue initializationGroupJson(rapidjson::kArrayType); JsonUtils::SerializeVector(JsonUtils::JsonIntSerializer, m_membersInGroupIds, initializationGroupJson, allocator); systemPropertiesJson.AddMember("initializationGroup", initializationGroupJson, allocator); } if (m_writeGroups) { JsonValue groupsJson(rapidjson::kArrayType); JsonUtils::SerializeVector(JsonUtils::JsonUtf8Serializer, m_groups, groupsJson, allocator); systemPropertiesJson.AddMember("groups", groupsJson, allocator); } if (m_writeEncounters) { JsonValue encountersJson(rapidjson::kArrayType); JsonUtils::SerializeVector(JsonUtils::JsonUtf8Serializer, m_encounters, encountersJson, allocator); systemPropertiesJson.AddMember("encounters", encountersJson, allocator); } if (m_writeQoSMeasurementsJson) { JsonDocument measurementsJson{ &allocator }; measurementsJson.Parse(m_qosMeasurementsJson.data()); systemPropertiesJson.AddMember("measurements", measurementsJson, allocator); } if (m_writeServerMeasurementsJson) { JsonDocument serverMeasurementsJson{ &allocator }; serverMeasurementsJson.Parse(m_serverMeasurementsJson.data()); systemPropertiesJson.AddMember("serverMeasurements", serverMeasurementsJson, allocator); } if (systemPropertiesJson.MemberCount()) { propertiesJson.AddMember("system", systemPropertiesJson, allocator); } if (m_writeCustomPropertiesJson) { propertiesJson.AddMember("custom", JsonValue{}.CopyFrom(m_customPropertiesJson, allocator).Move(), allocator); } if (propertiesJson.MemberCount()) { json.AddMember("properties", propertiesJson, allocator); } } } MultiplayerSessionMember* MultiplayerSessionMember::Get(const XblMultiplayerSessionMember* member) { if (member == nullptr || member->Internal == nullptr) { XSAPI_ASSERT(false); } return static_cast(member->Internal); } void MultiplayerSessionMember::SetExternalMemberPointer(XblMultiplayerSessionMember& member) { auto internalMember = Get(&member); internalMember->m_member = &member; member.InitialTeam = internalMember->m_initialTeam.empty() ? nullptr : internalMember->m_initialTeam.data(); member.CustomConstantsJson = internalMember->m_customConstantsJson.empty() ? nullptr :internalMember->m_customConstantsJson.data(); member.SecureDeviceBaseAddress64 = internalMember->m_secureDeviceAddressBase64.empty() ? nullptr : internalMember->m_secureDeviceAddressBase64.data(); member.Roles = internalMember->m_roles.empty() ? nullptr : internalMember->m_roles.data(); member.CustomPropertiesJson = internalMember->m_customPropertiesString.empty() ? nullptr : internalMember->m_customPropertiesString.data(); member.MatchmakingResultServerMeasurementsJson = internalMember->m_matchmakingResultServerMeasurementsJson.empty() ? nullptr : internalMember->m_matchmakingResultServerMeasurementsJson.data(); member.ServerMeasurementsJson = internalMember->m_serverMeasurementsJson.empty() ? nullptr : internalMember->m_serverMeasurementsJson.data(); member.MembersInGroupIds = internalMember->m_membersInGroupIds.empty() ? nullptr : internalMember->m_membersInGroupIds.data(); member.QosMeasurementsJson = internalMember->m_qosMeasurementsJson.empty() ? nullptr : internalMember->m_qosMeasurementsJson.data(); member.Groups = internalMember->m_groups.empty() ? nullptr : internalMember->m_groups.data(); member.Encounters = internalMember->m_encounters.empty() ? nullptr : internalMember->m_encounters.data(); } NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_CPP_END ================================================ FILE: Source/Services/Multiplayer/multiplayer_session_reference.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "multiplayer_internal.h" using namespace xbox::services; using namespace xbox::services::legacy; using namespace xbox::services::multiplayer; STDAPI XblMultiplayerSessionReferenceParseFromUriPath( _In_ const char* path, _Out_ XblMultiplayerSessionReference* ref ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(ref); // / 0 / 1 / 2 / 3 / 4 / 5 // /serviceconfigs/{scid}/sessiontemplates/{session-template-name}/sessions/{session-name} xsapi_internal_vector pathComponents = utils::string_split_internal(xsapi_internal_string(path), '/'); if (pathComponents.size() < 6) { return E_INVALIDARG; } memset(ref, 0, sizeof(XblMultiplayerSessionReference)); utils::strcpy(ref->Scid, sizeof(ref->Scid), pathComponents.at(1).data()); utils::strcpy(ref->SessionTemplateName, sizeof(ref->SessionTemplateName), pathComponents.at(3).data()); utils::strcpy(ref->SessionName, sizeof(ref->SessionName), pathComponents.at(5).data()); return S_OK; } CATCH_RETURN() STDAPI XblMultiplayerSessionReferenceToUriPath( _In_ const XblMultiplayerSessionReference* sessionReference, _Out_ XblMultiplayerSessionReferenceUri* sessionReferenceUri ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(sessionReference == nullptr || sessionReferenceUri == nullptr); xsapi_internal_stringstream uriStream; uriStream << "/serviceconfigs/"; uriStream << sessionReference->Scid; uriStream << "/sessiontemplates/"; uriStream << sessionReference->SessionTemplateName; uriStream << "/sessions/"; uriStream << sessionReference->SessionName; memset(sessionReferenceUri, 0, sizeof(XblMultiplayerSessionReferenceUri)); utils::strcpy(sessionReferenceUri->value, sizeof(sessionReferenceUri->value), uriStream.str().data()); return S_OK; } CATCH_RETURN() /// /// Checks whether an XblMultiplayerSessionReference is well formed. It is considered well formed if none of the /// fields are empty strings. /// STDAPI_(bool) XblMultiplayerSessionReferenceIsValid( _In_ const XblMultiplayerSessionReference* sessionReference ) XBL_NOEXCEPT try { if (sessionReference == nullptr) { return false; } return sessionReference->Scid[0] != 0 && sessionReference->SessionTemplateName[0] != 0 && sessionReference->SessionName[0] != 0; } CATCH_RETURN() /// /// Creates an XblMultiplayerSessionReference from a scid, session template name, and session name. /// STDAPI_(XblMultiplayerSessionReference) XblMultiplayerSessionReferenceCreate( _In_z_ const char* scid, _In_z_ const char* sessionTemplateName, _In_z_ const char* sessionName ) XBL_NOEXCEPT try { XblMultiplayerSessionReference out{}; if (scid != nullptr) { utils::strcpy(out.Scid, sizeof(out.Scid), scid); } if (sessionTemplateName != nullptr) { utils::strcpy(out.SessionTemplateName, sizeof(out.SessionTemplateName), sessionTemplateName); } if (sessionName != nullptr) { utils::strcpy(out.SessionName, sizeof(out.SessionName), sessionName); } return out; } CATCH_RETURN_WITH({}) NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_CPP_BEGIN bool operator==(const XblMultiplayerSessionReference& lhs, const XblMultiplayerSessionReference& rhs) { return utils::str_icmp(lhs.Scid, rhs.Scid) == 0 && utils::str_icmp(lhs.SessionName, rhs.SessionName) == 0 && utils::str_icmp(lhs.SessionTemplateName, rhs.SessionTemplateName) == 0; } NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_CPP_END ================================================ FILE: Source/Services/Multiplayer/multiplayer_subscription.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "multiplayer_internal.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_CPP_BEGIN const char mp_default_resourceUri[] = "https://sessiondirectory.xboxlive.com/connections/"; MultiplayerSubscription::MultiplayerSubscription() noexcept { m_resourceUri = mp_default_resourceUri; } const String& MultiplayerSubscription::RtaConnectionId() const { return m_connectionId; } XblFunctionContext MultiplayerSubscription::AddSessionChangedHandler( SessionChangedHandler handler ) noexcept { std::lock_guard lock{ m_mutexMultiplayerSubscription }; m_sessionChangedHandlers[m_nextToken] = std::move(handler); return m_nextToken++; } size_t MultiplayerSubscription::RemoveSessionChangedHandler( XblFunctionContext token ) noexcept { std::lock_guard lock{ m_mutexMultiplayerSubscription }; m_sessionChangedHandlers.erase(token); return m_sessionChangedHandlers.size(); } XblFunctionContext MultiplayerSubscription::AddConnectionIdChangedHandler( ConnectionIdChangedHandler handler ) noexcept { std::lock_guard lock{ m_mutexMultiplayerSubscription }; m_connectionIdChangedHandlers[m_nextToken] = std::move(handler); return m_nextToken++; } size_t MultiplayerSubscription::RemoveConnectionIdChangedHandler( XblFunctionContext token ) noexcept { std::lock_guard lock{ m_mutexMultiplayerSubscription }; m_connectionIdChangedHandlers.erase(token); return m_connectionIdChangedHandlers.size(); } void MultiplayerSubscription::OnSubscribe( _In_ const JsonValue& data ) noexcept { std::unique_lock lock{ m_mutexMultiplayerSubscription }; HRESULT hr = JsonUtils::ExtractJsonString(data, "ConnectionId", m_connectionId, true); if (FAILED(hr)) { LOGS_ERROR << __FUNCTION__ << ": Ignoring malformed payload"; return; } auto handlers{ m_connectionIdChangedHandlers }; lock.unlock(); for (auto& handler : handlers) { handler.second(m_connectionId); } } void MultiplayerSubscription::OnEvent( _In_ const JsonValue& data ) noexcept { if (!data.IsObject() || !data.HasMember("shoulderTaps")) { LOGS_ERROR << __FUNCTION__ << ": Ignoring malformed payload"; return; } List taps; const JsonValue& shoulderTaps = data["shoulderTaps"]; if (shoulderTaps.IsArray()) { for (const auto& tapValue : shoulderTaps.GetArray()) { String resourceName; JsonUtils::ExtractJsonString(tapValue, "resource", resourceName, true); Vector nameComponents = utils::string_split_internal(resourceName, '~'); if (nameComponents.size() != 3) { LOGS_ERROR << __FUNCTION__ << ": Resource has too many values"; continue; } taps.emplace_back(); auto& tap{ taps.back() }; tap.SessionReference = XblMultiplayerSessionReferenceCreate( nameComponents[0].data(), nameComponents[1].data(), nameComponents[2].data() ); JsonUtils::ExtractJsonInt(tapValue, "changeNumber", tap.ChangeNumber, false); JsonUtils::ExtractJsonStringToCharArray(tapValue, "branch", tap.Branch, sizeof(tap.Branch)); LOGS_DEBUG << __FUNCTION__ << ": Resource=" << resourceName; } } std::unique_lock lock{ m_mutexMultiplayerSubscription }; auto handlers{ m_sessionChangedHandlers }; lock.unlock(); for (auto& handler : handlers) { for (auto& tap : taps) { handler.second(tap); } } } NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_CPP_END ================================================ FILE: Source/Services/Multiplayer/multiplayer_transfer_handle_post_request.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "multiplayer_internal.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_CPP_BEGIN MultiplayerTransferHandlePostRequest::MultiplayerTransferHandlePostRequest( _In_ XblMultiplayerSessionReference targetSessionReference, _In_ XblMultiplayerSessionReference originSessionReference ) : m_originSessionReference(std::move(originSessionReference)), m_targetSessionReference(std::move(targetSessionReference)) { XSAPI_ASSERT(XblMultiplayerSessionReferenceIsValid(&m_targetSessionReference)); XSAPI_ASSERT(XblMultiplayerSessionReferenceIsValid(&m_originSessionReference)); } const XblMultiplayerSessionReference& MultiplayerTransferHandlePostRequest::OriginSessionReference() const { return m_originSessionReference; } const XblMultiplayerSessionReference& MultiplayerTransferHandlePostRequest::TargetSessionReference() const { return m_targetSessionReference; } void MultiplayerTransferHandlePostRequest::Serialize(_Out_ JsonValue& json, _In_ JsonDocument::AllocatorType& allocator) const { json.SetObject(); json.AddMember("type", "transfer", allocator); JsonValue targetSessionJson; Serializers::SerializeSessionReference(m_targetSessionReference, targetSessionJson, allocator); json.AddMember("sessionRef", targetSessionJson, allocator); json.AddMember("version", MULTIPLAYER_HANDLE_VERSION, allocator); JsonValue originSessionJson; Serializers::SerializeSessionReference(m_originSessionReference, originSessionJson, allocator); json.AddMember("originSessionRef", originSessionJson, allocator); } NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_CPP_END ================================================ FILE: Source/Services/MultiplayerActivity/multiplayer_activity_api.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "xbox_live_context_internal.h" #include "multiplayer_activity_internal.h" #ifdef XSAPI_NOTIFICATION_SERVICE #include "notification_internal.h" #endif using namespace xbox::services::multiplayer_activity; #ifdef XSAPI_NOTIFICATION_SERVICE using namespace xbox::services::notification; #endif STDAPI XblMultiplayerActivityUpdateRecentPlayers( _In_ XblContextHandle xblContextHandle, _In_reads_(updatesCount) const XblMultiplayerActivityRecentPlayerUpdate* updates, _In_ size_t updatesCount ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(xblContextHandle == nullptr); return xblContextHandle->MultiplayerActivityService()->UpdateRecentPlayers(updates, updatesCount); } CATCH_RETURN() STDAPI XblMultiplayerActivityFlushRecentPlayersAsync( _In_ XblContextHandle xblContextHandle, _In_ XAsyncBlock* async ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(xblContextHandle == nullptr); return RunAsync(async, __FUNCTION__, [ xblContext{ xblContextHandle->shared_from_this() } ] (XAsyncOp op, const XAsyncProviderData* data) { switch (op) { case XAsyncOp::DoWork: { RETURN_HR_IF_FAILED(xblContext->MultiplayerActivityService()->FlushRecentPlayers(data->async)); return E_PENDING; } default: { return S_OK; } } }); } CATCH_RETURN() STDAPI XblMultiplayerActivitySetActivityAsync( _In_ XblContextHandle xblContextHandle, _In_ const XblMultiplayerActivityInfo* activityInfo, _In_ bool allowCrossPlatformJoin, _In_ XAsyncBlock* async ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(xblContextHandle == nullptr || activityInfo == nullptr || activityInfo->connectionString == nullptr); return RunAsync(async, __FUNCTION__, [ xblContext{ xblContextHandle->shared_from_this() }, info = ActivityInfo{ activityInfo }, allowCrossPlatformJoin ] (XAsyncOp op, const XAsyncProviderData* data) { switch (op) { case XAsyncOp::DoWork: { RETURN_HR_IF_FAILED(xblContext->MultiplayerActivityService()->SetActivity(info, allowCrossPlatformJoin, data->async)); return E_PENDING; } default: { return S_OK; } } }); } CATCH_RETURN() STDAPI XblMultiplayerActivityGetActivityAsync( _In_ XblContextHandle xblContextHandle, _In_reads_(xuidsCount) const uint64_t* xuidsPtr, _In_ size_t xuidsCount, _In_ XAsyncBlock* async ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(xblContextHandle == nullptr || xuidsPtr == nullptr || xuidsCount == 0); return RunAsync(async, __FUNCTION__, [ xblContext{ xblContextHandle->shared_from_this() }, xuids = Vector(xuidsPtr, xuidsPtr + xuidsCount), activityInfo = Vector{} ] (XAsyncOp op, const XAsyncProviderData* data) mutable { switch (op) { case XAsyncOp::DoWork: { RETURN_HR_IF_FAILED(xblContext->MultiplayerActivityService()->GetActivity(xuids, { data->async->queue, [ &activityInfo, async{ data->async } ] (Result> result) { size_t requiredBufferSize{ 0 }; if (Succeeded(result)) { activityInfo = result.ExtractPayload(); for (const auto& a : activityInfo) { requiredBufferSize += sizeof(XblMultiplayerActivityInfo); if (!a.connectionString.empty()) { requiredBufferSize += (a.connectionString.size() + 1); } if (!a.groupId.empty()) { requiredBufferSize += (a.groupId.size() + 1); } } // Add padding to store the arraysize requiredBufferSize += sizeof(size_t); } XAsyncComplete(async, result.Hresult(), requiredBufferSize); } })); return E_PENDING; } case XAsyncOp::GetResult: { auto activityCountPtr{ static_cast(data->buffer) }; auto activityInfoPtr{ reinterpret_cast(activityCountPtr + 1) }; auto stringPtr{ reinterpret_cast(activityInfoPtr + activityInfo.size()) }; size_t bufferSize{ sizeof(size_t) }; *activityCountPtr = activityInfo.size(); for (const auto& a : activityInfo) { new (activityInfoPtr) XblMultiplayerActivityInfo { a.xuid, nullptr, a.joinRestriction, a.maxPlayers, a.currentPlayers, nullptr, a.platform }; if (!a.connectionString.empty()) { utils::strcpy(stringPtr, a.connectionString.size() + 1, a.connectionString.data()); activityInfoPtr->connectionString = stringPtr; stringPtr += (a.connectionString.size() + 1); bufferSize += (a.connectionString.size() + 1); } if (!a.groupId.empty()) { utils::strcpy(stringPtr, a.groupId.size() + 1, a.groupId.data()); activityInfoPtr->groupId = stringPtr; stringPtr += (a.groupId.size() + 1); bufferSize += (a.groupId.size() + 1); } bufferSize += sizeof(XblMultiplayerActivityInfo); ++activityInfoPtr; } assert(static_cast(data->buffer) + bufferSize == stringPtr); return S_OK; } default: { return S_OK; } } }); } CATCH_RETURN() STDAPI XblMultiplayerActivityGetActivityResultSize( _In_ XAsyncBlock* async, _Out_ size_t* resultSizeInBytes ) XBL_NOEXCEPT try { return XAsyncGetResultSize(async, resultSizeInBytes); } CATCH_RETURN() STDAPI XblMultiplayerActivityGetActivityResult( _In_ XAsyncBlock* async, _In_ size_t bufferSize, _Out_writes_bytes_to_(bufferSize, *bufferUsed) void* buffer, _Outptr_ XblMultiplayerActivityInfo** results, _Out_ size_t* resultCount, _Out_opt_ size_t* bufferUsed ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(results == nullptr || resultCount == nullptr); auto hr = XAsyncGetResult(async, nullptr, bufferSize, buffer, bufferUsed); if (SUCCEEDED(hr)) { auto resultCountPtr{ static_cast(buffer) }; *resultCount = *resultCountPtr; *results = reinterpret_cast(resultCountPtr + 1); } return hr; } CATCH_RETURN() STDAPI XblMultiplayerActivityDeleteActivityAsync( _In_ XblContextHandle xblContextHandle, _In_ XAsyncBlock* async ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(xblContextHandle); return RunAsync(async, __FUNCTION__, [ xblContext{ xblContextHandle->shared_from_this() } ] (XAsyncOp op, const XAsyncProviderData* data) { switch (op) { case XAsyncOp::DoWork: { RETURN_HR_IF_FAILED(xblContext->MultiplayerActivityService()->DeleteActivity(data->async)); return E_PENDING; } default: { return S_OK; } } }); } CATCH_RETURN() STDAPI XblMultiplayerActivitySendInvitesAsync( _In_ XblContextHandle xblContextHandle, _In_ const uint64_t* xuidsPtr, _In_ size_t xuidsCount, _In_ bool allowCrossPlatformJoin, _In_opt_z_ const char* _connectionString, _In_ XAsyncBlock* async ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(xblContextHandle == nullptr || xuidsPtr == nullptr || xuidsCount == 0); return RunAsync(async, __FUNCTION__, [ xblContext{ xblContextHandle->shared_from_this() }, xuids{ Vector(xuidsPtr, xuidsPtr + xuidsCount) }, allowCrossPlatformJoin, connectionString = _connectionString ? String{ _connectionString } : String{} ] (XAsyncOp op, const XAsyncProviderData* data) { switch (op) { case XAsyncOp::DoWork: { RETURN_HR_IF_FAILED(xblContext->MultiplayerActivityService()->SendInvites(xuids, allowCrossPlatformJoin, connectionString, data->async)); return E_PENDING; } default: { return S_OK; } } }); } CATCH_RETURN() #if (HC_PLATFORM == HC_PLATFORM_WIN32 || HC_PLATFORM_IS_EXTERNAL) && !defined(XSAPI_UNIT_TESTS) STDAPI_(XblFunctionContext) XblMultiplayerActivityAddInviteHandler( _In_ XblContextHandle xblContext, _In_ XblMultiplayerActivityInviteHandler* handler, _In_opt_ void* context ) XBL_NOEXCEPT try { if (xblContext == nullptr || handler == nullptr) { return XblFunctionContext{ 0 }; } auto rtaNotificationService = std::dynamic_pointer_cast(xblContext->NotificationService()); return rtaNotificationService->AddGameInviteHandler(NotificationSubscription::MultiplayerActivityInviteHandler{ [ handler, context ] (const notification::MultiplayerActivityInviteData& args) { try { handler(&args, context); } catch (...) { LOGS_ERROR << __FUNCTION__ << ": exception in client handler!"; } } }); } CATCH_RETURN() STDAPI XblMultiplayerActivityRemoveInviteHandler( _In_ XblContextHandle xblContext, _In_ XblFunctionContext token ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(xblContext); auto rtaNotificationService = std::dynamic_pointer_cast(xblContext->NotificationService()); rtaNotificationService->RemoveNotificationHandler(token); return S_OK; } CATCH_RETURN() #endif ================================================ FILE: Source/Services/MultiplayerActivity/multiplayer_activity_info.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "multiplayer_activity_internal.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN namespace multiplayer_activity { ActivityInfo::ActivityInfo(const XblMultiplayerActivityInfo* info) noexcept : XblMultiplayerActivityInfo{ *info }, connectionString{ info->connectionString } { if (info->groupId) { groupId = info->groupId; } } ActivityInfo::ActivityInfo(uint64_t xuid) noexcept : XblMultiplayerActivityInfo{ xuid } { } Result> ActivityInfo::Deserialize( const JsonValue& responseJson, uint32_t titleId ) noexcept { if (!responseJson.HasMember("userActivities") || !responseJson["userActivities"].IsArray()) { return WEB_E_INVALID_JSON_STRING; } Vector result; const auto& users{ responseJson["userActivities"].GetArray() }; for (const auto& user : users) { uint64_t xuid{}; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonXuid(user, "userId", xuid, true)); // Users may have multiple activities, but we only care about their activities within our title auto activities = JsonUtils::ExtractJsonArray(user, "activities", true); for (const auto& activity : activities) { uint32_t activityTitleId{}; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonInt(activity, "titleId", activityTitleId, true)); if (activityTitleId == titleId) { ActivityInfo i{ xuid }; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(activity, "connectionString", i.connectionString)); String joinRestriction; JsonUtils::ExtractJsonString(activity, "joinRestriction", joinRestriction, false); i.joinRestriction = EnumValue(joinRestriction.data()); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonSizeT(activity, "maxPlayers", i.maxPlayers)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonSizeT(activity, "currentPlayers", i.currentPlayers)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(activity, "groupId", i.groupId)); String platform; JsonUtils::ExtractJsonString(activity, "platform", platform, false); i.platform = EnumValue(XblMultiplayerActivityPlatform::All)>(platform.data()); result.push_back(i); } } } return result; } } NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END ================================================ FILE: Source/Services/MultiplayerActivity/multiplayer_activity_internal.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "xsapi-c/multiplayer_activity_c.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN namespace multiplayer_activity { class ActivityInfo : public XblMultiplayerActivityInfo { public: ActivityInfo(const XblMultiplayerActivityInfo* info) noexcept; static Result> Deserialize( const JsonValue& responseJson, uint32_t titleId ) noexcept; String connectionString; String groupId; private: ActivityInfo(uint64_t xuid) noexcept; }; class MultiplayerActivityService : public std::enable_shared_from_this { public: MultiplayerActivityService( _In_ User&& user, _In_ const TaskQueue& queue, _In_ std::shared_ptr settings ) noexcept; ~MultiplayerActivityService() noexcept; HRESULT UpdateRecentPlayers( _In_reads_(updatesCount) const XblMultiplayerActivityRecentPlayerUpdate* updates, _In_ size_t updatesCount ) noexcept; HRESULT FlushRecentPlayers( _In_ AsyncContext>&& async ) noexcept; HRESULT SetActivity( _In_ const ActivityInfo& info, _In_ bool allowCrossPlatformJoin, _In_ AsyncContext async ) const noexcept; HRESULT GetActivity( _In_ const Vector& xuids, _In_ AsyncContext>> async ) const noexcept; HRESULT DeleteActivity( _In_ AsyncContext async ) const noexcept; HRESULT SendInvites( _In_ const Vector& xuids, _In_ bool allowCrossPlatformJoin, _In_ const String& connectionString, _In_ AsyncContext async ) const noexcept; private: struct RecentPlayerUpdateMetadata { time_t timestamp{}; XblMultiplayerActivityEncounterType encounterType{}; }; UnorderedMap m_pendingRecentPlayerUpdates{}; bool m_recentPlayersUpdateScheduled{ false }; static uint64_t GetSequenceNumber(); void ScheduleRecentPlayersUpdate() noexcept; static XblMultiplayerActivityPlatform GetLocalPlatform() noexcept; User m_user; TaskQueue m_queue; std::shared_ptr m_xboxLiveContextSettings; uint32_t m_titleId{ AppConfig::Instance()->TitleId() }; mutable std::mutex m_mutex{}; }; } NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END ================================================ FILE: Source/Services/MultiplayerActivity/multiplayer_activity_service.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "multiplayer_activity_internal.h" #if HC_PLATFORM == HC_PLATFORM_GDK #include #endif #define RECENT_PLAYERS_UPLOAD_INTERVAL_MS 5000 #define MPA_SERVICE_NAME "multiplayeractivity" NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN namespace multiplayer_activity { constexpr auto PlatformName = EnumName(XblMultiplayerActivityPlatform::All)>; MultiplayerActivityService::MultiplayerActivityService( _In_ User&& user, _In_ const TaskQueue& queue, _In_ std::shared_ptr settings ) noexcept : m_user{ std::move(user) }, m_queue{ queue.DeriveWorkerQueue() }, m_xboxLiveContextSettings{ std::move(settings) } { } MultiplayerActivityService::~MultiplayerActivityService() noexcept { FlushRecentPlayers(m_queue); // Terminating m_queue to cancel next scheduled flush. FlushRecentPlayers call above will not be canceled // since the work will happen on a queue derived from m_queue. m_queue.Terminate(false); } HRESULT MultiplayerActivityService::UpdateRecentPlayers( _In_reads_(updatesCount) const XblMultiplayerActivityRecentPlayerUpdate* updates, _In_ size_t updatesCount ) noexcept { RETURN_HR_INVALIDARGUMENT_IF(updates == nullptr || updatesCount == 0); std::lock_guard lock{ m_mutex }; time_t now{ time(nullptr) }; for (size_t i = 0; i < updatesCount; ++i) { m_pendingRecentPlayerUpdates[updates[i].xuid] = RecentPlayerUpdateMetadata{ now, updates[i].encounterType }; } if (!m_recentPlayersUpdateScheduled) { m_recentPlayersUpdateScheduled = true; ScheduleRecentPlayersUpdate(); } return S_OK; } HRESULT MultiplayerActivityService::FlushRecentPlayers( _In_ AsyncContext>&& async ) noexcept { std::lock_guard lock{ m_mutex }; if (m_pendingRecentPlayerUpdates.empty()) { async.Complete(S_OK); return S_OK; } // Build HTTP request class ServiceCall : public XblHttpCall { public: ServiceCall(User&& user) noexcept : XblHttpCall{ std::move(user) } { } HRESULT InitServiceCall( _In_ std::shared_ptr contextSettings, _In_ uint32_t titleId ) noexcept { m_globalState = GlobalState::Get(); if (!m_globalState) { return E_XBL_NOT_INITIALIZED; } Stringstream path{}; path << "/titles/" << titleId << "/recentplayers"; RETURN_HR_IF_FAILED(XblHttpCall::Init( contextSettings, "POST", XblHttpCall::BuildUrl(MPA_SERVICE_NAME, path.str()), xbox_live_api::post_recent_players )); RETURN_HR_IF_FAILED(XblHttpCall::SetUserAgent(HttpCallAgent::MultiplayerActivity)); return S_OK; } HRESULT PerformWithRetry( AsyncContext>&& async ) { return XblHttpCall::Perform(AsyncContext{ async.Queue().DeriveWorkerQueue(), [async](HttpResult httpResult) mutable { HandleHttpResult(std::move(httpResult), std::move(async)); } }); } private: static void HandleHttpResult( HttpResult result, AsyncContext>&& async ) noexcept { auto sharedThis{ std::dynamic_pointer_cast(result.Payload()) }; if (Failed(result)) { return async.Complete(result); } auto httpResult{ result.Payload()->Result() }; switch (httpResult) { case HTTP_E_STATUS_BAD_REQUEST: // Retrying 400 unlikely to resolve the issue so just treat that the same as // a success and complete the async operation case HTTP_E_STATUS_DENIED: // XblHttpCall already retries 401 so don't do it again here. case S_OK: { return async.Complete(S_OK); } default: { // For other failures, backoff and retry auto backoff{ __min(std::pow(2, ++sharedThis->m_iteration), 60) * 1000 }; async.Queue().RunWork([ sharedThis, async ] { HRESULT hr = sharedThis->ResetAndCopyForRetry(); if (FAILED(hr)) { return async.Complete(hr); } hr = sharedThis->XblHttpCall::Perform(AsyncContext{ async.Queue().DeriveWorkerQueue(), [async](HttpResult httpResult) mutable { HandleHttpResult(std::move(httpResult), std::move(async)); } }); if (FAILED(hr)) { return async.Complete(hr); } }, backoff); } } } uint32_t m_iteration{ 0 }; std::shared_ptr m_globalState{ nullptr }; }; Result userResult = m_user.Copy(); RETURN_HR_IF_FAILED(userResult.Hresult()); auto serviceCall = MakeShared(userResult.ExtractPayload()); RETURN_HR_IF_FAILED(serviceCall->InitServiceCall(m_xboxLiveContextSettings, m_titleId)); JsonDocument requestBody{ rapidjson::kObjectType }; auto& a{ requestBody.GetAllocator() }; JsonValue recentPlayers{ rapidjson::kArrayType }; for (auto& update : m_pendingRecentPlayerUpdates) { JsonValue player{ rapidjson::kObjectType }; player.AddMember("id", JsonValue{ utils::uint64_to_internal_string(update.first).data(), a }.Move(), a); player.AddMember("timestamp", JsonUtils::SerializeTime(update.second.timestamp, a).Move(), a); player.AddMember("encounterType", JsonValue{ EnumName(update.second.encounterType).data(), a }.Move(), a); recentPlayers.PushBack(player.Move(), a); } requestBody.AddMember("recentPlayers", recentPlayers.Move(), a); RETURN_HR_IF_FAILED(serviceCall->SetRequestBody(requestBody)); RETURN_HR_IF_FAILED(serviceCall->PerformWithRetry(std::move(async))); m_pendingRecentPlayerUpdates.clear(); return S_OK; } uint64_t MultiplayerActivityService::GetSequenceNumber() { uint64_t dateTime = xbox::services::datetime::utc_now().to_interval(); // eg. 131472330440000000 const uint64_t dateTimeFromJan1st2015 = 130645440000000000; if (dateTime < dateTimeFromJan1st2015) { return static_cast(time(nullptr)); // Clock is wrong and is not yet sync'd with internet time so just revert to old logic } else { uint64_t dateTimeSince2015 = dateTime - dateTimeFromJan1st2015; // eg. 826888900000000 uint64_t dateTimeTrimmed = dateTimeSince2015 >> 16; // divide by 2^16 to get it to sub second range. eg. 12617323303 return dateTimeTrimmed; } } HRESULT MultiplayerActivityService::SetActivity( _In_ const ActivityInfo& info, _In_ bool allowCrossPlatformJoin, _In_ AsyncContext async ) const noexcept { Stringstream path{}; path << "/titles/" << m_titleId << "/users/" << m_user.Xuid() << "/activities"; JsonDocument requestBody{ rapidjson::kObjectType }; auto& a{ requestBody.GetAllocator() }; requestBody.AddMember("sequenceNumber", GetSequenceNumber(), a); requestBody.AddMember("connectionString", JsonValue{ info.connectionString.data(), a }, a); requestBody.AddMember("joinRestriction", JsonValue{ EnumName(info.joinRestriction).data(), a }, a); if (info.maxPlayers) { requestBody.AddMember("maxPlayers", static_cast(info.maxPlayers), a); } if (info.currentPlayers) { requestBody.AddMember("currentPlayers", static_cast(info.currentPlayers), a); } if (!info.groupId.empty()) { requestBody.AddMember("groupId", JsonValue{ info.groupId.data(), a }, a); } if (!allowCrossPlatformJoin) { requestBody.AddMember("platform", JsonValue{ PlatformName(GetLocalPlatform()).data(), a }, a); } Result userResult = m_user.Copy(); RETURN_HR_IF_FAILED(userResult.Hresult()); auto httpCall = MakeShared(userResult.ExtractPayload()); RETURN_HR_IF_FAILED(httpCall->Init( m_xboxLiveContextSettings, "PUT", XblHttpCall::BuildUrl(MPA_SERVICE_NAME, path.str()), xbox_live_api::set_activity )); RETURN_HR_IF_FAILED(httpCall->SetRequestBody(requestBody)); return httpCall->Perform({ async.Queue().DeriveWorkerQueue(), [ async ] (HttpResult httpResult) { HRESULT hr{ Failed(httpResult) ? httpResult.Hresult() : httpResult.Payload()->Result() }; async.Complete(hr); } }); } HRESULT MultiplayerActivityService::GetActivity( _In_ const Vector& xuids, _In_ AsyncContext>> async ) const noexcept { Stringstream path{}; path << "/titles/" << m_titleId << "/activities/query"; JsonDocument requestBody{ rapidjson::kObjectType }; auto& a{ requestBody.GetAllocator() }; JsonValue userList; JsonUtils::SerializeVector(JsonUtils::JsonXuidSerializer, xuids, userList, a); requestBody.AddMember("users", userList.Move(), a); Result userResult = m_user.Copy(); RETURN_HR_IF_FAILED(userResult.Hresult()); auto httpCall = MakeShared(userResult.ExtractPayload()); RETURN_HR_IF_FAILED(httpCall->Init( m_xboxLiveContextSettings, "POST", XblHttpCall::BuildUrl(MPA_SERVICE_NAME, path.str()), xbox_live_api::get_activity_batch )); RETURN_HR_IF_FAILED(httpCall->SetRequestBody(requestBody)); return httpCall->Perform({ async.Queue().DeriveWorkerQueue(), [ async, titleId{ m_titleId } ] (HttpResult httpResult) { HRESULT hr{ Failed(httpResult) ? httpResult.Hresult() : httpResult.Payload()->Result() }; if (SUCCEEDED(hr)) { return async.Complete(ActivityInfo::Deserialize(httpResult.Payload()->GetResponseBodyJson(), titleId)); } return async.Complete(hr); } }); } HRESULT MultiplayerActivityService::DeleteActivity( _In_ AsyncContext async ) const noexcept { Stringstream path; path << "/titles/" << m_titleId << "/users/" << m_user.Xuid() << "/activities"; JsonDocument requestBody{ rapidjson::kObjectType }; auto& a{ requestBody.GetAllocator() }; requestBody.AddMember("sequenceNumber", GetSequenceNumber(), a); Result userResult = m_user.Copy(); RETURN_HR_IF_FAILED(userResult.Hresult()); auto httpCall = MakeShared(userResult.ExtractPayload()); RETURN_HR_IF_FAILED(httpCall->Init( m_xboxLiveContextSettings, "DELETE", XblHttpCall::BuildUrl(MPA_SERVICE_NAME, path.str()), xbox_live_api::delete_activity )); RETURN_HR_IF_FAILED(httpCall->SetRequestBody(requestBody)); return httpCall->Perform({ async.Queue().DeriveWorkerQueue(), [ async ] (HttpResult httpResult) { HRESULT hr{ Failed(httpResult) ? httpResult.Hresult() : httpResult.Payload()->Result() }; async.Complete(hr); } }); } HRESULT MultiplayerActivityService::SendInvites( _In_ const Vector& xuids, _In_ bool allowCrossPlatformJoin, _In_ const String& connectionString, _In_ AsyncContext async ) const noexcept { Stringstream path; path << "/titles/" << m_titleId << "/invites"; JsonDocument requestBody{ rapidjson::kObjectType }; auto& a{ requestBody.GetAllocator() }; if (!allowCrossPlatformJoin) { requestBody.AddMember("platform", JsonValue{ PlatformName(GetLocalPlatform()).data(), a }, a); } if (!connectionString.empty()) { requestBody.AddMember("connectionString", JsonValue{ connectionString.data(), a }, a); } JsonValue invitedUsersArray{ rapidjson::kArrayType }; for (auto xuid : xuids) { auto xuidString{ utils::uint64_to_internal_string(xuid) }; invitedUsersArray.PushBack(JsonValue{ xuidString.data(), a }, a); } requestBody.AddMember("invitedUsers", invitedUsersArray.Move(), a); Result userResult = m_user.Copy(); RETURN_HR_IF_FAILED(userResult.Hresult()); auto httpCall = MakeShared(userResult.ExtractPayload()); RETURN_HR_IF_FAILED(httpCall->Init( m_xboxLiveContextSettings, "POST", XblHttpCall::BuildUrl(MPA_SERVICE_NAME, path.str()), xbox_live_api::mpa_send_invites )); RETURN_HR_IF_FAILED(httpCall->SetRequestBody(requestBody)); return httpCall->Perform({ async.Queue().DeriveWorkerQueue(), [ async ] (HttpResult httpResult) { HRESULT hr{ Failed(httpResult) ? httpResult.Hresult() : httpResult.Payload()->Result() }; async.Complete(hr); } }); } void MultiplayerActivityService::ScheduleRecentPlayersUpdate() noexcept { auto hr = m_queue.RunWork([weakThis = std::weak_ptr{ shared_from_this() }] { auto pThis{ weakThis.lock() }; if (pThis) { pThis->FlushRecentPlayers({ pThis->m_queue, [ pThis ] (Result result) { // FlushRecentPlayers already has retry logic. If we get to this point, just log the error // and schedule the next upload. if (Failed(result)) { LOGS_ERROR << "MultiplayerActivity::FlushRecentPlayers failed with HRESULT " << result.Hresult(); } pThis->ScheduleRecentPlayersUpdate(); } }); } }, RECENT_PLAYERS_UPLOAD_INTERVAL_MS ); if (FAILED(hr)) { // Not much we can do if RunWork fails so just log the error and return LOGS_ERROR << __FUNCTION__ << " failed with HRESULT " << hr; } } XblMultiplayerActivityPlatform MultiplayerActivityService::GetLocalPlatform() noexcept { #if HC_PLATFORM == HC_PLATFORM_GDK auto GDKDeviceType{ XSystemGetDeviceType() }; switch (GDKDeviceType) { case XSystemDeviceType::Pc: { return XblMultiplayerActivityPlatform::WindowsOneCore; } case XSystemDeviceType::XboxOne: case XSystemDeviceType::XboxOneS: case XSystemDeviceType::XboxOneX: case XSystemDeviceType::XboxOneXDevkit: { return XblMultiplayerActivityPlatform::XboxOne; } case XSystemDeviceType::XboxScarlettLockhart: case XSystemDeviceType::XboxScarlettAnaconda: case XSystemDeviceType::XboxScarlettDevkit: { return XblMultiplayerActivityPlatform::Scarlett; } case XSystemDeviceType::Unknown: default: { LOGS_DEBUG << "Unable to detect GDK device type"; assert(false); return XblMultiplayerActivityPlatform::Unknown; } } #else constexpr XblMultiplayerActivityPlatform localPlatform { #if HC_PLATFORM == HC_PLATFORM_WIN32 XblMultiplayerActivityPlatform::Win32 #elif HC_PLATFORM == HC_PLATFORM_UWP XblMultiplayerActivityPlatform::WindowsOneCore #elif HC_PLATFORM == HC_PLATFORM_XDK XblMultiplayerActivityPlatform::XboxOne #elif HC_PLATFORM == HC_PLATFORM_ANDROID XblMultiplayerActivityPlatform::Android #elif HC_PLATFORM == HC_PLATFORM_IOS XblMultiplayerActivityPlatform::iOS #elif HC_PLATFORM == HC_PLATFORM_MAC XblMultiplayerActivityPlatform::MacOS #elif HC_PLATFORM == HC_PLATFORM_NINTENDO_SWITCH XblMultiplayerActivityPlatform::Nintendo #elif HC_PLATFORM_IS_PLAYSTATION XblMultiplayerActivityPlatform::PlayStation #else XblMultiplayerActivityPlatform::Unknown #endif }; static_assert(localPlatform != XblMultiplayerActivityPlatform::Unknown, "Unable to detect platform"); return localPlatform; #endif } } NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END ================================================ FILE: Source/Services/Notification/Mobile/notification_service_mobile.cpp ================================================ #include "pch.h" #include "notification_internal.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_NOTIFICATION_CPP_BEGIN #if HC_PLATFORM == HC_PLATFORM_IOS || HC_PLATFORM == HC_PLATFORM_ANDROID MobileNotificationService::MobileNotificationService( _In_ User&& user, _In_ std::shared_ptr contextSettings ) : NotificationService(std::move(user), contextSettings) {} HRESULT MobileNotificationService::RegisterWithNotificationService( _In_ const String& endpointId, _In_ AsyncContext async) noexcept { RETURN_HR_INVALIDARGUMENT_IF_EMPTY_STRING(endpointId); // http://xboxwiki/wiki/DeviceEndpoint_Object#Filter_Sources Vector notificationFilterList; #if HC_PLATFORM == HC_PLATFORM_ANDROID notificationFilterList.push_back({ NotificationTypeFilterSourceType::Multiplayer, 1 }); // Game invites notificationFilterList.push_back({ NotificationTypeFilterSourceType::Multiplayer, 8 }); // Game invites with connection string notificationFilterList.push_back({ NotificationTypeFilterSourceType::Achievements, 1 }); // Achievement unlocked #endif auto currentEndPointId = AppConfig::Instance()->EndpointId(); if (!currentEndPointId.empty()) { std::weak_ptr thisWeak = std::dynamic_pointer_cast(shared_from_this()); // If already subscribed before, unsubscribe the old endpoint first return UnregisterFromNotificationHelper( currentEndPointId, { async.Queue(), [ thisWeak, endpointId, async, notificationFilterList ](HRESULT result) { if (FAILED(result)) { return async.Complete(result); } std::shared_ptr pThis(thisWeak.lock()); if (pThis != nullptr) { pThis->RegisterForNotificationsHelper( utils::create_guid(true), endpointId, #if HC_PLATFORM == HC_PLATFORM_ANDROID _T("Android"), _T("AndroidDevice"), _T("XSAPI_ANDROID"), #elif HC_PLATFORM == HC_PLATFORM_IOS _T("iOS"), _T("iOSDevice"), _T("XSAPI_I"), #endif notificationFilterList, async ); } } }); } else { return RegisterForNotificationsHelper( utils::create_guid(true), endpointId, #if HC_PLATFORM == HC_PLATFORM_ANDROID _T("Android"), _T("AndroidDevice"), _T("XSAPI_ANDROID"), #elif HC_PLATFORM == HC_PLATFORM_IOS _T("iOS"), _T("iOSDevice"), _T("XSAPI_I"), #endif notificationFilterList, async ); } } #endif NAMESPACE_MICROSOFT_XBOX_SERVICES_NOTIFICATION_CPP_END ================================================ FILE: Source/Services/Notification/RTA/notification_service_rta.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "notification_internal.h" #include "real_time_activity_manager.h" #include "multiplayer_internal.h" #if HC_PLATFORM == HC_PLATFORM_WIN32 || HC_PLATFORM_IS_EXTERNAL NAMESPACE_MICROSOFT_XBOX_SERVICES_NOTIFICATION_CPP_BEGIN RTANotificationService::RTANotificationService( _In_ User&& user, _In_ const TaskQueue& queue, _In_ std::shared_ptr contextSettings, _In_ std::shared_ptr rtaManager ) noexcept : NotificationService(std::move(user), contextSettings), m_taskQueue{ queue.DeriveWorkerQueue() }, m_rtaManager{ std::move(rtaManager) } { } RTANotificationService::~RTANotificationService() noexcept { m_rtaManager->RemoveSubscription(m_user, m_rtaSubscription); m_rtaManager->Deactivate(m_user); } HRESULT RTANotificationService::Initialize() noexcept { // Always register with notification service as it powers SPOP for Win32 // Subscribing to events requires two separate steps: // 1) Creating and adding an RTA subscription to the notification endpoint // 2) Registering with the notification service std::lock_guard lock{ m_mutex }; auto copyUserResult = m_user.Copy(); RETURN_HR_IF_FAILED(copyUserResult.Hresult()); m_rtaManager->Activate(m_user); m_rtaSubscription = MakeShared(copyUserResult.ExtractPayload(), m_taskQueue, AppConfig::Instance()->TitleId()); RETURN_HR_IF_FAILED(m_rtaManager->AddSubscription(m_user, m_rtaSubscription)); return RegisterWithNotificationService( m_rtaSubscription->ResourceUri(), AsyncContext{ m_taskQueue, [](HRESULT hr) { if (FAILED(hr)) { LOGS_ERROR << "Failed to register with Notification Service hr=" << std::hex << hr; } } }); } HRESULT RTANotificationService::RegisterWithNotificationService( _In_ const String& uriData, _In_ AsyncContext async ) noexcept { std::lock_guard lock{ m_mutex }; Stringstream titleId; titleId << AppConfig::Instance()->TitleId(); const char platform[]{ #if HC_PLATFORM == HC_PLATFORM_WIN32 "Win32", #elif HC_PLATFORM == HC_PLATFORM_NINTENDO_SWITCH "NintendoSwitch", #elif HC_PLATFORM_IS_PLAYSTATION "PlayStation", #endif }; Vector notificationFilterList; notificationFilterList.push_back({ NotificationTypeFilterSourceType::Multiplayer, 1 }); notificationFilterList.push_back({ NotificationTypeFilterSourceType::Multiplayer, 8 }); notificationFilterList.push_back({ NotificationTypeFilterSourceType::Achievements, 1 }); return NotificationService::RegisterForNotificationsHelper( utils::create_guid(true), // applicationInstanceId uriData, platform, "", "", notificationFilterList, std::move(async) ); } XblFunctionContext RTANotificationService::AddGameInviteHandler( _In_ NotificationSubscription::MPSDInviteHandler handler ) noexcept { std::lock_guard lock{ m_mutex }; assert(m_rtaSubscription); return m_rtaSubscription->AddHandler(std::move(handler)); } XblFunctionContext RTANotificationService::AddGameInviteHandler( _In_ NotificationSubscription::MultiplayerActivityInviteHandler handler ) noexcept { std::lock_guard lock{ m_mutex }; assert(m_rtaSubscription); return m_rtaSubscription->AddHandler(std::move(handler)); } XblFunctionContext RTANotificationService::AddAchievementUnlockNotificationHandler( _In_ NotificationSubscription::AchievementUnlockHandler handler ) noexcept { std::lock_guard lock{ m_mutex }; assert(m_rtaSubscription); return m_rtaSubscription->AddHandler(std::move(handler)); } void RTANotificationService::RemoveNotificationHandler( _In_ XblFunctionContext token ) noexcept { std::lock_guard lock{ m_mutex }; m_rtaSubscription->RemoveHandler(token); } NAMESPACE_MICROSOFT_XBOX_SERVICES_NOTIFICATION_CPP_END #endif ================================================ FILE: Source/Services/Notification/RTA/notification_subscription.cpp ================================================ #include "pch.h" #include "notification_subscription.h" #include "real_time_activity_manager.h" #include "multiplayer_internal.h" #if HC_PLATFORM == HC_PLATFORM_WIN32 || HC_PLATFORM_IS_EXTERNAL NAMESPACE_MICROSOFT_XBOX_SERVICES_NOTIFICATION_CPP_BEGIN NotificationSubscription::NotificationSubscription( _In_ User&& user, _In_ TaskQueue queue, _In_ uint32_t titleId ) noexcept : m_user{ std::move(user) }, m_queue{ std::move(queue) } { Stringstream ss; ss << "https://notify.xboxlive.com/users/xuid(" << m_user.Xuid() << ")/deviceId/current/titleId/" << titleId; m_resourceUri = ss.str(); } const String& NotificationSubscription::ResourceUri() const noexcept { return m_resourceUri; } XblFunctionContext NotificationSubscription::AddHandler(MPSDInviteHandler&& handler) noexcept { std::lock_guard lock{ m_mutex }; m_mpsdInviteHandlers[m_nextToken] = std::move(handler); return m_nextToken++; } XblFunctionContext NotificationSubscription::AddHandler(MultiplayerActivityInviteHandler&& handler) noexcept { std::lock_guard lock{ m_mutex }; m_mpaInviteHandlers[m_nextToken] = std::move(handler); return m_nextToken++; } XblFunctionContext NotificationSubscription::AddHandler(AchievementUnlockHandler&& handler) noexcept { std::lock_guard lock{ m_mutex }; m_achievementUnlockHandlers[m_nextToken] = std::move(handler); return m_nextToken++; } size_t NotificationSubscription::RemoveHandler(XblFunctionContext token) noexcept { std::lock_guard lock{ m_mutex }; m_mpaInviteHandlers.erase(token); m_mpsdInviteHandlers.erase(token); m_achievementUnlockHandlers.erase(token); // return the total number of handlers remaining return m_mpaInviteHandlers.size() + m_mpsdInviteHandlers.size() + m_achievementUnlockHandlers.size(); } void NotificationSubscription::OnEvent( _In_ const JsonValue& data ) noexcept { // Notifications from several services are delivered via the same RTA subscription. Examine the payload to // distinguish between them. if (!data.IsObject()) { LOGS_ERROR << __FUNCTION__ << ": Ignoring unrecognized payload"; } else if (data.HasMember("inviteHandle")) { String originatingService; JsonUtils::ExtractJsonString(data, "service", originatingService); if (originatingService != "achievements") { // Deserialize and send invite notification auto deserializationResult = GameInviteNotificationEventArgs::Deserialize(data["inviteHandle"]); if (Succeeded(deserializationResult)) { std::unique_lock lock{ m_mutex }; auto handlers{ m_mpsdInviteHandlers }; lock.unlock(); for (auto& handler : handlers) { handler.second(deserializationResult.Payload()); } } } } else if (data.HasMember("notificationData")) { // Deserialize and send invite notification auto deserializationResult = MultiplayerActivityInviteData::Deserialize(data["notificationData"]); if (Succeeded(deserializationResult)) { std::unique_lock lock{ m_mutex }; auto handlers{ m_mpaInviteHandlers }; lock.unlock(); for (auto& handler : handlers) { handler.second(deserializationResult.Payload()); } } } else if (data.HasMember("KickNotification")) { XalUserGetTokenAndSignatureArgs args{}; args.forceRefresh = true; args.url = "https://xboxlive.com/"; args.method = "GET"; auto async = MakeUnique(); async->queue = m_queue.GetHandle(); async->callback = [](XAsyncBlock* async) { // Take ownership of async block UniquePtr asyncUnique{ async }; // Get the result even though we aren't using it so Xal can release it size_t bufferSize{}; HRESULT hr = XalUserGetTokenAndSignatureSilentlyResultSize(async, &bufferSize); if (SUCCEEDED(hr)) { Vector buffer(bufferSize); XalUserGetTokenAndSignatureData* xalTokenSignatureData{ nullptr }; hr = XalUserGetTokenAndSignatureSilentlyResult(async, bufferSize, buffer.data(), &xalTokenSignatureData, nullptr); UNREFERENCED_PARAMETER(hr); } }; if (SUCCEEDED(XalUserGetTokenAndSignatureSilentlyAsync(m_user.Handle(), &args, async.get()))) { async.release(); } } else if (data.HasMember("service")) { String originatingService; JsonUtils::ExtractJsonString(data, "service", originatingService); if (originatingService == "achievements") { // Deserialize and send invite notification auto deserializationResult = AchievementUnlockEvent::Deserialize(data); if (Succeeded(deserializationResult)) { std::unique_lock lock{ m_mutex }; auto handlers{ m_achievementUnlockHandlers }; lock.unlock(); for (auto& handler : handlers) { handler.second(deserializationResult.Payload()); } } } } else { LOGS_ERROR << __FUNCTION__ << ": Ignoring unrecognized payload"; } } GameInviteNotificationEventArgs::GameInviteNotificationEventArgs( const GameInviteNotificationEventArgs& other ) noexcept : XblGameInviteNotificationEventArgs{ other }, m_inviteHandleId{ other.m_inviteHandleId }, m_inviteProtocol{ other.m_inviteProtocol }, m_inviteContext{ other.m_inviteContext }, m_senderImageUrl{ other.m_senderImageUrl } { if (!m_inviteHandleId.empty()) { inviteHandleId = m_inviteHandleId.data(); } if (!m_inviteProtocol.empty()) { inviteProtocol = m_inviteProtocol.data(); } if (!m_inviteContext.empty()) { inviteContext = m_inviteContext.data(); } if (!m_senderImageUrl.empty()) { senderImageUrl = m_senderImageUrl.data(); } } Result GameInviteNotificationEventArgs::Deserialize( _In_ const JsonValue& inviteHandle ) noexcept { if (!inviteHandle.IsObject()) { return WEB_E_INVALID_JSON_STRING; } GameInviteNotificationEventArgs result{}; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonXuid(inviteHandle, "invitedXuid", result.invitedXboxUserId, true)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonXuid(inviteHandle, "senderXuid", result.senderXboxUserId, true)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(inviteHandle, "id", result.m_inviteHandleId, true)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(inviteHandle, "inviteProtocol", result.m_inviteProtocol, true)); if (inviteHandle.IsObject() && inviteHandle.HasMember("inviteAttributes")) { const JsonValue& inviteAttributesValue = inviteHandle["inviteAttributes"]; if (inviteAttributesValue.HasMember("context")) { RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(inviteAttributesValue, "context", result.m_inviteContext, true)); } } RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonTimeT(inviteHandle, "expiration", result.expiration, true)); result.sessionReference = xbox::services::multiplayer::Serializers::DeserializeSessionReference(inviteHandle["sessionRef"]).Payload(); if (inviteHandle.IsObject() && inviteHandle.HasMember("inviteInfo")) { const JsonValue& inviteInfoValue = inviteHandle["inviteInfo"]; String sender, senderMGT, senderMGTSuffix, senderUniqueMGT; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(inviteInfoValue, "sender", sender, true)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(inviteInfoValue, "senderModernGamertag", senderMGT, true)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(inviteInfoValue, "senderModernGamertagSuffix", senderMGTSuffix, true)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(inviteInfoValue, "senderUniqueModernGamertag", senderUniqueMGT, true)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(inviteInfoValue, "senderImageUrl", result.m_senderImageUrl, true)); utils::strcpy(result.senderGamertag, sizeof(result.senderGamertag), sender.c_str()); utils::strcpy(result.senderModernGamertag, sizeof(result.senderModernGamertag), senderMGT.c_str()); utils::strcpy(result.senderModernGamertagSuffix, sizeof(result.senderModernGamertagSuffix), senderMGTSuffix.c_str()); utils::strcpy(result.senderUniqueModernGamertag, sizeof(result.senderUniqueModernGamertag), senderUniqueMGT.c_str()); } else { //required return WEB_E_INVALID_JSON_STRING; } return result; } MultiplayerActivityInviteData::MultiplayerActivityInviteData( const MultiplayerActivityInviteData& other ) noexcept : XblMultiplayerActivityInviteData{ other }, m_senderImageUrl{ other.m_senderImageUrl }, m_titleName{ other.m_titleName }, m_titleImageUrl{ other.m_titleImageUrl }, m_connectionString{ other.m_connectionString } { if (!m_titleName.empty()) { titleName = m_titleName.data(); } if (!m_connectionString.empty()) { connectionString = m_connectionString.data(); } if (!m_titleImageUrl.empty()) { titleImageUrl = m_titleImageUrl.data(); } if (!m_senderImageUrl.empty()) { senderImageUrl = m_senderImageUrl.data(); } } Result MultiplayerActivityInviteData::Deserialize( const JsonValue& notificationData ) noexcept { if (!notificationData.IsObject()) { return WEB_E_INVALID_JSON_STRING; } MultiplayerActivityInviteData data{}; if (notificationData.HasMember("sender")) { auto& senderJson{ notificationData["sender"] }; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonXuid(senderJson, "xuid", data.senderXuid, true)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(senderJson, "imageUrl", data.m_senderImageUrl)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonStringToCharArray(senderJson, "gamertag", data.senderGamertag, sizeof(data.senderGamertag))); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonStringToCharArray(senderJson, "modernGamertag", data.senderModernGamertag, sizeof(data.senderModernGamertag))); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonStringToCharArray(senderJson, "modernGamertagSuffix", data.senderModernGamertagSuffix, sizeof(data.senderModernGamertagSuffix))); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonStringToCharArray(senderJson, "uniqueModernGamertag", data.senderUniqueModernGamertag, sizeof(data.senderUniqueModernGamertag))); } else { return WEB_E_INVALID_JSON_STRING; } RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonXuid(notificationData, "invitedUser", data.invitedXuid, true)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(notificationData, "titleName", data.m_titleName, true)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(notificationData, "titleImageUrl", data.m_titleImageUrl)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(notificationData, "connectionString", data.m_connectionString)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonTimeT(notificationData, "expirationTime", data.expirationTime)); return data; } AchievementUnlockEvent::AchievementUnlockEvent(AchievementUnlockEvent&& event) noexcept : m_achievementId(std::move(event.m_achievementId)), m_achievementDescription(std::move(event.m_achievementDescription)), m_achievementName(std::move(event.m_achievementName)), m_achievementIconUri(std::move(event.m_achievementIconUri)), m_deepLink(event.m_deepLink) { // strings for the C API achievementId = m_achievementId.c_str(); achievementName = m_achievementName.c_str(); achievementDescription = m_achievementDescription.c_str(); achievementIcon = m_achievementIconUri.c_str(); titleId = event.titleId; gamerscore = event.gamerscore; rarityPercentage = event.rarityPercentage; rarityCategory = event.rarityCategory; timeUnlocked = event.timeUnlocked; xboxUserId = event.xboxUserId; } AchievementUnlockEvent::AchievementUnlockEvent(const AchievementUnlockEvent& event) : m_achievementId(event.m_achievementId), m_achievementDescription(event.m_achievementDescription), m_achievementName(event.m_achievementName), m_achievementIconUri(event.m_achievementIconUri), m_deepLink(event.m_deepLink) { // strings for the C API achievementId = m_achievementId.c_str(); achievementName = m_achievementName.c_str(); achievementDescription = m_achievementDescription.c_str(); achievementIcon = m_achievementIconUri.c_str(); titleId = event.titleId; gamerscore = event.gamerscore; rarityPercentage = event.rarityPercentage; rarityCategory = event.rarityCategory; timeUnlocked = event.timeUnlocked; xboxUserId = event.xboxUserId; } Result AchievementUnlockEvent::Deserialize(_In_ const JsonValue& json) noexcept { AchievementUnlockEvent result{}; if (!json.IsObject()) { return result; } double rarityPercentage = 0.0; // We are guaranteed that rarityPercentage is only float, but can only extract as double. uint64_t titleId = 0; // We are guaranteed that titleId is only uint32, but can only extract as uint64. String rarityCategory; // Will be converted into an enum value after extraction RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "achievementId", result.m_achievementId, true)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "achievementDescription", result.m_achievementDescription, false)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "achievementName", result.m_achievementName, true)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "achievementIcon", result.m_achievementIconUri, true)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonStringToUInt64(json, "titleId", titleId, true)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonUInt64(json, "gamerscore", result.gamerscore, true)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonXuid(json, "xuid", result.xboxUserId, true)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "extendedInfoUrl", result.m_deepLink, true)); // RarityPercentage and rarityCategory are only in payload version 2 so make them optional RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonDouble(json, "rarityPercentage", rarityPercentage, false)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "rarityCategory", rarityCategory, false)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonTimeT(json, "unlockTime", result.timeUnlocked)); result.titleId = static_cast(titleId); result.rarityPercentage = static_cast(rarityPercentage); result.rarityCategory = EnumValue(rarityCategory.c_str()); // strings for the C API result.achievementId = result.m_achievementId.c_str(); result.achievementName = result.m_achievementName.c_str(); if (!result.m_achievementDescription.empty()) { result.achievementDescription = result.m_achievementDescription.c_str(); } result.achievementIcon = result.m_achievementIconUri.c_str(); result.m_deepLink = result.m_deepLink.c_str(); return result; } NAMESPACE_MICROSOFT_XBOX_SERVICES_NOTIFICATION_CPP_END #endif ================================================ FILE: Source/Services/Notification/RTA/notification_subscription.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "xsapi-c/game_invite_c.h" #include "xsapi-c/achievements_c.h" #include "xsapi-c/multiplayer_activity_c.h" #include "real_time_activity_subscription.h" #if HC_PLATFORM == HC_PLATFORM_WIN32 || HC_PLATFORM_IS_EXTERNAL NAMESPACE_MICROSOFT_XBOX_SERVICES_NOTIFICATION_CPP_BEGIN // Event args for MPSD game invites struct GameInviteNotificationEventArgs : public XblGameInviteNotificationEventArgs { public: GameInviteNotificationEventArgs() noexcept = default; GameInviteNotificationEventArgs(const GameInviteNotificationEventArgs& other) noexcept; GameInviteNotificationEventArgs& operator=(GameInviteNotificationEventArgs other) noexcept = delete; static Result Deserialize( _In_ const JsonValue& json ) noexcept; private: String m_inviteHandleId; String m_inviteProtocol; String m_inviteContext; String m_senderImageUrl; }; struct MultiplayerActivityInviteData : public XblMultiplayerActivityInviteData { public: MultiplayerActivityInviteData() noexcept = default; MultiplayerActivityInviteData(const MultiplayerActivityInviteData& other) noexcept; MultiplayerActivityInviteData& operator=(MultiplayerActivityInviteData other) noexcept = delete; static Result Deserialize( const JsonValue& json ) noexcept; private: String m_senderImageUrl; String m_titleName; String m_titleImageUrl; String m_connectionString; }; struct AchievementUnlockEvent : public XblAchievementUnlockEvent { public: AchievementUnlockEvent() = default; AchievementUnlockEvent(AchievementUnlockEvent&& event) noexcept; AchievementUnlockEvent(const AchievementUnlockEvent& event); static Result Deserialize(_In_ const JsonValue& json) noexcept; private: String m_achievementId; String m_achievementDescription; String m_achievementName; String m_achievementIconUri; String m_deepLink; }; class NotificationSubscription : public real_time_activity::Subscription { public: NotificationSubscription( User&& user, TaskQueue queue, uint32_t titleId ) noexcept; const String& ResourceUri() const noexcept; using MPSDInviteHandler = Function; using MultiplayerActivityInviteHandler = Function; using AchievementUnlockHandler = Function; XblFunctionContext AddHandler(MPSDInviteHandler&& handler) noexcept; XblFunctionContext AddHandler(MultiplayerActivityInviteHandler&& handler) noexcept; XblFunctionContext AddHandler(AchievementUnlockHandler&& handler) noexcept; size_t RemoveHandler(XblFunctionContext token) noexcept; protected: void OnEvent(_In_ const JsonValue& event) noexcept override; private: User m_user; TaskQueue m_queue; std::mutex m_mutex; XblFunctionContext m_nextToken{ 1 }; Map m_mpsdInviteHandlers; Map m_mpaInviteHandlers; Map m_achievementUnlockHandlers; }; NAMESPACE_MICROSOFT_XBOX_SERVICES_NOTIFICATION_CPP_END #endif ================================================ FILE: Source/Services/Notification/UWP/notification_service_uwp.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "notification_internal.h" #include "xsapi_utils.h" #include "xsapi-cpp/system.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_NOTIFICATION_CPP_BEGIN UWPNotificationService::UWPNotificationService( _In_ User&& user, _In_ std::shared_ptr contextSettings ) noexcept : NotificationService(std::move(user), contextSettings) { } HRESULT UWPNotificationService::RegisterWithNotificationService( _In_ AsyncContext async ) noexcept { std::weak_ptr thisWeak = std::dynamic_pointer_cast(shared_from_this()); //hold onto a reference to global state to prevent cleanup while the subscribe call is still out std::shared_ptr holdCleanup{ GlobalState::Get() }; // Setup WNS Channel for Push Notifications auto notificationChannelAsyncOp = Windows::Networking::PushNotifications::PushNotificationChannelManager::CreatePushNotificationChannelForApplicationAsync(); notificationChannelAsyncOp->Completed = ref new Windows::Foundation::AsyncOperationCompletedHandler( [thisWeak, async, holdCleanup](Windows::Foundation::IAsyncOperation^ asyncOp, Windows::Foundation::AsyncStatus status) { if (status == Windows::Foundation::AsyncStatus::Completed) { try { auto channel = asyncOp->GetResults(); channel->PushNotificationReceived += ref new Windows::Foundation::TypedEventHandler( [thisWeak, async](Windows::Networking::PushNotifications::PushNotificationChannel ^ channel, Windows::Networking::PushNotifications::PushNotificationReceivedEventArgs^ args) { try { std::shared_ptr pThis(thisWeak.lock()); if (pThis != nullptr) { try { pThis->OnPushNotificationReceived(channel, args); } catch (...) { LOG_ERROR("OnPushNotificationReceived error"); } } } catch (...) { LOG_ERROR("OnPushNotificationReceived error"); } }); auto family = Windows::System::Profile::AnalyticsInfo::VersionInfo->DeviceFamily; auto form = Windows::System::Profile::AnalyticsInfo::DeviceForm; auto version = Windows::System::Profile::AnalyticsInfo::VersionInfo->DeviceFamilyVersion; xsapi_internal_string platform; if (family == "Windows.Mobile") { platform = "WindowsOneCoreMobile"; } else if (family == "Windows.Xbox") { platform = "Durango"; } else { platform = "WindowsOneCore"; } Vector notificationFilterList; std::shared_ptr pThis(thisWeak.lock()); if (pThis != nullptr) { pThis->RegisterForNotificationsHelper( utils::create_guid(true), // applicationInstanceId utils::internal_string_from_string_t(channel->Uri->Data()), platform, "", "", notificationFilterList, { async.Queue(), [thisWeak, async](HRESULT hr) { if (auto pThis{ thisWeak.lock() }) { if (SUCCEEDED(hr)) { async.Complete(hr); } else { LOG_ERROR("Failed to successfully register with notification service"); async.Complete(E_XBL_RUNTIME_ERROR); } } } }); } } catch (...) { LOG_ERROR("Failed to successfully register with notification service"); } } }); return S_OK; } void UWPNotificationService::OnPushNotificationReceived( _In_ Windows::Networking::PushNotifications::PushNotificationChannel ^sender, _In_ Windows::Networking::PushNotifications::PushNotificationReceivedEventArgs ^args ) { HRESULT errc = S_OK; if (args && args->RawNotification && args->RawNotification->Content) { string_t content = args->RawNotification->Content->Data(); JsonDocument parsedJson; parsedJson.Parse(utils::internal_string_from_string_t(content).c_str()); if (parsedJson.HasParseError()) { errc = WEB_E_INVALID_JSON_STRING; } xsapi_internal_string notificationTypeString; xsapi_internal_string xuid; if (parsedJson.IsObject() && parsedJson.HasMember("xboxLiveNotification")) { const JsonValue& xboxLiveNotificationJson = parsedJson["xboxLiveNotification"]; errc |= JsonUtils::ExtractJsonString(xboxLiveNotificationJson, "notificationType", notificationTypeString); errc |= JsonUtils::ExtractJsonString(xboxLiveNotificationJson, "userXuid", xuid); } if (!errc) { LOGS_INFO << "Received WNS notification, type: " << notificationTypeString << ", xuid: " << xuid; if (utils::str_icmp(notificationTypeString.c_str(), "spop") == 0) { User::SetTokenExpired(utils::internal_string_to_uint64(xuid)); } } else { LOGS_ERROR << "Receiving WNS notification error: " << errc << ", message: " << utils::convert_hresult_to_error_name(errc); } } } NAMESPACE_MICROSOFT_XBOX_SERVICES_NOTIFICATION_CPP_END ================================================ FILE: Source/Services/Notification/iOS/notification_helper.mm ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "notification_helper.h" using namespace xbox::services; NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN #if XSAPI_I xbox_live_result parse_notification(NSDictionary* notificationInfo) { NSError *error; NSData *jsonData = [NSJSONSerialization dataWithJSONObject:notificationInfo options:0 error:&error]; if (error != nil) { string_t errorDescription = string_t([[NSString stringWithFormat:@"Error parsing the notification payload! Error: %@", [error localizedDescription]] UTF8String]); return xbox_live_result(xbox_live_error_code::json_error, errorDescription); } else { xbox_live_notification result; string_t notificationStr([[[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding] UTF8String]); JsonDocument payload; payload.Parse(utils::internal_string_from_string_t(notificationStr).c_str()); if(!payload.HasParseError()) { HRESULT errc = S_OK; string_t title; string_t body; if(payload.IsObject() && payload.HasMember("aps") && payload.HasMember("data")) { const JsonValue& aps = payload["aps"]; //required const JsonValue& data = payload["data"]; //required string_t type; errc |= JsonUtils::ExtractJsonString(data, "type", type, true); if(aps.IsObject() && aps.HasMember("alert")) { const JsonValue& alert = aps["alert"]; //required if(utils::str_icmp(type, _T("xbox_live_game_invite")) == 0) { result.type = xbox_live_notification_type::game_invite; string_t locKey; errc |= JsonUtils::ExtractJsonString(alert, "loc-key", locKey, true); string_t titleLocKey; errc |= JsonUtils::ExtractJsonString(alert, "title-loc-key", titleLocKey, true); std::vector bodyArgs; if(alert.IsObject() && alert.HasMember("loc-args")) { const JsonValue& locArgs= alert["loc-args"]; //requried for(const JsonValue& arg : locArgs.GetArray()) { xsapi_internal_string argString; errc |= JsonUtils::ExtractJsonAsString(arg, argString); bodyArgs.push_back(utils::string_t_from_internal_string(argString)); } NSString* titleLocStr = NSLocalizedString([NSString stringWithUTF8String:titleLocKey.c_str()], nil); NSString* bodyLocStr = NSLocalizedString([NSString stringWithUTF8String:locKey.c_str()], nil); //The below snippet formats the localization string one parameter at a time. stringstream_t ss; NSArray *items = [bodyLocStr componentsSeparatedByString:@"%"]; uint32_t index = 0; for (NSString* item in items) { if(item.length > 0) { NSString *format = @"%"; format = [format stringByAppendingString:item]; ss << [[NSString stringWithFormat:format, [NSString stringWithUTF8String:bodyArgs[index].c_str()]] UTF8String]; index = MIN((uint32_t)bodyArgs.size(), index + 1); } } title = [titleLocStr UTF8String]; body = ss.str(); } } else if(utils::str_icmp(type, _T("xbox_live_achievement_unlock")) == 0) { result.type = xbox_live_notification_type::achievement_unlocked; errc |= JsonUtils::ExtractJsonString(alert, "title", title, true); errc |= JsonUtils::ExtractJsonString(alert, "body", body, true); } else { result.type = xbox_live_notification_type::unknown; } } if(data.IsObject() && data.HasMember("xbl")) { const JsonValue& xblInfo = data["xbl"]; //required result.title = title; result.body = body; result.data = JsonUtils::SerializeJson(xblInfo); return xbox_live_result(result, ConvertHrToXblErrorCode(errc)); } } } return xbox_live_result(result, xbox_live_error_code::json_error); } } #endif NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END ================================================ FILE: Source/Services/Notification/notification_api.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "notification_internal.h" #include "multiplayer_internal.h" #include "xbox_live_context_internal.h" using namespace xbox::services; using namespace xbox::services::notification; #if HC_PLATFORM == HC_PLATFORM_IOS || HC_PLATFORM == HC_PLATFORM_ANDROID STDAPI XblNotificationSubscribeToNotificationsAsync( _In_ XblContextHandle xboxLiveContext, _In_ XAsyncBlock* asyncBlock, _In_ const char* registrationToken ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(registrationToken == nullptr || strlen(registrationToken) <= 1); RETURN_HR_INVALIDARGUMENT_IF_NULL(xboxLiveContext); RETURN_HR_INVALIDARGUMENT_IF_NULL(asyncBlock); return RunAsync(asyncBlock, __FUNCTION__, [ xblContext{ xboxLiveContext->shared_from_this() }, registrationToken ] (XAsyncOp op, const XAsyncProviderData* data) mutable { switch (op) { case XAsyncOp::DoWork: { auto result = xblContext->NotificationService()->RegisterWithNotificationService( registrationToken, { data->async->queue, [ async{ data->async } ] (HRESULT hr) { XAsyncComplete(async, hr, 0); } }); if (FAILED(result)) { return result; } return E_PENDING; } default: return S_OK; } }); } CATCH_RETURN() /// /// Unsubscribes title from push notifications. /// STDAPI XblNotificationUnsubscribeFromNotificationsAsync( _In_ XblContextHandle xboxLiveContext, _In_ XAsyncBlock* asyncBlock ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(xboxLiveContext); RETURN_HR_INVALIDARGUMENT_IF_NULL(asyncBlock); return RunAsync(asyncBlock, __FUNCTION__, [ xblContext{ xboxLiveContext->shared_from_this() } ] (XAsyncOp op, const XAsyncProviderData* data) mutable { switch (op) { case XAsyncOp::DoWork: { auto result = xblContext->NotificationService()->UnregisterFromNotificationService( { data->async->queue, [ async{ data->async } ] (HRESULT hr) { XAsyncComplete(async, hr, 0); } }); if (FAILED(result)) { return result; } return E_PENDING; } default: return S_OK; } }); } CATCH_RETURN() #elif HC_PLATFORM == HC_PLATFORM_WIN32 || HC_PLATFORM_IS_EXTERNAL STDAPI XblGameInviteRegisterForEventAsync( _In_ XblContextHandle xboxLiveContext, _In_ XAsyncBlock* async ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(xboxLiveContext); RETURN_HR_INVALIDARGUMENT_IF_NULL(async); return RunAsync(async, __FUNCTION__, [ xblContext{ xboxLiveContext->shared_from_this() } ] (XAsyncOp op, const XAsyncProviderData* data) mutable { switch (op) { case XAsyncOp::Begin: { XAsyncComplete(data->async, S_OK, sizeof(XblRealTimeActivitySubscriptionHandle)); return S_OK; } case XAsyncOp::GetResult: { // Return a dummy subscription handle that is cleaned up when client unsubscribes auto handlePtr = static_cast(data->buffer); *handlePtr = Make(); return S_OK; } default: return S_OK; } }); } CATCH_RETURN() STDAPI XblGameInviteRegisterForEventResult( _In_ XAsyncBlock* async, _Out_ XblRealTimeActivitySubscriptionHandle* handle ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(async); RETURN_HR_INVALIDARGUMENT_IF_NULL(handle); XblRealTimeActivitySubscriptionHandle handleCopy{ nullptr }; auto hr = XAsyncGetResult(async, nullptr, sizeof(XblRealTimeActivitySubscriptionHandle), &handleCopy, nullptr); if (SUCCEEDED(hr)) { *handle = handleCopy; } else { *handle = nullptr; } return hr; } CATCH_RETURN() STDAPI XblGameInviteUnregisterForEventAsync( _In_ XblContextHandle xblContextHandle, _In_ XblRealTimeActivitySubscriptionHandle subscriptionHandle, _In_ XAsyncBlock* async ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(xblContextHandle == nullptr || subscriptionHandle == nullptr); return RunAsync(async, __FUNCTION__, [ xblContext{ xblContextHandle->shared_from_this() }, subscriptionHandle ] (XAsyncOp op, const XAsyncProviderData* data) mutable { switch (op) { case XAsyncOp::Begin: { Delete(subscriptionHandle); XAsyncComplete(data->async, S_OK, 0); return S_OK; } default: return S_OK; } }); } CATCH_RETURN() STDAPI_(XblFunctionContext) XblGameInviteAddNotificationHandler( _In_ XblContextHandle xblContextHandle, _In_ XblGameInviteHandler* handler, _In_opt_ void* context ) XBL_NOEXCEPT try { // TODO really should be returning HRESULT E_INVALIDARG here if (xblContextHandle == nullptr || handler == nullptr) { return XblFunctionContext{ 0 }; } auto rtaNotificationService = std::dynamic_pointer_cast(xblContextHandle->NotificationService()); return rtaNotificationService->AddGameInviteHandler(NotificationSubscription::MPSDInviteHandler{ [ handler, context ] (const GameInviteNotificationEventArgs& args) { try { handler(&args, context); } catch (...) { LOGS_ERROR << __FUNCTION__ << ": exception in client handler!"; } } }); } CATCH_RETURN() STDAPI_(void) XblGameInviteRemoveNotificationHandler( _In_ XblContextHandle xblContextHandle, _In_ XblFunctionContext token ) XBL_NOEXCEPT try { if (xblContextHandle) { auto rtaNotificationService = std::dynamic_pointer_cast(xblContextHandle->NotificationService()); rtaNotificationService->RemoveNotificationHandler(token); } } CATCH_RETURN_WITH(;) STDAPI_(XblFunctionContext) XblAchievementUnlockAddNotificationHandler( _In_ XblContextHandle xblContextHandle, _In_ XblAchievementUnlockHandler* handler, _In_opt_ void* context ) XBL_NOEXCEPT try { // TODO really should be returning HRESULT E_INVALIDARG here if (xblContextHandle == nullptr || handler == nullptr) { return XblFunctionContext{ 0 }; } auto rtaNotificationService = std::dynamic_pointer_cast(xblContextHandle->NotificationService()); return rtaNotificationService->AddAchievementUnlockNotificationHandler( [handler, context] (const AchievementUnlockEvent& args) { try { handler(&args, context); } catch (...) { LOGS_ERROR << __FUNCTION__ << ": exception in client handler!"; } }); } CATCH_RETURN() STDAPI_(void) XblAchievementUnlockRemoveNotificationHandler( _In_ XblContextHandle xblContextHandle, _In_ XblFunctionContext functionContext ) XBL_NOEXCEPT try { auto rtaNotificationService = std::dynamic_pointer_cast(xblContextHandle->NotificationService()); rtaNotificationService->RemoveNotificationHandler(functionContext); } CATCH_RETURN_WITH(;) #endif ================================================ FILE: Source/Services/Notification/notification_internal.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #if HC_PLATFORM == HC_PLATFORM_WIN32 || HC_PLATFORM_IS_EXTERNAL #include "real_time_activity_subscription.h" #include "RTA/notification_subscription.h" #endif NAMESPACE_MICROSOFT_XBOX_SERVICES_NOTIFICATION_CPP_BEGIN enum NotificationTypeFilterSourceType { Media_Presence = 1, Presence_Online = 2, Broadcast = 3, Message = 4, Party_Invite_360 = 5, Multiplayer = 6, Achievements = 8 }; struct NotificationFilter { NotificationTypeFilterSourceType sourceType; uint32_t type; }; class NotificationService : public std::enable_shared_from_this { public: enum class RegistrationStatus : uint32_t { Unregistered, Unregistering, PendingUnregistration, Registered, Registering } m_registrationStatus{ RegistrationStatus::Unregistered }; NotificationService( _In_ User&& user, _In_ std::shared_ptr contextSettings); virtual ~NotificationService() = default; virtual HRESULT RegisterWithNotificationService( #if HC_PLATFORM == HC_PLATFORM_IOS || HC_PLATFORM == HC_PLATFORM_ANDROID || HC_PLATFORM == HC_PLATFORM_WIN32 || HC_PLATFORM_IS_EXTERNAL _In_ const String& endpointId, #endif _In_ AsyncContext async) = 0; virtual HRESULT UnregisterFromNotificationService( _In_ AsyncContext async); protected: AsyncContext m_registrationAsync; AsyncContext m_unregistrationAsync; HRESULT RegisterForNotificationsHelper( _In_ const String& applicationInstanceId, _In_ const String& uriData, _In_ const String& platform, _In_ const String& deviceName, _In_ const String& platformVersion, _In_ const Vector notificationFilterEnum, _In_ AsyncContext async ); HRESULT UnregisterFromNotificationHelper( _In_ const String& endpointId, _In_ AsyncContext async); User m_user; std::shared_ptr m_contextSettings; String m_endpointId; bool m_isInitialized{ false }; std::recursive_mutex m_mutex; }; #if HC_PLATFORM == HC_PLATFORM_ANDROID || HC_PLATFORM == HC_PLATFORM_IOS class MobileNotificationService : public NotificationService { public: MobileNotificationService( _In_ User&& user, _In_ std::shared_ptr contextSettings); HRESULT RegisterWithNotificationService( _In_ const String& uriData, _In_ AsyncContext async ) noexcept override; }; #endif #if HC_PLATFORM == HC_PLATFORM_WIN32 || HC_PLATFORM_IS_EXTERNAL class RTANotificationService : public NotificationService { public: RTANotificationService( _In_ User&& user, _In_ const TaskQueue& taskQueue, _In_ std::shared_ptr contextSettings, _In_ std::shared_ptr rtaManager ) noexcept; ~RTANotificationService() noexcept; HRESULT Initialize() noexcept; XblFunctionContext AddGameInviteHandler( _In_ NotificationSubscription::MPSDInviteHandler handler ) noexcept; XblFunctionContext AddGameInviteHandler( _In_ NotificationSubscription::MultiplayerActivityInviteHandler handler ) noexcept; void RemoveNotificationHandler( _In_ XblFunctionContext token ) noexcept; XblFunctionContext AddAchievementUnlockNotificationHandler( _In_ NotificationSubscription::AchievementUnlockHandler handler ) noexcept; HRESULT RegisterWithNotificationService( _In_ const String& endpointId, _In_ AsyncContext async ) noexcept override; private: TaskQueue m_taskQueue; std::shared_ptr m_rtaManager; std::shared_ptr m_rtaSubscription; }; #endif NAMESPACE_MICROSOFT_XBOX_SERVICES_NOTIFICATION_CPP_END ================================================ FILE: Source/Services/Notification/notification_service.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "xbox_live_context_internal.h" #include "notification_internal.h" #if HC_PLATFORM == HC_PLATFORM_WIN32 using namespace pplx; static const std::chrono::milliseconds RETRY_LENGTH(60 * 1000); static const int32_t NUM_RETRY_TIMES = 10; #endif NAMESPACE_MICROSOFT_XBOX_SERVICES_NOTIFICATION_CPP_BEGIN NotificationService::NotificationService( _In_ User&& user, _In_ std::shared_ptr contextSettings ) : m_user(std::move(user)), m_contextSettings(contextSettings) {} HRESULT NotificationService::UnregisterFromNotificationService( _In_ AsyncContext async) { std::lock_guard lock{ m_mutex }; switch (m_registrationStatus) { case RegistrationStatus::Unregistered: { // No work to do in this case async.Complete(S_OK); return S_OK; } case RegistrationStatus::Unregistering: case RegistrationStatus::PendingUnregistration: { // If we are already unregistering, collapse the AsyncContext and return m_unregistrationAsync = AsyncContext::Collapse({ std::move(m_unregistrationAsync), std::move(async) }); return S_OK; } case RegistrationStatus::Registering: { // Wait until registration finishes and then unregister m_registrationStatus = RegistrationStatus::PendingUnregistration; m_unregistrationAsync = std::move(async); return S_OK; } case RegistrationStatus::Registered: default: { // Otherwise proceed with unregistration m_registrationStatus = RegistrationStatus::Unregistering; m_unregistrationAsync = std::move(async); assert(!m_endpointId.empty()); return UnregisterFromNotificationHelper(m_endpointId, async); } } } HRESULT NotificationService::UnregisterFromNotificationHelper( _In_ const String& endpointId, _In_ AsyncContext async) { auto subpath = "/system/notifications/endpoints/" + endpointId; Result userResult = m_user.Copy(); RETURN_HR_IF_FAILED(userResult.Hresult()); auto httpCall = MakeShared(userResult.ExtractPayload()); HRESULT hr = httpCall->Init( m_contextSettings, "DELETE", XblHttpCall::BuildUrl("notify", subpath), xbox_live_api::subscribe_to_notifications ); RETURN_HR_IF_FAILED(hr); return httpCall->Perform(AsyncContext{ async.Queue().DeriveWorkerQueue(), [ thisWeakPtr = std::weak_ptr{ shared_from_this() }, async ](HttpResult httpResult) { if (auto pThis{ thisWeakPtr.lock() }) { if (pThis == nullptr) { async.Complete(E_XBL_RUNTIME_ERROR); return; } std::lock_guard lock{ pThis->m_mutex }; // If the call fails, log but still treat it as though we are now unregistered pThis->m_registrationStatus = RegistrationStatus::Unregistered; pThis->m_endpointId.clear(); HRESULT hr = httpResult.Hresult(); if (SUCCEEDED(hr)) { hr = httpResult.Payload()->Result(); if (FAILED(hr)) { LOG_ERROR("notification service attempt failed\n"); LOG_ERROR("http status code: "); LOGS_ERROR << httpResult.Payload()->HttpStatus(); } } async.Complete(hr); return pThis->m_unregistrationAsync.Complete(hr); } else { return async.Complete(E_XBL_RUNTIME_ERROR); } } }); } HRESULT NotificationService::RegisterForNotificationsHelper( _In_ const String& applicationInstanceId, _In_ const String& uriData, _In_ const String& platform, _In_ const String& deviceName, _In_ const String& platformVersion, _In_ const Vector notificationFilterEnum, _In_ AsyncContext async ) { switch (m_registrationStatus) { case RegistrationStatus::PendingUnregistration: { // This indicates registration is in progress. Since another registration was requested, abort the // pending unregistration, complete the unregistration AsyncContext, and collapse the register AsyncContexts m_registrationAsync = AsyncContext::Collapse({ std::move(m_registrationAsync), std::move(async) }); m_registrationStatus = RegistrationStatus::Registering; m_unregistrationAsync.Complete(E_ABORT); return S_OK; } case RegistrationStatus::Registered: { // If we have already registered, no work to do async.Complete(S_OK); return S_OK; } case RegistrationStatus::Registering: { // If we are already registering, collapse the AsyncContexts and return m_registrationAsync = AsyncContext::Collapse({ std::move(m_registrationAsync), std::move(async) }); return S_OK; } default: { auto workQueue = async.Queue().DeriveWorkerQueue(); m_registrationAsync = std::move(async); m_registrationStatus = RegistrationStatus::Registering; xsapi_internal_stringstream str; str << AppConfig::Instance()->TitleId(); xsapi_internal_string titleId = str.str(); JsonDocument payload(rapidjson::kObjectType); JsonDocument::AllocatorType& allocator = payload.GetAllocator(); payload.AddMember("systemId", JsonValue(applicationInstanceId.data(), allocator).Move(), allocator); payload.AddMember("endpointUri", JsonValue(uriData.data(), allocator).Move(), allocator); payload.AddMember("platform", JsonValue(platform.data(), allocator).Move(), allocator); if (!platformVersion.empty()) { payload.AddMember("platformVersion", JsonValue(platformVersion.data(), allocator).Move(), allocator); } #if HC_PLATFORM == HC_PLATFORM_IOS payload.AddMember("transport", "NotiHub", allocator); payload.AddMember("transportPath", JsonValue(AppConfig::Instance()->APNSEnvironment().c_str(), allocator).Move(), allocator); #elif HC_PLATFORM == HC_PLATFORM_ANDROID payload.AddMember("transport", "FCM", allocator); #elif HC_PLATFORM == HC_PLATFORM_WIN32 || HC_PLATFORM == HC_PLATFORM_GDK || HC_PLATFORM_IS_EXTERNAL payload.AddMember("transport", "RTA", allocator); #endif xsapi_internal_string locale = utils::get_locales(); payload.AddMember("locale", JsonValue(locale.c_str(), allocator).Move(), allocator); payload.AddMember("titleId", JsonValue(titleId.c_str(), allocator).Move(), allocator); if (!deviceName.empty()) { payload.AddMember("deviceName", JsonValue(deviceName.data(), allocator).Move(), allocator); } JsonValue filterJson(rapidjson::kArrayType); for (auto& notificationFilter : notificationFilterEnum) { JsonValue filterObj(rapidjson::kObjectType); filterObj.AddMember("action", "Include", allocator); filterObj.AddMember("source", notificationFilter.sourceType, allocator); filterObj.AddMember("type", notificationFilter.type, allocator); filterJson.PushBack(filterObj, allocator); } payload.AddMember("filters", filterJson, allocator); Result userResult = m_user.Copy(); RETURN_HR_IF_FAILED(userResult.Hresult()); auto httpCall = MakeShared(userResult.ExtractPayload()); RETURN_HR_IF_FAILED(httpCall->Init( m_contextSettings, "POST", XblHttpCall::BuildUrl("notify", "/system/notifications/endpoints"), xbox_live_api::subscribe_to_notifications )); RETURN_HR_IF_FAILED(httpCall->SetRequestBody(payload)); return httpCall->Perform(AsyncContext{ workQueue, [ thisWeakPtr = std::weak_ptr{ shared_from_this() } ](HttpResult httpResult) { std::shared_ptr pThis(thisWeakPtr.lock()); if (pThis == nullptr) { return; } HRESULT hr = httpResult.Hresult(); if (SUCCEEDED(hr)) { hr = httpResult.Payload()->Result(); if (SUCCEEDED(hr)) { const JsonValue& responseJson = httpResult.Payload()->GetResponseBodyJson(); hr = JsonUtils::ExtractJsonString(responseJson, "endpointId", pThis->m_endpointId); // Registration has succeeded at this point switch (pThis->m_registrationStatus) { case RegistrationStatus::Registering: { pThis->m_registrationStatus = RegistrationStatus::Registered; pThis->m_registrationAsync.Complete(hr); break; } default: { // No other states should be possible assert(false); } } } else { // Registration failed for some reason pThis->m_registrationStatus = RegistrationStatus::Unregistered; pThis->m_registrationAsync.Complete(E_XBL_RUNTIME_ERROR); } } } }); } }; } NAMESPACE_MICROSOFT_XBOX_SERVICES_NOTIFICATION_CPP_END ================================================ FILE: Source/Services/Presence/device_presence_change_subscription.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "presence_internal.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_PRESENCE_CPP_BEGIN DevicePresenceChangeSubscription::DevicePresenceChangeSubscription( _In_ uint64_t xuid, _In_ std::shared_ptr presenceService ) noexcept : m_xuid{ xuid }, m_presenceService{ presenceService } { Stringstream uri; uri << "https://userpresence.xboxlive.com/users/xuid(" << m_xuid << ")/devices"; m_resourceUri = uri.str(); } void DevicePresenceChangeSubscription::OnSubscribe( const JsonValue& data ) noexcept { if (data.IsNull()) { LOGS_ERROR << __FUNCTION__ << ": RTA payload unexpectedly null"; return; } auto presenceService{ m_presenceService.lock() }; if (presenceService) { auto deserializationResult = XblPresenceRecord::Deserialize(data); if (Succeeded(deserializationResult)) { for (const auto& deviceRecord : deserializationResult.Payload()->DeviceRecords()) { presenceService->HandleDevicePresenceChanged(m_xuid, deviceRecord.deviceType, true); } } } } void DevicePresenceChangeSubscription::OnEvent( _In_ const JsonValue& data ) noexcept { auto presenceService{ m_presenceService.lock() }; if (presenceService && data.IsString()) { auto devicePresenceValues = utils::string_split_internal(String{ data.GetString() }, ':'); if (devicePresenceValues.size() == 2) { presenceService->HandleDevicePresenceChanged( m_xuid, DeviceRecord::DeviceTypeFromString(devicePresenceValues[0]), utils::str_icmp_internal(devicePresenceValues[1], "true") == 0 ); } } } NAMESPACE_MICROSOFT_XBOX_SERVICES_PRESENCE_CPP_END ================================================ FILE: Source/Services/Presence/presence_api.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "xsapi-c/presence_c.h" #include "presence_internal.h" #include "xbox_live_context_internal.h" #include "real_time_activity_manager.h" using namespace xbox::services; using namespace xbox::services::presence; STDAPI XblPresenceRecordGetXuid( _In_ XblPresenceRecordHandle record, _Out_ uint64_t* xuid ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(record == nullptr || xuid == nullptr); *xuid = record->Xuid(); return S_OK; } CATCH_RETURN() STDAPI XblPresenceRecordGetUserState( _In_ XblPresenceRecordHandle record, _Out_ XblPresenceUserState* userState ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(record == nullptr || userState == nullptr); *userState = record->UserState(); return S_OK; } CATCH_RETURN() STDAPI XblPresenceRecordGetDeviceRecords( _In_ XblPresenceRecordHandle record, _Out_ const XblPresenceDeviceRecord** deviceRecords, _Out_ size_t* deviceRecordsCount ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(record == nullptr || deviceRecords == nullptr || deviceRecordsCount == nullptr); *deviceRecords = record->DeviceRecords().data(); *deviceRecordsCount = record->DeviceRecords().size(); return S_OK; } CATCH_RETURN() STDAPI XblPresenceRecordDuplicateHandle( _In_ XblPresenceRecordHandle record, _Out_ XblPresenceRecordHandle* duplicatedHandle ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(record == nullptr || duplicatedHandle == nullptr); record->AddRef(); *duplicatedHandle = record; return S_OK; } CATCH_RETURN() STDAPI_(void) XblPresenceRecordCloseHandle( _In_ XblPresenceRecordHandle record ) XBL_NOEXCEPT try { if (record) { record->DecRef(); } } CATCH_RETURN_WITH(;) STDAPI XblPresenceSetPresenceAsync( _In_ XblContextHandle xblContextHandle, _In_ bool isUserActiveInTitle, _In_opt_ XblPresenceRichPresenceIds* richPresenceIds, _In_ XAsyncBlock* async ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(xblContextHandle == nullptr || async == nullptr); VERIFY_XBL_INITIALIZED(); return RunAsync(async, __FUNCTION__, [ xblContext{ xblContextHandle->shared_from_this() }, titleRequest = TitleRequest{ isUserActiveInTitle, richPresenceIds } ] (XAsyncOp op, const XAsyncProviderData* data) mutable { switch (op) { case XAsyncOp::DoWork: { RETURN_PENDING_OR_HR(xblContext->PresenceService()->SetPresence( std::move(titleRequest), AsyncContext{ data->async } )); } default: { return S_OK; } } // end switch }); } CATCH_RETURN() STDAPI XblPresenceGetPresenceAsync( _In_ XblContextHandle xblContextHandle, _In_ uint64_t xuid, _In_ XAsyncBlock* async ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(xblContextHandle == nullptr || async == nullptr); VERIFY_XBL_INITIALIZED(); return RunAsync(async, __FUNCTION__, [ xblContext{ xblContextHandle->shared_from_this() }, xuid, presenceRecord = std::shared_ptr{ nullptr } // result ] (XAsyncOp op, const XAsyncProviderData* data) mutable { switch (op) { case XAsyncOp::DoWork: { RETURN_PENDING_OR_HR(xblContext->PresenceService()->GetPresence( xuid, AsyncContext>>{ TaskQueue{ data->async->queue }, [ &presenceRecord, asyncBlock{ data->async } ] (Result> result) { if (Succeeded(result)) { presenceRecord = result.ExtractPayload(); } XAsyncComplete(asyncBlock, result.Hresult(), sizeof(XblPresenceRecordHandle)); } })); } case XAsyncOp::GetResult: { auto handlePtr = static_cast(data->buffer); presenceRecord->AddRef(); *handlePtr = presenceRecord.get(); return S_OK; } default: { return S_OK; } } // end switch }); } CATCH_RETURN() STDAPI XblPresenceGetPresenceResult( _In_ XAsyncBlock* async, _Out_ XblPresenceRecordHandle* presenceRecordHandle ) XBL_NOEXCEPT try { return XAsyncGetResult(async, nullptr, sizeof(XblPresenceRecordHandle), presenceRecordHandle, nullptr); } CATCH_RETURN() STDAPI GetBatchPresenceProvider( _In_ XblContextHandle xblContextHandle, _In_ UserBatchRequest&& request, _In_ XAsyncBlock* async ) XBL_NOEXCEPT try { return RunAsync(async, __FUNCTION__, [ xblContext{ xblContextHandle->shared_from_this() }, request, presenceRecords = Vector>{} // result ] (XAsyncOp op, const XAsyncProviderData* data) mutable { switch (op) { case XAsyncOp::DoWork: { RETURN_PENDING_OR_HR(xblContext->PresenceService()->GetBatchPresence( std::move(request), { TaskQueue{ data->async->queue }, [ &presenceRecords, asyncBlock{ data->async } ] (Result>> result) { if (Succeeded(result)) { presenceRecords = result.ExtractPayload(); } XAsyncComplete(asyncBlock, result.Hresult(), sizeof(XblPresenceRecordHandle)* presenceRecords.size()); } })); } case XAsyncOp::GetResult: { auto handlesPtr = static_cast(data->buffer); for (const auto& record : presenceRecords) { record->AddRef(); *handlesPtr++ = record.get(); } return S_OK; } default: { return S_OK; } } // end switch }); } CATCH_RETURN() STDAPI XblPresenceGetPresenceForMultipleUsersAsync( _In_ XblContextHandle xblContextHandle, _In_ uint64_t* xuids, _In_ size_t xuidsCount, _In_opt_ XblPresenceQueryFilters* filters, _In_ XAsyncBlock* async ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(xblContextHandle == nullptr || xuids == nullptr || async == nullptr); VERIFY_XBL_INITIALIZED(); return GetBatchPresenceProvider( xblContextHandle, UserBatchRequest{ xuids, xuidsCount, filters }, async ); } CATCH_RETURN() STDAPI XblPresenceGetPresenceForMultipleUsersResultCount( _In_ XAsyncBlock* async, _Out_ size_t* resultCount ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(resultCount); size_t bufferSize; auto hr = XAsyncGetResultSize(async, &bufferSize); if (SUCCEEDED(hr)) { *resultCount = bufferSize / sizeof(XblPresenceRecordHandle); } return hr; } CATCH_RETURN() STDAPI XblPresenceGetPresenceForMultipleUsersResult( _In_ XAsyncBlock* async, _Out_writes_(presenceRecordHandlesCount) XblPresenceRecordHandle* presenceRecordHandles, _In_ size_t presenceRecordHandlesCount ) XBL_NOEXCEPT try { RETURN_HR_IF(presenceRecordHandlesCount == 0, S_OK); RETURN_HR_INVALIDARGUMENT_IF(presenceRecordHandles == nullptr); return XAsyncGetResult(async, nullptr, sizeof(XblPresenceRecordHandle) * presenceRecordHandlesCount, presenceRecordHandles, nullptr); } CATCH_RETURN() STDAPI XblPresenceGetPresenceForSocialGroupAsync( _In_ XblContextHandle xblContextHandle, _In_z_ const char* socialGroupName, _In_opt_ uint64_t* socialGroupOwnerXuid, _In_opt_ XblPresenceQueryFilters* filters, _In_ XAsyncBlock* async ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(xblContextHandle == nullptr || socialGroupName == nullptr || async == nullptr); VERIFY_XBL_INITIALIZED(); return GetBatchPresenceProvider( xblContextHandle, UserBatchRequest{ socialGroupName, socialGroupOwnerXuid, filters }, async ); } CATCH_RETURN() STDAPI XblPresenceGetPresenceForSocialGroupResultCount( _In_ XAsyncBlock* async, _Out_ size_t* resultCount ) XBL_NOEXCEPT try { return XblPresenceGetPresenceForMultipleUsersResultCount(async, resultCount); } CATCH_RETURN() STDAPI XblPresenceGetPresenceForSocialGroupResult( _In_ XAsyncBlock* async, _Out_ XblPresenceRecordHandle* presenceRecordHandles, _In_ size_t presenceRecordHandlesCount ) XBL_NOEXCEPT try { return XblPresenceGetPresenceForMultipleUsersResult(async, presenceRecordHandles, presenceRecordHandlesCount); } CATCH_RETURN() namespace xbox { namespace services { namespace presence { namespace legacy { struct Subscription : public XblRealTimeActivitySubscription { Subscription( std::shared_ptr presenceService, uint64_t xuid ) noexcept : m_presenceService{ presenceService }, m_xuid{ xuid } { presenceService->TrackUsers(Vector{ m_xuid }); } virtual ~Subscription() noexcept { if (auto presenceService{ m_presenceService.lock() }) { presenceService->StopTrackingUsers(Vector{ m_xuid }); } } protected: std::weak_ptr m_presenceService; private: uint64_t m_xuid{ 0 }; }; struct TitleSubscription : public Subscription { TitleSubscription( std::shared_ptr presenceService, uint64_t xuid, uint32_t titleId ) noexcept : Subscription{ presenceService, xuid }, m_titleId{ titleId } { presenceService->TrackAdditionalTitles(Vector{ m_titleId }); } ~TitleSubscription() noexcept { if (auto presenceService{ m_presenceService.lock() }) { presenceService->StopTrackingAdditionalTitles(Vector{ m_titleId }); } } private: uint32_t m_titleId{ 0 }; }; } } } } STDAPI XblPresenceSubscribeToDevicePresenceChange( _In_ XblContextHandle xblContextHandle, _In_ uint64_t xuid, _Out_ XblRealTimeActivitySubscriptionHandle* subscriptionHandle ) XBL_NOEXCEPT try { INIT_OUT_PTR_PARAM(subscriptionHandle); RETURN_HR_INVALIDARGUMENT_IF(xblContextHandle == nullptr || subscriptionHandle == nullptr); *subscriptionHandle = Make(xblContextHandle->PresenceService(), xuid); return S_OK; } CATCH_RETURN() STDAPI XblPresenceUnsubscribeFromDevicePresenceChange( _In_ XblContextHandle xblContextHandle, _In_ XblRealTimeActivitySubscriptionHandle subscriptionHandle ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(xblContextHandle == nullptr || subscriptionHandle == nullptr); Delete(subscriptionHandle); return S_OK; } CATCH_RETURN() STDAPI XblPresenceSubscribeToTitlePresenceChange( _In_ XblContextHandle xblContextHandle, _In_ uint64_t xuid, _In_ uint32_t titleId, _Out_ XblRealTimeActivitySubscriptionHandle* subscriptionHandle ) XBL_NOEXCEPT try { INIT_OUT_PTR_PARAM(subscriptionHandle); RETURN_HR_INVALIDARGUMENT_IF(xblContextHandle == nullptr || subscriptionHandle == nullptr); *subscriptionHandle = Make(xblContextHandle->PresenceService(), xuid, titleId); return S_OK; } CATCH_RETURN() STDAPI XblPresenceUnsubscribeFromTitlePresenceChange( _In_ XblContextHandle xblContext, _In_ XblRealTimeActivitySubscriptionHandle subscriptionHandle ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(xblContext == nullptr || subscriptionHandle == nullptr); Delete(subscriptionHandle); return S_OK; } CATCH_RETURN() STDAPI_(XblFunctionContext) XblPresenceAddDevicePresenceChangedHandler( _In_ XblContextHandle xblContext, _In_ XblPresenceDevicePresenceChangedHandler* handler, _In_opt_ void* context ) XBL_NOEXCEPT try { if (xblContext == nullptr || handler == nullptr) { return XblFunctionContext{ 0 }; } return xblContext->PresenceService()->AddDevicePresenceChangedHandler( [ handler, context ] (uint64_t xuid, XblPresenceDeviceType deviceType, bool isOnline) { try { handler(context, xuid, deviceType, isOnline); } catch (...) { LOGS_ERROR << __FUNCTION__ << ": exception in client handler!"; } }); } CATCH_RETURN() STDAPI XblPresenceRemoveDevicePresenceChangedHandler( _In_ XblContextHandle xblContext, _In_ XblFunctionContext token ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(xblContext); xblContext->PresenceService()->RemoveDevicePresenceChangedHandler(token); return S_OK; } CATCH_RETURN() STDAPI_(XblFunctionContext) XblPresenceAddTitlePresenceChangedHandler( _In_ XblContextHandle xblContext, _In_ XblPresenceTitlePresenceChangedHandler* handler, _In_opt_ void* context ) XBL_NOEXCEPT try { if (xblContext == nullptr || handler == nullptr) { return XblFunctionContext{ 0 }; } return xblContext->PresenceService()->AddTitlePresenceChangedHandler( [ handler, context ] (uint64_t xuid, uint32_t titleId, XblPresenceTitleState state) { try { handler(context, xuid, titleId, state); } catch (...) { LOGS_ERROR << __FUNCTION__ << ": exception in client handler!"; } }); } CATCH_RETURN() STDAPI XblPresenceRemoveTitlePresenceChangedHandler( _In_ XblContextHandle xblContext, _In_ XblFunctionContext token ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(xblContext); xblContext->PresenceService()->RemoveTitlePresenceChangedHandler(token); return S_OK; } CATCH_RETURN() STDAPI XblPresenceTrackUsers( _In_ XblContextHandle xblContext, _In_ const uint64_t* xuids, _In_ size_t xuidsCount ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(xblContext == nullptr || xuids == nullptr); return xblContext->PresenceService()->TrackUsers(Vector(xuids, xuids + xuidsCount)); } CATCH_RETURN() STDAPI XblPresenceStopTrackingUsers( _In_ XblContextHandle xblContext, _In_ const uint64_t* xuids, _In_ size_t xuidsCount ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(xblContext == nullptr || xuids == nullptr); return xblContext->PresenceService()->StopTrackingUsers(Vector(xuids, xuids + xuidsCount)); } CATCH_RETURN() STDAPI XblPresenceTrackAdditionalTitles( _In_ XblContextHandle xblContext, _In_ const uint32_t* titleIds, _In_ size_t titleIdsCount ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(xblContext == nullptr || titleIds == nullptr); return xblContext->PresenceService()->TrackAdditionalTitles(Vector(titleIds, titleIds + titleIdsCount)); } CATCH_RETURN() STDAPI XblPresenceStopTrackingAdditionalTitles( _In_ XblContextHandle xblContext, _In_ const uint32_t* titleIds, _In_ size_t titleIdsCount ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(xblContext == nullptr || titleIds == nullptr); return xblContext->PresenceService()->StopTrackingAdditionalTitles(Vector(titleIds, titleIds + titleIdsCount)); } CATCH_RETURN() ================================================ FILE: Source/Services/Presence/presence_device_record.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "presence_internal.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_PRESENCE_CPP_BEGIN DeviceRecord::~DeviceRecord() { for (auto& titleRecord : m_titleRecords) { Delete(titleRecord.titleName); Delete(titleRecord.richPresenceString); if (titleRecord.broadcastRecord) { Delete(titleRecord.broadcastRecord->broadcastId); Delete(titleRecord.broadcastRecord); }; } } DeviceRecord::DeviceRecord(const DeviceRecord& other) : m_deviceType{ other.m_deviceType }, m_titleRecords{ other.m_titleRecords } { for (auto& titleRecord : m_titleRecords) { titleRecord.titleName = Make(titleRecord.titleName); titleRecord.richPresenceString = Make(titleRecord.richPresenceString); titleRecord.broadcastRecord = Make(*titleRecord.broadcastRecord); titleRecord.broadcastRecord->broadcastId = Make(titleRecord.broadcastRecord->broadcastId); } } DeviceRecord& DeviceRecord::operator=(DeviceRecord other) { std::swap(m_deviceType, other.m_deviceType); std::swap(m_titleRecords, other.m_titleRecords); return *this; } XblPresenceDeviceType DeviceRecord::DeviceType() const { return m_deviceType; } const xsapi_internal_vector& DeviceRecord::TitleRecords() const { return m_titleRecords; } Result> DeviceRecord::Deserialize( _In_ const JsonValue& inputJson ) { if (inputJson.IsNull()) { return Result>(); } auto returnObject = MakeShared(); xsapi_internal_string type; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(inputJson, "type", type)); returnObject->m_deviceType = DeviceTypeFromString(type); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonVector( DeserializeTitleRecord, inputJson, "titles", returnObject->m_titleRecords, false )); return Result>(returnObject, S_OK); } Result DeviceRecord::DeserializeTitleRecord(_In_ const JsonValue& json) { if (json.IsNull()) { Result(); } XblPresenceTitleRecord titleRecord{}; HRESULT errc = S_OK; xsapi_internal_string id; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "id", id)) titleRecord.titleId = utils::internal_string_to_uint32(id); xsapi_internal_string titleName; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "name", titleName)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonTimeT(json, "lastModified", titleRecord.lastModified)); xsapi_internal_string state; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "state", state)); titleRecord.titleActive = (!state.empty() && utils::str_icmp_internal(state, "active") == 0); xsapi_internal_string placement; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "placement", placement)); titleRecord.viewState = TitleViewStateFromString(placement); xsapi_internal_string richPresenceString = ""; if (json.IsObject() && json.HasMember("activity")) { const JsonValue& activityJson = json["activity"]; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(activityJson, "richPresence", richPresenceString)); if (activityJson.IsObject() && activityJson.HasMember("broadcast")) { const JsonValue& broadcastRecordJson = activityJson["broadcast"]; if (!broadcastRecordJson.IsNull()) { auto broadcastRecordResult = DeserializeBroadcastRecord(broadcastRecordJson); if (SUCCEEDED(broadcastRecordResult.Hresult())) { titleRecord.broadcastRecord = Make(broadcastRecordResult.Payload()); } else { return Result(broadcastRecordResult.Hresult()); } } } } if (SUCCEEDED(errc)) { titleRecord.titleName = Make(titleName); titleRecord.richPresenceString = Make(richPresenceString); } return Result(titleRecord, errc); } Result DeviceRecord::DeserializeBroadcastRecord(_In_ const JsonValue& json) { if (json.IsNull()) { return Result(); } XblPresenceBroadcastRecord broadcastRecord{}; HRESULT errc = S_OK; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonStringToCharArray(json, "session", broadcastRecord.session, sizeof(broadcastRecord.session))); xsapi_internal_string provider; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "provider", provider)); broadcastRecord.provider = BroadcastProviderFromString(provider); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonInt(json, "viewers", broadcastRecord.viewerCount)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonTimeT(json, "started", broadcastRecord.startTime)); xsapi_internal_string broadcastId; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "id", broadcastId)); if (SUCCEEDED(errc)) { broadcastRecord.broadcastId = Make(broadcastId); } return Result(broadcastRecord, errc); } XblPresenceTitleViewState DeviceRecord::TitleViewStateFromString( _In_ const xsapi_internal_string &value ) { if (utils::str_icmp_internal(value, "full") == 0) { return XblPresenceTitleViewState::FullScreen; } else if (utils::str_icmp_internal(value, "fill") == 0) { return XblPresenceTitleViewState::Filled; } else if (utils::str_icmp_internal(value, "snapped") == 0) { return XblPresenceTitleViewState::Snapped; } else if (utils::str_icmp_internal(value, "background") == 0) { return XblPresenceTitleViewState::Background; } return XblPresenceTitleViewState::Unknown; } XblPresenceBroadcastProvider DeviceRecord::BroadcastProviderFromString( _In_ const xsapi_internal_string& value ) { if (utils::str_icmp_internal(value, "twitch") == 0) { return XblPresenceBroadcastProvider::Twitch; } return XblPresenceBroadcastProvider::Unknown; } XblPresenceDeviceType DeviceRecord::DeviceTypeFromString( _In_ const xsapi_internal_string& value ) { if (utils::str_icmp_internal(value, "MoLive") == 0) { return XblPresenceDeviceType::Windows8; } else if (utils::str_icmp_internal(value, "MCapensis") == 0) { return XblPresenceDeviceType::XboxOne; } else { // The other enums line up with strings so EnumValue to automap return EnumValue(value.data()); } } xsapi_internal_string DeviceRecord::DeviceTypeAsString( _In_ XblPresenceDeviceType deviceType ) { switch (deviceType) { case XblPresenceDeviceType::Windows8: return "MoLive"; default: // The other enums line up with enums so EnumName to automap return EnumName(deviceType); } } NAMESPACE_MICROSOFT_XBOX_SERVICES_PRESENCE_CPP_END ================================================ FILE: Source/Services/Presence/presence_internal.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "xsapi-c/presence_c.h" #include "real_time_activity_subscription.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_PRESENCE_CPP_BEGIN class UserBatchRequest { public: UserBatchRequest( _In_ uint64_t* xuids, _In_ size_t xuidsCount, _In_opt_ XblPresenceQueryFilters* filters ) noexcept; UserBatchRequest( _In_ String&& socialGroup, _In_opt_ uint64_t* socialGroupOwnerXuid, _In_opt_ XblPresenceQueryFilters* filters ) noexcept; static String StringFromDetailLevel( _In_ XblPresenceDetailLevel level ); void Serialize(_Out_ JsonValue& serializedObject, _In_ JsonDocument::AllocatorType& allocator) const; private: UserBatchRequest(_In_opt_ XblPresenceQueryFilters* filters) noexcept; Vector m_xuids; String m_socialGroup; String m_socialGroupOwnerXuid; Vector m_deviceTypes; Vector m_titleIds; XblPresenceDetailLevel m_presenceDetailLevel{ XblPresenceDetailLevel::Default }; bool m_onlineOnly{ false }; bool m_broadcastingOnly{ false }; }; class TitleRequest { public: TitleRequest( _In_ bool isUserActive, _In_opt_ const XblPresenceRichPresenceIds* richPresenceIds ); void Serialize(_Out_ JsonValue& serializedObject, _In_ JsonDocument::AllocatorType& allocator); private: bool m_isUserActive; String m_scid; String m_presenceId; Vector m_presenceTokenIds; }; class PresenceService; class TitlePresenceChangeSubscription : public real_time_activity::Subscription, public std::enable_shared_from_this { public: TitlePresenceChangeSubscription( _In_ uint64_t xuid, _In_ uint32_t titleId, _In_ std::shared_ptr presenceService ) noexcept; protected: void OnSubscribe(const JsonValue& data) noexcept override; void OnEvent(const JsonValue& event) noexcept override; private: uint64_t m_xuid; uint32_t m_titleId; std::weak_ptr m_presenceService; }; class DevicePresenceChangeSubscription : public real_time_activity::Subscription, public std::enable_shared_from_this { public: DevicePresenceChangeSubscription( _In_ uint64_t xuid, _In_ std::shared_ptr presenceService ) noexcept; protected: void OnSubscribe(const JsonValue& data) noexcept override; void OnEvent(_In_ const JsonValue& event) noexcept override; private: uint64_t m_xuid; std::weak_ptr m_presenceService; }; class DeviceRecord { public: DeviceRecord() = default; DeviceRecord(const DeviceRecord& other); DeviceRecord& operator=(DeviceRecord other); ~DeviceRecord(); XblPresenceDeviceType DeviceType() const; const Vector& TitleRecords() const; static Result> Deserialize(_In_ const JsonValue& json); static String DeviceTypeAsString(_In_ XblPresenceDeviceType deviceType); static XblPresenceDeviceType DeviceTypeFromString(_In_ const String& value); private: static Result DeserializeTitleRecord(_In_ const JsonValue& json); static Result DeserializeBroadcastRecord(_In_ const JsonValue& json); static XblPresenceTitleViewState TitleViewStateFromString(_In_ const String& viewState); static XblPresenceBroadcastProvider BroadcastProviderFromString(_In_ const String& provider); XblPresenceDeviceType m_deviceType{ XblPresenceDeviceType::Unknown }; Vector m_titleRecords; }; namespace legacy { // To support legacy RTA path, subscriptions require some extra state to be tracked struct Subscription; struct TitleSubscription; } class PresenceService : public std::enable_shared_from_this { public: PresenceService( _In_ User&& user, _In_ const TaskQueue& backgroundQueue, _In_ std::shared_ptr xboxLiveContextSettings, _In_ std::shared_ptr rtaManager ) noexcept; ~PresenceService() noexcept; typedef Function TitlePresenceChangedHandler; XblFunctionContext AddTitlePresenceChangedHandler( TitlePresenceChangedHandler handler ) noexcept; void RemoveTitlePresenceChangedHandler( _In_ XblFunctionContext context ) noexcept; typedef Function DevicePresenceChangedHandler; XblFunctionContext AddDevicePresenceChangedHandler( DevicePresenceChangedHandler handler ) noexcept; void RemoveDevicePresenceChangedHandler( _In_ XblFunctionContext context ) noexcept; HRESULT TrackUsers( const Vector& xuids ) noexcept; HRESULT StopTrackingUsers( const Vector& xuids ) noexcept; HRESULT TrackAdditionalTitles( const Vector& titleIds ) noexcept; HRESULT StopTrackingAdditionalTitles( const Vector& titleIds ) noexcept; HRESULT SetPresence( _In_ TitleRequest&& titleRequest, _In_ AsyncContext async ) const noexcept; HRESULT GetPresence( _In_ uint64_t xuid, _In_ AsyncContext>> async ) const noexcept; HRESULT GetBatchPresence( _In_ UserBatchRequest&& batchRequest, _In_ AsyncContext>>> async ) const noexcept; private: void HandleDevicePresenceChanged( _In_ uint64_t xuid, _In_ XblPresenceDeviceType deviceType, _In_ bool isUserLoggedOnDevice ) const noexcept; void HandleTitlePresenceChanged( _In_ uint64_t xuid, _In_ uint32_t titleId, _In_ XblPresenceTitleState state ) const noexcept; void HandleRTAResync(); static Result>> DeserializeBatchPresenceRecordsResponse( const JsonValue& json ) noexcept; User m_user; TaskQueue m_queue; std::shared_ptr m_xboxLiveContextSettings; std::shared_ptr m_rtaManager; XblFunctionContext m_resyncHandlerToken{ 0 }; Map m_devicePresenceChangedHandlers; Map m_titlePresenceChangedHandlers; XblFunctionContext m_nextHandlerToken{ 1 }; struct TrackedXuidSubscriptions { size_t refCount{ 0 }; std::shared_ptr devicePresenceChangedSub; Map> titlePresenceChangedSubscriptions; }; Map m_trackedXuids; Map m_trackedTitles; uint32_t const m_titleId; mutable std::recursive_mutex m_mutex; friend class DevicePresenceChangeSubscription; friend class TitlePresenceChangeSubscription; }; NAMESPACE_MICROSOFT_XBOX_SERVICES_PRESENCE_CPP_END struct XblPresenceRecord : public xbox::services::RefCounter, public std::enable_shared_from_this { public: XblPresenceRecord() = default; uint64_t Xuid() const; XblPresenceUserState UserState() const; const Vector& DeviceRecords() const; bool IsUserPlayingTitle(_In_ uint32_t titleId) const; static xbox::services::Result> Deserialize(_In_ const JsonValue& json); static XblPresenceUserState UserStateFromString(_In_ const xbox::services::String& value); protected: // RefCounter overrides std::shared_ptr GetSharedThis() override; private: uint64_t m_xuid{}; XblPresenceUserState m_userState{ XblPresenceUserState::Unknown }; Vector m_deviceRecords; Vector> m_deviceRecordsInternal; }; ================================================ FILE: Source/Services/Presence/presence_record.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #if HC_PLATFORM == HC_PLATFORM_UWP || HC_PLATFORM == HC_PLATFORM_XDK || defined(XSAPI_UNIT_TESTS) #include "social_manager_internal.h" #endif #include "presence_internal.h" using namespace xbox::services; using namespace xbox::services::presence; uint64_t XblPresenceRecord::Xuid() const { return m_xuid; } XblPresenceUserState XblPresenceRecord::UserState() const { return m_userState; } const xsapi_internal_vector& XblPresenceRecord::DeviceRecords() const { return m_deviceRecords; } bool XblPresenceRecord::IsUserPlayingTitle( _In_ uint32_t titleId ) const { if (m_userState == XblPresenceUserState::Offline || m_userState == XblPresenceUserState::Unknown) { return false; } for (const auto& deviceRecord : m_deviceRecordsInternal) { for (const auto& titleRecord : deviceRecord->TitleRecords()) { if (titleRecord.titleId == titleId) { return titleRecord.titleActive; } } } return false; } Result> XblPresenceRecord::Deserialize( _In_ const JsonValue& json ) { if (json.IsNull()) { return Result>(nullptr); } auto presenceRecord = MakeShared(); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonXuid(json, "xuid", presenceRecord->m_xuid)); xsapi_internal_string state; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "state", state)); presenceRecord->m_userState = UserStateFromString(state); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonVector>( DeviceRecord::Deserialize, json, "devices", presenceRecord->m_deviceRecordsInternal, false )); for (const auto& deviceRecordInternal : presenceRecord->m_deviceRecordsInternal) { XblPresenceDeviceRecord deviceRecord { deviceRecordInternal->DeviceType(), deviceRecordInternal->TitleRecords().data(), deviceRecordInternal->TitleRecords().size() }; presenceRecord->m_deviceRecords.push_back(std::move(deviceRecord)); } return Result>(presenceRecord, S_OK); } XblPresenceUserState XblPresenceRecord::UserStateFromString( _In_ const xsapi_internal_string& value ) { if (utils::str_icmp_internal(value, "Online") == 0) { return XblPresenceUserState::Online; } else if (utils::str_icmp_internal(value, "Away") == 0) { return XblPresenceUserState::Away; } else if (utils::str_icmp_internal(value, "Offline") == 0) { return XblPresenceUserState::Offline; } return XblPresenceUserState::Unknown; } std::shared_ptr XblPresenceRecord::GetSharedThis() { return shared_from_this(); } ================================================ FILE: Source/Services/Presence/presence_service.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "presence_internal.h" #include "xbox_live_context_internal.h" #include "real_time_activity_manager.h" using namespace xbox::services::system; using namespace xbox::services::real_time_activity; NAMESPACE_MICROSOFT_XBOX_SERVICES_PRESENCE_CPP_BEGIN PresenceService::PresenceService( _In_ User&& user, _In_ const TaskQueue& queue, _In_ std::shared_ptr xboxLiveContextSettings, _In_ std::shared_ptr rtaManager ) noexcept : m_user{ std::move(user) }, m_queue{ queue.DeriveWorkerQueue() }, m_xboxLiveContextSettings{ xboxLiveContextSettings }, m_rtaManager{ rtaManager }, m_titleId{ AppConfig::Instance()->TitleId() } { // Track title presence changes for this title by default m_trackedTitles.insert(std::pair{ m_titleId, 1 }); } PresenceService::~PresenceService() noexcept { if (m_resyncHandlerToken) { m_rtaManager->RemoveResyncHandler(m_user, m_resyncHandlerToken); } for (auto& xuidPair : m_trackedXuids) { if (!m_devicePresenceChangedHandlers.empty()) { m_rtaManager->RemoveSubscription(m_user, xuidPair.second.devicePresenceChangedSub); } if (!m_titlePresenceChangedHandlers.empty()) { for (auto& titlePair : xuidPair.second.titlePresenceChangedSubscriptions) { m_rtaManager->RemoveSubscription(m_user, titlePair.second); } } } } XblFunctionContext PresenceService::AddTitlePresenceChangedHandler( TitlePresenceChangedHandler handler ) noexcept { std::unique_lock lock{ m_mutex }; m_titlePresenceChangedHandlers[m_nextHandlerToken] = std::move(handler); auto token = m_nextHandlerToken++; // Add subs to RTA manager if needed if (m_titlePresenceChangedHandlers.empty()) { for (auto& xuidPair : m_trackedXuids) { for (auto& titlePair : m_trackedTitles) { auto sub{ MakeShared(xuidPair.first, titlePair.first, shared_from_this()) }; xuidPair.second.titlePresenceChangedSubscriptions[titlePair.first] = sub; m_rtaManager->AddSubscription(m_user, sub); } } } return token; } void PresenceService::RemoveTitlePresenceChangedHandler( _In_ XblFunctionContext context ) noexcept { std::lock_guard lock{ m_mutex }; auto removed{ m_titlePresenceChangedHandlers.erase(context) }; if (removed && m_titlePresenceChangedHandlers.empty()) { for (auto& xuidPair : m_trackedXuids) { for (auto& titlePair : xuidPair.second.titlePresenceChangedSubscriptions) { m_rtaManager->RemoveSubscription(m_user, titlePair.second); titlePair.second.reset(); } } } } XblFunctionContext PresenceService::AddDevicePresenceChangedHandler( DevicePresenceChangedHandler handler ) noexcept { std::lock_guard lock{ m_mutex }; // Add subs to RTA manager if needed if (m_devicePresenceChangedHandlers.empty()) { for (auto& pair : m_trackedXuids) { pair.second.devicePresenceChangedSub = MakeShared(pair.first, shared_from_this()); m_rtaManager->AddSubscription(m_user, pair.second.devicePresenceChangedSub); } } m_devicePresenceChangedHandlers[m_nextHandlerToken] = std::move(handler); return m_nextHandlerToken++; } void PresenceService::RemoveDevicePresenceChangedHandler( _In_ XblFunctionContext context ) noexcept { std::lock_guard lock{ m_mutex }; auto removed{ m_devicePresenceChangedHandlers.erase(context) }; if (removed && m_devicePresenceChangedHandlers.empty()) { for (auto& pair : m_trackedXuids) { m_rtaManager->RemoveSubscription(m_user, pair.second.devicePresenceChangedSub); pair.second.devicePresenceChangedSub.reset(); } } } HRESULT PresenceService::TrackUsers( const Vector& xuids ) noexcept { std::lock_guard lock{ m_mutex }; if (!m_resyncHandlerToken) { m_resyncHandlerToken = m_rtaManager->AddResyncHandler(m_user, [weakThis = std::weak_ptr{ shared_from_this() } ] { auto sharedThis = weakThis.lock(); if (sharedThis) { sharedThis->HandleRTAResync(); } }); } for (auto& xuid : xuids) { // If we don't have them already, create RTA subscriptions for the new user auto iter{ m_trackedXuids.find(xuid) }; if (iter == m_trackedXuids.end()) { TrackedXuidSubscriptions newSubs{}; newSubs.refCount = 1; // If there are existing handlers, add the new subs to RTA managers if (!m_devicePresenceChangedHandlers.empty()) { newSubs.devicePresenceChangedSub = MakeShared(xuid, shared_from_this()); RETURN_HR_IF_FAILED(m_rtaManager->AddSubscription(m_user, newSubs.devicePresenceChangedSub)); } if (!m_titlePresenceChangedHandlers.empty()) { for (auto& pair : m_trackedTitles) { auto sub{ MakeShared(xuid, pair.first, shared_from_this()) }; newSubs.titlePresenceChangedSubscriptions[pair.first] = sub; RETURN_HR_IF_FAILED(m_rtaManager->AddSubscription(m_user, sub)); } } m_trackedXuids[xuid] = std::move(newSubs); } else { ++(iter->second.refCount); } } return S_OK; } HRESULT PresenceService::StopTrackingUsers( const Vector& xuids ) noexcept { std::lock_guard lock{ m_mutex }; for (auto& xuid : xuids) { auto iter{ m_trackedXuids.find(xuid) }; if (iter != m_trackedXuids.end() && --(iter->second.refCount) == 0) { // Remove the subs from RTA manager as necessary if (!m_devicePresenceChangedHandlers.empty()) { RETURN_HR_IF_FAILED(m_rtaManager->RemoveSubscription(m_user, iter->second.devicePresenceChangedSub)); } if (!m_titlePresenceChangedHandlers.empty()) { for (auto& pair : iter->second.titlePresenceChangedSubscriptions) { RETURN_HR_IF_FAILED(m_rtaManager->RemoveSubscription(m_user, pair.second)); } } m_trackedXuids.erase(iter); } } return S_OK; } HRESULT PresenceService::TrackAdditionalTitles( const Vector& titleIds ) noexcept { std::lock_guard lock{ m_mutex }; for (auto& titleId : titleIds) { auto iter{ m_trackedTitles.find(titleId) }; if (iter == m_trackedTitles.end()) { m_trackedTitles[titleId] = 1; // If its a new title, create the appropriate subscriptions for (auto& pair : m_trackedXuids) { // Add new subs to RTA manager if we have handlers if (!m_titlePresenceChangedHandlers.empty()) { auto sub{ MakeShared(pair.first, titleId, shared_from_this()) }; pair.second.titlePresenceChangedSubscriptions[titleId] = sub; RETURN_HR_IF_FAILED(m_rtaManager->AddSubscription(m_user, sub)); } } } else { ++(iter->second); } } return S_OK; } HRESULT PresenceService::StopTrackingAdditionalTitles( const Vector& titleIds ) noexcept { std::lock_guard lock{ m_mutex }; List removedTitles{}; for (auto& titleId : titleIds) { // Don't allow removal of the current title if (titleId == m_titleId) { continue; } auto iter{ m_trackedTitles.find(titleId) }; if (iter != m_trackedTitles.end() && --(iter->second) == 0) { for (auto& pair : m_trackedXuids) { auto& subs{ pair.second.titlePresenceChangedSubscriptions }; auto subIter{ subs.find(titleId) }; assert(subIter != subs.end()); // Remove subs from RTA manager as necessary if (!m_titlePresenceChangedHandlers.empty()) { RETURN_HR_IF_FAILED(m_rtaManager->RemoveSubscription(m_user, subIter->second)); } subs.erase(subIter); } m_trackedTitles.erase(iter); } } return S_OK; } HRESULT PresenceService::SetPresence( _In_ TitleRequest&& request, _In_ AsyncContext async ) const noexcept { Stringstream subpath; subpath << "/users/xuid(" << m_user.Xuid() << ")/devices/current/titles/current"; Result userResult = m_user.Copy(); RETURN_HR_IF_FAILED(userResult.Hresult()); auto httpCall = MakeShared(userResult.ExtractPayload()); RETURN_HR_IF_FAILED(httpCall->Init( m_xboxLiveContextSettings, "POST", XblHttpCall::BuildUrl("userpresence", subpath.str()), xbox_live_api::set_presence_helper )); JsonDocument titleRequestJson(rapidjson::kObjectType); request.Serialize(titleRequestJson, titleRequestJson.GetAllocator()); RETURN_HR_IF_FAILED(httpCall->SetRequestBody(titleRequestJson)); RETURN_HR_IF_FAILED(httpCall->SetHeader(CONTRACT_VERSION_HEADER, "3")); return httpCall->Perform({ async.Queue(), [ async ] (HttpResult result) { HRESULT hr{ Failed(result) ? result.Hresult() : result.Payload()->Result() }; async.Complete(hr); } }); } HRESULT PresenceService::GetPresence( _In_ uint64_t xuid, _In_ AsyncContext>> async ) const noexcept { Stringstream subpath; subpath << "/users/xuid(" << xuid << ")?level=all"; Result userResult = m_user.Copy(); RETURN_HR_IF_FAILED(userResult.Hresult()); auto httpCall = MakeShared(userResult.ExtractPayload()); RETURN_HR_IF_FAILED(httpCall->Init( m_xboxLiveContextSettings, "GET", XblHttpCall::BuildUrl("userpresence", subpath.str()), xbox_live_api::get_presence )); httpCall->SetHeader(CONTRACT_VERSION_HEADER, "3"); return httpCall->Perform({ async.Queue(), [ async ] (HttpResult result) { HRESULT hr{ Failed(result) ? result.Hresult() : result.Payload()->Result() }; if (SUCCEEDED(hr)) { return async.Complete(XblPresenceRecord::Deserialize(result.Payload()->GetResponseBodyJson())); } return async.Complete(hr); } }); } HRESULT PresenceService::GetBatchPresence( _In_ UserBatchRequest&& batchRequest, _In_ AsyncContext>>> async ) const noexcept { Result userResult = m_user.Copy(); RETURN_HR_IF_FAILED(userResult.Hresult()); auto httpCall = MakeShared(userResult.ExtractPayload()); RETURN_HR_IF_FAILED(httpCall->Init( m_xboxLiveContextSettings, "POST", XblHttpCall::BuildUrl("userpresence", "/users/batch"), xbox_live_api::get_presence_for_multiple_users )); JsonDocument batchRequestJson(rapidjson::kObjectType); batchRequest.Serialize(batchRequestJson, batchRequestJson.GetAllocator()); httpCall->SetRequestBody(batchRequestJson); httpCall->SetHeader(CONTRACT_VERSION_HEADER, "3"); return httpCall->Perform({ async.Queue().GetHandle(), [ async ] (HttpResult result) { HRESULT hr{ Failed(result) ? result.Hresult() : result.Payload()->Result() }; if (SUCCEEDED(hr)) { return async.Complete(DeserializeBatchPresenceRecordsResponse(result.Payload()->GetResponseBodyJson())); } return async.Complete(hr); } }); } void PresenceService::HandleDevicePresenceChanged( _In_ uint64_t xuid, _In_ XblPresenceDeviceType deviceType, _In_ bool isUserLoggedOnDevice ) const noexcept { std::unique_lock lock{ m_mutex }; auto handlers{ m_devicePresenceChangedHandlers }; lock.unlock(); for (auto& pair : handlers) { pair.second(xuid, deviceType, isUserLoggedOnDevice); } } void PresenceService::HandleTitlePresenceChanged( _In_ uint64_t xuid, _In_ uint32_t titleId, _In_ XblPresenceTitleState state ) const noexcept { std::unique_lock lock{ m_mutex }; auto handlers{ m_titlePresenceChangedHandlers }; lock.unlock(); for (auto& pair : handlers) { pair.second(xuid, titleId, state); } } void PresenceService::HandleRTAResync() { std::unique_lock lock{ m_mutex }; LOGS_DEBUG << "Resyncing " << m_trackedXuids.size() << " Presence Subscriptions"; auto weakThis = std::weak_ptr{ shared_from_this() }; auto handleGetPresenceResult = [weakThis, this](Result>> result) { auto sharedThis = weakThis.lock(); if (!sharedThis) { return; } std::unique_lock lock{ m_mutex }; if(Succeeded(result)) { Vector trackedTitles; for (auto& pair : m_trackedTitles) { trackedTitles.push_back(pair.first); } auto titlePresenceChangedHandlers{ m_titlePresenceChangedHandlers }; auto devicePresenceChangedHandlers{ m_devicePresenceChangedHandlers }; lock.unlock(); for (auto& presenceRecord : result.Payload()) { // Invoke title presence changed subs for tracked titles for (auto titleId : trackedTitles) { bool isPlaying = presenceRecord->IsUserPlayingTitle(titleId); for (auto& pair : titlePresenceChangedHandlers) { pair.second(presenceRecord->Xuid(), titleId, isPlaying ? XblPresenceTitleState::Started : XblPresenceTitleState::Ended); } } // Invoke device presence changed handlers for (const auto& deviceRecord : presenceRecord->DeviceRecords()) { for (auto& pair : devicePresenceChangedHandlers) { pair.second(presenceRecord->Xuid(), deviceRecord.deviceType, true); } } } } }; Vector trackedXuids; for (auto& pair : m_trackedXuids) { trackedXuids.push_back(pair.first); } if (trackedXuids.empty()) { // nothing to resync return; } else if (trackedXuids.size() > 1) { // Make batch query GetBatchPresence( UserBatchRequest{ trackedXuids.data(), trackedXuids.size(), nullptr }, AsyncContext>>>{ m_queue, std::move(handleGetPresenceResult) } ); } else { GetPresence( trackedXuids.front(), AsyncContext>>{m_queue, [batchResultHandler = std::move(handleGetPresenceResult)](Result> result) { if (Succeeded(result)) { batchResultHandler(Vector>{ result.ExtractPayload() }); } else { batchResultHandler(result.Hresult()); } } }); } } Result>> PresenceService::DeserializeBatchPresenceRecordsResponse( const JsonValue& json ) noexcept { Vector> presenceRecords; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonVector>( XblPresenceRecord::Deserialize, json, presenceRecords )); return presenceRecords; } NAMESPACE_MICROSOFT_XBOX_SERVICES_PRESENCE_CPP_END ================================================ FILE: Source/Services/Presence/presence_title_request.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "presence_internal.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_PRESENCE_CPP_BEGIN TitleRequest::TitleRequest( _In_ bool isUserActive, _In_opt_ const XblPresenceRichPresenceIds* richPresenceIds ) : m_isUserActive{ isUserActive } { if (richPresenceIds) { m_scid = richPresenceIds->scid; m_presenceId = richPresenceIds->presenceId; for (auto i = 0u; i < richPresenceIds->presenceTokenIdsCount; ++i) { m_presenceTokenIds.push_back(richPresenceIds->presenceTokenIds[i]); } } } void TitleRequest::Serialize(_Out_ JsonValue& serializedObject, _In_ JsonDocument::AllocatorType& allocator) { serializedObject.SetObject(); xsapi_internal_string state = m_isUserActive ? "active" : "inactive"; serializedObject.AddMember("state", JsonValue(state.c_str(), allocator).Move(), allocator); if (!m_scid.empty() && !m_presenceId.empty()) { JsonValue richPresenceJson(rapidjson::kObjectType); richPresenceJson.AddMember("id", JsonValue(m_presenceId.c_str(), allocator).Move(), allocator); richPresenceJson.AddMember("scid", JsonValue(m_scid.c_str(), allocator).Move(), allocator); if (!m_presenceTokenIds.empty()) { JsonValue presenceTokenIDsJson(rapidjson::kArrayType); JsonUtils::SerializeVector(JsonUtils::JsonStringSerializer, m_presenceTokenIds, presenceTokenIDsJson, allocator); richPresenceJson.AddMember("params", presenceTokenIDsJson, allocator); } serializedObject.AddMember("activity", JsonValue(rapidjson::kObjectType).AddMember("richPresence", richPresenceJson, allocator).Move(), allocator); } } NAMESPACE_MICROSOFT_XBOX_SERVICES_PRESENCE_CPP_END ================================================ FILE: Source/Services/Presence/presence_user_batch_request.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "presence_internal.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_PRESENCE_CPP_BEGIN UserBatchRequest::UserBatchRequest( _In_ uint64_t* xuids, _In_ size_t xuidsCount, _In_opt_ XblPresenceQueryFilters* filters ) noexcept : UserBatchRequest{ filters } { for (size_t i = 0; i < xuidsCount; ++i) { m_xuids.push_back(utils::uint64_to_internal_string(xuids[i])); } } UserBatchRequest::UserBatchRequest( _In_ xsapi_internal_string&& socialGroup, _In_opt_ uint64_t* socialGroupOwnerXuid, _In_opt_ XblPresenceQueryFilters* filters ) noexcept : UserBatchRequest{ filters } { m_socialGroup = socialGroup; if (socialGroupOwnerXuid) { m_socialGroupOwnerXuid = utils::uint64_to_internal_string(*socialGroupOwnerXuid); } } UserBatchRequest::UserBatchRequest( _In_opt_ XblPresenceQueryFilters* filters ) noexcept { if (filters) { for (size_t i = 0; i < filters->deviceTypesCount; ++i) { m_deviceTypes.push_back(DeviceRecord::DeviceTypeAsString(filters->deviceTypes[i])); } for (size_t i = 0; i < filters->titleIdsCount; ++i) { m_titleIds.push_back(utils::uint64_to_internal_string(filters->titleIds[i])); } m_presenceDetailLevel = filters->detailLevel; m_onlineOnly = filters->onlineOnly; m_broadcastingOnly = filters->broadcastingOnly; } } xsapi_internal_string UserBatchRequest::StringFromDetailLevel( _In_ XblPresenceDetailLevel level ) { switch (level) { case XblPresenceDetailLevel::User: return "user"; case XblPresenceDetailLevel::Device: return "device"; case XblPresenceDetailLevel::Title: return "title"; case XblPresenceDetailLevel::All: return "all"; default: return ""; } } void UserBatchRequest::Serialize(_Out_ JsonValue& serializedObject, _In_ JsonDocument::AllocatorType& allocator) const { serializedObject.SetObject(); if (!m_xuids.empty()) { JsonValue xuidsVectorJson(rapidjson::kObjectType); JsonUtils::SerializeVector( JsonUtils::JsonStringSerializer, m_xuids, xuidsVectorJson, allocator ); serializedObject.AddMember("users", xuidsVectorJson, allocator); } else if (!m_socialGroup.empty()) { serializedObject.AddMember("group", JsonValue(m_socialGroup.c_str(), allocator).Move(), allocator); if (!m_socialGroupOwnerXuid.empty()) { serializedObject.AddMember("groupXuid",JsonValue( m_socialGroupOwnerXuid.c_str(), allocator).Move(), allocator); } } if (m_deviceTypes.size() > 0) { JsonValue deviceTypesJson(rapidjson::kArrayType); JsonUtils::SerializeVector( JsonUtils::JsonStringSerializer, m_deviceTypes, deviceTypesJson, allocator ); serializedObject.AddMember("deviceTypes", deviceTypesJson, allocator); } if (m_titleIds.size() > 0) { JsonValue titleIdsJson(rapidjson::kArrayType); JsonUtils::SerializeVector( JsonUtils::JsonStringSerializer, m_titleIds, titleIdsJson, allocator ); serializedObject.AddMember("titles", titleIdsJson, allocator); } auto presenceDetailLevel = StringFromDetailLevel(m_presenceDetailLevel); if (!presenceDetailLevel.empty()) { serializedObject.AddMember("level", JsonValue(presenceDetailLevel.c_str(), allocator).Move(), allocator); } serializedObject.AddMember("onlineOnly", m_onlineOnly, allocator); serializedObject.AddMember("broadcastingOnly", m_broadcastingOnly, allocator); } NAMESPACE_MICROSOFT_XBOX_SERVICES_PRESENCE_CPP_END ================================================ FILE: Source/Services/Presence/title_presence_change_subscription.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "presence_internal.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_PRESENCE_CPP_BEGIN using namespace xbox::services::real_time_activity; TitlePresenceChangeSubscription::TitlePresenceChangeSubscription( _In_ uint64_t xuid, _In_ uint32_t titleId, _In_ std::shared_ptr presenceService ) noexcept : m_xuid{ xuid }, m_titleId{ titleId }, m_presenceService{ presenceService } { Stringstream uri; uri << "https://userpresence.xboxlive.com/users/xuid(" << m_xuid << ")/titles/" << m_titleId; m_resourceUri = uri.str(); } void TitlePresenceChangeSubscription::OnSubscribe( const JsonValue& data ) noexcept { if (data.IsNull()) { LOGS_ERROR << __FUNCTION__ << ": RTA payload unexpectedly null"; return; } auto presenceService{ m_presenceService.lock() }; if (presenceService) { auto deserializationResult = XblPresenceRecord::Deserialize(data); if (Succeeded(deserializationResult)) { bool isPlaying{ deserializationResult.Payload()->IsUserPlayingTitle(m_titleId) }; presenceService->HandleTitlePresenceChanged( m_xuid, m_titleId, isPlaying ? XblPresenceTitleState::Started : XblPresenceTitleState::Ended ); } } } void TitlePresenceChangeSubscription::OnEvent( const JsonValue& data ) noexcept { auto presenceService{ m_presenceService.lock() }; if (presenceService && data.IsString()) { //data is formatted as "state:titleId" xsapi_internal_string state = data.GetString(); state = state.substr(0, state.find(':')); presenceService->HandleTitlePresenceChanged(m_xuid, m_titleId, EnumValue(state.c_str())); } } NAMESPACE_MICROSOFT_XBOX_SERVICES_PRESENCE_CPP_END ================================================ FILE: Source/Services/Privacy/permission_check_result.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "privacy_service_internal.h" constexpr auto XblPrivilegeValue = EnumValue; NAMESPACE_MICROSOFT_XBOX_SERVICES_PRIVACY_CPP_BEGIN PermissionCheckResult::PermissionCheckResult( const PermissionCheckResult& other ) noexcept { *this = other; } PermissionCheckResult::PermissionCheckResult( PermissionCheckResult&& other ) noexcept : m_reasons{ std::move(other.m_reasons) } { isAllowed = other.isAllowed; targetXuid = other.targetXuid; targetUserType = other.targetUserType; permissionRequested = other.permissionRequested; reasons = m_reasons.data(); reasonsCount = m_reasons.size(); } PermissionCheckResult& PermissionCheckResult::operator=( const PermissionCheckResult& other ) noexcept { m_reasons = other.m_reasons; isAllowed = other.isAllowed; targetXuid = other.targetXuid; targetUserType = other.targetUserType; permissionRequested = other.permissionRequested; reasons = m_reasons.data(); reasonsCount = m_reasons.size(); return *this; } Result PermissionCheckResult::Deserialize( _In_ const JsonValue& json, _In_ XblPermission permissionRequested ) noexcept { if (json.IsNull()) { return WEB_E_INVALID_JSON_STRING; } PermissionCheckResult result{}; HRESULT errc = S_OK; result.permissionRequested = permissionRequested; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonBool(json, "isAllowed", result.isAllowed, true)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonVector( [&errc](_In_ const JsonValue& json) { XblPermissionDenyReasonDetails result{}; String reasonString; errc = JsonUtils::ExtractJsonString(json, "reason", reasonString, true); result.reason = EnumValue(reasonString.data()); if (SUCCEEDED(errc)) { String restrictedSetting; errc = JsonUtils::ExtractJsonString(json, "restrictedSetting", restrictedSetting, false); switch (result.reason) { case XblPermissionDenyReason::MissingPrivilege: // intentional fallthrough case XblPermissionDenyReason::PrivilegeRestrictsTarget: { result.restrictedPrivilege = XblPrivilegeValue(restrictedSetting.data()); break; } case XblPermissionDenyReason::PrivacySettingRestrictsTarget: { result.restrictedPrivacySetting = EnumValue(restrictedSetting.data()); break; } default: break; } } return Result(result, errc); }, json, "reasons", result.m_reasons, false )); result.reasons = result.m_reasons.data(); result.reasonsCount = result.m_reasons.size(); if (FAILED(errc)) { return WEB_E_INVALID_JSON_STRING; } return result; } Result> PermissionCheckResult::BatchDeserialize( _In_ const JsonValue& json, _In_ const xsapi_internal_vector& permissionsRequested ) noexcept { if (json.IsNull()) { return WEB_E_INVALID_JSON_STRING; } xsapi_internal_vector result; if (json.IsObject() && json.HasMember("responses")) { const JsonValue& responsesJsonArray = json["responses"]; if (responsesJsonArray.IsArray()) { for (const JsonValue& userJson : responsesJsonArray.GetArray()) { if (userJson.IsObject() && userJson.HasMember("user")) { const JsonValue& userObj = userJson["user"]; uint64_t xuid = 0; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonXuid(userObj, "xuid", xuid, false)); String anonymousUserTypeString; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(userObj, "anonymousUser", anonymousUserTypeString, false)); auto userType = EnumValue(anonymousUserTypeString.data()); xsapi_internal_vector userResults; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonVector( [&xuid, &userType](const JsonValue& json) { auto result { Deserialize(json, XblPermission::Unknown) }; auto& payload { result.Payload() }; payload.targetXuid = xuid; payload.targetUserType = userType; return Result(payload, result.Hresult()); }, userJson, "permissions", userResults, true )); if (userResults.size() != permissionsRequested.size()) { LOG_DEBUG("The resulting number of items did not match the number of items requested!"); return WEB_E_INVALID_JSON_STRING; } for (size_t i = 0; i < userResults.size(); ++i) { userResults[i].permissionRequested = permissionsRequested[i]; } result.insert(result.end(), userResults.begin(), userResults.end()); } else { //required return WEB_E_INVALID_JSON_STRING; } } } } return result; } size_t PermissionCheckResult::SizeOf() const noexcept { return sizeof(XblPermissionCheckResult) + m_reasons.size() * sizeof(XblPermissionDenyReasonDetails); } NAMESPACE_MICROSOFT_XBOX_SERVICES_PRIVACY_CPP_END ================================================ FILE: Source/Services/Privacy/privacy_api.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "privacy_service_internal.h" #include "xbox_live_context_internal.h" using namespace xbox::services; using namespace xbox::services::privacy; STDAPI XblPrivacyGetAvoidListAsync( _In_ XblContextHandle xblContextHandle, _In_ XAsyncBlock* async ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(xblContextHandle); return RunAsync(async, __FUNCTION__, [ xblContext{ xblContextHandle->shared_from_this() }, avoidList = xsapi_internal_vector{} ] (XAsyncOp op, const XAsyncProviderData* data) mutable { switch (op) { case XAsyncOp::DoWork: { RETURN_HR_IF_FAILED(xblContext->PrivacyService()->GetAvoidList({ data->async->queue, [ &avoidList, async{ data->async } ] (Result> result) { if (Succeeded(result)) { avoidList = result.ExtractPayload(); } XAsyncComplete(async, result.Hresult(), avoidList.size() * sizeof(uint64_t)); } })); return E_PENDING; } case XAsyncOp::GetResult: { memcpy(data->buffer, avoidList.data(), avoidList.size() * sizeof(uint64_t)); return S_OK; } default: { return S_OK; } } }); } CATCH_RETURN() STDAPI XblPrivacyGetAvoidListResultCount( _In_ XAsyncBlock* async, _Out_ size_t* xuidCount ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(async == nullptr || xuidCount == nullptr); size_t resultSize{ 0 }; RETURN_HR_IF_FAILED(XAsyncGetResultSize(async, &resultSize)); *xuidCount = resultSize / sizeof(uint64_t); return S_OK; } CATCH_RETURN() STDAPI XblPrivacyGetAvoidListResult( _In_ XAsyncBlock* async, _In_ size_t xuidCount, _Out_writes_(xuidCount) uint64_t* xuids ) XBL_NOEXCEPT try { RETURN_HR_IF(xuidCount == 0, S_OK); return XAsyncGetResult(async, nullptr, xuidCount * sizeof(uint64_t), xuids, nullptr); } CATCH_RETURN() HRESULT CopyPermissionCheckResult( const PermissionCheckResult& result, void* buffer ) noexcept try { auto resultPtr = static_cast(buffer); auto reasonsPtr = reinterpret_cast(resultPtr + 1); new (resultPtr) XblPermissionCheckResult{ result.isAllowed, result.targetXuid, result.targetUserType, result.permissionRequested, reasonsPtr, result.reasonsCount }; memcpy(reasonsPtr, result.reasons, sizeof(XblPermissionDenyReasonDetails) * result.reasonsCount); return S_OK; } CATCH_RETURN() STDAPI XblPrivacyCheckPermissionAsync( _In_ XblContextHandle xblContextHandle, _In_ XblPermission permission, _In_ uint64_t targetXuid, _In_ XAsyncBlock* async ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(xblContextHandle); return RunAsync(async, __FUNCTION__, [ xblContext{ xblContextHandle->shared_from_this() }, result = PermissionCheckResult{}, targetXuid, permission ] (XAsyncOp op, const XAsyncProviderData* data) mutable { switch (op) { case XAsyncOp::DoWork: { RETURN_HR_IF_FAILED(xblContext->PrivacyService()->CheckPermission(permission, targetXuid, { data->async->queue, [ &result, async{ data->async } ] (Result r) { if (Succeeded(r)) { result = r.ExtractPayload(); } XAsyncComplete(async, r.Hresult(), result.SizeOf()); } })); return E_PENDING; } case XAsyncOp::GetResult: { return CopyPermissionCheckResult(result, data->buffer); } default: { return S_OK; } } }); } CATCH_RETURN() STDAPI XblPrivacyCheckPermissionResultSize( _In_ XAsyncBlock* async, _Out_ size_t* resultSizeInBytes ) XBL_NOEXCEPT try { return XAsyncGetResultSize(async, resultSizeInBytes); } CATCH_RETURN() STDAPI XblPrivacyCheckPermissionResult( _In_ XAsyncBlock* async, _In_ size_t bufferSize, _Out_writes_bytes_to_(bufferSize, *bufferUsed) void* buffer, _Outptr_ XblPermissionCheckResult** result, _Out_opt_ size_t* bufferUsed ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(result); auto hr = XAsyncGetResult(async, nullptr, bufferSize, buffer, bufferUsed); if (SUCCEEDED(hr)) { *result = static_cast(buffer); } return hr; } CATCH_RETURN() STDAPI XblPrivacyCheckPermissionForAnonymousUserAsync( _In_ XblContextHandle xblContextHandle, _In_ XblPermission permission, _In_ XblAnonymousUserType userType, _In_ XAsyncBlock* async ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(xblContextHandle); return RunAsync(async, __FUNCTION__, [ xblContext{ xblContextHandle->shared_from_this() }, result = PermissionCheckResult{}, permission, userType ] (XAsyncOp op, const XAsyncProviderData* data) mutable { switch (op) { case XAsyncOp::DoWork: { RETURN_HR_IF_FAILED(xblContext->PrivacyService()->CheckPermission(permission, userType, { data->async->queue, [ &result, async{ data->async } ] (Result r) { if (Succeeded(r)) { result = r.ExtractPayload(); } XAsyncComplete(async, r.Hresult(), result.SizeOf()); } })); return E_PENDING; } case XAsyncOp::GetResult: { return CopyPermissionCheckResult(result, data->buffer); } default: { return S_OK; } } }); } CATCH_RETURN() STDAPI XblPrivacyCheckPermissionForAnonymousUserResultSize( _In_ XAsyncBlock* async, _Out_ size_t* resultSizeInBytes ) XBL_NOEXCEPT try { return XAsyncGetResultSize(async, resultSizeInBytes); } CATCH_RETURN() STDAPI XblPrivacyCheckPermissionForAnonymousUserResult( _In_ XAsyncBlock* async, _In_ size_t bufferSize, _Out_writes_bytes_to_(bufferSize, *bufferUsed) void* buffer, _Outptr_ XblPermissionCheckResult** result, _Out_opt_ size_t* bufferUsed ) XBL_NOEXCEPT try { return XblPrivacyCheckPermissionResult(async, bufferSize, buffer, result, bufferUsed); } CATCH_RETURN() STDAPI XblPrivacyBatchCheckPermissionAsync( _In_ XblContextHandle xblContextHandle, _In_reads_(permissionsCount) XblPermission* permissionsToCheck, _In_ size_t permissionsCount, _In_reads_(xuidsCount) uint64_t* targetXuids, _In_ size_t xuidsCount, _In_reads_(userTypesCount) XblAnonymousUserType* targetUserTypes, _In_ size_t userTypesCount, _In_ XAsyncBlock* async ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(xblContextHandle); RETURN_HR_INVALIDARGUMENT_IF(permissionsToCheck == nullptr || permissionsCount == 0); RETURN_HR_INVALIDARGUMENT_IF(targetXuids == nullptr && xuidsCount != 0); RETURN_HR_INVALIDARGUMENT_IF(targetUserTypes == nullptr && userTypesCount != 0); RETURN_HR_INVALIDARGUMENT_IF(xuidsCount == 0 && userTypesCount == 0); return RunAsync(async, __FUNCTION__, [ xblContext{ xblContextHandle->shared_from_this() }, permissions = xsapi_internal_vector(permissionsToCheck, permissionsToCheck + permissionsCount), xuids = xsapi_internal_vector(targetXuids, targetXuids + xuidsCount), userTypes = xsapi_internal_vector(targetUserTypes, targetUserTypes + userTypesCount), result = xsapi_internal_vector{} ] (XAsyncOp op, const XAsyncProviderData* data) mutable { switch (op) { case XAsyncOp::DoWork: { RETURN_HR_IF_FAILED(xblContext->PrivacyService()->BatchCheckPermission(std::move(permissions), xuids, userTypes, { data->async->queue, [ &result, async{ data->async } ] (Result> r) { if (Succeeded(r)) { result = r.ExtractPayload(); } size_t requiredBufferSize{ 0 }; for (auto& i : result) { requiredBufferSize += i.SizeOf(); } XAsyncComplete(async, r.Hresult(), requiredBufferSize); } })); return E_PENDING; } case XAsyncOp::GetResult: { auto resultPtr = static_cast(data->buffer); auto reasonsPtr = reinterpret_cast(resultPtr + result.size()); for (const auto& elt : result) { new (resultPtr) XblPermissionCheckResult{ elt.isAllowed, elt.targetXuid, elt.targetUserType, elt.permissionRequested, reasonsPtr, elt.reasonsCount }; memcpy(reasonsPtr, elt.reasons, sizeof(XblPermissionDenyReasonDetails) * elt.reasonsCount); resultPtr++; reasonsPtr += elt.reasonsCount; } return S_OK; } default: { return S_OK; } } }); } CATCH_RETURN() STDAPI XblPrivacyBatchCheckPermissionResultSize( _In_ XAsyncBlock* async, _Out_ size_t* resultSizeInBytes ) XBL_NOEXCEPT try { return XAsyncGetResultSize(async, resultSizeInBytes); } CATCH_RETURN() STDAPI XblPrivacyBatchCheckPermissionResult( _In_ XAsyncBlock* async, _In_ size_t bufferSize, _Out_writes_bytes_to_(bufferSize, *bufferUsed) void* buffer, _Outptr_ XblPermissionCheckResult** results, _Out_ size_t* resultsCount, _Out_opt_ size_t* bufferUsed ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(results == nullptr || resultsCount == nullptr); // bufferUsed is needed to calculate resultsCount auto bufferUsedPtr = MakeUnique(); if (bufferUsed == nullptr) { bufferUsed = bufferUsedPtr.get(); } auto hr = XAsyncGetResult(async, nullptr, bufferSize, buffer, bufferUsed); if (SUCCEEDED(hr)) { *results = static_cast(buffer); // Calulate how many items are in the results array size_t count{ 0 }; size_t verifiedSize{ 0 }; for(; verifiedSize < *bufferUsed; ++count) { verifiedSize += sizeof(XblPermissionCheckResult); verifiedSize += ((*results)[count].reasonsCount * sizeof(XblPermissionDenyReasonDetails)); } assert(verifiedSize == *bufferUsed); *resultsCount = count; } return hr; } CATCH_RETURN() STDAPI XblPrivacyGetMuteListAsync( _In_ XblContextHandle xblContextHandle, _In_ XAsyncBlock* async ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(xblContextHandle); return RunAsync(async, __FUNCTION__, [ xblContext{ xblContextHandle->shared_from_this() }, muteList = xsapi_internal_vector{} ] (XAsyncOp op, const XAsyncProviderData* data) mutable { switch (op) { case XAsyncOp::DoWork: { RETURN_HR_IF_FAILED(xblContext->PrivacyService()->GetMuteList({ data->async->queue, [ &muteList, async{ data->async } ] (Result> result) { if (Succeeded(result)) { muteList = result.ExtractPayload(); } XAsyncComplete(async, result.Hresult(), muteList.size() * sizeof(uint64_t)); } })); return E_PENDING; } case XAsyncOp::GetResult: { memcpy(data->buffer, muteList.data(), muteList.size() * sizeof(uint64_t)); return S_OK; } default: { return S_OK; } } }); } CATCH_RETURN() STDAPI XblPrivacyGetMuteListResultCount( _In_ XAsyncBlock* async, _Out_ size_t* xuidCount ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(async == nullptr || xuidCount == nullptr); size_t resultSize{ 0 }; RETURN_HR_IF_FAILED(XAsyncGetResultSize(async, &resultSize)); *xuidCount = resultSize / sizeof(uint64_t); return S_OK; } CATCH_RETURN() STDAPI XblPrivacyGetMuteListResult( _In_ XAsyncBlock* async, _In_ size_t xuidCount, _Out_writes_(xuidCount) uint64_t* xuids ) XBL_NOEXCEPT try { RETURN_HR_IF(xuidCount == 0, S_OK); return XAsyncGetResult(async, nullptr, xuidCount * sizeof(uint64_t), xuids, nullptr); } CATCH_RETURN() ================================================ FILE: Source/Services/Privacy/privacy_service.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "privacy_service_internal.h" #include "xbox_live_context_internal.h" using namespace xbox::services; using namespace xbox::services::legacy; using namespace xbox::services::privacy; constexpr auto XblPermissionName = EnumName; NAMESPACE_MICROSOFT_XBOX_SERVICES_PRIVACY_CPP_BEGIN PrivacyService::PrivacyService( _In_ User&& user, _In_ std::shared_ptr contextSettings ) noexcept : m_user{ std::move(user) }, m_contextSettings{ contextSettings } { } HRESULT PrivacyService::GetAvoidList( _In_ AsyncContext>> async ) const noexcept { return GetUserList(PrivacyListType::Avoid, async); } HRESULT PrivacyService::GetMuteList( _In_ AsyncContext>> async ) const noexcept { return GetUserList(PrivacyListType::Mute, async); } HRESULT PrivacyService::GetUserList( _In_ PrivacyListType listType, _In_ AsyncContext>> async ) const noexcept { xsapi_internal_stringstream path; path << "/users/xuid(" << m_user.Xuid() << ")/people/" << (listType == PrivacyListType::Mute ? "mute" : "avoid"); Result userResult = m_user.Copy(); RETURN_HR_IF_FAILED(userResult.Hresult()); auto httpCall = MakeShared(userResult.ExtractPayload()); RETURN_HR_IF_FAILED(httpCall->Init( m_contextSettings, "GET", XblHttpCall::BuildUrl("privacy", path.str()), xbox_live_api::get_avoid_or_mute_list )); return httpCall->Perform({ async.Queue().DeriveWorkerQueue(), [ async ] (HttpResult httpResult) { HRESULT hr{ Failed(httpResult) ? httpResult.Hresult() : httpResult.Payload()->Result() }; if (SUCCEEDED(hr)) { return async.Complete(DeserializeUserList(httpResult.Payload()->GetResponseBodyJson())); } return async.Complete(hr); } }); } HRESULT PrivacyService::CheckPermission( _In_ XblPermission permission, _In_ uint64_t targetXuid, _In_ AsyncContext> async ) const noexcept { // users/xuid({xuid})/permission/validate?setting={setting}&target=xuid({targetXuid}) Stringstream targetQuery; targetQuery << "xuid(" << targetXuid << ")"; return CheckPermission(permission, targetQuery.str(), { async.Queue(), [ targetXuid, async ] (Result result) { result.Payload().targetXuid = targetXuid; async.Complete(result); } }); } HRESULT PrivacyService::CheckPermission( _In_ XblPermission permission, _In_ XblAnonymousUserType userType, _In_ AsyncContext> async ) const noexcept { return CheckPermission(permission, EnumName(userType), { async.Queue(), [ userType, async ] (Result result) { result.Payload().targetUserType = userType; async.Complete(result); } }); } HRESULT PrivacyService::CheckPermission( _In_ XblPermission permission, _In_ const String& targetQuery, _In_ AsyncContext> async ) const noexcept { // users/xuid({xuid})/permission/validate?setting={setting}&target={target}) xbox::services::uri_builder subPathBuilder; xsapi_internal_stringstream path; path << "/users/xuid(" << m_user.Xuid() << ")/permission/validate"; subPathBuilder.append_path(path.str()); subPathBuilder.append_query("setting", XblPermissionName(permission).data()); subPathBuilder.append_query("target", targetQuery); Result userResult = m_user.Copy(); RETURN_HR_IF_FAILED(userResult.Hresult()); auto httpCall = MakeShared(userResult.ExtractPayload()); RETURN_HR_IF_FAILED(httpCall->Init( m_contextSettings, "GET", XblHttpCall::BuildUrl("privacy", subPathBuilder.to_string()), xbox_live_api::check_permission_with_target_user )); return httpCall->Perform({ async.Queue().DeriveWorkerQueue(), [ async, permission ] (HttpResult httpResult) { HRESULT hr{ Failed(httpResult) ? httpResult.Hresult() : httpResult.Payload()->Result() }; if (SUCCEEDED(hr)) { return async.Complete(PermissionCheckResult::Deserialize(httpResult.Payload()->GetResponseBodyJson(), permission)); } return async.Complete(hr); } }); } HRESULT PrivacyService::BatchCheckPermission( _In_ xsapi_internal_vector permissions, _In_ const xsapi_internal_vector& targetXuids, _In_ const xsapi_internal_vector& userTypes, _In_ AsyncContext>> async ) const noexcept { // users/xuid({xuid})/permission/validate xsapi_internal_stringstream path; path << "/users/xuid(" << m_user.Xuid() << ")/permission/validate"; // Set request body to something like: //{ // "users": // [ // {"xuid":"12345"}, // {"anonymousUser":"crossNetworkUser"} // ], // "permissions": // [ // "ViewTargetGameHistory", // "ViewTargetProfile" // ] //} JsonDocument requestBody(rapidjson::kObjectType); JsonDocument::AllocatorType& allocator = requestBody.GetAllocator(); JsonValue usersJson(rapidjson::kArrayType); for (auto xuid : targetXuids) { JsonValue userJson(rapidjson::kObjectType); userJson.AddMember("xuid", JsonValue(utils::uint64_to_internal_string(xuid).c_str(), allocator).Move(), allocator); usersJson.PushBack(userJson, allocator); } for (auto userType : userTypes) { JsonValue userJson(rapidjson::kObjectType); userJson.AddMember("anonymousUser", JsonValue(EnumName(userType).data(), allocator).Move(), allocator); usersJson.PushBack(userJson, allocator); } requestBody.AddMember("users", usersJson, allocator); JsonValue permissionsJson(rapidjson::kArrayType); JsonUtils::SerializeVector( [](XblPermission permission, JsonValue& outObj, JsonDocument::AllocatorType& allocator) { outObj.SetString(XblPermissionName(permission).data(), allocator); }, permissions, permissionsJson, allocator ); requestBody.AddMember("permissions", permissionsJson, allocator); Result userResult = m_user.Copy(); RETURN_HR_IF_FAILED(userResult.Hresult()); auto httpCall = MakeShared(userResult.ExtractPayload()); RETURN_HR_IF_FAILED(httpCall->Init( m_contextSettings, "POST", XblHttpCall::BuildUrl("privacy", path.str()), xbox_live_api::check_multiple_permissions_with_multiple_target_users )); RETURN_HR_IF_FAILED(httpCall->SetRequestBody(requestBody)); return httpCall->Perform({ async.Queue().DeriveWorkerQueue(), [ async, permissionsRequested{ std::move(permissions) } ] (HttpResult httpResult) { HRESULT hr{ Failed(httpResult) ? httpResult.Hresult() : httpResult.Payload()->Result() }; if (SUCCEEDED(hr)) { return async.Complete(PermissionCheckResult::BatchDeserialize(httpResult.Payload()->GetResponseBodyJson(), permissionsRequested)); } return async.Complete(hr); } }); } Result> PrivacyService::DeserializeUserList( _In_ const JsonValue& json ) noexcept { HRESULT errc = S_OK; xsapi_internal_vector xuids; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonVector( [&errc](_In_ const JsonValue& json) { if (json.IsNull()) { return Result(WEB_E_INVALID_JSON_STRING); } uint64_t xuid = 0; HRESULT tempErr = JsonUtils::ExtractJsonXuid(json, "xuid", xuid, true); if (FAILED(tempErr)) { errc = tempErr; } return Result(xuid, errc); }, json, ("users"), xuids, true )); if (FAILED(errc)) { return WEB_E_INVALID_JSON_STRING; } return xuids; } NAMESPACE_MICROSOFT_XBOX_SERVICES_PRIVACY_CPP_END ================================================ FILE: Source/Services/Privacy/privacy_service_internal.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "xsapi-c/privacy_c.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_PRIVACY_CPP_BEGIN class PermissionCheckResult : public XblPermissionCheckResult { public: PermissionCheckResult() noexcept = default; PermissionCheckResult(const PermissionCheckResult& other) noexcept; PermissionCheckResult(PermissionCheckResult&& other) noexcept; PermissionCheckResult& operator=(const PermissionCheckResult& other) noexcept; ~PermissionCheckResult() noexcept = default; static Result Deserialize( _In_ const JsonValue& json, _In_ XblPermission requestedPermission ) noexcept; static Result> BatchDeserialize( _In_ const JsonValue& json, _In_ const xsapi_internal_vector& permissionsRequested ) noexcept; size_t SizeOf() const noexcept; private: xsapi_internal_vector m_reasons; }; class PrivacyService : public std::enable_shared_from_this { public: PrivacyService( _In_ User&& user, _In_ std::shared_ptr contextSettings ) noexcept; HRESULT GetAvoidList( _In_ AsyncContext>> async ) const noexcept; HRESULT GetMuteList( _In_ AsyncContext>> async ) const noexcept; HRESULT CheckPermission( _In_ XblPermission permission, _In_ uint64_t targetXuid, _In_ AsyncContext> async ) const noexcept; HRESULT CheckPermission( _In_ XblPermission permissionToCheck, _In_ XblAnonymousUserType userType, _In_ AsyncContext> async ) const noexcept; HRESULT BatchCheckPermission( _In_ xsapi_internal_vector permissionsToCheck, _In_ const xsapi_internal_vector& targetXuids, _In_ const xsapi_internal_vector& userTypes, _In_ AsyncContext>> async ) const noexcept; private: enum class PrivacyListType { Avoid, Mute }; HRESULT GetUserList( _In_ PrivacyListType listType, _In_ AsyncContext>> async ) const noexcept; static Result> DeserializeUserList( _In_ const JsonValue& json ) noexcept; HRESULT CheckPermission( _In_ XblPermission permissionToCheck, _In_ const String& targetQuery, _In_ AsyncContext> async ) const noexcept; User m_user; std::shared_ptr m_contextSettings; }; NAMESPACE_MICROSOFT_XBOX_SERVICES_PRIVACY_CPP_END ================================================ FILE: Source/Services/RealTimeActivityManager/real_time_activity_api.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "xsapi-c/real_time_activity_c.h" #include "real_time_activity_manager.h" #include "xbox_live_context_internal.h" std::atomic XblRealTimeActivitySubscription::s_nextId{ 1 }; STDAPI XblRealTimeActivitySubscriptionGetState( _In_ XblRealTimeActivitySubscriptionHandle subscriptionHandle, _Out_ XblRealTimeActivitySubscriptionState* state ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(subscriptionHandle == nullptr || state == nullptr); *state = subscriptionHandle->state; return S_OK; } CATCH_RETURN() STDAPI XblRealTimeActivitySubscriptionGetId( _In_ XblRealTimeActivitySubscriptionHandle subscriptionHandle, _Out_ uint32_t* id ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(subscriptionHandle == nullptr || id == nullptr); *id = subscriptionHandle->id; return S_OK; } CATCH_RETURN() STDAPI XblRealTimeActivityActivate( _In_ XblContextHandle xboxLiveContext ) XBL_NOEXCEPT try { LOGS_DEBUG << __FUNCTION__; if (auto state{ GlobalState::Get() }) { state->RTAManager()->Activate(xboxLiveContext->User(), true); } return S_OK; } CATCH_RETURN() STDAPI XblRealTimeActivityDeactivate( _In_ XblContextHandle xboxLiveContext ) XBL_NOEXCEPT try { LOGS_DEBUG << __FUNCTION__; if (auto state{ GlobalState::Get() }) { state->RTAManager()->Deactivate(xboxLiveContext->User()); } return S_OK; } CATCH_RETURN() STDAPI_(XblFunctionContext) XblRealTimeActivityAddConnectionStateChangeHandler( _In_ XblContextHandle xboxLiveContext, _In_ XblRealTimeActivityConnectionStateChangeHandler* handler, _In_opt_ void* context ) XBL_NOEXCEPT try { LOGS_DEBUG << __FUNCTION__; RETURN_HR_INVALIDARGUMENT_IF(xboxLiveContext == nullptr || handler == nullptr); if (auto state{ GlobalState::Get() }) { return state->RTAManager()->AddStateChangedHandler(xboxLiveContext->User(), [ handler, context ] (XblRealTimeActivityConnectionState state) { try { handler(context, state); } catch (...) { LOGS_ERROR << __FUNCTION__ << ": exception in client handler!"; } }); } else { return E_XBL_NOT_INITIALIZED; } } CATCH_RETURN() STDAPI XblRealTimeActivityRemoveConnectionStateChangeHandler( _In_ XblContextHandle xboxLiveContext, _In_ XblFunctionContext token ) XBL_NOEXCEPT try { LOGS_DEBUG << __FUNCTION__; RETURN_HR_INVALIDARGUMENT_IF_NULL(xboxLiveContext); if (auto state{ GlobalState::Get() }) { state->RTAManager()->RemoveStateChangedHandler(xboxLiveContext->User(), token); return S_OK; } else { return E_XBL_NOT_INITIALIZED; } } CATCH_RETURN() STDAPI_(XblFunctionContext) XblRealTimeActivityAddSubscriptionErrorHandler( _In_ XblContextHandle xboxLiveContext, _In_ XblRealTimeActivitySubscriptionErrorHandler* handler, _In_opt_ void* context ) XBL_NOEXCEPT try { LOGS_DEBUG << __FUNCTION__ << ": DEPRECATED, No action taken by XSAPI."; UNREFERENCED_PARAMETER(xboxLiveContext); UNREFERENCED_PARAMETER(handler); UNREFERENCED_PARAMETER(context); return S_OK; } CATCH_RETURN() STDAPI XblRealTimeActivityRemoveSubscriptionErrorHandler( _In_ XblContextHandle xboxLiveContext, _In_ XblFunctionContext token ) XBL_NOEXCEPT try { LOGS_DEBUG << __FUNCTION__ << ": DEPRECATED, No action taken by XSAPI."; UNREFERENCED_PARAMETER(xboxLiveContext); UNREFERENCED_PARAMETER(token); return S_OK; } CATCH_RETURN() STDAPI_(XblFunctionContext) XblRealTimeActivityAddResyncHandler( _In_ XblContextHandle xboxLiveContext, _In_ XblRealTimeActivityResyncHandler* handler, _In_opt_ void* context ) XBL_NOEXCEPT try { LOGS_DEBUG << __FUNCTION__; RETURN_HR_INVALIDARGUMENT_IF(xboxLiveContext == nullptr || handler == nullptr); if (auto state{ GlobalState::Get() }) { return state->RTAManager()->AddResyncHandler(xboxLiveContext->User(), [ handler, context ] { try { handler(context); } catch (...) { LOGS_ERROR << __FUNCTION__ << ": exception in client handler!"; } }); } else { return E_XBL_NOT_INITIALIZED; } } CATCH_RETURN() STDAPI XblRealTimeActivityRemoveResyncHandler( _In_ XblContextHandle xboxLiveContext, _In_ XblFunctionContext token ) XBL_NOEXCEPT try { LOGS_DEBUG << __FUNCTION__; RETURN_HR_INVALIDARGUMENT_IF_NULL(xboxLiveContext); if (auto state{ GlobalState::Get() }) { state->RTAManager()->RemoveResyncHandler(xboxLiveContext->User(), token); return S_OK; } else { return E_XBL_NOT_INITIALIZED; } } CATCH_RETURN() ================================================ FILE: Source/Services/RealTimeActivityManager/real_time_activity_connection.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "real_time_activity_subscription.h" #include "real_time_activity_connection.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_RTA_CPP_BEGIN constexpr char s_rtaUri[]{ "wss://rta.xboxlive.com/connect" }; constexpr char s_rtaSubprotocol[]{ "rta.xboxlive.com.V2" }; struct ServiceSubscription { ServiceSubscription(String _uri, uint32_t _clientId) noexcept : uri{ std::move(_uri) }, clientId { _clientId } {} // Resource uri for the subscription String const uri; // Client ID is assigned by XSAPI and used to identify subscriptions. // Used as the unique SEQUENCE_N during RTA Sub/Unsub handshakes. uint32_t const clientId; // Assigned by service when we successfully subscribe. Used to identify subscription in RTA event messages. uint32_t serviceId{ 0 }; // Current status with respect to RTA service enum class Status : uint32_t { // RTA service has no knowledge of this subscription Inactive, // Registered with RTA service Active, // Client has indicated that they want to subscribe while unsubscribing. Resubscribe will // happen as soon as unsubscribe handshake completes. PendingSubscribe, // We've sent subscribe message to RTA service but has not yet received the handshake response Subscribing, // Client has indicated they want to unsubscribe while subscribing. Unsubscribe requires // we have a serviceID. Unsubscribe will happen as soon as the subscribe handshake is finished. PendingUnsubscribe, // We've sent unsubscribe message to RTA service but has not yet received the handshake response Unsubscribing } status{ Status::Inactive }; uint32_t subscribeAttempt{ 0 }; Set> clientSubscriptions; List>> subscribeAsyncContexts; List>> unsubscribeAsyncContexts; // OnSubscribe data payload JsonDocument onSubscribeData{ rapidjson::kNullType }; }; // RTA message types and error codes define by service here http://xboxwiki/wiki/Real_Time_Activity enum class MessageType : uint32_t { Subscribe = 1, Unsubscribe = 2, Event = 3, Resync = 4 }; enum class ErrorCode : uint32_t { Success = 0, UnknownResource = 1, SubscriptionLimitReached = 2, NoResourceData = 3, Throttled = 1001, ServiceUnavailable = 1002 }; HRESULT ConvertRTAErrorCode(ErrorCode rtaErrorCode) noexcept { switch (rtaErrorCode) { case ErrorCode::Success: return S_OK; case ErrorCode::SubscriptionLimitReached: return E_XBL_RTA_SUBSCRIPTION_LIMIT_REACHED; case ErrorCode::NoResourceData: return E_XBL_RTA_ACCESS_DENIED; default: return E_XBL_RTA_GENERIC_ERROR; } } Connection::Connection( User&& user, const TaskQueue& queue, ConnectionStateChangedHandler stateChangedHandler, real_time_activity::ResyncHandler resyncHandler ) noexcept : m_user{ std::move(user) }, m_queue{ queue.DeriveWorkerQueue() }, m_stateChangedHandler{ std::move(stateChangedHandler) }, m_resyncHandler{ std::move(resyncHandler) } { LOGS_DEBUG << __FUNCTION__ << "[" << this << "]"; } Connection::~Connection() noexcept { LOGS_DEBUG << __FUNCTION__ << "[" << this << "]"; m_queue.Terminate(false); } Result> Connection::Make( User&& user, const TaskQueue& queue, ConnectionStateChangedHandler stateChangedHandler, real_time_activity::ResyncHandler resyncHandler ) noexcept { auto rtaConnection = std::shared_ptr( new (Alloc(sizeof(Connection))) Connection { std::move(user), queue, std::move(stateChangedHandler), std::move(resyncHandler) }, Deleter(), Allocator() ); rtaConnection->m_stateChangedHandler(rtaConnection->m_state); rtaConnection->ScheduleConnect(); return rtaConnection; } void Connection::Cleanup() { std::unique_lock lock{ m_lock }; if (m_websocket) { // Clear our disconnect handler to disable auto-reconnect logic m_websocket->SetDisconnectHandler(nullptr); m_websocket->Disconnect(); } List>> pendingAsyncContexts; for (auto& subPair : m_subsByClientId) { auto& sub{ subPair.second }; pendingAsyncContexts.insert(pendingAsyncContexts.end(), sub->subscribeAsyncContexts.begin(), sub->subscribeAsyncContexts.end()); sub->subscribeAsyncContexts.clear(); pendingAsyncContexts.insert(pendingAsyncContexts.end(), sub->unsubscribeAsyncContexts.begin(), sub->unsubscribeAsyncContexts.end()); sub->unsubscribeAsyncContexts.clear(); } lock.unlock(); m_queue.Terminate( false, [pendingAsyncContexts = std::move(pendingAsyncContexts)]() { for (auto& async : pendingAsyncContexts) { async.Complete(E_ABORT); } } ); } HRESULT Connection::AddSubscription( std::shared_ptr sub, AsyncContext> async ) noexcept { assert(sub); std::unique_lock lock{ m_lock }; std::shared_ptr serviceSub{ nullptr }; auto serviceSubIter = m_subsByUri.find(sub->ResourceUri()); if (serviceSubIter != m_subsByUri.end()) { serviceSub = serviceSubIter->second; } else { serviceSub = MakeShared(sub->ResourceUri(), m_nextSubId++); assert(m_subsByClientId.find(serviceSub->clientId) == m_subsByClientId.end()); m_subsByClientId[serviceSub->clientId] = serviceSub; m_subsByUri[serviceSub->uri] = serviceSub; } serviceSub->clientSubscriptions.emplace(sub); LOGS_DEBUG << __FUNCTION__ << ": [" << serviceSub->clientId << "] Uri=" << serviceSub->uri << ", ServiceStatus=" << EnumName(serviceSub->status); switch (serviceSub->status) { case ServiceSubscription::Status::Inactive: { serviceSub->subscribeAsyncContexts.push_back(std::move(async)); // If our connection is active, immediately register with RTA service if (m_state == XblRealTimeActivityConnectionState::Connected) { return SendSubscribeMessage(serviceSub, std::move(lock)); } return S_OK; } case ServiceSubscription::Status::PendingUnsubscribe: { // Client previously removed subscription while we were subscribing. Reset the state to subscribing and complete unsubscribe // operations with E_ABORT serviceSub->status = ServiceSubscription::Status::Subscribing; serviceSub->subscribeAsyncContexts.push_back(std::move(async)); List>> unsubscribeAsyncContexts{ std::move(serviceSub->unsubscribeAsyncContexts) }; lock.unlock(); for (auto& asyncContext : unsubscribeAsyncContexts) { asyncContext.Complete(E_ABORT); } return S_OK; } case ServiceSubscription::Status::Unsubscribing: { // Wait for unsubscribe to finish before resubscribing serviceSub->status = ServiceSubscription::Status::PendingSubscribe; serviceSub->subscribeAsyncContexts.push_back(std::move(async)); return S_OK; } case ServiceSubscription::Status::Active: { // Subscription is already active, trivially complete lock.unlock(); // Pass along original OnSubscribe payload for this subscription sub->OnSubscribe(serviceSub->onSubscribeData); async.Complete(S_OK); return S_OK; } case ServiceSubscription::Status::PendingSubscribe: case ServiceSubscription::Status::Subscribing: { serviceSub->subscribeAsyncContexts.push_back(std::move(async)); return S_OK; } default: { assert(false); return E_UNEXPECTED; } } } HRESULT Connection::RemoveSubscription( std::shared_ptr sub, AsyncContext> async ) noexcept { assert(sub); std::unique_lock lock{ m_lock }; std::shared_ptr serviceSub{ nullptr }; auto serviceSubIter = m_subsByUri.find(sub->ResourceUri()); if (serviceSubIter != m_subsByUri.end()) { serviceSub = serviceSubIter->second; } else { return S_OK; } serviceSub->clientSubscriptions.erase(sub); LOGS_DEBUG << __FUNCTION__ << ": [" << serviceSub->clientId << "] Uri=" << serviceSub->uri << ", ServiceStatus=" << EnumName(serviceSub->status); if (!serviceSub->clientSubscriptions.empty()) { // Service subscription still needed by other clients. Complete asyncContext but don't unsubscribe from service lock.unlock(); async.Complete(S_OK); return S_OK; } switch (serviceSub->status) { case ServiceSubscription::Status::Inactive: { // RTA service has no knowledge of inactive subs. Just remove from our local state and complete the AsyncContext. assert(serviceSub->serviceId == 0); m_subsByClientId.erase(serviceSub->clientId); m_subsByUri.erase(serviceSub->uri); lock.unlock(); async.Complete(S_OK); return S_OK; } case ServiceSubscription::Status::Active: { // Unregister subscription from RTA service serviceSub->unsubscribeAsyncContexts.push_back(std::move(async)); return SendUnsubscribeMessage(serviceSub, std::move(lock)); } case ServiceSubscription::Status::PendingSubscribe: { // Client previously added the subscription while we were unsubscribing. Reset the state to unsubscribe and complete // subscribe operations with E_ABORT serviceSub->status = ServiceSubscription::Status::Unsubscribing; serviceSub->unsubscribeAsyncContexts.push_back(std::move(async)); List>> subscribeAsyncContexts{ std::move(serviceSub->subscribeAsyncContexts) }; lock.unlock(); for (auto& asyncContext : subscribeAsyncContexts) { asyncContext.Complete(E_ABORT); } return S_OK; } case ServiceSubscription::Status::Subscribing: { // We are in the process of subscribing. RTA protocol doesn't allow us to unsubscribe // until subscription is complete, so just mark the subscription as pending unsubscribe. // After the subscription completes, we will unsubscribe and complete the AsyncContext. serviceSub->status = ServiceSubscription::Status::PendingUnsubscribe; serviceSub->unsubscribeAsyncContexts.push_back(std::move(async)); return S_OK; } case ServiceSubscription::Status::PendingUnsubscribe: case ServiceSubscription::Status::Unsubscribing: { serviceSub->unsubscribeAsyncContexts.push_back(std::move(async)); return S_OK; } default: { assert(false); return E_UNEXPECTED; } } } size_t Connection::SubscriptionCount() const noexcept { std::unique_lock lock{ m_lock }; return m_subsByClientId.size(); } JsonDocument Connection::AssembleSubscribeMessage(std::shared_ptr sub) const noexcept { // Payload format [, , ] sub->status = ServiceSubscription::Status::Subscribing; JsonDocument request{ rapidjson::kArrayType }; auto& a{ request.GetAllocator() }; request.PushBack(static_cast(MessageType::Subscribe), a); request.PushBack(sub->clientId, a); request.PushBack(JsonValue{ sub->uri.data(), a }, a); return request; } HRESULT Connection::SendSubscribeMessage( std::shared_ptr sub, std::unique_lock&& lock ) const noexcept { JsonDocument request = AssembleSubscribeMessage(sub); lock.unlock(); return SendAssembledMessage(request); } HRESULT Connection::SendAssembledMessage(_In_ const JsonValue& request) const noexcept { String requestString{ JsonUtils::SerializeJson(request) }; LOGS_DEBUG << __FUNCTION__ << "[" << this << "]: " << requestString; return m_websocket->Send(requestString.data()); } HRESULT Connection::SendUnsubscribeMessage( std::shared_ptr sub, std::unique_lock&& lock ) const noexcept { // Payload format [, , ] sub->status = ServiceSubscription::Status::Unsubscribing; JsonDocument request{ rapidjson::kArrayType }; auto& a{ request.GetAllocator() }; request.PushBack(static_cast(MessageType::Unsubscribe), a); request.PushBack(sub->clientId, a); request.PushBack(sub->serviceId, a); lock.unlock(); String requestString{ JsonUtils::SerializeJson(request) }; LOGS_DEBUG << __FUNCTION__ << "[" << this << "]: " << requestString; return m_websocket->Send(requestString.data()); } void Connection::SubscribeResponseHandler(_In_ const JsonValue& message) noexcept { // Payload format [, , , , ] std::unique_lock lock{ m_lock }; auto clientId = message[1].GetUint(); auto errorCode = static_cast(message[2].GetUint()); auto subIter{ m_subsByClientId.find(clientId) }; if (subIter == m_subsByClientId.end()) { // Ignore unexpected message LOGS_DEBUG << "__FUNCTION__" << ": [" << clientId << "] Ignoring unexpected message"; return; } auto serviceSub{ subIter->second }; switch (errorCode) { case ErrorCode::Success: { serviceSub->serviceId = message[3].GetInt(); serviceSub->onSubscribeData.CopyFrom(message[4], serviceSub->onSubscribeData.GetAllocator()); m_subsByServiceId[serviceSub->serviceId] = serviceSub; List>> subscribeAsyncContexts{ std::move(serviceSub->subscribeAsyncContexts) }; List> clientSubs{ serviceSub->clientSubscriptions.begin(), serviceSub->clientSubscriptions.end() }; switch (serviceSub->status) { case ServiceSubscription::Status::Subscribing: { serviceSub->status = ServiceSubscription::Status::Active; break; } case ServiceSubscription::Status::PendingUnsubscribe: { // Client has removed the subscription while subscribe handshake was happening, // so immediately begin unsubscribing. SendUnsubscribeMessage(serviceSub, std::move(lock)); break; } default: { // Any other Status indicates a XSAPI bug assert(false); break; } } if (lock) { lock.unlock(); } for (auto& asyncContext : subscribeAsyncContexts) { asyncContext.Complete(ConvertRTAErrorCode(errorCode)); } for (auto& clientSub : clientSubs) { clientSub->OnSubscribe(serviceSub->onSubscribeData); } return; } case ErrorCode::UnknownResource: case ErrorCode::SubscriptionLimitReached: case ErrorCode::NoResourceData: { // With the possible exception of SubscriptionLimitReached, these are all indicative of a bug in XSAPI LOGS_ERROR << __FUNCTION__ << ": Failed with [" << static_cast(errorCode) << "]"; return; } case ErrorCode::Throttled: case ErrorCode::ServiceUnavailable: { auto serviceStatus{ serviceSub->status }; serviceSub->status = ServiceSubscription::Status::Inactive; switch (serviceStatus) { case ServiceSubscription::Status::Subscribing: { uint64_t backoff = __min(std::pow(serviceSub->subscribeAttempt++, 2), 60) * 1000; m_queue.RunWork([weakSub = std::weak_ptr{ serviceSub }, weakThis = std::weak_ptr{ shared_from_this() }] { auto sharedThis{ weakThis.lock() }; if (sharedThis) { std::unique_lock lock{ sharedThis->m_lock }; auto serviceSub{ weakSub.lock() }; if (serviceSub && serviceSub->status == ServiceSubscription::Status::Inactive) { sharedThis->SendSubscribeMessage(serviceSub, std::move(lock)); } } }, backoff ); return; } case ServiceSubscription::Status::PendingUnsubscribe: { m_subsByClientId.erase(serviceSub->clientId); m_subsByUri.erase(serviceSub->uri); // Complete subscribe operations with error, but don't retry since the client has since removed the subscription List>> subscribeAsyncContexts{ std::move(serviceSub->subscribeAsyncContexts) }; // Unsubscribe operations are also trivially done at this point List>> unsubscribeAsyncContexts{ std::move(serviceSub->unsubscribeAsyncContexts) }; lock.unlock(); for (auto& asyncContext : subscribeAsyncContexts) { asyncContext.Complete(ConvertRTAErrorCode(errorCode)); } for (auto& asyncContext : unsubscribeAsyncContexts) { asyncContext.Complete(S_OK); } return; } default: { assert(false); return; } } } default: { LOGS_ERROR << __FUNCTION__ << ": Failed with unrecognized error code [" << static_cast(errorCode) << "]"; return; } } } void Connection::UnsubscribeResponseHandler(_In_ const JsonValue& message) noexcept { // Payload format [, , ] std::unique_lock lock{ m_lock }; auto clientId = message[1].GetUint(); auto errorCode = static_cast(message[2].GetUint()); if (errorCode != ErrorCode::Success) { // Not sure why unsubscribing would ever fail LOGS_ERROR << __FUNCTION__ << ": Failed with error code [" << static_cast(errorCode) << "]"; } auto subIter{ m_subsByClientId.find(clientId) }; if (subIter == m_subsByClientId.end()) { // Ignore unexpected message LOGS_DEBUG << "__FUNCTION__" << ": [" << clientId << "] Ignoring unexpected message"; return; } auto serviceSub{ subIter->second }; m_subsByServiceId.erase(serviceSub->serviceId); serviceSub->serviceId = 0; LOGS_DEBUG << __FUNCTION__ << ": [" << serviceSub->clientId <<"] ServiceStatus=" << EnumName(serviceSub->status); List>> unsubscribeAsyncContexts{ std::move(serviceSub->unsubscribeAsyncContexts) }; switch (serviceSub->status) { case ServiceSubscription::Status::Unsubscribing: { // We can now remove the subscription from our state entirely m_subsByClientId.erase(serviceSub->clientId); m_subsByUri.erase(serviceSub->uri); serviceSub->status = ServiceSubscription::Status::Inactive; break; } case ServiceSubscription::Status::PendingSubscribe: { // Client has re-added the subscription while unsubscibe handshake was happening, // so immediately begin subscribing. SendSubscribeMessage(serviceSub, std::move(lock)); break; } default: { assert(false); break; } } if (lock) { lock.unlock(); } for (auto& asyncContext : unsubscribeAsyncContexts) { asyncContext.Complete(ConvertRTAErrorCode(errorCode)); } } void Connection::EventHandler(_In_ const JsonValue& message) const noexcept { // Payload format [, , ] std::unique_lock lock{ m_lock }; auto serviceId = message[1].GetInt(); const auto& data = message[2]; auto subIter{ m_subsByServiceId.find(serviceId) }; assert(subIter != m_subsByServiceId.end()); auto serviceSub = subIter->second; lock.unlock(); for (auto& clientSub : serviceSub->clientSubscriptions) { clientSub->OnEvent(data); } } void Connection::ConnectCompleteHandler(WebsocketResult result) noexcept { LOGS_DEBUG << __FUNCTION__ << ": WebsocketResult [" << result.hr << "," << result.platformErrorCode << "]"; std::unique_lock lock{ m_lock }; if (SUCCEEDED(result.hr)) { // Ignore resync messages for 5 minutes after connecting to // avoid overwhelming the service with update requests m_ignoreResyncTimer = xbox::services::datetime::utc_now() + xbox::services::datetime::from_minutes(5); m_state = XblRealTimeActivityConnectionState::Connected; m_connectTime = std::chrono::system_clock::now(); m_connectAttempt = 0; m_connectNum++; assert(m_subsByServiceId.empty()); List subMessages{}; for (auto& pair : m_subsByClientId) { assert(pair.second->status == ServiceSubscription::Status::Inactive); subMessages.push_back(AssembleSubscribeMessage(pair.second)); } // RTA v2 has a lifetime of 2 hours. After 2 hours RTA service will disconnect the title. On some platforms // the websocket stack is able to recognize that the disconnect happened and notify libHttpClient/xsapi, // triggering a reconnect attempt, but on others the disconnect goes undetected. To be more defensive against // this, xsapi proactively will disconnect from rta after ~90 minutes and trigger the reconnect flow. #define CONNECTION_TIMEOUT_MS (90 /*mins*/ * 60 /*seconds/min*/ * 1000 /*ms/second*/) m_queue.RunWork([weakThis = std::weak_ptr{ shared_from_this() }] { if (auto sharedThis{ weakThis.lock() }) { std::unique_lock lock{ sharedThis->m_lock }; if ((std::chrono::system_clock::now() - sharedThis->m_connectTime).count() >= CONNECTION_TIMEOUT_MS) { auto socket = sharedThis->m_websocket; lock.unlock(); // Disconnect so that auto-reconnect logic kicks in socket->Disconnect(); } } }, CONNECTION_TIMEOUT_MS ); lock.unlock(); for (auto& request : subMessages) { SendAssembledMessage(request); } } else { if (m_connectAttempt > 3) { m_state = XblRealTimeActivityConnectionState::Disconnected; } ScheduleConnect(); } if (lock) { lock.unlock(); } m_stateChangedHandler(m_state); } void Connection::ScheduleConnect() noexcept { LOGS_DEBUG << __FUNCTION__; // Backoff and attempt to connect again. auto timeNow{ chrono_clock_t::now() }; double lerpScaler = (timeNow.time_since_epoch().count() % 10000) / 10000.0; // from 0 to 1 based on clock double jitterDelayMin = 0.0f; double jitterDelayMax = (m_connectNum == 0) ? 0.0f : 5000.0f; // don't bother jitter in initial connection since it doesn't need it uint64_t jitterDelay = static_cast(jitterDelayMin + jitterDelayMax * lerpScaler); // lerp between min & max wait uint64_t retryBackoffWithJitter = __min(std::pow(m_connectAttempt, 2), 60) * 1000 + jitterDelay; m_connectAttempt++; m_queue.RunWork([weakThis = std::weak_ptr{ shared_from_this() }, this] { auto sharedThis{ weakThis.lock() }; if (sharedThis) { std::unique_lock lock{ m_lock }; LOGS_DEBUG << "RTA::Connection Initializing WebSocket and attempting connect. Subcount=" << m_subsByClientId.size(); auto hr = InitializeWebsocket(); if (FAILED(hr)) { ScheduleConnect(); } else { m_state = XblRealTimeActivityConnectionState::Connecting; lock.unlock(); sharedThis->m_stateChangedHandler(sharedThis->m_state); sharedThis->m_websocket->Connect(s_rtaUri, s_rtaSubprotocol); // Do synchronous failures need to be handled here? } } }, retryBackoffWithJitter ); } void Connection::DisconnectHandler(WebSocketCloseStatus status) noexcept { LOGS_DEBUG << __FUNCTION__ << ": WebocketCloseStatus [" << static_cast(status) << "]"; List>> unsubscribeAsyncContexts; List>> subscribeAsyncContexts; std::unique_lock lock{ m_lock }; // All subs are inactive if we are disconnected m_subsByServiceId.clear(); // Update state of our subs for (auto subsIter = m_subsByClientId.begin(); subsIter != m_subsByClientId.end();) { auto serviceSub{ subsIter->second }; switch (serviceSub->status) { case ServiceSubscription::Status::Inactive: case ServiceSubscription::Status::Active: case ServiceSubscription::Status::Subscribing: { ++subsIter; break; } case ServiceSubscription::Status::PendingSubscribe: { // Complete the Unsubscribe AsyncContext, but since the client re-added the subscription, // don't remove it from our state unsubscribeAsyncContexts.insert(unsubscribeAsyncContexts.end(), serviceSub->unsubscribeAsyncContexts.begin(), serviceSub->unsubscribeAsyncContexts.end()); serviceSub->unsubscribeAsyncContexts.clear(); ++subsIter; break; } // For subscriptions which removed by clients, complete the relevant AsyncContexts and erase the // subscription from our state case ServiceSubscription::Status::PendingUnsubscribe: { subscribeAsyncContexts.insert(subscribeAsyncContexts.end(), serviceSub->subscribeAsyncContexts.begin(), serviceSub->subscribeAsyncContexts.end()); serviceSub->subscribeAsyncContexts.clear(); // Intentional fallthrough } case ServiceSubscription::Status::Unsubscribing: { unsubscribeAsyncContexts.insert(unsubscribeAsyncContexts.end(), serviceSub->unsubscribeAsyncContexts.begin(), serviceSub->unsubscribeAsyncContexts.end()); serviceSub->unsubscribeAsyncContexts.clear(); m_subsByUri.erase(serviceSub->uri); subsIter = m_subsByClientId.erase(subsIter); break; } default: { assert(false); break; } } serviceSub->serviceId = 0; serviceSub->subscribeAttempt = 0; serviceSub->status = ServiceSubscription::Status::Inactive; } ScheduleConnect(); lock.unlock(); for (auto& async : unsubscribeAsyncContexts) { // Consider any unsubscribes successful. Service will eventually drop our subscriptions // since we disconnected anyways async.Complete(S_OK); } for (auto& async : subscribeAsyncContexts) { async.Complete(E_XBL_RTA_GENERIC_ERROR); } } void Connection::WebsocketMessageReceived(const String& message) noexcept { // Payload format defined here http://xboxwiki/wiki/Real_Time_Activity LOGS_DEBUG << __FUNCTION__ << "[" << this << "]: " << message; JsonDocument msgJson; msgJson.Parse(message.c_str()); MessageType messageType = static_cast(msgJson[0].GetInt()); switch (messageType) { case MessageType::Subscribe: { SubscribeResponseHandler(msgJson); break; } case MessageType::Unsubscribe: { UnsubscribeResponseHandler(msgJson); break; } case MessageType::Event: { EventHandler(msgJson); break; } case MessageType::Resync: { int64_t delta = m_ignoreResyncTimer.to_interval() - xbox::services::datetime::utc_now().to_interval(); if (delta < 0) { m_resyncHandler(); } break; } default: { LOGS_ERROR << "Received unrecognized RTA payload, ignoring"; break; } } } HRESULT Connection::InitializeWebsocket() noexcept { LOGS_DEBUG << __FUNCTION__; if (m_websocket) { m_websocket->SetConnectCompleteHandler([](WebsocketResult) {}); m_websocket->SetDisconnectHandler([](WebSocketCloseStatus) {}); m_websocket->SetReceiveHandler([](String) {}); } auto copyUserResult = m_user.Copy(); RETURN_HR_IF_FAILED(copyUserResult.Hresult()); m_websocket = IWebsocket::Make(copyUserResult.ExtractPayload(), m_queue); std::weak_ptr thisWeakPtr{ shared_from_this() }; m_websocket->SetConnectCompleteHandler([thisWeakPtr](WebsocketResult result) { auto sharedThis{ thisWeakPtr.lock() }; if (sharedThis) { sharedThis->ConnectCompleteHandler(result); } }); m_websocket->SetDisconnectHandler([thisWeakPtr](WebSocketCloseStatus status) { auto sharedThis{ thisWeakPtr.lock() }; if (sharedThis) { sharedThis->DisconnectHandler(status); } }); m_websocket->SetReceiveHandler([thisWeakPtr](String message) { auto sharedThis{ thisWeakPtr.lock() }; if (sharedThis) { sharedThis->WebsocketMessageReceived(message); } }); return S_OK; } NAMESPACE_MICROSOFT_XBOX_SERVICES_RTA_CPP_END ================================================ FILE: Source/Services/RealTimeActivityManager/real_time_activity_connection.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "xsapi-c/real_time_activity_c.h" #include "real_time_activity_manager.h" #include "web_socket.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_RTA_CPP_BEGIN struct ServiceSubscription; class Connection : public std::enable_shared_from_this { public: static Result> Make( User&& user, const TaskQueue& queue, ConnectionStateChangedHandler stateChangedHandler, ResyncHandler resyncHandler ) noexcept; ~Connection() noexcept; // Disconnect WebSocket, terminate background queue, and complete any pending async operations void Cleanup(); HRESULT AddSubscription( std::shared_ptr subscription, AsyncContext> async ) noexcept; HRESULT RemoveSubscription( std::shared_ptr subscription, AsyncContext> async ) noexcept; size_t SubscriptionCount() const noexcept; #if HC_PLATFORM == HC_PLATFORM_GDK void AppStateChangeNotificationReceived( bool isSuspended ) noexcept; #endif private: Connection( User&& user, const TaskQueue& queue, ConnectionStateChangedHandler stateChangedHandler, ResyncHandler resyncHandler ) noexcept; JsonDocument AssembleSubscribeMessage( std::shared_ptr sub ) const noexcept; // RTA protocol implementation HRESULT SendSubscribeMessage( std::shared_ptr subscription, std::unique_lock&& lock ) const noexcept; HRESULT SendUnsubscribeMessage( std::shared_ptr subscription, std::unique_lock&& lock ) const noexcept; HRESULT SendAssembledMessage(_In_ const JsonValue& message) const noexcept; void SubscribeResponseHandler(_In_ const JsonValue& message) noexcept; void UnsubscribeResponseHandler(_In_ const JsonValue& message) noexcept; void EventHandler(_In_ const JsonValue& message) const noexcept; // IWebsocket handlers void ConnectCompleteHandler(WebsocketResult result) noexcept; void DisconnectHandler(WebSocketCloseStatus result) noexcept; void WebsocketMessageReceived(const String& message) noexcept; HRESULT InitializeWebsocket() noexcept; void ScheduleConnect() noexcept; User m_user; TaskQueue const m_queue; std::shared_ptr m_websocket; uint32_t m_connectAttempt{ 0 }; uint32_t m_connectNum{ 0 }; std::chrono::time_point m_connectTime; XblRealTimeActivityConnectionState m_state{ XblRealTimeActivityConnectionState::Connecting }; xbox::services::datetime m_ignoreResyncTimer; const ConnectionStateChangedHandler m_stateChangedHandler; const real_time_activity::ResyncHandler m_resyncHandler; Map> m_subsByUri; // needed to add/remove client subscription Map> m_subsByClientId; // needed for subscribe/unsubscribe handshake Map> m_subsByServiceId; // needed to handle subscription events uint32_t m_nextSubId{ 1 }; mutable std::mutex m_lock; }; NAMESPACE_MICROSOFT_XBOX_SERVICES_RTA_CPP_END ================================================ FILE: Source/Services/RealTimeActivityManager/real_time_activity_manager.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "real_time_activity_manager.h" #include "real_time_activity_connection.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_RTA_CPP_BEGIN RealTimeActivityManager::RealTimeActivityManager( const TaskQueue& queue ) noexcept : m_queue{ queue.DeriveWorkerQueue() } { } RealTimeActivityManager::~RealTimeActivityManager() noexcept { HC_TRACE_MESSAGE(XSAPI, HCTraceLevel::Verbose, __FUNCTION__); } void RealTimeActivityManager::Cleanup() { std::unique_lock lock{ m_lock }; // Don't invoke disconnect handlers during cleanup m_stateChangedHandlers.clear(); Map> connections{ std::move(m_rtaConnections) }; m_rtaConnections.clear(); lock.unlock(); for (auto& pair : connections) { pair.second->Cleanup(); } } HRESULT RealTimeActivityManager::AddSubscription( const User& user, std::shared_ptr subscription ) noexcept { assert(subscription); RETURN_HR_INVALIDARGUMENT_IF_NULL(subscription); std::lock_guard lock{ m_lock }; auto connectionResult{ GetConnection(user) }; RETURN_HR_IF_FAILED(connectionResult.Hresult()); return connectionResult.ExtractPayload()->AddSubscription(subscription, AsyncContext>{ m_queue }); } HRESULT RealTimeActivityManager::RemoveSubscription( const User& user, std::shared_ptr subscription ) noexcept { assert(subscription); RETURN_HR_INVALIDARGUMENT_IF_NULL(subscription); std::lock_guard lock{ m_lock }; auto iter{ m_rtaConnections.find(user.Xuid()) }; if (iter == m_rtaConnections.end()) { return S_OK; } return iter->second->RemoveSubscription(subscription, AsyncContext>{ m_queue, [ xuid{ user.Xuid() }, weakThis{ std::weak_ptr{ shared_from_this() } } ] (Result result) { if (auto sharedThis{ weakThis.lock() }) { std::unique_lock lock{ sharedThis->m_lock }; // If that was the last remaining subscription and there are no remaining legacy activations, close the connection auto iter{ sharedThis->m_rtaConnections.find(xuid) }; if (iter != sharedThis->m_rtaConnections.end() && iter->second->SubscriptionCount() == 0 && sharedThis->m_legacyActivations[xuid] == 0) { LOGS_DEBUG << __FUNCTION__ << ": No remaining activations or subscriptions, tearing down connection"; iter->second->Cleanup(); sharedThis->m_rtaConnections.erase(iter); // Maintain legacy behavior and raise Disconnected event even on intentional shutdown auto handlers{ sharedThis->m_stateChangedHandlers[xuid] }; lock.unlock(); for (auto& handler : handlers) { handler.second(XblRealTimeActivityConnectionState::Disconnected); } } } } }); } XblFunctionContext RealTimeActivityManager::AddStateChangedHandler( const User& user, ConnectionStateChangedHandler handler ) noexcept { std::lock_guard lock{ m_lock }; m_stateChangedHandlers[user.Xuid()][m_nextToken] = std::move(handler); return m_nextToken++; } void RealTimeActivityManager::RemoveStateChangedHandler( const User& user, XblFunctionContext token ) noexcept { std::lock_guard lock{ m_lock }; m_stateChangedHandlers[user.Xuid()].erase(token); } XblFunctionContext RealTimeActivityManager::AddResyncHandler( const User& user, ResyncHandler handler ) noexcept { std::lock_guard lock{ m_lock }; m_resyncHandlers[user.Xuid()][m_nextToken] = std::move(handler); return m_nextToken++; } void RealTimeActivityManager::RemoveResyncHandler( const User& user, XblFunctionContext token ) noexcept { std::lock_guard lock{ m_lock }; m_resyncHandlers[user.Xuid()].erase(token); } void RealTimeActivityManager::Activate( const User& user, bool titleActivation ) noexcept { std::lock_guard lock{ m_lock }; m_legacyActivations[user.Xuid()]++; // If the title manually activated, establish RTA connection if its not already set up if (titleActivation) { m_titleActivated = true; (void)GetConnection(user); } } void RealTimeActivityManager::Deactivate( const User& user ) noexcept { std::unique_lock lock{ m_lock }; auto& activationCount{ m_legacyActivations[user.Xuid()] }; auto connectionIter{ m_rtaConnections.find(user.Xuid()) }; if (connectionIter == m_rtaConnections.end()) { return; } assert(activationCount > 0); if (activationCount > 0 && --activationCount == 0) { // When the activation count reaches 0, tear down the WebSocket connection if the title // manually activated/deactivated RTA or if there are no remaining subscriptions. // The second case is important due to a race condition between RemoveSubscription and Deactivate. if (m_titleActivated || connectionIter->second->SubscriptionCount() == 0) { LOGS_DEBUG << __FUNCTION__ << ": No remaining activations tearing down connection"; connectionIter->second->Cleanup(); m_rtaConnections.erase(connectionIter); // Maintain legacy behavior and raise Disconnected event even on intentional shutdown auto handlers{ m_stateChangedHandlers[user.Xuid()] }; lock.unlock(); for (auto& handler : handlers) { handler.second(XblRealTimeActivityConnectionState::Disconnected); } } } } void RealTimeActivityManager::TriggerResync() const noexcept { std::unique_lock lock{ m_lock }; auto handlers{ m_resyncHandlers }; lock.unlock(); for (auto& userPair : handlers) { for (auto& handlerPair : userPair.second) { handlerPair.second(); } } } Result> RealTimeActivityManager::GetConnection( const User& user ) noexcept { std::shared_ptr connection; auto iter{ m_rtaConnections.find(user.Xuid()) }; if (iter != m_rtaConnections.end()) { connection = iter->second; } else { ConnectionStateChangedHandler stateChangedHandler = [ xuid{ user.Xuid() }, weakThis{ std::weak_ptr{ shared_from_this() } } ] (XblRealTimeActivityConnectionState state) { if (auto sharedThis{ weakThis.lock() }) { std::unique_lock lock{ sharedThis->m_lock }; auto handlers{ sharedThis->m_stateChangedHandlers[xuid] }; lock.unlock(); for (auto& handler : handlers) { handler.second(state); } } }; ResyncHandler resyncHandler = [ xuid{ user.Xuid() }, weakThis{ std::weak_ptr{ shared_from_this() } } ] (void) { if (auto sharedThis{ weakThis.lock() }) { std::unique_lock lock{ sharedThis->m_lock }; auto handlers{ sharedThis->m_resyncHandlers[xuid] }; lock.unlock(); for (auto& handler : handlers) { handler.second(); } } }; auto copyUserResult{ user.Copy() }; if (Failed(copyUserResult)) { return copyUserResult.Hresult(); } auto connectionResult = Connection::Make(copyUserResult.ExtractPayload(), m_queue, std::move(stateChangedHandler), std::move(resyncHandler)); if (Failed(connectionResult)) { return connectionResult.Hresult(); } connection = connectionResult.ExtractPayload(); m_rtaConnections[user.Xuid()] = connection; } return connection; } NAMESPACE_MICROSOFT_XBOX_SERVICES_RTA_CPP_END // Test Hook HRESULT XblTestHooksTriggerRTAResync() { auto state = GlobalState::Get(); if (!state) { return E_XBL_NOT_INITIALIZED; } state->RTAManager()->TriggerResync(); return S_OK; } ================================================ FILE: Source/Services/RealTimeActivityManager/real_time_activity_manager.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "xsapi-c/real_time_activity_c.h" #include "real_time_activity_subscription.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_RTA_CPP_BEGIN typedef Callback ConnectionStateChangedHandler; typedef Function ResyncHandler; class RealTimeActivityManager : public std::enable_shared_from_this { public: RealTimeActivityManager(const TaskQueue& queue) noexcept; ~RealTimeActivityManager() noexcept; // Finalize RTA communication and disconnect all WebSockets void Cleanup(); HRESULT AddSubscription( const User& user, std::shared_ptr subscription ) noexcept; HRESULT RemoveSubscription( const User& user, std::shared_ptr subscription ) noexcept; XblFunctionContext AddStateChangedHandler( const User& user, ConnectionStateChangedHandler handler ) noexcept; void RemoveStateChangedHandler( const User& user, XblFunctionContext token ) noexcept; XblFunctionContext AddResyncHandler( const User& user, ResyncHandler handler ) noexcept; void RemoveResyncHandler( const User& user, XblFunctionContext token ) noexcept; // Legacy API support. Allow titles to explicitly activate and deactivate the RTA connection. // Internal components will also maintain the activation count to maintain existing behavior. void Activate( const User& user, bool titleActivation = false ) noexcept; void Deactivate( const User& user ) noexcept; // Test Hook void TriggerResync() const noexcept; private: Result> GetConnection( const User& user ) noexcept; Map> m_rtaConnections; TaskQueue const m_queue; XblFunctionContext m_nextToken{ 1 }; Map> m_stateChangedHandlers; Map> m_resyncHandlers; uint32_t m_pendingOperations{ 0 }; // Legacy API support Map m_legacyActivations; bool m_titleActivated{ false }; mutable std::recursive_mutex m_lock; }; NAMESPACE_MICROSOFT_XBOX_SERVICES_RTA_CPP_END ================================================ FILE: Source/Services/RealTimeActivityManager/real_time_activity_subscription.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "xsapi-c/real_time_activity_c.h" // Support for legacy APIs struct XblRealTimeActivitySubscription { XblRealTimeActivitySubscription() noexcept = default; virtual ~XblRealTimeActivitySubscription() noexcept = default; const XblRealTimeActivitySubscriptionState state{ XblRealTimeActivitySubscriptionState::Unknown }; const uint32_t id{ s_nextId++ }; private: static std::atomic s_nextId; }; NAMESPACE_MICROSOFT_XBOX_SERVICES_RTA_CPP_BEGIN class Subscription { public: Subscription() noexcept = default; virtual ~Subscription() noexcept = default; String const& ResourceUri() const { return m_resourceUri; } virtual void OnSubscribe(const JsonValue& data) noexcept { UNREFERENCED_PARAMETER(data); // If the data is non-null, this should have been handled by the child class assert(data.IsNull()); }; virtual void OnEvent(const JsonValue& event) noexcept = 0; protected: String m_resourceUri; }; NAMESPACE_MICROSOFT_XBOX_SERVICES_RTA_CPP_END ================================================ FILE: Source/Services/Social/Manager/peoplehub_service.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "peoplehub_service.h" #include "presence_internal.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_SOCIAL_MANAGER_CPP_BEGIN PeoplehubService::PeoplehubService( _In_ User&& user, _In_ std::shared_ptr httpCallSettings, _In_ uint32_t titleId ) noexcept : m_user{ std::move(user) }, m_httpSettings{ std::move(httpCallSettings) }, m_titleId{ titleId } { } HRESULT PeoplehubService::GetSocialGraph( _In_ uint64_t xuid, _In_ XblSocialManagerExtraDetailLevel decorations, _In_ AsyncContext>> async ) const noexcept { return MakeServiceCall(xuid, decorations, RelationshipType::Social, Vector{}, async); } HRESULT PeoplehubService::GetSocialUsers( _In_ uint64_t xuid, _In_ XblSocialManagerExtraDetailLevel decorations, _In_ const Vector& xuids, _In_ AsyncContext>> async ) const noexcept { return MakeServiceCall(xuid, decorations, RelationshipType::Batch, xuids, async); } HRESULT PeoplehubService::MakeServiceCall( _In_ uint64_t xuid, _In_ XblSocialManagerExtraDetailLevel decorations, _In_ RelationshipType relationshipType, _In_opt_ const Vector& batchUsers, _In_ AsyncContext>> async ) const noexcept { Stringstream subpath; JsonDocument bodyJson; subpath << "/users/xuid(" << xuid << ")/people/"; switch (relationshipType) { case RelationshipType::Social: { subpath << "friends"; break; } case RelationshipType::Batch: { subpath << "batch"; JsonValue xuidJson = JsonValue(rapidjson::kArrayType); for (size_t i = 0; i < batchUsers.size(); ++i) { xuidJson.PushBack(JsonValue(utils::uint64_to_internal_string(batchUsers[i]).c_str(), bodyJson.GetAllocator()).Move(), bodyJson.GetAllocator()); } bodyJson.SetObject(); bodyJson.AddMember("xuids", xuidJson, bodyJson.GetAllocator()); break; } } // Always include the presenceDetail decoration subpath << "/decoration/presenceDetail"; if (decorations != XblSocialManagerExtraDetailLevel::NoExtraDetail) { if ((decorations & XblSocialManagerExtraDetailLevel::TitleHistoryLevel) == XblSocialManagerExtraDetailLevel::TitleHistoryLevel) { subpath << ",titlehistory(" << m_titleId << ")"; } if ((decorations & XblSocialManagerExtraDetailLevel::PreferredColorLevel) == XblSocialManagerExtraDetailLevel::PreferredColorLevel) { subpath << ",preferredcolor"; } } Result userResult = m_user.Copy(); RETURN_HR_IF_FAILED(userResult.Hresult()); auto httpCall = MakeShared(userResult.ExtractPayload()); RETURN_HR_IF_FAILED(httpCall->Init( m_httpSettings, relationshipType == RelationshipType::Batch ? "POST" : "GET", XblHttpCall::BuildUrl("peoplehub", subpath.str()), xbox_live_api::get_social_graph )); httpCall->SetXblServiceContractVersion(7); if (!bodyJson.IsNull()) { httpCall->SetRequestBody(bodyJson); } return httpCall->Perform({ async.Queue(), [ async ] (HttpResult httpResult) { HRESULT hr{ Failed(httpResult) ? httpResult.Hresult() : httpResult.Payload()->Result() }; if (FAILED(hr)) { return async.Complete(hr); } auto httpCall{ httpResult.ExtractPayload() }; auto header = httpCall->GetResponseHeader("x-xbl-servicedefault"); if (!header.empty()) { LOGS_ERROR << "Peoplehub dependency failed to load: " << header; return async.Complete({ utils::convert_xbox_live_error_code_to_hresult(xbl_error_code::http_status_424_failed_dependency) }); } Vector users; JsonDocument responseBodyJson = httpCall->GetResponseBodyJson(); auto peopleJsonArray = JsonUtils::ExtractJsonArray( responseBodyJson, "people", false ); for (auto& user : peopleJsonArray) { auto result{ DeserializeUser(user) }; if (Succeeded(result)) { users.push_back(result.ExtractPayload()); } else { return async.Complete({ result.Hresult() }); } } return async.Complete(users); } }); } Result PeoplehubService::DeserializeUser( const JsonValue& json ) { XblSocialManagerUser user{}; if (!json.IsObject()) { return user; } RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonXuid(json, "xuid", user.xboxUserId, true)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonBool(json, "isFriend", user.isFriend)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonBool(json, "isFavorite", user.isFavorite)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonStringToCharArray(json, "displayName", user.displayName, sizeof(user.displayName))); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonStringToCharArray(json, "realName", user.realName, sizeof(user.realName))); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonStringToCharArray(json, "displayPicRaw", user.displayPicUrlRaw, sizeof(user.displayPicUrlRaw))); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonBool(json, "useAvatar", user.useAvatar)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonStringToCharArray(json, "gamertag", user.gamertag, sizeof(user.gamertag))); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonStringToCharArray(json, "modernGamertag", user.modernGamertag, sizeof(user.modernGamertag))); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonStringToCharArray(json, "modernGamertagSuffix", user.modernGamertagSuffix, sizeof(user.modernGamertagSuffix))); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonStringToCharArray(json, "uniqueModernGamertag", user.uniqueModernGamertag, sizeof(user.uniqueModernGamertag))); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonStringToCharArray(json, "gamerScore", user.gamerscore, sizeof(user.gamerscore))); // isFavorite should reflect both isFavorite && isFriend from the service response user.isFavorite = user.isFavorite && user.isFriend; // Shim isFollowingUser and isFollowedByCaller to be true if isFriend is true // This is purely for compatibility purposes and doesnt reflect a follower/following relationship user.isFollowedByCaller = user.isFriend; user.isFollowingUser = user.isFriend; auto presenceRecordResult = DeserializePresenceRecord(json); if (Succeeded(presenceRecordResult)) { user.presenceRecord = presenceRecordResult.ExtractPayload(); } else { return { presenceRecordResult.Hresult() }; } if (json.IsObject() && json.HasMember("titleHistory")) { auto titleHistoryResult = DeserializeTitleHistory(json["titleHistory"]); if (Succeeded(titleHistoryResult)) { user.titleHistory = titleHistoryResult.ExtractPayload(); } else { return { titleHistoryResult.Hresult() }; } } if (json.IsObject() && json.HasMember("preferredColor")) { auto preferredColorResult = DeserializePreferredColor(json["preferredColor"]); if (Succeeded(preferredColorResult)) { user.preferredColor = preferredColorResult.ExtractPayload(); } else { return { preferredColorResult.Hresult() }; } } return Result{ user }; } Result PeoplehubService::DeserializePresenceRecord( const JsonValue& json ) { XblSocialManagerPresenceRecord record{}; if (!json.IsObject()) { return record; } String presenceState; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "presenceState", presenceState)); record.userState = XblPresenceRecord::UserStateFromString(presenceState); auto presenceDetailsJson = JsonUtils::ExtractJsonArray( json, "presenceDetails", false ); for (const auto& titleRecordJson : presenceDetailsJson) { auto titleRecordResult = DeserializePresenceTitleRecord(titleRecordJson); if (Succeeded(titleRecordResult)) { record.presenceTitleRecords[record.presenceTitleRecordCount++] = titleRecordResult.ExtractPayload(); } if (record.presenceTitleRecordCount >= XBL_NUM_PRESENCE_RECORDS) { break; } } return Result{ record }; } Result PeoplehubService::DeserializePresenceTitleRecord( const JsonValue& json ) { XblSocialManagerPresenceTitleRecord record{}; if (!json.IsObject()) { return { record }; } String device, state, titleId; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "Device", device)); record.deviceType = presence::DeviceRecord::DeviceTypeFromString(device); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonStringToCharArray(json, "PresenceText", record.presenceText, sizeof(record.presenceText))); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "State", state)); record.isTitleActive = utils::str_icmp_internal(state, "active") == 0; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "TitleId", titleId)); record.titleId = utils::internal_string_to_uint32(titleId); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonBool(json, "IsPrimary", record.isPrimary)); //get titleName from Presence string: format should be "Title - Rich Presence Text" for (int i = 0; i < XBL_TITLE_NAME_CHAR_SIZE; i++) { char c = record.presenceText[i]; if (c == '-' || c == '\0') { record.titleName[i] = '\0'; break; } record.titleName[i] = c; } return Result{ record }; } Result PeoplehubService::DeserializePreferredColor( const JsonValue& json ) { XblPreferredColor color{}; if (!json.IsObject()) { return { color }; } RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonStringToCharArray(json, "primaryColor", color.primaryColor, sizeof(color.primaryColor))); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonStringToCharArray(json, "secondaryColor", color.secondaryColor, sizeof(color.secondaryColor))); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonStringToCharArray(json, "tertiaryColor", color.tertiaryColor, sizeof(color.tertiaryColor))); return Result{ color }; } Result PeoplehubService::DeserializeTitleHistory( const JsonValue& json ) { XblTitleHistory titleHistory{}; if (!json.IsObject()) { return titleHistory; } // If PeopleHub service fails to query TitleHistory, the "lastTimePlayed" field may be null. // We don't want to fail deserialization in this case, so just return that the user has not played constexpr const char* lastTimePlayedKey{ "lastTimePlayed" }; if (json.HasMember(lastTimePlayedKey) && json[lastTimePlayedKey].IsString()) { RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonTimeT( json, lastTimePlayedKey, titleHistory.lastTimeUserPlayed, true )); } constexpr const char* lastTimePlayedTextKey{ "lastTimePlayedText" }; if (json.HasMember(lastTimePlayedTextKey) && json[lastTimePlayedTextKey].IsString()) { RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonStringToCharArray( json, lastTimePlayedTextKey, titleHistory.lastTimeUserPlayedText, XBL_LAST_TIME_PLAYED_CHAR_SIZE )); } titleHistory.hasUserPlayed = titleHistory.lastTimeUserPlayed != 0; return Result{ titleHistory }; } NAMESPACE_MICROSOFT_XBOX_SERVICES_SOCIAL_MANAGER_CPP_END ================================================ FILE: Source/Services/Social/Manager/peoplehub_service.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "xsapi-c/social_manager_c.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_SOCIAL_MANAGER_CPP_BEGIN class PeoplehubService { public: PeoplehubService( _In_ User&& user, _In_ std::shared_ptr httpCallSettings, _In_ uint32_t titleId ) noexcept; HRESULT GetSocialGraph( _In_ uint64_t xuid, _In_ XblSocialManagerExtraDetailLevel decorations, _In_ AsyncContext>> async ) const noexcept; HRESULT GetSocialUsers( _In_ uint64_t xuid, _In_ XblSocialManagerExtraDetailLevel decorations, _In_ const Vector& xuids, _In_ AsyncContext>> async ) const noexcept; private: enum class RelationshipType { Social, Batch }; HRESULT MakeServiceCall( _In_ uint64_t xuid, _In_ XblSocialManagerExtraDetailLevel decorations, _In_ RelationshipType relationshipType, _In_opt_ const Vector& batchUsers, _In_ AsyncContext>> async ) const noexcept; static Result DeserializeUser(const JsonValue& json); static Result DeserializePresenceRecord(const JsonValue& json); static Result DeserializePresenceTitleRecord(const JsonValue& json); static Result DeserializePreferredColor(const JsonValue& json); static Result DeserializeTitleHistory(const JsonValue& json); User m_user; std::shared_ptr m_httpSettings; uint32_t const m_titleId; }; NAMESPACE_MICROSOFT_XBOX_SERVICES_SOCIAL_MANAGER_CPP_END ================================================ FILE: Source/Services/Social/Manager/social_graph.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "social_graph.h" #include "xbox_live_context_internal.h" #include "social_manager_user_group.h" #include "perf_tester.h" // Max full graph refresh interval - 20 mins in ms #define GRAPH_REFRESH_INTERVAL_MS (20 * 60 * 1000) // Presence poll interval - 30 seconds in ms. Presence is only polled if rich presence polling is enabled. #ifdef XSAPI_UNIT_TESTS #define PRESENCE_POLL_INTERVAL_MS (1 * 1000) #else #define PRESENCE_POLL_INTERVAL_MS (30 * 1000) #endif NAMESPACE_MICROSOFT_XBOX_SERVICES_SOCIAL_MANAGER_CPP_BEGIN // Helper class to manage batching, retrying, and throttling of service calls needed by SocialGraph. // Seperating the state needed to manage the service calls from the actual SocialGraph state for clarity and // easier state synchronization. struct ServiceCallManager : public std::enable_shared_from_this { // Handlers invoked when Presence and Peoplehub Polls complete using PresenceResultHandler = std::function>&&)>; using PeopleHubResultHandler = std::function&&)>; ServiceCallManager( const User& user, const TaskQueue& queue, XblSocialManagerExtraDetailLevel peoplehubDetailLevel, std::shared_ptr presenceService, std::shared_ptr peoplehubService, PresenceResultHandler presenceResultHandler, PeopleHubResultHandler peopleHubResultHandler ) noexcept; ~ServiceCallManager() noexcept; // Poll rich presence for a set of users. Result delivered via PresenceResultHandler. HRESULT PollPresence(const Vector& xuids) noexcept; // Poll PeopleHub profiles for a set of users. Result delivered via PeoplehubResultHandler. HRESULT PollPeopleHub(const Vector& xuids) noexcept; // Get PeopleHub profiles for all followed users. Non-batched, but service failures will be retried // automatically. Result delivered via 'handler' arg HRESULT PeopleHubGetFollowedUsers(PeopleHubResultHandler handler) const noexcept; // Needed to check compatibility between determined detail level and XblPresenceFilter for a social group XblSocialManagerExtraDetailLevel GetDetailLevel() const noexcept; private: HRESULT PollPresenceServiceCall(std::unique_lock lock) noexcept; HRESULT PollPeopleHubServiceCall(std::unique_lock lock) noexcept; static constexpr uint32_t c_failureRetryIntervalMs{ 10000 }; // Adhere to service throttle limits static constexpr uint32_t c_presencePollIntervalMs{ 500 }; TaskQueue const m_queue; XblSocialManagerExtraDetailLevel const m_peoplehubDetailLevel; uint64_t const m_localUserXuid; // Presence polling state UnorderedSet m_usersPendingPresence; bool m_presencePollInProgress{ false }; PresenceResultHandler const m_presenceResultHandler; // Peoplehub polling state UnorderedSet m_usersPendingPeoplehub; bool m_peoplehubPollInProgress{ false }; PeopleHubResultHandler const m_peopleHubResultHandler; std::shared_ptr m_presenceService; std::shared_ptr m_peoplehubService; std::mutex m_mutex; }; /// ----------------------------------------------------------------------------------------------- /// SocialGraph implementation /// ----------------------------------------------------------------------------------------------- SocialGraph::SocialGraph( _In_ User&& localUser, _In_ const TaskQueue& queue, _In_ std::shared_ptr rtaManager ) noexcept : m_user{ MakeShared(std::move(localUser))}, m_queue{ queue.DeriveWorkerQueue() }, m_rtaManager{ std::move(rtaManager) } { } HRESULT SocialGraph::Initialize() noexcept { // Maintain legacy RTA activation count. m_rtaManager->Activate(*m_user); auto copiedUser = m_user->Copy(); RETURN_HR_IF_FAILED(copiedUser.Hresult()); m_xblContext = XblContext::Make(copiedUser.ExtractPayload()); RETURN_HR_IF_FAILED(m_xblContext->Initialize(m_rtaManager)); m_xblContext->Settings()->SetHttpUserAgent(HttpCallAgent::SocialManager); return S_OK; } SocialGraph::~SocialGraph() { // Terminate any background work m_queue.Terminate(false); if (m_socialRelationshipChangedToken) { m_xblContext->SocialService()->RemoveSocialRTAHandler(m_socialRelationshipChangedToken); } if (m_devicePresenceChangedToken) { m_xblContext->PresenceService()->RemoveDevicePresenceChangedHandler(m_devicePresenceChangedToken); } if (m_titlePresenceChangedToken) { m_xblContext->PresenceService()->RemoveTitlePresenceChangedHandler(m_titlePresenceChangedToken); } if (m_rtaResyncToken) { m_rtaManager->RemoveResyncHandler(*m_user, m_rtaResyncToken); } m_rtaManager->Deactivate(*m_user); } Result> SocialGraph::Make( _In_ User&& user, _In_ const XblSocialManagerExtraDetailLevel detailLevel, _In_ const TaskQueue& queue, _In_ std::shared_ptr rtaManager ) noexcept { Result userResult = user.Copy(); if (userResult.Hresult()) { return userResult.Hresult(); } auto graph = std::shared_ptr( new (Alloc(sizeof(SocialGraph))) SocialGraph{ userResult.ExtractPayload(), queue, rtaManager }, Deleter(), Allocator() ); auto hr = graph->Initialize(); if (FAILED(hr)) { return hr; } std::weak_ptr weakGraph{ graph }; auto peoplehubService = MakeShared(std::move(user), graph->m_xblContext->Settings(), AppConfig::Instance()->TitleId()); auto presenceService = graph->m_xblContext->PresenceService(); auto peoplehubResultHandler = [weakGraph](Vector&& profiles) { if (auto graph{ weakGraph.lock() }) { graph->PeoplehubResultHandler(profiles); } }; auto presenceResultHandler = [weakGraph](Vector>&& records) { if (auto graph{ weakGraph.lock() }) { graph->PresenceResultHandler(records); } }; graph->m_serviceCallManager = MakeShared( user, queue, detailLevel, presenceService, std::move(peoplehubService), presenceResultHandler, peoplehubResultHandler ); graph->m_getSocialGraphTask = PeriodicTask::MakeAndRun(queue, GRAPH_REFRESH_INTERVAL_MS, [weakGraph] { if (auto graph{ weakGraph.lock() }) { graph->m_serviceCallManager->PeopleHubGetFollowedUsers([weakGraph](Vector&& profiles) { if (auto graph{ weakGraph.lock() }) { // Update observer counts based on updated follower list std::unique_lock lock{ graph->m_mutex }; Vector previouslyFollowedXuids, followedXuids; for (auto& pair : graph->m_profiles) { if (pair.second->isFollowedByCaller) { previouslyFollowedXuids.push_back(pair.first); } } std::transform(profiles.begin(), profiles.end(), std::back_inserter(followedXuids), [](const XblSocialManagerUser& profile) { return profile.xboxUserId; }); lock.unlock(); // Don't repoll profiles since we just called PeopleHub graph->TrackUsers(followedXuids, PeoplehubPollMode::Never); graph->StopTrackingUsers(previouslyFollowedXuids); // Generate graph updates graph->PeoplehubResultHandler(profiles); // Build initial graph on background thread since we don't care about generating events during initialization if (!graph->m_initialized) { lock.lock(); Vector events; Vector> affectedUsers; graph->ApplyGraphUpdates(events, affectedUsers); graph->m_initialized = true; } } }); std::unique_lock lock{ graph->m_mutex }; Vector nonFollowedXuids; for (auto& pair : graph->m_profiles) { if (!pair.second->isFollowedByCaller) { nonFollowedXuids.push_back(pair.first); } } lock.unlock(); if (!nonFollowedXuids.empty()) { graph->m_serviceCallManager->PollPeopleHub(nonFollowedXuids); } } }); graph->m_socialRelationshipChangedToken = graph->m_xblContext->SocialService()->AddSocialRelationshipChangedHandler( [weakGraph](const XblSocialRelationshipChangeEventArgs& args) { if (auto graph{ weakGraph.lock() }) { graph->SocialRelationshipChangedHandler(args); } }); // Presence change handlers can in theory be optimized to avoid a service call when presence ends, // but it requires remembering a lot more state. Handling all presence changes notifications uniformly // by just repolling presence simplifies the logic dramatically. graph->m_devicePresenceChangedToken = presenceService->AddDevicePresenceChangedHandler( [weakGraph](uint64_t xuid, XblPresenceDeviceType deviceType, bool isUserLoggedOnDevice) { UNREFERENCED_PARAMETER(deviceType); UNREFERENCED_PARAMETER(isUserLoggedOnDevice); if (auto graph{ weakGraph.lock() }) { graph->m_serviceCallManager->PollPresence({ xuid }); } }); graph->m_titlePresenceChangedToken = presenceService->AddTitlePresenceChangedHandler( [weakGraph](uint64_t xuid, uint32_t titleId, XblPresenceTitleState state) { UNREFERENCED_PARAMETER(titleId); UNREFERENCED_PARAMETER(state); if (auto graph{ weakGraph.lock() }) { graph->m_serviceCallManager->PollPresence({ xuid }); } }); graph->m_rtaResyncToken = rtaManager->AddResyncHandler(user, [weakGraph] { if (auto graph{ weakGraph.lock() }) { graph->m_getSocialGraphTask->ScheduleImmediately(); } }); return graph; } std::shared_ptr SocialGraph::LocalUser() const noexcept { return m_user; } void SocialGraph::DoWork( _Inout_ Vector& events, _Inout_ Vector>& affectedUsers ) noexcept { PERF_START(); // For performance reasons, don't wait for the mutex if a background thread holds it std::unique_lock lock{ m_mutex, std::defer_lock }; if (lock.try_lock() && m_initialized) { // Raise the LocalUserAdded Event in the first DoWork call after the intial graph has loaded if (!m_localUserAdded) { events.push_back(XblSocialManagerEvent{ m_user->Handle(), XblSocialManagerEventType::LocalUserAdded }); m_localUserAdded = true; } // Apply graph updates ApplyGraphUpdates(events, affectedUsers); // Notify groups of graph changes for (auto& pair : m_groups) { // If group isn't initialized, schedule initialization to background queue. switch (pair.second) { case GroupInitializationStage::Pending: { pair.second = GroupInitializationStage::Scheduled; m_queue.RunWork([this, sharedThis{ shared_from_this() }, group{ pair.first }] { std::unique_lock lock{ m_mutex }; group->Initialize(m_profiles); m_groups[group] = GroupInitializationStage::Complete; }); break; } case GroupInitializationStage::Complete: { pair.first->DoWork(events); break; } case GroupInitializationStage::Scheduled: default: { break; } } } } PERF_STOP(); } void SocialGraph::RegisterGroup(std::shared_ptr group) noexcept { std::lock_guard lock{ m_mutex }; // If this is a new group or an existing group that has already completed initialization, set initialization stage // so that it gets initialized during DoWork. auto iter{ m_groups.find(group) }; if (iter == m_groups.end() || iter->second == GroupInitializationStage::Complete) { m_groups[group] = GroupInitializationStage::Pending; // Check if the filter is one that relies on title history but TitleHistoryLevel is not set if ((group->presenceFilter == XblPresenceFilter::TitleOffline || group->presenceFilter == XblPresenceFilter::TitleOnlineOutsideTitle || group->presenceFilter == XblPresenceFilter::AllTitle) && (m_serviceCallManager->GetDetailLevel() & XblSocialManagerExtraDetailLevel::TitleHistoryLevel) != XblSocialManagerExtraDetailLevel::TitleHistoryLevel) { LOGS_DEBUG << "TitleOffline, TitleOnlineOutsideTitle, and AllTitle filters require XblSocialManagerExtraDetailLevel::TitleHistoryLevel to be set for this user"; } } } void SocialGraph::UnregisterGroup(std::shared_ptr group) noexcept { std::lock_guard lock{ m_mutex }; m_groups.erase(group); } void SocialGraph::TrackUsers( _In_ const Vector& xuids ) noexcept { // Only poll PeopleHub for profiles not already in the graph TrackUsers(xuids, PeoplehubPollMode::IfNew); } void SocialGraph::TrackUsers( _In_ const Vector& xuids, _In_ PeoplehubPollMode refreshMode ) noexcept { std::unique_lock lock{ m_mutex }; Vector refreshXuids; for (auto xuid : xuids) { auto iter = m_trackedUsers.find(xuid); if (iter == m_trackedUsers.end()) { // Add new graph entry m_trackedUsers.emplace(xuid, TrackedUser{ xuid, m_xblContext->PresenceService() }); if (refreshMode == PeoplehubPollMode::Always || refreshMode == PeoplehubPollMode::IfNew) { refreshXuids.emplace_back(xuid); } } else { ++iter->second.refCount; if (refreshMode == PeoplehubPollMode::Always) { refreshXuids.emplace_back(xuid); } } } lock.unlock(); if (!refreshXuids.empty()) { m_serviceCallManager->PollPeopleHub(refreshXuids); } } void SocialGraph::StopTrackingUsers( _In_ const Vector& xuids ) noexcept { std::lock_guard lock{ m_mutex }; for (auto xuid : xuids) { auto iter{ m_trackedUsers.find(xuid) }; if (iter != m_trackedUsers.end() && --iter->second.refCount == 0) { m_trackedUsers.erase(iter); m_pendingUpdates[xuid] = { ProfileChanges::None, nullptr }; } } } void SocialGraph::SetRichPresencePolling(bool enabled) noexcept { if (enabled != m_presencePollingEnabled) { m_presencePollingEnabled = enabled; if (m_presencePollingEnabled) { assert(!m_getPresenceForGraphTask); m_getPresenceForGraphTask = PeriodicTask::MakeAndRun(m_queue, PRESENCE_POLL_INTERVAL_MS, [ weakGraph = std::weak_ptr{ shared_from_this() }, this ] { if (auto graph{weakGraph.lock()}) { std::unique_lock lock{ m_mutex }; Vector xuids; std::transform(m_trackedUsers.begin(), m_trackedUsers.end(), std::back_inserter(xuids), [](const auto& pair) { return pair.first; }); lock.unlock(); m_serviceCallManager->PollPresence(xuids); } }); } else { assert(m_getPresenceForGraphTask); m_getPresenceForGraphTask.reset(); } } } void SocialGraph::PeoplehubResultHandler( const Vector& users ) noexcept { PERF_START(); std::unique_lock lock{ m_mutex }; for (auto& profile : users) { auto iter{ m_profiles.find(profile.xboxUserId) }; if (iter == m_profiles.end()) { m_pendingUpdates[profile.xboxUserId] = { ProfileChanges::None, MakeShared(profile) }; } else if (auto changes = CompareProfiles(*iter->second, profile)) { m_pendingUpdates[profile.xboxUserId] = { changes, MakeShared(profile) }; } } PERF_STOP(); } void SocialGraph::PresenceResultHandler( const Vector>& presenceRecords ) noexcept { PERF_START(); std::unique_lock lock{ m_mutex }; for (auto& record : presenceRecords) { // When comparing with existing profile, first check if there is a pending update std::shared_ptr compareProfile{ nullptr }; auto updatesIter{ m_pendingUpdates.find(record->Xuid()) }; if (updatesIter != m_pendingUpdates.end()) { compareProfile = updatesIter->second.second; } if (!compareProfile) { auto profilesIter{ m_profiles.find(record->Xuid()) }; if (profilesIter != m_profiles.end()) { compareProfile = profilesIter->second; } } // If there is no entry to compare, ignore. The profile (including presence) will be updated // when the associated Peoplehub call completes if (compareProfile) { auto smRecord{ ConvertPresenceRecord(record) }; if (memcmp(&compareProfile->presenceRecord, &smRecord, sizeof(XblSocialManagerPresenceRecord))) { auto updatedProfile = MakeShared(*compareProfile); memcpy(&updatedProfile->presenceRecord, &smRecord, sizeof(XblSocialManagerPresenceRecord)); m_pendingUpdates[updatedProfile->xboxUserId] = { ProfileChanges::PresenceChanged, updatedProfile }; } } } PERF_STOP(); } void SocialGraph::SocialRelationshipChangedHandler( const XblSocialRelationshipChangeEventArgs& args ) noexcept { // Depending on the type of notification and whether or not the user is still being tracked // in our graph, we will need to refresh the profiles for a subset of the affected xuids. Vector affectedXuids(args.xboxUserIds, args.xboxUserIds + args.xboxUserIdsCount); Vector refreshXuids; switch (args.socialNotification) { case XblSocialNotificationType::Added: { TrackUsers(affectedXuids, PeoplehubPollMode::Always); break; } case XblSocialNotificationType::Changed: { refreshXuids = std::move(affectedXuids); break; } case XblSocialNotificationType::Removed: { StopTrackingUsers(affectedXuids); // If the users are still in the graph, refresh their profile std::unique_lock lock{ m_mutex }; for (auto xuid : affectedXuids) { if(m_trackedUsers.find(xuid) != m_trackedUsers.end()) { refreshXuids.push_back(xuid); } } break; } default: { LOGS_DEBUG << "Unknown social notification received and ignored."; } } if (!refreshXuids.empty()) { m_serviceCallManager->PollPeopleHub(refreshXuids); } } void SocialGraph::AddOrUpdateEvent( XblSocialManagerEventType type, const std::shared_ptr& affectedUser, Vector& events, Vector>& affectedUsers ) noexcept { PERF_START(); // Update affected users set affectedUsers.push_back(affectedUser); // Try to update an existing event first for (auto& event : events) { if (event.eventType == type) { uint8_t affectedUserIndex{ 0 }; for (; affectedUserIndex < std::extent::value && event.usersAffected[affectedUserIndex]; ++affectedUserIndex); if (affectedUserIndex < std::extent::value) { event.usersAffected[affectedUserIndex] = affectedUser.get(); return; } } } // If we couldn't update an existing event, add a new one events.emplace_back(); auto& newEvent{ events.back() }; newEvent.eventType = type; newEvent.user = m_user->Handle(); newEvent.usersAffected[0] = affectedUser.get(); PERF_STOP(); } void SocialGraph::ApplyGraphUpdates( _Inout_ Vector& events, _Inout_ Vector>& affectedUsers ) noexcept { PERF_START(); // Apply updates. After initialization, apply at most MAX_GRAPH_UPDATES_PER_FRAME size_t updatesApplied = 0; size_t updateLimit{ m_initialized ? MAX_GRAPH_UPDATES_PER_FRAME : 1000u }; for (auto updateIter = m_pendingUpdates.begin(); updateIter != m_pendingUpdates.end() && updatesApplied < updateLimit; updateIter = m_pendingUpdates.erase(updateIter), ++updatesApplied) { auto& xuid{ updateIter->first }; auto trackedUserIter{ m_trackedUsers.find(xuid) }; auto profileIter{ m_profiles.find(xuid) }; auto profileChanges{ updateIter->second.first }; auto& updatedProfile{ updateIter->second.second }; // If we are no longer tracking the user remove the profile and add event if (trackedUserIter == m_trackedUsers.end() && profileIter != m_profiles.end()) { AddOrUpdateEvent(XblSocialManagerEventType::UsersRemovedFromSocialGraph, profileIter->second, events, affectedUsers); m_profiles.erase(profileIter); } else if (updatedProfile) // This could be null in cases where the user is untracked/tracked in the same DoWork cycle { // If this is a new profile, generate only a user added event. Otherwise generate depending on the profileChanges if (profileIter == m_profiles.end()) { AddOrUpdateEvent(XblSocialManagerEventType::UsersAddedToSocialGraph, updatedProfile, events, affectedUsers); m_profiles.insert({ xuid, updatedProfile }); } else { profileIter->second = updatedProfile; if (profileChanges & ProfileChanges::PresenceChanged) { AddOrUpdateEvent(XblSocialManagerEventType::PresenceChanged, updatedProfile, events, affectedUsers); } if (profileChanges & ProfileChanges::RelationshipChanged) { AddOrUpdateEvent(XblSocialManagerEventType::SocialRelationshipsChanged, updatedProfile, events, affectedUsers); } if (profileChanges & ProfileChanges::ProfileChanged) { AddOrUpdateEvent(XblSocialManagerEventType::ProfilesChanged, updatedProfile, events, affectedUsers); } } } } PERF_STOP(); } ProfileChanges SocialGraph::CompareProfiles( const XblSocialManagerUser& old, const XblSocialManagerUser& updated ) noexcept { auto changes{ ProfileChanges::None }; // ProfileChanges::RelationshipChanged indicates favorite/following/followed changed if (old.isFollowedByCaller != updated.isFollowedByCaller || old.isFollowingUser != updated.isFollowingUser || old.isFavorite != updated.isFavorite ) { changes |= ProfileChanges::RelationshipChanged; } // ProfileChanges::PresenceChange indicates a change in the presence record if (memcmp(&old.presenceRecord, &updated.presenceRecord, sizeof(XblSocialManagerPresenceRecord))) { changes |= ProfileChanges::PresenceChanged; } // ProfileChanges::ProfileChanged indicates any other change if (utils::str_icmp(old.gamerscore, updated.gamerscore) != 0 || old.titleHistory.hasUserPlayed != updated.titleHistory.hasUserPlayed || old.titleHistory.lastTimeUserPlayed != updated.titleHistory.lastTimeUserPlayed || utils::str_icmp(old.displayPicUrlRaw, updated.displayPicUrlRaw) != 0 || old.useAvatar != updated.useAvatar || utils::str_icmp(old.gamertag, updated.gamertag) != 0 || utils::str_icmp(old.modernGamertag, updated.modernGamertag) != 0 || utils::str_icmp(old.modernGamertagSuffix, updated.modernGamertagSuffix) != 0 || utils::str_icmp(old.uniqueModernGamertag, updated.uniqueModernGamertag) != 0 || utils::str_icmp(old.displayName, updated.displayName) != 0 || utils::str_icmp(old.realName, updated.realName) != 0 || utils::str_icmp(old.preferredColor.primaryColor, updated.preferredColor.primaryColor) != 0 || utils::str_icmp(old.preferredColor.secondaryColor, updated.preferredColor.secondaryColor) != 0 || utils::str_icmp(old.preferredColor.tertiaryColor, updated.preferredColor.tertiaryColor) != 0 ) { changes |= ProfileChanges::ProfileChanged; } return changes; } XblSocialManagerPresenceRecord SocialGraph::ConvertPresenceRecord( std::shared_ptr presenceRecord ) noexcept { XblSocialManagerPresenceRecord smPresenceRecord{}; smPresenceRecord.userState = presenceRecord->UserState(); for (auto& deviceRecord : presenceRecord->DeviceRecords()) { for (uint32_t i = 0; i < deviceRecord.titleRecordsCount && smPresenceRecord.presenceTitleRecordCount < XBL_NUM_PRESENCE_RECORDS; ++i) { auto& smTitleRecord{ smPresenceRecord.presenceTitleRecords[smPresenceRecord.presenceTitleRecordCount++] }; auto& newTitleRecord{ deviceRecord.titleRecords[i] }; smTitleRecord.titleId = newTitleRecord.titleId; smTitleRecord.isTitleActive = newTitleRecord.titleActive; utils::strcpy(smTitleRecord.titleName, sizeof(smTitleRecord.titleName), newTitleRecord.titleName); utils::strcpy(smTitleRecord.presenceText, sizeof(smTitleRecord.presenceText), newTitleRecord.richPresenceString); smTitleRecord.isBroadcasting = newTitleRecord.broadcastRecord != nullptr; smTitleRecord.deviceType = deviceRecord.deviceType; } if (smPresenceRecord.presenceTitleRecordCount == XBL_NUM_PRESENCE_RECORDS) { break; } } return smPresenceRecord; } /// ----------------------------------------------------------------------------------------------- /// TrackedUser implementation /// ----------------------------------------------------------------------------------------------- TrackedUser::TrackedUser( uint64_t _xuid, std::shared_ptr _presenceService ) noexcept : xuid{ _xuid }, presenceService{ std::move(_presenceService) } { PERF_START(); HRESULT hr = presenceService->TrackUsers({ xuid }); assert(SUCCEEDED(hr)); UNREFERENCED_PARAMETER(hr); PERF_STOP(); } TrackedUser::TrackedUser(const TrackedUser& other) noexcept : TrackedUser{ other.xuid, other.presenceService } { } TrackedUser::~TrackedUser() noexcept { PERF_START(); assert(presenceService); presenceService->StopTrackingUsers({ xuid }); PERF_STOP(); } /// ----------------------------------------------------------------------------------------------- /// ServiceCallManager implementation /// ----------------------------------------------------------------------------------------------- ServiceCallManager::ServiceCallManager( const User& user, const TaskQueue& queue, XblSocialManagerExtraDetailLevel peoplehubDetailLevel, std::shared_ptr presenceService, std::shared_ptr peoplehubService, PresenceResultHandler presenceResultHandler, PeopleHubResultHandler peoplehubResultHandler ) noexcept : m_queue{ queue.DeriveWorkerQueue() }, m_peoplehubDetailLevel{ peoplehubDetailLevel }, m_localUserXuid{ user.Xuid() }, m_presenceResultHandler{ std::move(presenceResultHandler) }, m_peopleHubResultHandler{ std::move(peoplehubResultHandler) }, m_presenceService{ std::move(presenceService) }, m_peoplehubService{ std::move(peoplehubService) } { } ServiceCallManager::~ServiceCallManager() noexcept { m_queue.Terminate(false); } HRESULT ServiceCallManager::PollPresence(const Vector& xuids) noexcept { std::unique_lock lock{ m_mutex }; m_usersPendingPresence.insert(xuids.begin(), xuids.end()); if (!m_presencePollInProgress) { return PollPresenceServiceCall(std::move(lock)); } return S_OK; } HRESULT ServiceCallManager::PollPeopleHub(const Vector& xuids) noexcept { std::unique_lock lock{ m_mutex }; m_usersPendingPeoplehub.insert(xuids.begin(), xuids.end()); if (!m_peoplehubPollInProgress) { return PollPeopleHubServiceCall(std::move(lock)); } return S_OK; } HRESULT ServiceCallManager::PeopleHubGetFollowedUsers(PeopleHubResultHandler handler) const noexcept { return m_peoplehubService->GetSocialGraph(m_localUserXuid, m_peoplehubDetailLevel, { m_queue, [ weakThis = std::weak_ptr{ shared_from_this() }, this, handler{ std::move(handler) } ] (Result> result) { if (Failed(result)) { m_queue.RunWork([weakThis, this, handler] { if (auto sharedThis{ weakThis.lock() }) { PeopleHubGetFollowedUsers(handler); } }, c_failureRetryIntervalMs); } else { handler(result.ExtractPayload()); } } }); } HRESULT ServiceCallManager::PollPresenceServiceCall(std::unique_lock lock) noexcept { assert(lock.owns_lock()); if (m_usersPendingPresence.empty()) { return S_OK; } XblPresenceQueryFilters filters{}; filters.detailLevel = XblPresenceDetailLevel::All; Vector pollXuids{ m_usersPendingPresence.begin(), m_usersPendingPresence.end() }; auto hr = m_presenceService->GetBatchPresence( presence::UserBatchRequest{ pollXuids.data(), pollXuids.size(), &filters }, { m_queue, [ weakThis = std::weak_ptr{ shared_from_this() }, this, pollXuids ] (Result>> result) { if (auto sharedThis{ weakThis.lock() }) { uint32_t interval{ c_presencePollIntervalMs }; std::unique_lock lock{ sharedThis->m_mutex }; if (Failed(result)) { // Ensure failed xuids are retried. Increase poll interval for failures m_usersPendingPresence.insert(pollXuids.begin(), pollXuids.end()); interval = c_failureRetryIntervalMs; } else { m_presenceResultHandler(result.ExtractPayload()); } // Schedule next poll if xuids are still pending if (!m_usersPendingPresence.empty()) { m_queue.RunWork([weakThis, this] { if (auto sharedThis{ weakThis.lock() }) { PollPresenceServiceCall(std::unique_lock{ m_mutex }); } }, interval); } else { m_presencePollInProgress = false; } } } }); m_presencePollInProgress = true; m_usersPendingPresence.clear(); return hr; } HRESULT ServiceCallManager::PollPeopleHubServiceCall(std::unique_lock lock) noexcept { assert(lock.owns_lock()); if (m_usersPendingPeoplehub.empty()) { return S_OK; } Vector pollXuids{ m_usersPendingPeoplehub.begin(), m_usersPendingPeoplehub.end() }; auto hr = m_peoplehubService->GetSocialUsers(m_localUserXuid, m_peoplehubDetailLevel, pollXuids, { m_queue, [ weakThis = std::weak_ptr{ shared_from_this() }, this, pollXuids ] (Result> result) { if (auto sharedThis{ weakThis.lock() }) { uint32_t interval{ 0 }; std::unique_lock lock{ sharedThis->m_mutex }; if (Failed(result)) { // Ensure failed xuids are retried. Increase poll interval for failures m_usersPendingPeoplehub.insert(pollXuids.begin(), pollXuids.end()); interval = c_failureRetryIntervalMs; } else { m_peopleHubResultHandler(result.ExtractPayload()); } // Schedule next poll if xuids are still pending if (!m_usersPendingPeoplehub.empty()) { m_queue.RunWork([weakThis, this] { if (auto sharedThis{ weakThis.lock() }) { PollPeopleHubServiceCall(std::unique_lock{ m_mutex }); } }, interval); } else { m_peoplehubPollInProgress = false; } } } }); m_peoplehubPollInProgress = true; m_usersPendingPeoplehub.clear(); return hr; } XblSocialManagerExtraDetailLevel ServiceCallManager::GetDetailLevel() const noexcept { return m_peoplehubDetailLevel; } NAMESPACE_MICROSOFT_XBOX_SERVICES_SOCIAL_MANAGER_CPP_END ================================================ FILE: Source/Services/Social/Manager/social_graph.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "presence_internal.h" #include "social_internal.h" #include "peoplehub_service.h" #include "real_time_activity_manager.h" #ifdef max #undef max #endif // Throttle the maximum number of updates the SocialGraph will process each call to DoWork #define MAX_GRAPH_UPDATES_PER_FRAME 5 NAMESPACE_MICROSOFT_XBOX_SERVICES_SOCIAL_MANAGER_CPP_BEGIN // Enum describing how a profile has changed enum ProfileChanges : uint32_t { None = 0x0, ProfileChanged = 0x1, PresenceChanged = 0x2, RelationshipChanged = 0x4 }; DEFINE_ENUM_FLAG_OPERATORS(ProfileChanges); // Metadata about a user tracked by SocialGraph. Maintains RTA tracking and counts references within the SocialGraph struct TrackedUser { TrackedUser( uint64_t xuid, std::shared_ptr presenceService ) noexcept; TrackedUser(const TrackedUser& other) noexcept; TrackedUser& operator=(TrackedUser) = delete; ~TrackedUser() noexcept; uint32_t refCount{ 1 }; uint64_t xuid; std::shared_ptr presenceService; }; // Tracks profile and presence changes for other XboxLiveUsers. Automatically tracks users followed by the // local user, but additional remote users can be added explicitly. class SocialGraph : public std::enable_shared_from_this { public: static Result> Make( _In_ User&& localUser, _In_ const XblSocialManagerExtraDetailLevel detailLevel, _In_ const TaskQueue& queue, _In_ std::shared_ptr rtaManager ) noexcept; ~SocialGraph(); // LocalUser that the SocialGraph is for std::shared_ptr LocalUser() const noexcept; // Patches profile and presence updates that have happened since the last call to DoWork into the graph. // Appends XblSocialManagerEvents describing the updates to 'events' list. Appends all affected users to the list of affected users. // This is used to maintain their lifetime since the events reference them. void DoWork( _Inout_ Vector& events, _Inout_ Vector>& affectedUsers ) noexcept; // Registers/Unregisters an XblSocialManagerUserGroup backed by this graph. Registered groups // will be initialized and then notified of graph changes each call to DoWork void RegisterGroup(std::shared_ptr group) noexcept; void UnregisterGroup(std::shared_ptr group) noexcept; // Track a list of users. If they are not currently tracked, they will be added to the graph. void TrackUsers( _In_ const Vector& xuids ) noexcept; // Stop tracking a list of users that were previously added via TrackUsers. void StopTrackingUsers( _In_ const Vector& xuids ) noexcept; // Enable/Disbale rich presence polling. If set to true, creates a Periodic Task to refresh presence and // runs it immediately. void SetRichPresencePolling(bool enabled) noexcept; HRESULT Initialize() noexcept; private: SocialGraph( _In_ User&& localUser, _In_ const TaskQueue& queue, _In_ std::shared_ptr rtaManager ) noexcept; // Generate Graph Updates based on the result of a PeopleHub service call void PeoplehubResultHandler( const Vector& users ) noexcept; // Generate Graph Updates based on the result of a Presence service call void PresenceResultHandler( const Vector>& presenceRecords ) noexcept; // RTA handler void SocialRelationshipChangedHandler(const XblSocialRelationshipChangeEventArgs& args) noexcept; // Enum describing how to update a tracked User's profile enum PeoplehubPollMode { Never, IfNew, Always }; void TrackUsers( _In_ const Vector& xuids, _In_ PeoplehubPollMode refreshMode ) noexcept; // Helper that aggregates events based on graph updates inline void AddOrUpdateEvent( XblSocialManagerEventType type, const std::shared_ptr& affectedUser, Vector& events, Vector>& affectedUsers ) noexcept; // Applies all pending updates to local graph. Updates events and affected users void ApplyGraphUpdates( _Inout_ Vector& events, _Inout_ Vector>& affectedUsers ) noexcept; static ProfileChanges CompareProfiles( const XblSocialManagerUser& old, const XblSocialManagerUser& updated ) noexcept; static XblSocialManagerPresenceRecord ConvertPresenceRecord( std::shared_ptr presenceRecord ) noexcept; std::shared_ptr m_user; TaskQueue const m_queue; // Graph state UnorderedMap> m_profiles; UnorderedMap m_trackedUsers; UnorderedMap>> m_pendingUpdates; // Groups. Initialization stage indicates whether or not a group has been initialized yet enum GroupInitializationStage{ Pending, Scheduled, Complete }; Map, GroupInitializationStage> m_groups; bool m_presencePollingEnabled{ false }; bool m_localUserAdded{ false }; bool m_initialized{ false }; std::shared_ptr m_rtaManager; std::shared_ptr m_xblContext; std::shared_ptr m_serviceCallManager; // Handler tokens XblFunctionContext m_socialRelationshipChangedToken{ 0 }; XblFunctionContext m_devicePresenceChangedToken{ 0 }; XblFunctionContext m_titlePresenceChangedToken{ 0 }; XblFunctionContext m_rtaResyncToken{ 0 }; // Background PeriodicTasks std::shared_ptr m_getPresenceForGraphTask; std::shared_ptr m_getSocialGraphTask; mutable std::recursive_mutex m_mutex; }; NAMESPACE_MICROSOFT_XBOX_SERVICES_SOCIAL_MANAGER_CPP_END ================================================ FILE: Source/Services/Social/Manager/social_manager.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "social_manager_internal.h" #include "social_manager_user_group.h" #include "social_graph.h" #include "perf_tester.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_SOCIAL_MANAGER_CPP_BEGIN SocialManager::SocialManager() noexcept { // MAX_GRAPH_UPDATES_PER_FRAME doesn't directly map to a number XblSocialManagerEvents, but it is correlated so // we can use it to preallocate some memory for events nonetheless m_events.reserve(MAX_GRAPH_UPDATES_PER_FRAME * 2); m_affectedUsersLifetime.reserve(m_events.capacity() * std::extent::value); } HRESULT SocialManager::AddLocalUser( User&& user, XblSocialManagerExtraDetailLevel detailLevel, TaskQueue&& queue ) noexcept { std::lock_guard lock{ m_mutex }; auto xuid{ user.Xuid() }; if (m_graphs.find(xuid) != m_graphs.end()) { LOGS_ERROR << "User " << xuid << " already added to SocialManager"; return E_UNEXPECTED; } auto socialGraph = SocialGraph::Make(std::move(user), detailLevel, queue, GlobalState::Get()->RTAManager()); RETURN_HR_IF_FAILED(socialGraph.Hresult()); m_graphs[xuid] = socialGraph.ExtractPayload(); return S_OK; } HRESULT SocialManager::RemoveLocalUser( const User& user ) noexcept { // Destroy graph outside lock so DoWork thread isn't blocked std::shared_ptr graph{ nullptr }; std::lock_guard lock{ m_mutex }; auto xuid = user.Xuid(); auto graphIter{ m_graphs.find(xuid) }; if (graphIter == m_graphs.end()) { LOGS_ERROR << "User " << user.Xuid() << " was not added to SocialManager"; return E_UNEXPECTED; } graph = std::move(graphIter->second); m_graphs.erase(graphIter); // Ensure lifetime of User object since it is referenced in returned events m_removedUsersLifetime.push_back(graph->LocalUser()); // When a user is removed, also destroy any groups associated with the user for (auto iter = m_groups.begin(); iter != m_groups.end();) { if (iter->second->LocalUser()->Xuid() == user.Xuid()) { iter = m_groups.erase(iter); } else { ++iter; } } return S_OK; } Result> SocialManager::CreateUserGroup( const User& user, XblPresenceFilter presenceFilter, XblRelationshipFilter relationshipFilter ) noexcept { std::lock_guard lock{ m_mutex }; auto graphIter = m_graphs.find(user.Xuid()); if (graphIter == m_graphs.end()) { LOGS_ERROR << "Add user " << user.Xuid() << " to SocialManager before creating a user group for them"; return E_UNEXPECTED; } auto group = XblSocialManagerUserGroup::Make(graphIter->second, presenceFilter, relationshipFilter); m_groups[group.get()] = group; return group; } Result> SocialManager::CreateUserGroup( const xbox::services::User& user, Vector&& trackedUsers ) noexcept { std::lock_guard lock{ m_mutex }; auto graphIter = m_graphs.find(user.Xuid()); if (graphIter == m_graphs.end()) { LOGS_ERROR << "Add user " << user.Xuid() << " to SocialManager before creating a user group for them"; return E_UNEXPECTED; } auto group = XblSocialManagerUserGroup::Make(graphIter->second, std::move(trackedUsers)); m_groups[group.get()] = group; return group; } std::shared_ptr SocialManager::GetUserGroup( XblSocialManagerUserGroupHandle groupHandle ) const { std::lock_guard lock{ m_mutex }; auto groupIter = m_groups.find(groupHandle); if (groupIter == m_groups.end()) { LOGS_ERROR << "Group is not valid. It was destroyed with XblSocialManagerDestroyUserGroup or the associated local user was removed."; return nullptr; } return groupIter->second; } HRESULT SocialManager::DestroyUserGroup( XblSocialManagerUserGroupHandle groupHandle ) noexcept { std::lock_guard lock{ m_mutex }; auto groupIter = m_groups.find(groupHandle); if (groupIter == m_groups.end()) { LOGS_ERROR << "Group handle provided is invalid."; return E_FAIL; } auto xuid{ groupIter->second->LocalUser()->Xuid() }; auto graphIter = m_graphs.find(xuid); if (graphIter == m_graphs.end()) { LOGS_ERROR << "Add user " << xuid << " to SocialManager before creating a user group for them"; return E_UNEXPECTED; } graphIter->second->UnregisterGroup(groupIter->second); m_groups.erase(groupHandle); return S_OK; } const Vector& SocialManager::DoWork() noexcept { PERF_START(); std::unique_lock eventsLock{ m_eventsMutex }; m_events.clear(); m_affectedUsersLifetime.clear(); m_removedUsersLifetime.clear(); // For performance reasons, don't wait for the graph mutex if another thread holds it. std::unique_lock graphLock{ m_mutex, std::defer_lock }; if (graphLock.try_lock()) { for (auto& pair : m_graphs) { pair.second->DoWork(m_events, m_affectedUsersLifetime); } } PERF_STOP(); return m_events; } HRESULT SocialManager::SetRichPresencePolling( const User& user, bool enabled ) noexcept { std::lock_guard lock{ m_mutex }; auto graphIter = m_graphs.find(user.Xuid()); if (graphIter == m_graphs.end()) { LOGS_ERROR << "Add user " << user.Xuid() << " to SocialManager before creating a user group for them"; return E_UNEXPECTED; } graphIter->second->SetRichPresencePolling(enabled); return S_OK; } size_t SocialManager::LocalUserCount() const noexcept { return m_graphs.size(); } HRESULT SocialManager::GetLocalUsers( _In_ size_t usersCount, _Out_writes_(usersCount) XblUserHandle* users ) const noexcept { RETURN_HR_INVALIDARGUMENT_IF(usersCount < m_graphs.size()); RETURN_HR_INVALIDARGUMENT_IF(users == nullptr && usersCount > 0); std::lock_guard lock{ m_mutex }; size_t index{ 0 }; for (auto& pair : m_graphs) { users[index++] = pair.second->LocalUser()->Handle(); } return S_OK; } NAMESPACE_MICROSOFT_XBOX_SERVICES_SOCIAL_MANAGER_CPP_END ================================================ FILE: Source/Services/Social/Manager/social_manager_api.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "social_manager_internal.h" #include "social_manager_user_group.h" using namespace xbox::services; using namespace xbox::services::social::manager; NAMESPACE_MICROSOFT_XBOX_SERVICES_SOCIAL_MANAGER_CPP_BEGIN template Ret ApiImpl(Ret&& fallbackReturnValue, TWork&& work) noexcept { auto state{ GlobalState::Get() }; if (!state) { return fallbackReturnValue; } assert(state->SocialManager()); return work(*state->SocialManager()); } template HRESULT ApiImpl(TWork&& work) noexcept { return ApiImpl(E_XBL_NOT_INITIALIZED, std::move(work)); } NAMESPACE_MICROSOFT_XBOX_SERVICES_SOCIAL_MANAGER_CPP_END STDAPI_(bool) XblSocialManagerPresenceRecordIsUserPlayingTitle( _In_ const XblSocialManagerPresenceRecord* presenceRecord, _In_ uint32_t titleId ) XBL_NOEXCEPT try { if (presenceRecord == nullptr || presenceRecord->userState == XblPresenceUserState::Offline || presenceRecord->userState == XblPresenceUserState::Unknown) { return false; } for (uint32_t i = 0; i < presenceRecord->presenceTitleRecordCount; ++i) { if (presenceRecord->presenceTitleRecords[i].titleId == titleId) { return true; } } return false; } CATCH_RETURN_WITH(false); STDAPI XblSocialManagerUserGroupGetType( _In_ XblSocialManagerUserGroupHandle groupHandle, _Out_ XblSocialUserGroupType* type ) XBL_NOEXCEPT try { return ApiImpl([&](SocialManager& socialManager) { RETURN_HR_INVALIDARGUMENT_IF(groupHandle == nullptr || type == nullptr); auto group{ socialManager.GetUserGroup(groupHandle) }; if (!group) { return E_UNEXPECTED; } *type = group->type; return S_OK; }); } CATCH_RETURN() STDAPI XblSocialManagerUserGroupGetLocalUser( _In_ XblSocialManagerUserGroupHandle groupHandle, _Out_ XblUserHandle* localUser ) XBL_NOEXCEPT try { return ApiImpl([&](SocialManager& socialManager) { RETURN_HR_INVALIDARGUMENT_IF(groupHandle == nullptr || localUser == nullptr); auto group{ socialManager.GetUserGroup(groupHandle) }; if (!group) { return E_UNEXPECTED; } *localUser = group->LocalUser()->Handle(); return S_OK; }); } CATCH_RETURN() STDAPI XblSocialManagerUserGroupGetFilters( _In_ XblSocialManagerUserGroupHandle groupHandle, _Out_opt_ XblPresenceFilter* presenceFilter, _Out_opt_ XblRelationshipFilter* relationshipFilter ) XBL_NOEXCEPT try { return ApiImpl([&](SocialManager& socialManager) { RETURN_HR_INVALIDARGUMENT_IF(groupHandle == nullptr); auto group{ socialManager.GetUserGroup(groupHandle) }; if (!group || group->type != XblSocialUserGroupType::FilterType) { return E_UNEXPECTED; } if (presenceFilter) { *presenceFilter = group->presenceFilter; } if (relationshipFilter) { *relationshipFilter = group->relationshipFilter; } return S_OK; }); } CATCH_RETURN() STDAPI XblSocialManagerUserGroupGetUsers( _In_ XblSocialManagerUserGroupHandle groupHandle, _Outptr_result_maybenull_ XblSocialManagerUserPtrArray* users, _Out_ size_t* usersCount ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(groupHandle == nullptr || users == nullptr || usersCount == nullptr); *users = nullptr; return ApiImpl([&](SocialManager& socialManager) { auto group{ socialManager.GetUserGroup(groupHandle) }; if (!group) { *users = nullptr; *usersCount = 0; return E_UNEXPECTED; } auto& groupUsers = group->Users(); *users = groupUsers.data(); *usersCount = groupUsers.size(); return S_OK; }); } CATCH_RETURN() STDAPI XblSocialManagerUserGroupGetUsersTrackedByGroup( _In_ XblSocialManagerUserGroupHandle groupHandle, _Outptr_result_maybenull_ const uint64_t** trackedUsers, _Out_ size_t* trackedUsersCount ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(groupHandle == nullptr || trackedUsers == nullptr || trackedUsersCount == nullptr); *trackedUsers = nullptr; return ApiImpl([&](SocialManager& socialManager) { auto group{ socialManager.GetUserGroup(groupHandle) }; if (!group) { *trackedUsers = nullptr; *trackedUsersCount = 0; return E_UNEXPECTED; } const auto& groupTrackedUsers = group->TrackedUsers(); *trackedUsers = groupTrackedUsers.data(); *trackedUsersCount = groupTrackedUsers.size(); return S_OK; }); } CATCH_RETURN() STDAPI XblSocialManagerAddLocalUser( _In_ XblUserHandle user, _In_ XblSocialManagerExtraDetailLevel extraLevelDetail, _In_opt_ XTaskQueueHandle queue ) XBL_NOEXCEPT try { return ApiImpl([&](SocialManager& socialManager) { RETURN_HR_INVALIDARGUMENT_IF(user == nullptr); auto wrapUserResult{ User::WrapHandle(user) }; RETURN_HR_IF_FAILED(wrapUserResult.Hresult()); return socialManager.AddLocalUser(wrapUserResult.ExtractPayload(), extraLevelDetail, TaskQueue::DeriveWorkerQueue(queue)); }); } CATCH_RETURN() STDAPI XblSocialManagerRemoveLocalUser( _In_ XblUserHandle user ) XBL_NOEXCEPT try { return ApiImpl([&](SocialManager& socialManager) { RETURN_HR_INVALIDARGUMENT_IF_NULL(user); auto wrapUserResult{ User::WrapHandle(user) }; RETURN_HR_IF_FAILED(wrapUserResult.Hresult()); return socialManager.RemoveLocalUser(wrapUserResult.Payload()); }); } CATCH_RETURN() STDAPI XblSocialManagerDoWork( _Outptr_result_maybenull_ const XblSocialManagerEvent** socialEvents, _Out_ size_t* socialEventsCount ) XBL_NOEXCEPT try { INIT_OUT_PTR_PARAM(socialEvents); RETURN_HR_INVALIDARGUMENT_IF_NULL(socialEvents); RETURN_HR_INVALIDARGUMENT_IF_NULL(socialEventsCount); return ApiImpl([&](SocialManager& socialManager) { auto& events = socialManager.DoWork(); *socialEvents = events.empty() ? nullptr : events.data(); *socialEventsCount = events.size(); return S_OK; }); } CATCH_RETURN() STDAPI XblSocialManagerCreateSocialUserGroupFromFilters( _In_ XblUserHandle user, _In_ XblPresenceFilter presenceFilter, _In_ XblRelationshipFilter relationshipFilter, _Outptr_result_maybenull_ XblSocialManagerUserGroupHandle* group ) XBL_NOEXCEPT try { INIT_OUT_PTR_PARAM(group); return ApiImpl([&](SocialManager& socialManager) { RETURN_HR_INVALIDARGUMENT_IF(user == nullptr || group == nullptr); auto wrapUserResult{ User::WrapHandle(user) }; RETURN_HR_IF_FAILED(wrapUserResult.Hresult()); auto result = socialManager.CreateUserGroup(wrapUserResult.Payload(), presenceFilter, relationshipFilter); if (Succeeded(result)) { *group = result.ExtractPayload().get(); } return result.Hresult(); }); } CATCH_RETURN() STDAPI XblSocialManagerCreateSocialUserGroupFromList( _In_ XblUserHandle user, _In_ uint64_t* xuids, _In_ size_t xuidsCount, _Outptr_result_maybenull_ XblSocialManagerUserGroup** group ) XBL_NOEXCEPT try { INIT_OUT_PTR_PARAM(group); return ApiImpl([&](SocialManager& socialManager) { RETURN_HR_INVALIDARGUMENT_IF(user == nullptr || xuids == nullptr || xuidsCount <= 0 || xuidsCount > XBL_SOCIAL_MANAGER_MAX_USERS_FROM_LIST || group == nullptr); auto wrapUserResult{ User::WrapHandle(user) }; RETURN_HR_IF_FAILED(wrapUserResult.Hresult()); auto result = socialManager.CreateUserGroup(wrapUserResult.Payload(), Vector(xuids, xuids + xuidsCount)); if (Succeeded(result)) { *group = result.ExtractPayload().get(); } return result.Hresult(); }); } CATCH_RETURN() STDAPI XblSocialManagerDestroySocialUserGroup( _In_ XblSocialManagerUserGroupHandle groupHandle ) XBL_NOEXCEPT try { return ApiImpl([&](SocialManager& socialManager) { RETURN_HR_INVALIDARGUMENT_IF_NULL(groupHandle); return socialManager.DestroyUserGroup(groupHandle); }); } CATCH_RETURN() STDAPI_(size_t) XblSocialManagerGetLocalUserCount() XBL_NOEXCEPT try { return ApiImpl(0, [](SocialManager& socialManager) { return socialManager.LocalUserCount(); }); } CATCH_RETURN_WITH(0) STDAPI XblSocialManagerGetLocalUsers( _In_ size_t usersCount, _Out_writes_(usersCount) XblUserHandle* users ) XBL_NOEXCEPT try { return ApiImpl([&](SocialManager& socialManager) { return socialManager.GetLocalUsers(usersCount, users); }); } CATCH_RETURN() STDAPI XblSocialManagerUpdateSocialUserGroup( _In_ XblSocialManagerUserGroupHandle group, _In_ uint64_t* users, _In_ size_t usersCount ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(group == nullptr || users == nullptr || usersCount <= 0 || usersCount > XBL_SOCIAL_MANAGER_MAX_USERS_FROM_LIST); return group->UpdateTrackedUsers(Vector(users, users + usersCount)); } CATCH_RETURN() STDAPI XblSocialManagerSetRichPresencePollingStatus( _In_ XblUserHandle user, _In_ bool shouldEnablePolling ) XBL_NOEXCEPT try { return ApiImpl([&](SocialManager& socialManager) { auto wrapUserResult{ User::WrapHandle(user) }; RETURN_HR_IF_FAILED(wrapUserResult.Hresult()); return socialManager.SetRichPresencePolling(wrapUserResult.Payload(), shouldEnablePolling); }); } CATCH_RETURN() ================================================ FILE: Source/Services/Social/Manager/social_manager_internal.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "xsapi-c/social_manager_c.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_SOCIAL_MANAGER_CPP_BEGIN class SocialManager : public std::enable_shared_from_this { public: SocialManager() noexcept; HRESULT AddLocalUser( User&& user, XblSocialManagerExtraDetailLevel detailLevel, TaskQueue&& queue ) noexcept; HRESULT RemoveLocalUser( const User& user ) noexcept; Result> CreateUserGroup( const User& user, XblPresenceFilter presenceFilter, XblRelationshipFilter relationshipFilter ) noexcept; Result> CreateUserGroup( const User& user, Vector&& trackedUsers ) noexcept; // Because group lifetime is managed by SocialManager (handles are not refCounted), this API // is used to get a group if it is still valid. std::shared_ptr GetUserGroup( XblSocialManagerUserGroupHandle groupHandle ) const; HRESULT DestroyUserGroup( XblSocialManagerUserGroupHandle groupHandle ) noexcept; const Vector& DoWork() noexcept; HRESULT SetRichPresencePolling( const User& user, bool enabled ) noexcept; size_t LocalUserCount() const noexcept; HRESULT GetLocalUsers( _In_ size_t usersCount, _Out_writes_(usersCount) XblUserHandle* users ) const noexcept; private: // Controls access to m_events, which is only be written during DoWork mutable std::mutex m_eventsMutex; // Controls access to all other state, which may be updated from non-UI threads and is read by UI-thread (DoWork) mutable std::mutex m_mutex; Vector m_events; // Maintain lifetime for local users and XblSocialManagerUsers referenced in m_events Vector> m_affectedUsersLifetime; List> m_removedUsersLifetime; UnorderedMap> m_graphs; // Lifetime of groups is managed entirely by SocialManager (i.e. they are not refCounted). To maintain legacy // behavior, when a local user is removed, clean up all their groups. If an API call is made on an invalid group, // this map can be used to detect that. UnorderedMap> m_groups; }; NAMESPACE_MICROSOFT_XBOX_SERVICES_SOCIAL_MANAGER_CPP_END ================================================ FILE: Source/Services/Social/Manager/social_manager_user_group.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "social_manager_user_group.h" #include "perf_tester.h" using namespace xbox::services; using namespace xbox::services::social::manager; XblSocialManagerUserGroup::XblSocialManagerUserGroup( std::shared_ptr socialGraph, XblPresenceFilter _presenceFilter, XblRelationshipFilter _relationshipFilter ) noexcept : type{ XblSocialUserGroupType::FilterType }, presenceFilter{ _presenceFilter }, relationshipFilter{ _relationshipFilter }, m_localUser{ socialGraph->LocalUser() }, m_graph{ socialGraph } { } XblSocialManagerUserGroup::XblSocialManagerUserGroup( std::shared_ptr socialGraph, Vector&& trackedUsers ) noexcept : type{ XblSocialUserGroupType::UserListType }, m_localUser{ socialGraph->LocalUser() }, m_graph{ socialGraph }, m_trackedUsersView{ std::move(trackedUsers) }, m_trackedUsers{ m_trackedUsersView.begin(), m_trackedUsersView.end() } { m_usersView.reserve(m_trackedUsersView.size()); socialGraph->TrackUsers(m_trackedUsersView); } XblSocialManagerUserGroup::~XblSocialManagerUserGroup() noexcept { auto graph{ m_graph.lock() }; if (graph && type == XblSocialUserGroupType::UserListType ) { graph->StopTrackingUsers(m_trackedUsersView); } } std::shared_ptr XblSocialManagerUserGroup::Make( std::shared_ptr socialGraph, XblPresenceFilter presenceFilter, XblRelationshipFilter relationshipFilter ) noexcept { auto group = std::shared_ptr( new (Alloc(sizeof(XblSocialManagerUserGroup))) XblSocialManagerUserGroup{ socialGraph, presenceFilter, relationshipFilter }, Deleter(), Allocator() ); socialGraph->RegisterGroup(group); return group; } std::shared_ptr XblSocialManagerUserGroup::Make( std::shared_ptr socialGraph, Vector&& trackedUsers ) noexcept { auto group = std::shared_ptr( new (Alloc(sizeof(XblSocialManagerUserGroup))) XblSocialManagerUserGroup{ socialGraph, std::move(trackedUsers) }, Deleter(), Allocator() ); socialGraph->RegisterGroup(group); return group; } void XblSocialManagerUserGroup::Initialize(const UnorderedMap>& graphSnapshot) noexcept { std::lock_guard lock{ m_mutex }; switch (type) { case XblSocialUserGroupType::FilterType: { for (auto& pair : graphSnapshot) { if (IsMemberOfGroup(pair.second.get())) { m_users[pair.first] = pair.second.get(); } } // Do allocations for view structures now, but don't actually populate them until they // are requested. m_usersView.reserve(m_users.size()); m_trackedUsersView.reserve(m_users.size()); break; } case XblSocialUserGroupType::UserListType: { for (auto& xuid : m_trackedUsers) { auto graphIter{ graphSnapshot.find(xuid) }; if (graphIter != graphSnapshot.end()) { m_users[xuid] = graphIter->second.get(); } } break; } default: { assert(false); } } } void XblSocialManagerUserGroup::DoWork( _Inout_ Vector& events ) noexcept { PERF_START(); std::lock_guard lock{ m_mutex }; // Update users based on graph events for (auto& event : events) { switch (event.eventType) { case XblSocialManagerEventType::ProfilesChanged: case XblSocialManagerEventType::SocialRelationshipsChanged: case XblSocialManagerEventType::PresenceChanged: case XblSocialManagerEventType::UsersAddedToSocialGraph: { for (uint8_t i = 0; i < std::extent::value && event.usersAffected[i]; ++i) { if (IsMemberOfGroup(event.usersAffected[i])) { m_users[event.usersAffected[i]->xboxUserId] = event.usersAffected[i]; } else { m_users.erase(event.usersAffected[i]->xboxUserId); } } break; } case XblSocialManagerEventType::UsersRemovedFromSocialGraph: { for (uint8_t i = 0; i < std::extent::value && event.usersAffected[i]; ++i) { m_users.erase(event.usersAffected[i]->xboxUserId); } break; } default: { // We don't care about other types of events break; } } } // Generate loaded event if needed if (!m_loaded) { switch (type) { case XblSocialUserGroupType::FilterType: { // Because all followed users are automatically in the SocialGraph, Filter groups are loaded // as soon as initialization has completed events.push_back(XblSocialManagerEvent{ m_localUser->Handle(), XblSocialManagerEventType::SocialUserGroupLoaded, S_OK, this }); m_loaded = true; break; } case XblSocialUserGroupType::UserListType: { // List groups are loaded when the users they track have been added to the SocialGraph if (m_users.size() == m_trackedUsers.size()) { events.push_back(XblSocialManagerEvent{ m_localUser->Handle(), m_updated ? XblSocialManagerEventType::SocialUserGroupUpdated : XblSocialManagerEventType::SocialUserGroupLoaded, S_OK, this }); m_loaded = true; } break; } } } PERF_STOP(); } const Vector& XblSocialManagerUserGroup::Users() noexcept { PERF_START(); std::unique_lock lock{ m_mutex }; m_usersView.clear(); if (m_loaded) { std::transform(m_users.begin(), m_users.end(), std::back_inserter(m_usersView), [](const auto& pair) { return pair.second; }); } PERF_STOP(); return m_usersView; } const Vector& XblSocialManagerUserGroup::TrackedUsers() noexcept { PERF_START(); std::unique_lock lock{ m_mutex }; if (m_loaded) { switch (type) { case XblSocialUserGroupType::FilterType: { m_trackedUsersView.clear(); std::transform(m_users.begin(), m_users.end(), std::back_inserter(m_trackedUsersView), [](const auto& pair) { return pair.first; }); break; } case XblSocialUserGroupType::UserListType: { // Tracked users view is static break; } } } else { m_trackedUsersView.clear(); } PERF_STOP(); return m_trackedUsersView; } std::shared_ptr XblSocialManagerUserGroup::LocalUser() const noexcept { return m_localUser; } HRESULT XblSocialManagerUserGroup::UpdateTrackedUsers( Vector&& trackedUsers ) noexcept { std::unique_lock lock{ m_mutex }; auto graph{ m_graph.lock() }; assert(graph); switch (type) { case XblSocialUserGroupType::FilterType: { LOGS_ERROR << "Can't update tracked users for XblSocialUserGroupType::FilterType"; return E_UNEXPECTED; } case XblSocialUserGroupType::UserListType: { // Track new users before untracking old users to avoid users being removed from and // then immediately readded to the graph. graph->TrackUsers(trackedUsers); graph->StopTrackingUsers(m_trackedUsersView); m_trackedUsersView = trackedUsers; m_trackedUsers.clear(); m_trackedUsers.insert(m_trackedUsersView.begin(), m_trackedUsersView.end()); m_users.clear(); // Register again to retrigger initialization graph->RegisterGroup(shared_from_this()); m_loaded = false; m_updated = true; return S_OK; } default: { assert(false); return S_OK; } } } bool XblSocialManagerUserGroup::IsMemberOfGroup(XblSocialManagerUser const* user) const noexcept { switch (type) { case XblSocialUserGroupType::FilterType: { if ((relationshipFilter == XblRelationshipFilter::Friends && !user->isFollowedByCaller) || (relationshipFilter == XblRelationshipFilter::Favorite && !user->isFavorite)) { return false; } switch (presenceFilter) { case XblPresenceFilter::All: { return true; } case XblPresenceFilter::AllOffline: { return user->presenceRecord.userState == XblPresenceUserState::Offline; } case XblPresenceFilter::AllOnline: { return user->presenceRecord.userState == XblPresenceUserState::Online; } case XblPresenceFilter::AllTitle: { return user->titleHistory.hasUserPlayed; } case XblPresenceFilter::TitleOffline: { return user->titleHistory.hasUserPlayed && user->presenceRecord.userState == XblPresenceUserState::Offline; } case XblPresenceFilter::TitleOnline: { return XblSocialManagerPresenceRecordIsUserPlayingTitle(&user->presenceRecord, AppConfig::Instance()->TitleId()); } case XblPresenceFilter::TitleOnlineOutsideTitle: { return user->titleHistory.hasUserPlayed && user->presenceRecord.userState == XblPresenceUserState::Online && !XblSocialManagerPresenceRecordIsUserPlayingTitle(&user->presenceRecord, AppConfig::Instance()->TitleId()); } default: { return false; } } return true; } case XblSocialUserGroupType::UserListType: { return m_trackedUsers.find(user->xboxUserId) != m_trackedUsers.end(); } default: { assert(false); return false; } } } ================================================ FILE: Source/Services/Social/Manager/social_manager_user_group.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "social_graph.h" #pragma once // An XblSocialManagerUserGroup is a projection of a local user's SocialGraph. // It can be created as either a filtered set of the local user's followees or from a list of remote XboxLive user xuids. struct XblSocialManagerUserGroup : public std::enable_shared_from_this { public: // Creates a user group and registers it with the local user's underlying SocialGraph static std::shared_ptr Make( std::shared_ptr socialGraph, XblPresenceFilter presenceFilter, XblRelationshipFilter relationshipFilter ) noexcept; static std::shared_ptr Make( std::shared_ptr socialGraph, Vector&& trackedUsers ) noexcept; ~XblSocialManagerUserGroup() noexcept; // Initializes the group based on the current state of the SocialGraph. void Initialize(const UnorderedMap>& profiles) noexcept; // Updates user and tracked user list based on graph changes since last DoWork call. // Input events vector contains events generated by graph changes since the previous DoWork call. // Output events vector will also contain SocialUserGroupLoaded/Updated events if applicable. void DoWork(_Inout_ Vector& events) noexcept; // Properties of the group XblSocialUserGroupType const type; XblPresenceFilter const presenceFilter{ XblPresenceFilter::Unknown }; XblRelationshipFilter const relationshipFilter{ XblRelationshipFilter::Unknown }; // Public vector view of users tracked by the group. // For Filter groups, this will be a filtered list of the Local user's full GraphView. // For List groups, this will be fixed set of users. const Vector& Users() noexcept; // List of Xuids tracked by the group. const Vector& TrackedUsers() noexcept; // Local user whose SocialGraph the group is backed by std::shared_ptr LocalUser() const noexcept; // Updates the list of tracked Xuids. Only valid for List based groups. HRESULT UpdateTrackedUsers(Vector&& newUserList) noexcept; private: XblSocialManagerUserGroup( std::shared_ptr socialGraph, XblPresenceFilter presenceFilter, XblRelationshipFilter relationshipFilter ) noexcept; XblSocialManagerUserGroup( std::shared_ptr socialGraph, Vector&& trackedUsers ) noexcept; bool IsMemberOfGroup(XblSocialManagerUser const* user) const noexcept; std::shared_ptr m_localUser; std::weak_ptr m_graph; // Public views of users/trackedUesrs Vector m_usersView; Vector m_trackedUsersView; UnorderedMap m_users; UnorderedSet m_trackedUsers; bool m_loaded{ false }; bool m_updated{ false }; mutable std::mutex m_mutex; }; ================================================ FILE: Source/Services/Social/profile_api.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "xsapi-c/profile_c.h" #include "profile_internal.h" #include "xbox_live_context_internal.h" using namespace xbox::services; using namespace xbox::services::system; using namespace xbox::services::social; STDAPI XblProfileGetUserProfileAsync( _In_ XblContextHandle xboxLiveContextHandle, _In_ uint64_t xboxUserId, _In_ XAsyncBlock* async ) XBL_NOEXCEPT try { return XblProfileGetUserProfilesAsync(xboxLiveContextHandle, &xboxUserId, 1, async); } CATCH_RETURN() STDAPI XblProfileGetUserProfilesAsync( _In_ XblContextHandle xboxLiveContextHandle, _In_ uint64_t* xboxUserIds, _In_ size_t xboxUserIdsCount, _In_ XAsyncBlock* async ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(xboxLiveContextHandle == nullptr || xboxUserIds == nullptr || xboxUserIdsCount == 0 || async == nullptr); VERIFY_XBL_INITIALIZED(); return RunAsync(async, __FUNCTION__, [ xboxLiveContext{ xboxLiveContextHandle->shared_from_this() }, xuids = xsapi_internal_vector(xboxUserIds, xboxUserIds + xboxUserIdsCount), profiles = xsapi_internal_vector{} ] (XAsyncOp op, const XAsyncProviderData* data) mutable { switch (op) { case XAsyncOp::DoWork: { RETURN_HR_IF_FAILED(xboxLiveContext->ProfileService()->GetUserProfiles( std::move(xuids), AsyncContext>>{ data->async->queue, [ &profiles, asyncBlock{ data->async } ] (Result> result) { if (Succeeded(result)) { profiles = result.ExtractPayload(); } XAsyncComplete(asyncBlock, result.Hresult(), profiles.size() * sizeof(XblUserProfile)); } })); return E_PENDING; } case XAsyncOp::GetResult: { auto profilesPtr = static_cast(data->buffer); for (auto& profile : profiles) { *profilesPtr++ = profile; } return S_OK; } default: { return S_OK; } } }); } CATCH_RETURN() STDAPI XblProfileGetUserProfilesForSocialGroupAsync( _In_ XblContextHandle xboxLiveContextHandle, _In_z_ const char* socialGroup, _In_ XAsyncBlock* async ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(xboxLiveContextHandle == nullptr || socialGroup == nullptr || async == nullptr); VERIFY_XBL_INITIALIZED(); return RunAsync(async, __FUNCTION__, [ xboxLiveContext{ xboxLiveContextHandle->shared_from_this() }, group = xsapi_internal_string{ socialGroup }, profiles = xsapi_internal_vector{} ] (XAsyncOp op, const XAsyncProviderData* data) mutable { switch (op) { case XAsyncOp::DoWork: { RETURN_HR_IF_FAILED(xboxLiveContext->ProfileService()->GetUserProfilesForSocialGroup( std::move(group), AsyncContext>>{ data->async->queue, [ &profiles, asyncBlock{ data->async } ] (Result> result) { if (Succeeded(result)) { profiles = result.ExtractPayload(); } XAsyncComplete(asyncBlock, result.Hresult(), profiles.size() * sizeof(XblUserProfile)); } })); return E_PENDING; } case XAsyncOp::GetResult: { auto profilesPtr = static_cast(data->buffer); for (auto& profile : profiles) { *profilesPtr++ = profile; } return S_OK; } default: { return S_OK; } } }); } CATCH_RETURN() STDAPI XblProfileGetUserProfilesResultCount( _In_ XAsyncBlock* async, _Out_ size_t* profileCount ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(async == nullptr || profileCount == nullptr); size_t bufferSize; auto hr = XAsyncGetResultSize(async, &bufferSize); if (SUCCEEDED(hr)) { *profileCount = bufferSize / sizeof(XblUserProfile); } return hr; } CATCH_RETURN() STDAPI XblProfileGetUserProfileResult( _In_ XAsyncBlock* async, _Out_ XblUserProfile* profile ) XBL_NOEXCEPT try { return XAsyncGetResult(async, nullptr, sizeof(XblUserProfile), profile, nullptr); } CATCH_RETURN() STDAPI XblProfileGetUserProfilesResult( _In_ XAsyncBlock* async, _In_ size_t profilesCount, _Out_writes_(profilesCount) XblUserProfile* profiles ) XBL_NOEXCEPT try { RETURN_HR_IF(profilesCount == 0, S_OK); RETURN_HR_INVALIDARGUMENT_IF_NULL(async); size_t actualProfilesCount = 0; auto hr = XblProfileGetUserProfilesResultCount(async, &actualProfilesCount); if (SUCCEEDED(hr)) { RETURN_HR_INVALIDARGUMENT_IF(actualProfilesCount > profilesCount); } hr = XAsyncGetResult(async, nullptr, profilesCount * sizeof(XblUserProfile), profiles, nullptr); return hr; } CATCH_RETURN() STDAPI XblProfileGetUserProfilesForSocialGroupResultCount( _In_ XAsyncBlock* async, _Out_ size_t* profileCount ) XBL_NOEXCEPT try { return XblProfileGetUserProfilesResultCount(async, profileCount); } CATCH_RETURN() STDAPI XblProfileGetUserProfilesForSocialGroupResult( _In_ XAsyncBlock* async, _In_ size_t profilesCount, _Out_writes_(profilesCount) XblUserProfile* profiles ) XBL_NOEXCEPT try { return XblProfileGetUserProfilesResult(async, profilesCount, profiles); } CATCH_RETURN() ================================================ FILE: Source/Services/Social/profile_internal.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "xsapi-c/profile_c.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_SOCIAL_CPP_BEGIN class ProfileService : public std::enable_shared_from_this { public: ProfileService( _In_ User&& user, _In_ std::shared_ptr xboxLiveContextSettings, _In_ std::shared_ptr appConfig ) noexcept; HRESULT GetUserProfiles( _In_ xsapi_internal_vector&& xuids, _In_ AsyncContext>> async ) const noexcept; HRESULT GetUserProfilesForSocialGroup( _In_ xsapi_internal_string&& socialGroup, _In_ AsyncContext>> async ) const noexcept; private: static Result> DeserializeUserProfiles( _In_ const JsonValue& json ) noexcept; static Result DeserializeUserProfile( _In_ const JsonValue& json ) noexcept; User m_user; std::shared_ptr m_xboxLiveContextSettings; std::shared_ptr m_appConfig; }; NAMESPACE_MICROSOFT_XBOX_SERVICES_SOCIAL_CPP_END ================================================ FILE: Source/Services/Social/profile_service.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "profile_internal.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_SOCIAL_CPP_BEGIN using namespace xbox::services; static const char* s_settings[] = { "AppDisplayName", "AppDisplayPicRaw", "GameDisplayName", "GameDisplayPicRaw", "Gamerscore", "Gamertag", "ModernGamertag", "ModernGamertagSuffix", "UniqueModernGamertag" }; ProfileService::ProfileService( _In_ User&& user, _In_ std::shared_ptr xboxLiveContextSettings, _In_ std::shared_ptr appConfig ) noexcept : m_user{ std::move(user) }, m_xboxLiveContextSettings{ std::move(xboxLiveContextSettings) }, m_appConfig{ std::move(appConfig) } { } HRESULT ProfileService::GetUserProfiles( _In_ xsapi_internal_vector&& xuids, _In_ AsyncContext>> async ) const noexcept { Result userResult = m_user.Copy(); RETURN_HR_IF_FAILED(userResult.Hresult()); auto httpCall = MakeShared(userResult.ExtractPayload()); RETURN_HR_IF_FAILED(httpCall->Init( m_xboxLiveContextSettings, "POST", XblHttpCall::BuildUrl("profile", "/users/batch/profile/settings"), xbox_live_api::get_user_profiles )); httpCall->SetHeader(CONTRACT_VERSION_HEADER, "2"); JsonDocument request(rapidjson::kObjectType); JsonValue userIdsJson(rapidjson::kArrayType); JsonUtils::SerializeVector(JsonUtils::JsonXuidSerializer, xuids, userIdsJson, request.GetAllocator()); request.AddMember("userIds", userIdsJson, request.GetAllocator()); JsonValue settingsArray(rapidjson::kArrayType); for (size_t i = 0; i < ARRAYSIZE(s_settings); ++i) { settingsArray.PushBack(JsonValue(s_settings[i], request.GetAllocator()).Move(), request.GetAllocator()); } request.AddMember("settings", settingsArray, request.GetAllocator()); httpCall->SetRequestBody(request); return httpCall->Perform({ async.Queue(), [ async ] (HttpResult result) { HRESULT hr{ Failed(result) ? result.Hresult() : result.Payload()->Result() }; if (FAILED(hr)) { return async.Complete(hr); } return async.Complete(DeserializeUserProfiles(result.Payload()->GetResponseBodyJson())); } }); } HRESULT ProfileService::GetUserProfilesForSocialGroup( _In_ xsapi_internal_string&& socialGroup, _In_ AsyncContext>> async ) const noexcept { xsapi_internal_stringstream pathAndQuery; pathAndQuery << "/users/me/profile/settings/people/"; pathAndQuery << socialGroup; pathAndQuery << "?settings="; for (uint32_t i = 0; i < ARRAYSIZE(s_settings); ++i) { if (i > 0) { pathAndQuery << ","; } pathAndQuery << s_settings[i]; } Result userResult = m_user.Copy(); RETURN_HR_IF_FAILED(userResult.Hresult()); auto httpCall = MakeShared(userResult.ExtractPayload()); RETURN_HR_IF_FAILED(httpCall->Init( m_xboxLiveContextSettings, "GET", XblHttpCall::BuildUrl("profile", pathAndQuery.str()), xbox_live_api::get_user_profiles_for_social_group )); httpCall->SetHeader(CONTRACT_VERSION_HEADER, "2"); return httpCall->Perform({ async.Queue(), [ async ] (HttpResult result) { HRESULT hr{ Failed(result) ? result.Hresult() : result.Payload()->Result() }; if (FAILED(hr)) { return async.Complete(hr); } return async.Complete(DeserializeUserProfiles(result.Payload()->GetResponseBodyJson())); } }); } Result> ProfileService::DeserializeUserProfiles( _In_ const JsonValue& json ) noexcept { if (json.IsNull()) { return WEB_E_INVALID_JSON_STRING; } HRESULT errc = S_OK; xsapi_internal_vector profilesVector; RETURN_HR_IF_FAILED( JsonUtils::ExtractJsonVector( DeserializeUserProfile, json, "profileUsers", profilesVector, true )); return Result>{ profilesVector, errc }; } Result ProfileService::DeserializeUserProfile( _In_ const JsonValue& json ) noexcept { if (json.IsNull()) { return WEB_E_INVALID_JSON_STRING; } XblUserProfile profile{}; HRESULT errc = S_OK; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonXuid(json, "id", profile.xboxUserId, true)); if (json.IsObject() && json.HasMember("settings")) { const JsonValue& settingsJson = json["settings"]; if(settingsJson.IsArray()) for (const auto& setting : settingsJson.GetArray()) { xsapi_internal_string id; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(setting, "id", id, true)); xsapi_internal_string value; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(setting, "value", value, true)); if (id == "AppDisplayName") { utils::strcpy(profile.appDisplayName, sizeof(profile.appDisplayName), value.data()); } else if (id == "AppDisplayPicRaw") { utils::strcpy(profile.appDisplayPictureResizeUri, sizeof(profile.appDisplayPictureResizeUri), value.data()); } else if (id == "GameDisplayName") { utils::strcpy(profile.gameDisplayName, sizeof(profile.gameDisplayName), value.data()); } else if (id == "GameDisplayPicRaw") { utils::strcpy(profile.gameDisplayPictureResizeUri, sizeof(profile.gameDisplayPictureResizeUri), value.data()); } else if (id == "Gamerscore") { utils::strcpy(profile.gamerscore, sizeof(profile.gamerscore), value.data()); } else if (id == "Gamertag") { utils::strcpy(profile.gamertag, sizeof(profile.gamertag), value.data()); } else if (id == "ModernGamertag") { utils::strcpy(profile.modernGamertag, sizeof(profile.modernGamertag), value.data()); } else if (id == "ModernGamertagSuffix") { utils::strcpy(profile.modernGamertagSuffix, sizeof(profile.modernGamertagSuffix), value.data()); } else if (id == "UniqueModernGamertag") { utils::strcpy(profile.uniqueModernGamertag, sizeof(profile.uniqueModernGamertag), value.data()); } } } else { //required return WEB_E_INVALID_JSON_STRING; } return Result{ std::move(profile), errc }; } NAMESPACE_MICROSOFT_XBOX_SERVICES_SOCIAL_CPP_END ================================================ FILE: Source/Services/Social/reputation_feedback_request.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "social_internal.h" #include "multiplayer_internal.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_SOCIAL_CPP_BEGIN ReputationFeedbackRequest::ReputationFeedbackRequest( _In_ uint64_t xuid, _In_ XblReputationFeedbackType feedbackType, _In_opt_ const XblMultiplayerSessionReference* sessionReference, _In_z_ const char* reasonMessage, _In_opt_z_ const char* evidenceResourceId ) { xsapi_internal_stringstream pathAndQuery; pathAndQuery << "/users/xuid(" << xuid << ")/feedback"; m_pathAndQuery = pathAndQuery.str(); m_requestBody.SetObject(); JsonDocument::AllocatorType& allocator = m_requestBody.GetAllocator(); if (sessionReference) { JsonValue sessionRefJson; multiplayer::Serializers::SerializeSessionReference(*sessionReference, sessionRefJson, allocator); m_requestBody.AddMember("sessionRef", sessionRefJson, allocator); } else { m_requestBody.AddMember("sessionRef", JsonValue(rapidjson::kNullType), allocator); } xsapi_internal_string feedbackTypeString = ReputationFeedbackTypeToString(feedbackType); m_requestBody.AddMember("feedbackType", JsonValue(feedbackTypeString.c_str(), allocator).Move(), allocator); m_requestBody.AddMember("textReason", JsonValue(reasonMessage, allocator).Move(), allocator); if (evidenceResourceId) { m_requestBody.AddMember("evidenceId", JsonValue(evidenceResourceId, allocator).Move(), allocator); } else { m_requestBody.AddMember("evidenceId", JsonValue(rapidjson::kNullType), allocator); } } ReputationFeedbackRequest::ReputationFeedbackRequest( _In_ const XblReputationFeedbackItem* items, _In_ size_t itemsCount ) : m_pathAndQuery{ "/users/batchtitlefeedback" } { m_requestBody.SetObject(); JsonDocument::AllocatorType& allocator = m_requestBody.GetAllocator(); JsonValue itemArrayJson(rapidjson::kArrayType); for (size_t i = 0; i < itemsCount; ++i) { // Example: // // "targetXuid": "33445566778899", // "titleId" : "6487", // "sessionRef" : // { // "scid": "1234-1234-471F-B696-07B61F09EC20", // "templateName" : "CaptureFlag5", // "name" : "Example556932", // }, // "feedbackType": "FairPlayKillsTeammates", // "textReason" : "Killed 19 team members in a single session", // "evidenceId" : null JsonValue itemJson(rapidjson::kObjectType); itemJson.AddMember("targetXuid", JsonValue(utils::uint64_to_internal_string(items[i].xboxUserId).c_str(), allocator).Move(), allocator); itemJson.AddMember("titleId", JsonValue(rapidjson::kNullType), allocator); if (items[i].sessionReference) { JsonValue sessionRefJson; multiplayer::Serializers::SerializeSessionReference(*items[i].sessionReference, sessionRefJson, allocator); itemJson.AddMember("sessionRef", sessionRefJson, allocator); } else { itemJson.AddMember("sessionRef", JsonValue(rapidjson::kNullType), allocator); } xsapi_internal_string feedbackTypeString = ReputationFeedbackTypeToString(items[i].feedbackType); itemJson.AddMember("feedbackType", JsonValue(feedbackTypeString.c_str(), allocator).Move(), allocator); itemJson.AddMember("textReason", JsonValue(items[i].reasonMessage, allocator).Move(), allocator); if (items[i].evidenceResourceId) { itemJson.AddMember("evidenceId", JsonValue(items[i].evidenceResourceId, allocator).Move(), allocator); } else { itemJson.AddMember("evidenceId", JsonValue(rapidjson::kNullType), allocator); } itemArrayJson.PushBack(itemJson, allocator); } m_requestBody.AddMember("items", itemArrayJson, allocator); } ReputationFeedbackRequest::ReputationFeedbackRequest(const ReputationFeedbackRequest& other) { m_pathAndQuery = other.m_pathAndQuery; JsonUtils::CopyFrom(m_requestBody, other.m_requestBody); } const xsapi_internal_string& ReputationFeedbackRequest::PathAndQuery() const noexcept { return m_pathAndQuery; } const JsonValue& ReputationFeedbackRequest::Body() const noexcept { return m_requestBody; } xsapi_internal_string ReputationFeedbackRequest::ReputationFeedbackTypeToString( XblReputationFeedbackType feedbackType ) { switch (feedbackType) { case XblReputationFeedbackType::FairPlayKillsTeammates: return "FairPlayKillsTeammates"; case XblReputationFeedbackType::FairPlayCheater: return "FairPlayCheater"; case XblReputationFeedbackType::FairPlayTampering: return "FairPlayTampering"; case XblReputationFeedbackType::FairPlayQuitter: return "FairPlayQuitter"; case XblReputationFeedbackType::FairPlayKicked : return "FairPlayKicked"; case XblReputationFeedbackType::CommunicationsInappropriateVideo: return "CommsInappropriateVideo"; case XblReputationFeedbackType::CommunicationsAbusiveVoice: return "CommsAbusiveVoice"; case XblReputationFeedbackType::InappropriateUserGeneratedContent: return "UserContentInappropriateUGC"; case XblReputationFeedbackType::PositiveSkilledPlayer: return "PositiveSkilledPlayer"; case XblReputationFeedbackType::PositiveHelpfulPlayer: return "PositiveHelpfulPlayer"; case XblReputationFeedbackType::PositiveHighQualityUserGeneratedContent: return "PositiveHighQualityUGC"; case XblReputationFeedbackType::CommsPhishing: return "CommsPhishing"; case XblReputationFeedbackType::CommsPictureMessage: return "CommsPictureMessage"; case XblReputationFeedbackType::CommsSpam: return "CommsSpam"; case XblReputationFeedbackType::CommsTextMessage: return "CommsTextMessage"; case XblReputationFeedbackType::CommsVoiceMessage: return "CommsVoiceMessage"; case XblReputationFeedbackType::FairPlayConsoleBanRequest: return "FairPlayConsoleBanRequest"; case XblReputationFeedbackType::FairPlayIdler: return "FairPlayIdler"; case XblReputationFeedbackType::FairPlayUserBanRequest: return "FairPlayUserBanRequest"; case XblReputationFeedbackType::UserContentGamerpic: return "UserContentGamertag"; case XblReputationFeedbackType::UserContentPersonalInfo: return "UserContentPersonalInfo"; case XblReputationFeedbackType::FairPlayUnsporting: return "FairplayUnsporting"; case XblReputationFeedbackType::FairPlayLeaderboardCheater: return "FairplayLeaderboardCheater"; default: { assert(false); return xsapi_internal_string{}; } } } NAMESPACE_MICROSOFT_XBOX_SERVICES_SOCIAL_CPP_END ================================================ FILE: Source/Services/Social/reputation_service.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "social_internal.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_SOCIAL_CPP_BEGIN ReputationService::ReputationService( _In_ User&& user, _In_ std::shared_ptr xboxLiveContextSettings ) noexcept : m_user{ std::move(user) }, m_xboxLiveContextSettings{ std::move(xboxLiveContextSettings) } { } HRESULT ReputationService::SubmitFeedback( const ReputationFeedbackRequest& request, AsyncContext async ) const noexcept { Result userResult = m_user.Copy(); RETURN_HR_IF_FAILED(userResult.Hresult()); auto httpCall = MakeShared(userResult.ExtractPayload()); RETURN_HR_IF_FAILED(httpCall->Init( m_xboxLiveContextSettings, "POST", XblHttpCall::BuildUrl("reputation", request.PathAndQuery()), xbox_live_api::submit_reputation_feedback )); httpCall->SetRetryAllowed(false); httpCall->SetXblServiceContractVersion(101); httpCall->SetRequestBody(request.Body()); return httpCall->Perform({ async.Queue(), [async](HttpResult httpResult) { async.Complete(Failed(httpResult) ? httpResult.Hresult() : httpResult.Payload()->Result()); } }); } NAMESPACE_MICROSOFT_XBOX_SERVICES_SOCIAL_CPP_END ================================================ FILE: Source/Services/Social/social_api.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "social_internal.h" #include "xbox_live_context_internal.h" #include "real_time_activity_subscription.h" using namespace xbox::services; using namespace xbox::services::social; STDAPI XblSocialGetSocialRelationshipsAsync( _In_ XblContextHandle xboxLiveContextHandle, _In_ uint64_t xuid, _In_ XblSocialRelationshipFilter filter, _In_ size_t startIndex, _In_ size_t maxItems, _In_ XAsyncBlock* async ) XBL_NOEXCEPT try { VERIFY_XBL_INITIALIZED(); RETURN_HR_INVALIDARGUMENT_IF(xboxLiveContextHandle == nullptr || async == nullptr); return RunAsync(async, __FUNCTION__, [ xboxLiveContext{ xboxLiveContextHandle->shared_from_this() }, xuid, filter, startIndex, maxItems, socialRelationshipResult = std::shared_ptr{ nullptr } ] (XAsyncOp op, const XAsyncProviderData* data) mutable { switch (op) { case XAsyncOp::DoWork: { RETURN_HR_IF_FAILED(xboxLiveContext->SocialService()->GetSocialRelationships(xuid, filter, startIndex, maxItems, { data->async->queue, [ &socialRelationshipResult, asyncBlock{ data->async } ] (Result> result) { if (Succeeded(result)) { socialRelationshipResult = result.ExtractPayload(); } XAsyncComplete(asyncBlock, result.Hresult(), sizeof(XblSocialRelationshipResultHandle)); } })); return E_PENDING; } case XAsyncOp::GetResult: { auto resultHandle = static_cast(data->buffer); socialRelationshipResult->AddRef(); *resultHandle = socialRelationshipResult.get(); return S_OK; } default: { return S_OK; } } }); } CATCH_RETURN() STDAPI XblSocialGetSocialRelationshipsResult( _In_ XAsyncBlock* async, _Out_ XblSocialRelationshipResultHandle* handle ) XBL_NOEXCEPT try { return XAsyncGetResult(async, nullptr, sizeof(XblSocialRelationshipResultHandle), handle, nullptr); } CATCH_RETURN() STDAPI XblSocialRelationshipResultGetRelationships( _In_ XblSocialRelationshipResultHandle resultHandle, _Out_ const XblSocialRelationship** relationships, _Out_ size_t* relationshipsCount ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(resultHandle == nullptr || relationships == nullptr || relationshipsCount == nullptr); *relationships = resultHandle->SocialRelationships().data(); *relationshipsCount = resultHandle->SocialRelationships().size(); return S_OK; } CATCH_RETURN() STDAPI XblSocialRelationshipResultHasNext( _In_ XblSocialRelationshipResultHandle resultHandle, _Out_ bool* hasNext ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(resultHandle == nullptr || hasNext == nullptr); *hasNext = resultHandle->HasNext(); return S_OK; } CATCH_RETURN() STDAPI XblSocialRelationshipResultGetTotalCount( _In_ XblSocialRelationshipResultHandle resultHandle, _Out_ size_t* totalCount ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(resultHandle == nullptr || totalCount == nullptr); *totalCount = resultHandle->TotalCount(); return S_OK; } CATCH_RETURN() STDAPI XblSocialRelationshipResultGetNextAsync( _In_ XblContextHandle xboxLiveContext, _In_ XblSocialRelationshipResultHandle resultHandle, _In_ size_t maxItems, _In_ XAsyncBlock* async ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(xboxLiveContext == nullptr || resultHandle == nullptr || async == nullptr); return XblSocialGetSocialRelationshipsAsync(xboxLiveContext, xboxLiveContext->Xuid(), resultHandle->Filter, resultHandle->ContinuationSkip, maxItems, async); } CATCH_RETURN() STDAPI XblSocialRelationshipResultGetNextResult( _In_ XAsyncBlock* async, _Out_ XblSocialRelationshipResultHandle* handle ) XBL_NOEXCEPT try { return XAsyncGetResult(async, nullptr, sizeof(XblSocialRelationshipResultHandle), handle, nullptr); } CATCH_RETURN() STDAPI XblSocialRelationshipResultDuplicateHandle( _In_ XblSocialRelationshipResultHandle handle, _Out_ XblSocialRelationshipResultHandle* duplicatedHandle ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(handle == nullptr || duplicatedHandle == nullptr); handle->AddRef(); *duplicatedHandle = handle; return S_OK; } CATCH_RETURN() STDAPI_(void) XblSocialRelationshipResultCloseHandle( _In_ XblSocialRelationshipResultHandle handle ) XBL_NOEXCEPT try { if (handle) { handle->DecRef(); } } CATCH_RETURN_WITH(;) STDAPI XblSocialSubscribeToSocialRelationshipChange( _In_ XblContextHandle xboxLiveContext, _In_ uint64_t xuid, _Out_ XblRealTimeActivitySubscriptionHandle* subscriptionHandle ) XBL_NOEXCEPT try { UNREFERENCED_PARAMETER(xboxLiveContext); UNREFERENCED_PARAMETER(xuid); RETURN_HR_INVALIDARGUMENT_IF_NULL(subscriptionHandle); *subscriptionHandle = Make(); return S_OK; } CATCH_RETURN() STDAPI XblSocialUnsubscribeFromSocialRelationshipChange( _In_ XblContextHandle xboxLiveContext, _In_ XblRealTimeActivitySubscriptionHandle subscriptionHandle ) XBL_NOEXCEPT try { UNREFERENCED_PARAMETER(xboxLiveContext); RETURN_HR_INVALIDARGUMENT_IF_NULL(subscriptionHandle); Delete(subscriptionHandle); return S_OK; } CATCH_RETURN() STDAPI_(XblFunctionContext) XblSocialAddSocialRelationshipChangedHandler( _In_ XblContextHandle xboxLiveContext, _In_ XblSocialRelationshipChangedHandler handler, _In_opt_ void* context ) XBL_NOEXCEPT try { // TODO really should be returning HRESULT E_INVALIDARG here if (xboxLiveContext == nullptr || handler == nullptr) { return XblFunctionContext{ 0 }; } return xboxLiveContext->SocialService()->AddSocialRelationshipChangedHandler( [ handler, context ] (const XblSocialRelationshipChangeEventArgs& args) { try { handler(&args, context); } catch (...) { LOGS_ERROR << __FUNCTION__ << ": exception in client handler!"; } }); } CATCH_RETURN() STDAPI XblSocialRemoveSocialRelationshipChangedHandler( _In_ XblContextHandle xboxLiveContext, _In_ XblFunctionContext handlerFunctionContext ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(xboxLiveContext); xboxLiveContext->SocialService()->RemoveSocialRTAHandler(handlerFunctionContext); return S_OK; } CATCH_RETURN() STDAPI XblSocialAddFriendRequestCountChangedHandler( _In_ XblContextHandle xboxLiveContext, _In_ XblSocialIncomingFriendRequestCountChangedHandler handler, _In_opt_ void* handlerContext, _Out_ XblFunctionContext* handlerFunctionContext ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(xboxLiveContext == nullptr); RETURN_HR_INVALIDARGUMENT_IF(handler == nullptr); RETURN_HR_INVALIDARGUMENT_IF(handlerFunctionContext == nullptr); *handlerFunctionContext = xboxLiveContext->SocialService()->AddFriendRequestCountChangedHandler( [ handler, handlerContext ] (const XblSocialFriendRequestCountChangedEventArgs& args) { try { handler(&args, handlerContext); } catch (...) { LOGS_ERROR << __FUNCTION__ << ": exception in client handler!"; } }); return S_OK; } CATCH_RETURN() STDAPI XblSocialRemoveFriendRequestCountChangedHandler( _In_ XblContextHandle xboxLiveContext, _In_ XblFunctionContext handlerFunctionContext ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(xboxLiveContext); xboxLiveContext->SocialService()->RemoveSocialRTAHandler(handlerFunctionContext); return S_OK; } CATCH_RETURN() HRESULT SubmitReputationFeedback( _In_ XblContextHandle xboxLiveContextHandle, _In_ ReputationFeedbackRequest r, _In_ XAsyncBlock* async ) { assert(async); return RunAsync(async, __FUNCTION__, [ xboxLiveContext{ xboxLiveContextHandle->shared_from_this() }, request{ std::move(r) } ] (XAsyncOp op, const XAsyncProviderData* data) { switch (op) { case XAsyncOp::DoWork: { RETURN_HR_IF_FAILED(xboxLiveContext->ReputationService()->SubmitFeedback(request, data->async)); return E_PENDING; } default: { return S_OK; } } }); } STDAPI XblSocialSubmitReputationFeedbackAsync( _In_ XblContextHandle xboxLiveContextHandle, _In_ uint64_t xuid, _In_ XblReputationFeedbackType feedbackType, _In_opt_ const XblMultiplayerSessionReference* sessionReference, _In_z_ const char* reasonMessage, _In_opt_z_ const char* evidenceResourceId, _In_ XAsyncBlock* async ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(xboxLiveContextHandle == nullptr || reasonMessage == nullptr || async == nullptr); VERIFY_XBL_INITIALIZED(); return SubmitReputationFeedback( xboxLiveContextHandle, ReputationFeedbackRequest{ xuid, feedbackType, sessionReference, reasonMessage, evidenceResourceId }, async ); } CATCH_RETURN() STDAPI XblSocialSubmitBatchReputationFeedbackAsync( _In_ XblContextHandle xboxLiveContextHandle, _In_ const XblReputationFeedbackItem* feedbackItems, _In_ size_t feedbackItemsCount, _In_ XAsyncBlock* async ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(xboxLiveContextHandle == nullptr || feedbackItems == nullptr || async == nullptr); VERIFY_XBL_INITIALIZED(); return SubmitReputationFeedback( xboxLiveContextHandle, ReputationFeedbackRequest{ feedbackItems, feedbackItemsCount }, async ); } CATCH_RETURN() ================================================ FILE: Source/Services/Social/social_internal.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "xsapi-c/social_c.h" #include "real_time_activity_subscription.h" #include "string_array.h" struct XblSocialRelationshipResult : public xbox::services::RefCounter, public std::enable_shared_from_this { public: XblSocialRelationshipResult() noexcept = default; XblSocialRelationshipResult(const XblSocialRelationshipResult&) = delete; XblSocialRelationshipResult& operator=(XblSocialRelationshipResult) = delete; static xbox::services::Result> Deserialize(const JsonValue& json); const xsapi_internal_vector& SocialRelationships() const noexcept; // Service paging metadata bool HasNext() const noexcept; size_t TotalCount() const noexcept; size_t ContinuationSkip{ 0 }; XblSocialRelationshipFilter Filter{ XblSocialRelationshipFilter::All }; private: std::shared_ptr GetSharedThis(); size_t m_totalCount{ 0 }; xsapi_internal_vector m_socialRelationships; xsapi_internal_vector m_socialNetworks; }; NAMESPACE_MICROSOFT_XBOX_SERVICES_SOCIAL_CPP_BEGIN namespace legacy { class social_group_constants { public: /// /// Returns Favorites constant string /// static const char* favorite() { return "Favorites"; } /// /// Returns People constant string /// static const char* people() { return "People"; } }; } typedef Callback SocialRelationshipChangedHandler; typedef Callback FriendRequestCountChangedHandler; class SocialRelationshipChangeSubscription : public real_time_activity::Subscription { public: SocialRelationshipChangeSubscription(_In_ uint64_t xuid) noexcept; XblFunctionContext AddHandler(SocialRelationshipChangedHandler handler) noexcept; XblFunctionContext AddHandler(FriendRequestCountChangedHandler handler) noexcept; size_t RemoveHandler(XblFunctionContext token) noexcept; protected: void OnEvent(const JsonValue& data) noexcept override; private: uint64_t m_xuid; Map m_handlers; Map m_friendRequestCountChangedHandlers; XblFunctionContext m_nextHandlerToken{ 1 }; mutable std::mutex m_lock; }; class SocialService : public std::enable_shared_from_this { public: SocialService( _In_ User&& user, _In_ std::shared_ptr xboxLiveContextSettings, _In_ std::shared_ptr rtaManager ) noexcept; ~SocialService() noexcept; XblFunctionContext AddSocialRelationshipChangedHandler( _In_ SocialRelationshipChangedHandler handler ) noexcept; XblFunctionContext AddFriendRequestCountChangedHandler( _In_ FriendRequestCountChangedHandler handler ) noexcept; void RemoveSocialRTAHandler( _In_ XblFunctionContext token ) noexcept; HRESULT GetSocialRelationships( _In_ uint64_t xuid, _In_ XblSocialRelationshipFilter filter, _In_ size_t startIndex, _In_ size_t maxItems, _In_ AsyncContext>> async ) const noexcept; private: static xsapi_internal_string SocialRelationshipFilterToString( _In_ XblSocialRelationshipFilter filter ) noexcept; User m_user; std::shared_ptr m_xboxLiveContextSettings; std::shared_ptr m_rtaManager; std::shared_ptr m_socialRelationshipChangedSubscription; mutable std::mutex m_lock; }; class ReputationFeedbackRequest { public: ReputationFeedbackRequest( _In_ uint64_t xuid, _In_ XblReputationFeedbackType feedbackType, _In_opt_ const XblMultiplayerSessionReference* sessionReference, _In_z_ const char* reasonMessage, _In_opt_z_ const char* evidenceResourceId ); ReputationFeedbackRequest( _In_ const XblReputationFeedbackItem* items, _In_ size_t itemsCount ); ReputationFeedbackRequest(const ReputationFeedbackRequest& other); const xsapi_internal_string& PathAndQuery() const noexcept; const JsonValue& Body() const noexcept; static xsapi_internal_string ReputationFeedbackTypeToString( XblReputationFeedbackType feedbackType ); private: xsapi_internal_string m_pathAndQuery; JsonDocument m_requestBody; }; class ReputationService : public std::enable_shared_from_this { public: ReputationService( _In_ User&& user, _In_ std::shared_ptr xboxLiveContextSettings ) noexcept; HRESULT SubmitFeedback( const ReputationFeedbackRequest& request, AsyncContext async ) const noexcept; private: User m_user; std::shared_ptr m_xboxLiveContextSettings; }; NAMESPACE_MICROSOFT_XBOX_SERVICES_SOCIAL_CPP_END ================================================ FILE: Source/Services/Social/social_relationship_change_subscription.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "social_internal.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_SOCIAL_CPP_BEGIN SocialRelationshipChangeSubscription::SocialRelationshipChangeSubscription( _In_ uint64_t xuid ) noexcept : m_xuid{ xuid } { Stringstream uri; uri << "http://social.xboxlive.com/users/xuid(" << m_xuid << ")/friends"; m_resourceUri = uri.str(); } XblFunctionContext SocialRelationshipChangeSubscription::AddHandler( SocialRelationshipChangedHandler handler ) noexcept { std::lock_guard lock{ m_lock }; m_handlers[m_nextHandlerToken] = std::move(handler); return m_nextHandlerToken++; } XblFunctionContext SocialRelationshipChangeSubscription::AddHandler( FriendRequestCountChangedHandler handler ) noexcept { std::lock_guard lock{ m_lock }; m_friendRequestCountChangedHandlers[m_nextHandlerToken] = std::move(handler); return m_nextHandlerToken++; } size_t SocialRelationshipChangeSubscription::RemoveHandler( XblFunctionContext token ) noexcept { std::lock_guard lock{ m_lock }; m_handlers.erase(token); m_friendRequestCountChangedHandlers.erase(token); return m_handlers.size() + m_friendRequestCountChangedHandlers.size(); } void SocialRelationshipChangeSubscription::OnEvent( const JsonValue& data ) noexcept { // Payload format http://xboxwiki/wiki/RTA%3AEVENT#People // [, , {"NotificationType":"Added","Xuids":["2533274964271787"]}] String notificationTypeString; XblSocialNotificationType notificationType; Vector xuids; HRESULT hr = JsonUtils::ExtractJsonString(data, "NotificationType", notificationTypeString); if (SUCCEEDED(hr)) { notificationType = EnumValue(notificationTypeString.data()); switch (notificationType) { // Special handling for IncomingFriendRequestCountChanged as that payload is structured differently // and results in a different event handler firing case XblSocialNotificationType::IncomingFriendRequestCountChanged: { size_t incomingFriendRequestCount{}; hr = JsonUtils::ExtractJsonSizeT(data, "Count", incomingFriendRequestCount, true); if (SUCCEEDED(hr)) { XblSocialFriendRequestCountChangedEventArgs args { m_xuid, incomingFriendRequestCount }; std::unique_lock lock{ m_lock }; auto handlers{ m_friendRequestCountChangedHandlers }; lock.unlock(); for (auto& handler : handlers) { handler.second(args); } } else { LOGS_DEBUG << __FUNCTION__ << ": Ignoring malformed event"; } break; } // Everything else can be handled the same case XblSocialNotificationType::Added: case XblSocialNotificationType::Changed: case XblSocialNotificationType::Removed: { hr = JsonUtils::ExtractJsonVector( JsonUtils::JsonXuidExtractor, data, "Xuids", xuids, true ); if (SUCCEEDED(hr)) { XblSocialRelationshipChangeEventArgs args { m_xuid, notificationType, xuids.data(), xuids.size() }; std::unique_lock lock{ m_lock }; auto handlers{ m_handlers }; lock.unlock(); for (auto& handler : handlers) { handler.second(args); } } else { LOGS_DEBUG << __FUNCTION__ << ": Ignoring malformed event"; } break; } case XblSocialNotificationType::Unknown: default: { LOGS_DEBUG << __FUNCTION__ << ": Ignoring malformed event"; break; } } } else { LOGS_DEBUG << __FUNCTION__ << ": Ignoring malformed event"; } } NAMESPACE_MICROSOFT_XBOX_SERVICES_SOCIAL_CPP_END ================================================ FILE: Source/Services/Social/social_relationship_result.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "social_internal.h" using namespace xbox::services; using namespace xbox::services::social; Result> XblSocialRelationshipResult::Deserialize( const JsonValue& json ) { if (json.IsNull()) { return WEB_E_INVALID_JSON_STRING; } auto result{ MakeShared() }; HRESULT errc = S_OK; uint64_t totalCount = 0; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonInt(json, "totalCount", totalCount, false)); result->m_totalCount = static_cast(totalCount); if (json.IsObject() && json.HasMember("people")) { const JsonValue& peopleJson = json["people"]; if (peopleJson.IsArray()) { result->m_socialRelationships.reserve(peopleJson.Size()); result->m_socialNetworks.reserve(peopleJson.Size()); for (const JsonValue& person : peopleJson.GetArray()) { if (person.IsObject()) { XblSocialRelationship relationship{}; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonXuid(person, "xuid", relationship.xboxUserId, true)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonBool(person, "isFavorite", relationship.isFavorite)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonBool(person, "isFriend", relationship.isFriend)); // isFavorite should reflect the vlaues of 'isFavorite' and isFriend from the service response relationship.isFavorite = relationship.isFavorite && relationship.isFriend; // Shimming isFriend response into isFollowingCaller for compatibility purposes. // This does not reflect a follower/following relationship. relationship.isFollowingCaller = relationship.isFriend; xsapi_internal_vector socialNetworksInternalVector; JsonUtils::ExtractJsonVector(JsonUtils::JsonStringExtractor, person, "socialNetworks", socialNetworksInternalVector, false); UTF8StringArray socialNetworks(socialNetworksInternalVector); relationship.socialNetworks = socialNetworks.Data(); relationship.socialNetworksCount = socialNetworks.Size(); result->m_socialRelationships.push_back(std::move(relationship)); result->m_socialNetworks.push_back(std::move(socialNetworks)); } } } } return Result>{ result, errc }; } const xsapi_internal_vector& XblSocialRelationshipResult::SocialRelationships() const noexcept { return m_socialRelationships; } bool XblSocialRelationshipResult::HasNext() const noexcept { return ContinuationSkip < m_totalCount; } size_t XblSocialRelationshipResult::TotalCount() const noexcept { return m_totalCount; } std::shared_ptr XblSocialRelationshipResult::GetSharedThis() { return shared_from_this(); } ================================================ FILE: Source/Services/Social/social_service.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "social_internal.h" #include "real_time_activity_manager.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_SOCIAL_CPP_BEGIN SocialService::SocialService( _In_ User&& user, _In_ std::shared_ptr xboxLiveContextSettings, _In_ std::shared_ptr rtaManager ) noexcept : m_user{ std::move(user) }, m_xboxLiveContextSettings{ std::move(xboxLiveContextSettings) }, m_rtaManager{ std::move(rtaManager) } { } SocialService::~SocialService() noexcept { if (m_socialRelationshipChangedSubscription) { m_rtaManager->RemoveSubscription(m_user, m_socialRelationshipChangedSubscription); } } XblFunctionContext SocialService::AddSocialRelationshipChangedHandler( _In_ SocialRelationshipChangedHandler handler ) noexcept { std::lock_guard lock{ m_lock }; if (!m_socialRelationshipChangedSubscription) { m_socialRelationshipChangedSubscription = MakeShared(m_user.Xuid()); m_rtaManager->AddSubscription(m_user, m_socialRelationshipChangedSubscription); } return m_socialRelationshipChangedSubscription->AddHandler(std::move(handler)); } XblFunctionContext SocialService::AddFriendRequestCountChangedHandler( _In_ FriendRequestCountChangedHandler handler ) noexcept { std::lock_guard lock{ m_lock }; if (!m_socialRelationshipChangedSubscription) { m_socialRelationshipChangedSubscription = MakeShared(m_user.Xuid()); m_rtaManager->AddSubscription(m_user, m_socialRelationshipChangedSubscription); } return m_socialRelationshipChangedSubscription->AddHandler(std::move(handler)); } void SocialService::RemoveSocialRTAHandler( _In_ XblFunctionContext token ) noexcept { std::lock_guard lock{ m_lock }; if (m_socialRelationshipChangedSubscription) { if (m_socialRelationshipChangedSubscription->RemoveHandler(token) == 0) { m_rtaManager->RemoveSubscription(m_user, m_socialRelationshipChangedSubscription); m_socialRelationshipChangedSubscription.reset(); } } } HRESULT SocialService::GetSocialRelationships( _In_ uint64_t xuid, _In_ XblSocialRelationshipFilter filter, _In_ size_t startIndex, _In_ size_t maxItems, _In_ AsyncContext>> async ) const noexcept { xsapi_internal_stringstream subpath; subpath << "/users/xuid("; subpath << xuid; subpath << ")/people"; xsapi_internal_string nextDelimiter = "?"; subpath << nextDelimiter; subpath << "view="; subpath << SocialRelationshipFilterToString(filter); nextDelimiter = "&"; if (startIndex > 0) { subpath << nextDelimiter; subpath << "startIndex="; subpath << startIndex; nextDelimiter = "&"; } if (maxItems > 0) { subpath << nextDelimiter; subpath << "maxItems="; subpath << maxItems; nextDelimiter = "&"; } Result userResult = m_user.Copy(); RETURN_HR_IF_FAILED(userResult.Hresult()); auto httpCall = MakeShared(userResult.ExtractPayload()); RETURN_HR_IF_FAILED(httpCall->Init( m_xboxLiveContextSettings, "GET", XblHttpCall::BuildUrl("social", subpath.str()), xbox_live_api::get_social_relationships )); httpCall->SetXblServiceContractVersion(3); return httpCall->Perform({ async.Queue(), [ async, filter, startIndex ] (HttpResult httpResult) { HRESULT hr{ Failed(httpResult) ? httpResult.Hresult() : httpResult.Payload()->Result() }; if (FAILED(hr)) { return async.Complete(hr); } else { auto result{ XblSocialRelationshipResult::Deserialize(httpResult.Payload()->GetResponseBodyJson()) }; if (Succeeded(result)) { result.Payload()->ContinuationSkip = startIndex + result.Payload()->SocialRelationships().size(); result.Payload()->Filter = filter; } return async.Complete(result); } } }); } xsapi_internal_string SocialService::SocialRelationshipFilterToString( _In_ XblSocialRelationshipFilter filter ) noexcept { switch (filter) { case XblSocialRelationshipFilter::Favorite: { return "Favorite"; } case XblSocialRelationshipFilter::LegacyXboxLiveFriends: { return "LegacyXboxLiveFriends"; } case XblSocialRelationshipFilter::All: default: { return "FriendList"; } } } NAMESPACE_MICROSOFT_XBOX_SERVICES_SOCIAL_CPP_END ================================================ FILE: Source/Services/Stats/requested_statistics.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "user_statistics_internal.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_USERSTATISTICS_CPP_BEGIN RequestedStatistics::RequestedStatistics() { } RequestedStatistics::RequestedStatistics( _In_ xsapi_internal_string serviceConfigurationId, _In_ xsapi_internal_vector statistics ) : m_serviceConfigurationId(serviceConfigurationId), m_statistics(statistics) { } RequestedStatistics::RequestedStatistics( _In_ XblRequestedStatistics requestedStatistics ) { m_serviceConfigurationId = xsapi_internal_string(requestedStatistics.serviceConfigurationId); m_statistics = utils::string_array_to_internal_string_vector(requestedStatistics.statistics, requestedStatistics.statisticsCount); } const xsapi_internal_string& RequestedStatistics::ServiceConfigurationId() const { return m_serviceConfigurationId; } const xsapi_internal_vector& RequestedStatistics::Statistics() const { return m_statistics; } NAMESPACE_MICROSOFT_XBOX_SERVICES_USERSTATISTICS_CPP_END ================================================ FILE: Source/Services/Stats/service_configuration_statistic.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "xsapi_utils.h" #include "user_statistics_internal.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_USERSTATISTICS_CPP_BEGIN ServiceConfigurationStatistic::ServiceConfigurationStatistic() { } ServiceConfigurationStatistic::ServiceConfigurationStatistic( _In_ xsapi_internal_string serviceConfigurationId, _In_ xsapi_internal_vector stats ) : m_serviceConfigurationId(serviceConfigurationId), m_stats(stats) { } const xsapi_internal_string& ServiceConfigurationStatistic::ServiceConfigurationId() const { return m_serviceConfigurationId; } const xsapi_internal_vector& ServiceConfigurationStatistic::Statistics() const { return m_stats; } void ServiceConfigurationStatistic::SetServiceConfigurationId(_In_ xsapi_internal_string serviceConfigId) { m_serviceConfigurationId = std::move(serviceConfigId); } /* static */ Result ServiceConfigurationStatistic::Deserialize( _In_ const JsonValue& json ) { ServiceConfigurationStatistic returnResult; if (json.IsNull()) { return returnResult; } xsapi_internal_string scid; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "scid", scid, true)); xsapi_internal_vector stats; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonVector(Statistic::Deserialize, json, "stats", stats, true)); returnResult = ServiceConfigurationStatistic( scid, stats ); return Result(returnResult, S_OK); } size_t ServiceConfigurationStatistic::SizeOf() const { size_t size = sizeof(XblServiceConfigurationStatistic); for (Statistic stat : m_stats) { size += stat.SizeOf(); } return size; } char* ServiceConfigurationStatistic::Serialize(XblServiceConfigurationStatistic* serviceConfigStat, char* buffer) const { utils::strcpy(serviceConfigStat->serviceConfigurationId, m_serviceConfigurationId.size() + 1, m_serviceConfigurationId.c_str()); serviceConfigStat->statisticsCount = (uint32_t)m_stats.size(); serviceConfigStat->statistics = reinterpret_cast(buffer); buffer += sizeof(XblStatistic) * m_stats.size(); for (size_t i = 0; i < m_stats.size(); i++) { buffer = m_stats[i].Serialize(&serviceConfigStat->statistics[i], buffer); } return buffer; } NAMESPACE_MICROSOFT_XBOX_SERVICES_USERSTATISTICS_CPP_END ================================================ FILE: Source/Services/Stats/statistic.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "xsapi_utils.h" #include "user_statistics_internal.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_USERSTATISTICS_CPP_BEGIN Statistic::Statistic() { } // class statistic Statistic::Statistic( _In_ xsapi_internal_string name, _In_ xsapi_internal_string type, _In_ xsapi_internal_string value ) : m_statName(std::move(name)), m_statType(std::move(type)), m_value(std::move(value)) { } const xsapi_internal_string& Statistic::StatisticName() const { return m_statName; } const xsapi_internal_string& Statistic::StatisticType() const { return m_statType; } const xsapi_internal_string& Statistic::Value() const { return m_value; } void Statistic::SetStatisticName( _In_ xsapi_internal_string name ) { m_statName = std::move(name); } void Statistic::SetStatisticType( _In_ xsapi_internal_string type ) { m_statType = std::move(type); } void Statistic::SetStatisticValue( _In_ xsapi_internal_string value ) { m_value = std::move(value); } /* static */ Result Statistic::Deserialize( _In_ const JsonValue& json ) { Statistic returnResult; if (json.IsNull()) { return returnResult; } xsapi_internal_string statname, type, value; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "statname", statname, true)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "type", type, true)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "value", value, true)); returnResult = Statistic( statname, type, value ); return Result(returnResult, S_OK); } size_t Statistic::SizeOf() const { size_t size = sizeof(XblStatistic); size += m_statName.size() + 1; size += m_statType.size() + 1; size += m_value.size() + 1; return size; } char* Statistic::Serialize(XblStatistic* statistic, char* buffer) const { utils::strcpy(buffer, m_statName.size() + 1, m_statName.c_str()); statistic->statisticName = static_cast(buffer); buffer += m_statName.size() + 1; utils::strcpy(buffer, m_statType.size() + 1, m_statType.c_str()); statistic->statisticType = static_cast(buffer); buffer += m_statType.size() + 1; utils::strcpy(buffer, m_value.size() + 1, m_value.c_str()); statistic->value = static_cast(buffer); buffer += m_value.size() + 1; return buffer; } NAMESPACE_MICROSOFT_XBOX_SERVICES_USERSTATISTICS_CPP_END ================================================ FILE: Source/Services/Stats/statistic_change_subscription.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "user_statistics_internal.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_USERSTATISTICS_CPP_BEGIN StatisticChangeSubscription::StatisticChangeSubscription( _In_ uint64_t xuid, _In_ String scid, _In_ String statisticName, _In_ std::shared_ptr statisticsService ) noexcept : m_xuid{ xuid }, m_scid{ std::move(scid) }, m_statisticName{ std::move(statisticName) }, m_statisticsService{ statisticsService } { Stringstream uriPath; uriPath << "https://userstats.xboxlive.com/users/xuid(" << m_xuid << ")/scids/" << m_scid << "/stats/" << m_statisticName; m_resourceUri = uriPath.str(); } void StatisticChangeSubscription::OnSubscribe( _In_ const JsonValue& data ) noexcept { // Payload format http://xboxwiki/wiki/RTA%3ASUBSCRIBE#Subscribing_to_Stats // [, , { "name": "Kills", "type" : "Integer", "value": 219 }] if (data.IsNull()) { LOGS_ERROR << __FUNCTION__ << ": RTA payload unexpectedly null"; return; } else if (auto statisticsService{ m_statisticsService.lock() }) { String name; String value; JsonUtils::ExtractJsonString(data, "name", name); assert(name == m_statisticName); JsonUtils::ExtractJsonString(data, "type", m_statisticType); if (data.IsObject() && data.HasMember("value")) { const JsonValue& field = data["value"]; if (field.IsInt()) { Stringstream stream; stream << field.GetInt(); value = stream.str(); } else if (field.IsInt64()) { Stringstream stream; stream << field.GetInt64(); value = stream.str(); } else if (field.IsUint()) { Stringstream stream; stream << field.GetUint(); value = stream.str(); } else if (field.IsUint64()) { Stringstream stream; stream << field.GetUint64(); value = stream.str(); } else if (field.IsDouble()) { Stringstream stream; stream << field.GetDouble(); value = stream.str(); } else if (field.IsBool()) { Stringstream stream; stream << field.GetBool(); value = stream.str(); } else if (field.IsString()) { value = field.GetString(); } } statisticsService->HandleStatisticChanged(StatisticChangeEventArgs { m_xuid, m_scid, m_statisticName, m_statisticType, std::move(value) }); } } void StatisticChangeSubscription::OnEvent( _In_ const JsonValue& data ) noexcept { // Payload format http://xboxwiki/wiki/RTA:EVENT#Stats // [, , "Value"] if (data.IsNull()) { LOGS_ERROR << __FUNCTION__ << ": RTA payload unexpectedly null"; } else if (auto statisticsService{ m_statisticsService.lock() }) { statisticsService->HandleStatisticChanged(StatisticChangeEventArgs { m_xuid, m_scid, m_statisticName, m_statisticType, JsonUtils::SerializeJson(data) }); } } StatisticChangeEventArgs::StatisticChangeEventArgs( _In_ uint64_t _xboxUserId, _In_ const String& _serviceConfigurationId, _In_ String _statisticName, _In_ String _statisticType, _In_ String _value ) noexcept : XblStatisticChangeEventArgs{ _xboxUserId }, m_statisticName{ std::move(_statisticName) }, m_statisticType{ std::move(_statisticType) }, m_value{ std::move(_value) } { utils::strcpy(serviceConfigurationId, sizeof(serviceConfigurationId), _serviceConfigurationId.data()); latestStatistic = XblStatistic { m_statisticName.data(), m_statisticType.data(), m_value.data() }; } NAMESPACE_MICROSOFT_XBOX_SERVICES_USERSTATISTICS_CPP_END ================================================ FILE: Source/Services/Stats/title_managed_statistics_api.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "title_managed_statistics_internal.h" #include "xbox_live_context_internal.h" using namespace xbox::services; using namespace xbox::services::user_statistics; STDAPI XblTitleManagedStatsWriteAsync( _In_ XblContextHandle xblContextHandle, _In_ uint64_t xboxUserId, _In_ const XblTitleManagedStatistic* statistics, _In_ size_t statisticsCount, _In_ XAsyncBlock* async ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(xblContextHandle); // It is only valid to write stats for the local user, but the service will return 200 if we try to write // stats for another user (it just won't actually update the SVD). Add a client side check so games get // an error in this case. RETURN_HR_INVALIDARGUMENT_IF(xblContextHandle->Xuid() != xboxUserId); RETURN_HR_INVALIDARGUMENT_IF(statistics == nullptr || statisticsCount == 0); return RunAsync(async, __FUNCTION__, [ xboxLiveContext = xblContextHandle->shared_from_this(), statList = Vector(statistics, statistics + statisticsCount) ] (XAsyncOp op, const XAsyncProviderData* data) { switch (op) { case XAsyncOp::DoWork: { RETURN_HR_IF_FAILED(xboxLiveContext->TitleManagedStatisticsService()->WriteTitleManagedStatisticsAsync( statList, data->async )); return E_PENDING; } default: { return S_OK; } } }); } CATCH_RETURN() HRESULT UpdateStatsAsync( _In_ XblContextHandle xblContextHandle, _In_ Vector&& stats, _In_ XAsyncBlock* async ) noexcept { RETURN_HR_INVALIDARGUMENT_IF_NULL(xblContextHandle); return RunAsync(async, __FUNCTION__, [ xboxLiveContext = xblContextHandle->shared_from_this(), stats{ std::move(stats) } ] (XAsyncOp op, const XAsyncProviderData* data) { switch (op) { case XAsyncOp::DoWork: { RETURN_HR_IF_FAILED(xboxLiveContext->TitleManagedStatisticsService()->UpdateTitleManagedStatistics( stats, data->async )); return E_PENDING; } default: { return S_OK; } } }); } STDAPI XblTitleManagedStatsUpdateStatsAsync( _In_ XblContextHandle xblContextHandle, _In_ const XblTitleManagedStatistic* statistics, _In_ size_t statisticsCount, _In_ XAsyncBlock* async ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(statistics == nullptr || statisticsCount == 0); return UpdateStatsAsync( xblContextHandle, Vector(statistics, statistics + statisticsCount), async ); } CATCH_RETURN() STDAPI XblTitleManagedStatsDeleteStatsAsync( _In_ XblContextHandle xblContextHandle, _In_ const char** names, _In_ size_t count, _In_ XAsyncBlock* async ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(names == nullptr || count == 0); return UpdateStatsAsync( xblContextHandle, Vector(names, names + count), async ); } CATCH_RETURN() ================================================ FILE: Source/Services/Stats/title_managed_statistics_internal.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "xsapi-c/title_managed_statistics_c.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_USERSTATISTICS_CPP_BEGIN struct TitleManagedStatistic { TitleManagedStatistic(const XblTitleManagedStatistic& s) noexcept : name{ s.statisticName }, type{ static_cast(s.statisticType) }, numberValue{ s.numberValue }, stringValue{ s.stringValue ? s.stringValue : "" } { } // The statswrite PATCH endpoint allows stat deletions by writing a null // JSON value for an existing statname. This constructor is used to create a // a "NULL" TitleManagedStatistic to be used for deletion. TitleManagedStatistic(const char* nameToDelete) noexcept : name{ nameToDelete }, type{ Type::Null } { } String name; enum class Type : uint32_t { Number = static_cast(XblTitleManagedStatType::Number), String = static_cast(XblTitleManagedStatType::String), Null } type; double numberValue{ 0.0 }; String stringValue; }; class TitleManagedStatisticsService: public std::enable_shared_from_this { public: TitleManagedStatisticsService( _In_ User&& user, _In_ std::shared_ptr xboxLiveContextSettings ) noexcept; HRESULT WriteTitleManagedStatisticsAsync( _In_ const Vector& statistics, _In_ AsyncContext async ) const noexcept; HRESULT UpdateTitleManagedStatistics( _In_ const Vector& statistics, _In_ AsyncContext async ) const noexcept; private: User m_user; std::shared_ptr m_xboxLiveContextSettings; String m_statswritePath; static uint64_t GetRevisionFromClock() noexcept; static JsonDocument SerializeStatistics( const Vector& stats ) noexcept; }; NAMESPACE_MICROSOFT_XBOX_SERVICES_USERSTATISTICS_CPP_END ================================================ FILE: Source/Services/Stats/title_managed_statistics_service.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "title_managed_statistics_internal.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_USERSTATISTICS_CPP_BEGIN TitleManagedStatisticsService::TitleManagedStatisticsService( _In_ User&& user, _In_ std::shared_ptr xboxLiveContextSettings ) noexcept : m_user{ std::move(user) }, m_xboxLiveContextSettings(std::move(xboxLiveContextSettings)) { Stringstream ss; ss << "/stats/users/" << m_user.Xuid() << "/scids/" << AppConfig::Instance()->Scid(); m_statswritePath = ss.str(); } HRESULT TitleManagedStatisticsService::WriteTitleManagedStatisticsAsync( _In_ const Vector& statistics, _In_ AsyncContext async ) const noexcept { JsonDocument request{ SerializeStatistics(statistics) }; JsonDocument::AllocatorType& allocator = request.GetAllocator(); request.AddMember("$schema", "http://stats.xboxlive.com/2017-1/schema#", allocator); request.AddMember("previousRevision", 0, allocator); request.AddMember("revision", GetRevisionFromClock(), allocator); request.AddMember("timestamp", JsonValue(xbox::services::datetime::utc_now().to_string(xbox::services::datetime::date_format::ISO_8601).c_str(), allocator).Move(), allocator); Result userResult = m_user.Copy(); RETURN_HR_IF_FAILED(userResult.Hresult()); auto httpCall = MakeShared(userResult.ExtractPayload()); RETURN_HR_IF_FAILED(httpCall->Init( m_xboxLiveContextSettings, "POST", XblHttpCall::BuildUrl("statswrite", m_statswritePath), xbox_live_api::post_stats_value_document )); RETURN_HR_IF_FAILED(httpCall->SetXblServiceContractVersion(4)); RETURN_HR_IF_FAILED(httpCall->SetRequestBody(request)); return httpCall->Perform({ async.Queue().GetHandle(), [ async ] (HttpResult result) { HRESULT hr{ Failed(result) ? result.Hresult() : result.Payload()->Result() }; return async.Complete(hr); } }); } HRESULT TitleManagedStatisticsService::UpdateTitleManagedStatistics( _In_ const Vector& statistics, _In_ AsyncContext async ) const noexcept { Result userResult = m_user.Copy(); RETURN_HR_IF_FAILED(userResult.Hresult()); auto httpCall = MakeShared(userResult.ExtractPayload()); RETURN_HR_IF_FAILED(httpCall->Init( m_xboxLiveContextSettings, "PATCH", XblHttpCall::BuildUrl("statswrite", m_statswritePath), xbox_live_api::patch_stats_value_document )); RETURN_HR_IF_FAILED(httpCall->SetXblServiceContractVersion(4)); RETURN_HR_IF_FAILED(httpCall->SetRequestBody(SerializeStatistics(statistics))); return httpCall->Perform({ async.Queue().DeriveWorkerQueue(), [ async ] (HttpResult result) { HRESULT hr{ Failed(result) ? result.Hresult() : result.Payload()->Result() }; return async.Complete(hr); } }); } uint64_t TitleManagedStatisticsService::GetRevisionFromClock() noexcept { uint64_t dateTime = xbox::services::datetime::utc_now().to_interval(); // eg. 131472330440000000 const uint64_t dateTimeFromJan1st2015 = 130645440000000000; if (dateTime < dateTimeFromJan1st2015) { return 1; // Clock is wrong and is not yet sync'd with internet time, so just setting the revision to 1 } else { uint64_t dateTimeSince2015 = dateTime - dateTimeFromJan1st2015; // eg. 826888900000000 uint64_t dateTimeTrimmed = dateTimeSince2015 >> 16; // divide by 2^16 to get it to sub second range. eg. 12617323303 return dateTimeTrimmed; } } JsonDocument TitleManagedStatisticsService::SerializeStatistics( const Vector& stats ) noexcept { assert(!stats.empty()); JsonDocument jsonDoc{ rapidjson::kObjectType }; auto& a{ jsonDoc.GetAllocator() }; JsonValue statsJson{ rapidjson::kObjectType }; JsonValue titleJson{ rapidjson::kObjectType }; for (auto& stat : stats) { JsonValue statJson{ rapidjson::kObjectType }; switch (stat.type) { case TitleManagedStatistic::Type::Number: { statJson.AddMember("value", stat.numberValue, a); break; } case TitleManagedStatistic::Type::String: { statJson.AddMember("value", JsonValue{ stat.stringValue.data(), a }.Move(), a); break; } case TitleManagedStatistic::Type::Null: { statJson.SetNull(); break; } default: { assert(false); break; } } titleJson.AddMember(JsonValue{ stat.name.data(), a }.Move(), statJson.Move(), a); } statsJson.AddMember("title", titleJson.Move(), a); jsonDoc.AddMember("stats", statsJson.Move(), a); return jsonDoc; } NAMESPACE_MICROSOFT_XBOX_SERVICES_USERSTATISTICS_CPP_END ================================================ FILE: Source/Services/Stats/user_statistics_api.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "xsapi-c/user_statistics_c.h" #include "user_statistics_internal.h" #include "xbox_live_context_internal.h" using namespace xbox::services; using namespace xbox::services::system; using namespace xbox::services::user_statistics; STDAPI XblUserStatisticsGetSingleUserStatisticAsync( _In_ XblContextHandle xblContext, _In_ uint64_t xboxUserId, _In_z_ const char* serviceConfigurationId, _In_z_ const char* statisticName, _In_ XAsyncBlock* async ) XBL_NOEXCEPT try { return XblUserStatisticsGetSingleUserStatisticsAsync( xblContext, xboxUserId, serviceConfigurationId, &statisticName, 1, async ); } CATCH_RETURN() STDAPI XblUserStatisticsGetSingleUserStatisticResultSize( _In_ XAsyncBlock* async, _Out_ size_t* resultSizeInBytes ) XBL_NOEXCEPT try { return XAsyncGetResultSize(async, resultSizeInBytes); } CATCH_RETURN() STDAPI XblUserStatisticsGetSingleUserStatisticResult( _In_ XAsyncBlock* async, _In_ size_t bufferSize, _Out_writes_bytes_to_(bufferSize, *bufferUsed) void* buffer, _Outptr_ XblUserStatisticsResult** ptrToBuffer, _Out_opt_ size_t* bufferUsed ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(ptrToBuffer); auto hr = XAsyncGetResult(async, nullptr, bufferSize, buffer, bufferUsed); if (SUCCEEDED(hr)) { *ptrToBuffer = static_cast(buffer); } return hr; } CATCH_RETURN() STDAPI XblUserStatisticsGetSingleUserStatisticsAsync( _In_ XblContextHandle xblContextHandle, _In_ uint64_t xboxUserId, _In_z_ const char* serviceConfigurationId, _In_ const char** statisticNames, _In_ size_t statisticNamesCount, _In_ XAsyncBlock* async ) XBL_NOEXCEPT try { VERIFY_XBL_INITIALIZED(); RETURN_HR_INVALIDARGUMENT_IF_NULL(xblContextHandle); RETURN_HR_INVALIDARGUMENT_IF_NULL(serviceConfigurationId); RETURN_HR_INVALIDARGUMENT_IF(statisticNames == nullptr || statisticNamesCount == 0); return RunAsync(async, __FUNCTION__, [ xblContext{ xblContextHandle->shared_from_this() }, resultPayload = UserStatisticsResult{}, xboxUserId, scid = String{ serviceConfigurationId }, statisticNames{ Vector(statisticNames, statisticNames + statisticNamesCount) } ] (XAsyncOp op, const XAsyncProviderData* data) mutable { switch (op) { case XAsyncOp::DoWork: { RETURN_HR_IF_FAILED(xblContext->UserStatisticsService()->GetSingleUserStatistics( xboxUserId, scid, statisticNames, { TaskQueue{ data->async->queue }.DeriveWorkerQueue(), [ &resultPayload, async{ data->async } ] (Result result) { if (Succeeded(result)) { resultPayload = std::move(result.ExtractPayload()); } XAsyncComplete(async, result.Hresult(), resultPayload.SizeOf()); } })); return E_PENDING; } case XAsyncOp::GetResult: { char* buffer = static_cast(data->buffer); ZeroMemory(buffer, data->bufferSize); buffer = resultPayload.Serialize(buffer); assert(static_cast(buffer) == static_cast(static_cast(data->buffer) + resultPayload.SizeOf())); } default: { return S_OK; } } }); } CATCH_RETURN() STDAPI XblUserStatisticsGetSingleUserStatisticsResultSize( _In_ XAsyncBlock* async, _Out_ size_t* resultSizeInBytes ) XBL_NOEXCEPT try { return XAsyncGetResultSize(async, resultSizeInBytes); } CATCH_RETURN() STDAPI XblUserStatisticsGetSingleUserStatisticsResult( _In_ XAsyncBlock* async, _In_ size_t bufferSize, _Out_writes_bytes_to_(bufferSize, *bufferUsed) void* buffer, _Outptr_ XblUserStatisticsResult** ptrToBuffer, _Out_opt_ size_t* bufferUsed ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(ptrToBuffer); auto hr = XAsyncGetResult(async, nullptr, bufferSize, buffer, bufferUsed); if (SUCCEEDED(hr)) { *ptrToBuffer = static_cast(buffer); } return hr; } CATCH_RETURN() STDAPI XblUserStatisticsGetMultipleUserStatisticsAsync( _In_ XblContextHandle xblContext, _In_ uint64_t* xboxUserIds, _In_ size_t xboxUserIdsCount, _In_z_ const char* serviceConfigurationId, _In_ const char** statisticNames, _In_ size_t statisticNamesCount, _In_ XAsyncBlock* async ) XBL_NOEXCEPT try { VERIFY_XBL_INITIALIZED(); RETURN_HR_INVALIDARGUMENT_IF_NULL(serviceConfigurationId); RETURN_HR_INVALIDARGUMENT_IF(statisticNames == nullptr || statisticNamesCount == 0); XblRequestedStatistics requestedStats{ {}, statisticNames, static_cast(statisticNamesCount) }; utils::strcpy(requestedStats.serviceConfigurationId, sizeof(requestedStats.serviceConfigurationId), serviceConfigurationId); return XblUserStatisticsGetMultipleUserStatisticsForMultipleServiceConfigurationsAsync( xblContext, xboxUserIds, static_cast(xboxUserIdsCount), &requestedStats, 1, async ); } CATCH_RETURN() STDAPI XblUserStatisticsGetMultipleUserStatisticsResultSize( _In_ XAsyncBlock* async, _Out_ size_t* resultSizeInBytes ) XBL_NOEXCEPT try { return XAsyncGetResultSize(async, resultSizeInBytes); } CATCH_RETURN() STDAPI XblUserStatisticsGetMultipleUserStatisticsResult( _In_ XAsyncBlock* async, _In_ size_t bufferSize, _Out_writes_bytes_to_(bufferSize, *bufferUsed) void* buffer, _Outptr_ XblUserStatisticsResult** results, _Out_ size_t* resultsCount, _Out_opt_ size_t* bufferUsed ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(results == nullptr || resultsCount == nullptr); size_t bufferUsedTemp{}; if (bufferUsed == nullptr) { bufferUsed = &bufferUsedTemp; } auto hr = XAsyncGetResult(async, nullptr, bufferSize, buffer, bufferUsed); if (SUCCEEDED(hr)) { *results = static_cast(buffer); auto sizePtr = reinterpret_cast(static_cast(buffer) + *bufferUsed) - 1; *resultsCount = *sizePtr; } return hr; } CATCH_RETURN() STDAPI XblUserStatisticsGetMultipleUserStatisticsForMultipleServiceConfigurationsAsync( _In_ XblContextHandle xblContextHandle, _In_ uint64_t* xboxUserIds, _In_ uint32_t xboxUserIdsCount, _In_ const XblRequestedStatistics* requestedStats, _In_ uint32_t requestedStatsCount, _In_ XAsyncBlock* async ) XBL_NOEXCEPT try { VERIFY_XBL_INITIALIZED(); RETURN_HR_INVALIDARGUMENT_IF_NULL(xblContextHandle); RETURN_HR_INVALIDARGUMENT_IF(xboxUserIds == nullptr || xboxUserIdsCount == 0); RETURN_HR_INVALIDARGUMENT_IF(requestedStats == nullptr || requestedStatsCount == 0); return RunAsync(async, __FUNCTION__, [ xblContext{ xblContextHandle->shared_from_this() }, resultPayload{ Vector{} }, xuids{ Vector(xboxUserIds, xboxUserIds + xboxUserIdsCount) }, requestedStats{ Vector(requestedStats, requestedStats + requestedStatsCount) } ] (XAsyncOp op, const XAsyncProviderData* data) mutable { switch (op) { case XAsyncOp::DoWork: { RETURN_HR_IF_FAILED(xblContext->UserStatisticsService()->GetMultipleUserStatisticsForMultipleServiceConfigurations( xuids, requestedStats, { TaskQueue{ data->async->queue }.DeriveWorkerQueue(), [ &resultPayload, async{ data->async } ] (Result> result) { size_t bufferSize{ 0 }; if (Succeeded(result)) { resultPayload = std::move(result.ExtractPayload()); for (auto& userStatResult : resultPayload) { bufferSize += userStatResult.SizeOf(); } // Add some padding at the end of the buffer to store the number of XblUserStatisticsResult // objects since there is no way to deduce it just from the buffer size bufferSize += sizeof(size_t); } XAsyncComplete(async, result.Hresult(), bufferSize); } })); return E_PENDING; } case XAsyncOp::GetResult: { char* buffer = static_cast(data->buffer); ZeroMemory(buffer, data->bufferSize); XblUserStatisticsResult * resultArr = reinterpret_cast(buffer); buffer += sizeof(XblUserStatisticsResult) * resultPayload.size(); size_t bufferSize{}; for (size_t i = 0; i < resultPayload.size(); i++) { bufferSize += resultPayload[i].SizeOf(); buffer = resultPayload[i].Serialize(&resultArr[i], buffer); } // Validate that everything that was expected to be written was written to the buffer size_t * resultArrSize = reinterpret_cast(buffer); XSAPI_ASSERT(static_cast(resultArrSize) == static_cast(static_cast(data->buffer) + bufferSize)); *resultArrSize = resultPayload.size(); return S_OK; } default: { return S_OK; } } }); } CATCH_RETURN() STDAPI XblUserStatisticsGetMultipleUserStatisticsForMultipleServiceConfigurationsResultSize( _In_ XAsyncBlock* async, _Out_ size_t* resultSizeInBytes ) XBL_NOEXCEPT try { return XAsyncGetResultSize(async, resultSizeInBytes); } CATCH_RETURN() STDAPI XblUserStatisticsGetMultipleUserStatisticsForMultipleServiceConfigurationsResult( _In_ XAsyncBlock* async, _In_ size_t bufferSize, _Out_writes_bytes_to_(bufferSize, *bufferUsed) void* buffer, _Outptr_ XblUserStatisticsResult** results, _Out_ size_t* resultsCount, _Out_opt_ size_t* bufferUsed ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(results == nullptr || resultsCount == nullptr); size_t bufferUsedTemp{}; if (bufferUsed == nullptr) { bufferUsed = &bufferUsedTemp; } auto hr = XAsyncGetResult(async, nullptr, bufferSize, buffer, bufferUsed); if (SUCCEEDED(hr)) { *results = static_cast(buffer); auto sizePtr = reinterpret_cast(static_cast(buffer) + *bufferUsed) - 1; *resultsCount = *sizePtr; } return hr; } CATCH_RETURN() namespace xbox { namespace services { namespace user_statistics { namespace legacy { struct Subscription : public XblRealTimeActivitySubscription { Subscription(uint64_t _xuid, String _scid, String _statName) noexcept : xuid{ _xuid }, scid{ std::move(_scid) }, statName{ std::move(_statName) } { } uint64_t xuid; String scid; String statName; }; } } } } STDAPI XblUserStatisticsSubscribeToStatisticChange( _In_ XblContextHandle xblContextHandle, _In_ uint64_t xuid, _In_z_ const char* scid, _In_z_ const char* statisticName, _Out_ XblRealTimeActivitySubscriptionHandle* subscriptionHandle ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(subscriptionHandle); HRESULT hr = XblUserStatisticsTrackStatistics( xblContextHandle, &xuid, 1, scid, &statisticName, 1 ); if (SUCCEEDED(hr)) { *subscriptionHandle = Make(xuid, scid, statisticName); } return hr; } CATCH_RETURN(); STDAPI XblUserStatisticsUnsubscribeFromStatisticChange( _In_ XblContextHandle xblContextHandle, _In_ XblRealTimeActivitySubscriptionHandle subscriptionHandle ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(xblContextHandle); RETURN_HR_INVALIDARGUMENT_IF_NULL(subscriptionHandle); auto sub{ static_cast(subscriptionHandle) }; const char* statName{ sub->statName.data() }; HRESULT hr = XblUserStatisticsStopTrackingStatistics( xblContextHandle, &sub->xuid, 1, sub->scid.data(), &statName, 1 ); assert(SUCCEEDED(hr)); Delete(sub); return hr; } CATCH_RETURN() STDAPI_(XblFunctionContext) XblUserStatisticsAddStatisticChangedHandler( _In_ XblContextHandle xblContextHandle, _In_ XblStatisticChangedHandler handler, _In_opt_ void* handlerContext ) XBL_NOEXCEPT try { if (xblContextHandle == nullptr || handler == nullptr) { return XblFunctionContext{ 0 }; } return xblContextHandle->UserStatisticsService()->AddStatisticChangedHandler( [ handler, handlerContext ] (const StatisticChangeEventArgs& eventArgs) { try { handler(eventArgs, handlerContext); } catch (...) { LOGS_ERROR << __FUNCTION__ << ": exception in client handler!"; } }); } CATCH_RETURN_WITH(-1) STDAPI_(void) XblUserStatisticsRemoveStatisticChangedHandler( _In_ XblContextHandle xblContextHandle, _In_ XblFunctionContext context ) XBL_NOEXCEPT try { if (xblContextHandle != nullptr) { xblContextHandle->UserStatisticsService()->RemoveStatisticChangedHandler(context); } } CATCH_RETURN_WITH(;) STDAPI XblUserStatisticsTrackStatistics( _In_ XblContextHandle xblContextHandle, _In_ const uint64_t* xboxUserIds, _In_ size_t xboxUserIdsCount, _In_z_ const char* serviceConfigurationId, _In_ const char** statisticNames, _In_ size_t statisticNamesCount ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(xblContextHandle); RETURN_HR_INVALIDARGUMENT_IF(xboxUserIds == nullptr || xboxUserIdsCount == 0); RETURN_HR_INVALIDARGUMENT_IF_NULL(serviceConfigurationId); RETURN_HR_INVALIDARGUMENT_IF(statisticNames == nullptr || statisticNamesCount == 0); return xblContextHandle->UserStatisticsService()->TrackStatistics( Vector{ xboxUserIds, xboxUserIds + xboxUserIdsCount }, serviceConfigurationId, Vector{ statisticNames, statisticNames + statisticNamesCount } ); } CATCH_RETURN() STDAPI XblUserStatisticsStopTrackingStatistics( _In_ XblContextHandle xblContextHandle, _In_ const uint64_t* xboxUserIds, _In_ size_t xboxUserIdsCount, _In_z_ const char* serviceConfigurationId, _In_ const char** statisticNames, _In_ size_t statisticNamesCount ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(xblContextHandle); RETURN_HR_INVALIDARGUMENT_IF(xboxUserIds == nullptr || xboxUserIdsCount == 0); RETURN_HR_INVALIDARGUMENT_IF_NULL(serviceConfigurationId); RETURN_HR_INVALIDARGUMENT_IF(statisticNames == nullptr || statisticNamesCount == 0); return xblContextHandle->UserStatisticsService()->StopTrackingStatistics( Vector{ xboxUserIds, xboxUserIds + xboxUserIdsCount }, serviceConfigurationId, Vector{ statisticNames, statisticNames + statisticNamesCount } ); } CATCH_RETURN() STDAPI XblUserStatisticsStopTrackingUsers( _In_ XblContextHandle xblContextHandle, _In_ const uint64_t* xboxUserIds, _In_ size_t xboxUserIdsCount ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(xblContextHandle); RETURN_HR_INVALIDARGUMENT_IF(xboxUserIds == nullptr || xboxUserIdsCount == 0); return xblContextHandle->UserStatisticsService()->StopTrackingUsers( Vector{ xboxUserIds, xboxUserIds + xboxUserIdsCount } ); } CATCH_RETURN() ================================================ FILE: Source/Services/Stats/user_statistics_internal.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "xsapi-c/user_statistics_c.h" #include "real_time_activity_subscription.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_USERSTATISTICS_CPP_BEGIN class Statistic { public: Statistic(); Statistic( _In_ String name, _In_ String type, _In_ String value ); static Result Deserialize( _In_ const JsonValue& json ); const String& StatisticName() const; const String& StatisticType() const; const String& Value() const; void SetStatisticName(_In_ String name); void SetStatisticType(_In_ String type); void SetStatisticValue(_In_ String value); size_t SizeOf() const; char* Serialize(XblStatistic* statistic, char* buffer) const; private: String m_statName; String m_statType; String m_value; }; class ServiceConfigurationStatistic { public: ServiceConfigurationStatistic(); ServiceConfigurationStatistic( _In_ String serviceConfigurationId, _In_ Vector stats ); static Result Deserialize( _In_ const JsonValue& json ); const String& ServiceConfigurationId() const; const Vector& Statistics() const; void SetServiceConfigurationId(_In_ String serviceConfigId); size_t SizeOf() const; char* Serialize(XblServiceConfigurationStatistic* serviceConfigStat, char* buffer) const; private: String m_serviceConfigurationId; Vector m_stats; }; class UserStatisticsResult { public: UserStatisticsResult(); UserStatisticsResult( _In_ String xboxUserId, _In_ Vector serviceConfigStatistics ); static Result Deserialize( _In_ const JsonValue& json ); const String& XboxUserId() const; const Vector& ServiceConfigurationStatistics() const; void SetServiceConfigurationId(_In_ String serviceConfigId); size_t SizeOf() const; char* Serialize(char* buffer) const; char* Serialize(XblUserStatisticsResult* userStatResult, char* buffer) const; private: String m_xboxUserId; Vector m_serviceConfigStatistics; }; class RequestedStatistics { public: RequestedStatistics(); RequestedStatistics( _In_ String serviceConfigurationId, _In_ Vector statistics ); RequestedStatistics( _In_ XblRequestedStatistics requestedStatistics ); const String& ServiceConfigurationId() const; const Vector& Statistics() const; private: String m_serviceConfigurationId; Vector m_statistics; }; class StatisticChangeEventArgs : public XblStatisticChangeEventArgs { public: StatisticChangeEventArgs( _In_ uint64_t xboxUserId, _In_ const String& serviceConfigurationId, _In_ String statisticName, _In_ String statisticType, _In_ String value ) noexcept; ~StatisticChangeEventArgs() = default; private: String m_statisticName; String m_statisticType; String m_value; }; class StatisticChangeSubscription : public real_time_activity::Subscription, public std::enable_shared_from_this { public: StatisticChangeSubscription( _In_ uint64_t xuid, _In_ String scid, _In_ String statisticName, _In_ std::shared_ptr statisticsService ) noexcept; protected: void OnSubscribe(_In_ const JsonValue& data) noexcept override; void OnEvent(_In_ const JsonValue& data) noexcept override; private: const uint64_t m_xuid; const String m_scid; const String m_statisticName; String m_statisticType; const std::weak_ptr m_statisticsService; }; class UserStatisticsService : public std::enable_shared_from_this { public: UserStatisticsService( _In_ User&& user, _In_ const TaskQueue& backgroundQueue, _In_ std::shared_ptr xboxLiveContextSettings, _In_ std::shared_ptr rtaManager ) noexcept; ~UserStatisticsService() noexcept; HRESULT GetSingleUserStatistic( _In_ uint64_t xuid, _In_ const String& serviceConfigurationId, _In_ const String& statisticName, _In_ AsyncContext> async ) const noexcept; HRESULT GetSingleUserStatistics( _In_ uint64_t xuid, _In_ const String& serviceConfigurationId, _In_ const Vector& statisticNames, _In_ AsyncContext> async ) const noexcept; HRESULT GetMultipleUserStatistics( _In_ const Vector& xuids, _In_ const String& serviceConfigurationId, _In_ const Vector& statisticNames, _In_ AsyncContext>> async ) const noexcept; HRESULT GetMultipleUserStatisticsForMultipleServiceConfigurations( _In_ const Vector& xuids, _In_ const Vector& requestedServiceConfigurationStatisticsCollection, _In_ AsyncContext>> async ) const noexcept; typedef Callback StatisticChangeHandler; XblFunctionContext AddStatisticChangedHandler( StatisticChangeHandler handler ) noexcept; void RemoveStatisticChangedHandler( XblFunctionContext token ) noexcept; HRESULT TrackStatistics( _In_ const Vector xuids, _In_ const String& scid, _In_ const Vector& statNames ) noexcept; HRESULT StopTrackingStatistics( _In_ const Vector xuids, _In_ const String& scid, _In_ const Vector& statNames ) noexcept; HRESULT StopTrackingUsers( _In_ const Vector& xuids ) noexcept; private: void HandleStatisticChanged( const StatisticChangeEventArgs& args ) const noexcept; void HandleRTAResync(); static String UserStatsSubpath( _In_ uint64_t xuid, _In_ const String& serviceConfigurationId, _In_ Vector statNames ) noexcept; User m_user; TaskQueue m_queue; std::shared_ptr m_xboxLiveContextSettings; std::shared_ptr m_rtaManager; XblFunctionContext m_resyncHandlerToken{ 0 }; Map m_statisticChangeHandlers; XblFunctionContext m_nextToken{ 1 }; struct SubscriptionHolder { size_t refCount{ 0 }; std::shared_ptr subscription; }; // Indexing on Xuid before StatName because the set of tracked Users is probably more // likely to change than the set of tracked Stats. Map, SubscriptionHolder>> m_trackedStatsByUser; // Tracked stats by scid. Needed to perform RTA resync Map> m_trackedStatsByScid; mutable std::recursive_mutex m_mutex; friend class StatisticChangeSubscription; }; NAMESPACE_MICROSOFT_XBOX_SERVICES_USERSTATISTICS_CPP_END ================================================ FILE: Source/Services/Stats/user_statistics_result.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "xsapi_utils.h" #include "user_statistics_internal.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_USERSTATISTICS_CPP_BEGIN UserStatisticsResult::UserStatisticsResult() { } UserStatisticsResult::UserStatisticsResult( _In_ xsapi_internal_string xboxUserId, _In_ xsapi_internal_vector serviceConfigStatistics ) : m_xboxUserId(std::move(xboxUserId)), m_serviceConfigStatistics(std::move(serviceConfigStatistics)) { } const xsapi_internal_string& UserStatisticsResult::XboxUserId() const { return m_xboxUserId; } const xsapi_internal_vector& UserStatisticsResult::ServiceConfigurationStatistics() const { return m_serviceConfigStatistics; } void UserStatisticsResult::SetServiceConfigurationId(_In_ xsapi_internal_string serviceConfigId) { for (auto& stat : m_serviceConfigStatistics) { stat.SetServiceConfigurationId(serviceConfigId); } } /* static */ Result UserStatisticsResult::Deserialize( _In_ const JsonValue& json ) { UserStatisticsResult returnResult; if (json.IsNull()) { return returnResult; } xsapi_internal_vector serviceConfigStatisticses; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonVector(ServiceConfigurationStatistic::Deserialize, json, "scids", serviceConfigStatisticses, false)); if (serviceConfigStatisticses.size() == 0) { xsapi_internal_vector statistics; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonVector(Statistic::Deserialize, json, "stats", statistics, false)); ServiceConfigurationStatistic serviceConfigStatistics(xsapi_internal_string(), statistics); serviceConfigStatisticses.push_back(serviceConfigStatistics); } xsapi_internal_string xuid; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "xuid", xuid, true)); returnResult = UserStatisticsResult( xuid, serviceConfigStatisticses ); return Result(returnResult, S_OK); } size_t UserStatisticsResult::SizeOf() const { size_t size = sizeof(XblUserStatisticsResult); for (ServiceConfigurationStatistic serviceConfigStat : m_serviceConfigStatistics) { size += serviceConfigStat.SizeOf(); } return size; } char* UserStatisticsResult::Serialize(char* buffer) const { XblUserStatisticsResult* userStatResult = reinterpret_cast(buffer); buffer += sizeof(XblUserStatisticsResult); return Serialize(userStatResult, buffer); } char* UserStatisticsResult::Serialize(XblUserStatisticsResult* userStatResult, char* buffer) const { userStatResult->xboxUserId = utils::internal_string_to_uint64(m_xboxUserId); userStatResult->serviceConfigStatisticsCount = (uint32_t)m_serviceConfigStatistics.size(); userStatResult->serviceConfigStatistics = reinterpret_cast(buffer); buffer += sizeof(XblServiceConfigurationStatistic) * m_serviceConfigStatistics.size(); for (size_t i = 0; i < m_serviceConfigStatistics.size(); i++) { buffer = m_serviceConfigStatistics[i].Serialize(&userStatResult->serviceConfigStatistics[i], buffer); } return buffer; } NAMESPACE_MICROSOFT_XBOX_SERVICES_USERSTATISTICS_CPP_END ================================================ FILE: Source/Services/Stats/user_statistics_service.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "user_statistics_internal.h" #include "xbox_live_context_internal.h" #include "real_time_activity_manager.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_USERSTATISTICS_CPP_BEGIN UserStatisticsService::UserStatisticsService( _In_ User&& user, _In_ const TaskQueue& backgroundQueue, _In_ std::shared_ptr xboxLiveContextSettings, _In_ std::shared_ptr rtaManager ) noexcept : m_user{ std::move(user) }, m_queue{ backgroundQueue.DeriveWorkerQueue() }, m_xboxLiveContextSettings{ std::move(xboxLiveContextSettings) }, m_rtaManager{ std::move(rtaManager) } { } UserStatisticsService::~UserStatisticsService() noexcept { if (m_resyncHandlerToken) { m_rtaManager->RemoveResyncHandler(m_user, m_resyncHandlerToken); } if (!m_statisticChangeHandlers.empty()) { for (auto& userPair : m_trackedStatsByUser) { for (auto& statPair : userPair.second) { m_rtaManager->RemoveSubscription(m_user, statPair.second.subscription); } } } } HRESULT UserStatisticsService::GetSingleUserStatistic( _In_ uint64_t xuid, _In_ const String& scid, _In_ const String& statisticName, _In_ AsyncContext> async ) const noexcept { return GetSingleUserStatistics(xuid, scid, Vector{ statisticName }, std::move(async)); } HRESULT UserStatisticsService::GetSingleUserStatistics( _In_ uint64_t xuid, _In_ const String& scid, _In_ const Vector& statisticNames, _In_ AsyncContext> async ) const noexcept { RETURN_HR_INVALIDARGUMENT_IF(scid.empty()); Result userResult = m_user.Copy(); RETURN_HR_IF_FAILED(userResult.Hresult()); auto httpCall = MakeShared(userResult.ExtractPayload()); HRESULT hr = httpCall->Init( m_xboxLiveContextSettings, "GET", XblHttpCall::BuildUrl("userstats", UserStatsSubpath(xuid, scid, statisticNames)), xbox_live_api::get_single_user_statistics ); RETURN_HR_IF_FAILED(hr); RETURN_HR_IF_FAILED(httpCall->SetXblServiceContractVersion(1)); return httpCall->Perform(AsyncContext{ async.Queue().DeriveWorkerQueue(), [ async, scid ] (HttpResult httpResult) { HRESULT hr{ Failed(httpResult) ? httpResult.Hresult() : httpResult.Payload()->Result() }; if (SUCCEEDED(hr)) { auto result = UserStatisticsResult::Deserialize(httpResult.Payload()->GetResponseBodyJson()); result.Payload().SetServiceConfigurationId(scid); return async.Complete(result); } async.Complete(hr); }}); } HRESULT UserStatisticsService::GetMultipleUserStatistics( _In_ const Vector& xuids, _In_ const String& serviceConfigurationId, _In_ const Vector& statisticNames, _In_ AsyncContext>> async ) const noexcept { return GetMultipleUserStatisticsForMultipleServiceConfigurations( xuids, Vector{ RequestedStatistics{ serviceConfigurationId, statisticNames } }, std::move(async) ); } HRESULT UserStatisticsService::GetMultipleUserStatisticsForMultipleServiceConfigurations( _In_ const Vector& xuids, _In_ const Vector& requestedServiceConfigurationStatisticsCollection, _In_ AsyncContext>> async ) const noexcept { // Set request body to something like: //{ // "requestedusers": // [ // "1234567890123460", // "1234567890123234" // ], // "requestedscids": // [ // { // "scid": "c402ff50-3e76-11e2-a25f-0800200c1212", // "requestedstats": // [ // "Game4FirefightKills", // "Game4FirefightHeadshots" // ] // }, // { // "scid": "c402ff50-3e76-11e2-a25f-0800200c0343", // "requestedstats": // [ // "OverallGameKills", // "GameHeadshots" // ] // } // ] //} RETURN_HR_INVALIDARGUMENT_IF(xuids.empty()); RETURN_HR_INVALIDARGUMENT_IF(requestedServiceConfigurationStatisticsCollection.empty()); JsonDocument rootJson{ rapidjson::kObjectType }; JsonDocument::AllocatorType& allocator{ rootJson.GetAllocator() }; JsonValue requestedUsersJsonArray{ rapidjson::kArrayType }; JsonUtils::SerializeVector(JsonUtils::JsonXuidSerializer, xuids, requestedUsersJsonArray, allocator); rootJson.AddMember("requestedusers", requestedUsersJsonArray.Move(), allocator); //requestedscids JsonValue requestedscidsJson{ rapidjson::kArrayType }; for (const auto& request : requestedServiceConfigurationStatisticsCollection) { JsonValue requestedJson{ rapidjson::kObjectType }; JsonValue val; val.SetString(request.ServiceConfigurationId().c_str(), allocator); requestedJson.AddMember("scid", val, allocator); JsonValue requestedstatsJson{ rapidjson::kArrayType }; for (const auto& stat : request.Statistics()) { JsonValue statValue; statValue.SetString(stat.c_str(), allocator); requestedstatsJson.PushBack(statValue, allocator); } requestedJson.AddMember("requestedstats", requestedstatsJson, allocator); requestedscidsJson.PushBack(requestedJson, allocator); } rootJson.AddMember("requestedscids", requestedscidsJson, allocator); Result userResult = m_user.Copy(); RETURN_HR_IF_FAILED(userResult.Hresult()); auto httpCall = MakeShared(userResult.ExtractPayload()); RETURN_HR_IF_FAILED(httpCall->Init( m_xboxLiveContextSettings, "POST", XblHttpCall::BuildUrl("userstats", "/batch?operation=read"), xbox_live_api::get_multiple_user_statistics_for_multiple_service_configurations )); RETURN_HR_IF_FAILED(httpCall->SetXblServiceContractVersion(1)); RETURN_HR_IF_FAILED(httpCall->SetRequestBody(JsonUtils::SerializeJson(rootJson))); return httpCall->Perform(AsyncContext{ async.Queue().DeriveWorkerQueue(), [ async ] (HttpResult httpResult) { HRESULT hr{ Failed(httpResult) ? httpResult.Hresult() : httpResult.Payload()->Result() }; if (SUCCEEDED(hr)) { Vector result; hr = JsonUtils::ExtractJsonVector( UserStatisticsResult::Deserialize, httpResult.Payload()->GetResponseBodyJson(), "users", result, true ); return async.Complete({ result, hr }); } async.Complete(hr); }}); } XblFunctionContext UserStatisticsService::AddStatisticChangedHandler( StatisticChangeHandler handler ) noexcept { std::lock_guard lock{ m_mutex }; if (!m_resyncHandlerToken) { m_resyncHandlerToken = m_rtaManager->AddResyncHandler(m_user, [weakThis = std::weak_ptr{ shared_from_this() }] { auto sharedThis = weakThis.lock(); if (sharedThis) { sharedThis->HandleRTAResync(); } }); } // Add subs to RTA manager if needed if (m_statisticChangeHandlers.empty()) { for (auto& userPair : m_trackedStatsByUser) { for (auto& statPair : userPair.second) { statPair.second.subscription = MakeShared(userPair.first, statPair.first.first, statPair.first.second, shared_from_this()); m_rtaManager->AddSubscription(m_user, statPair.second.subscription); } } } m_statisticChangeHandlers[m_nextToken] = std::move(handler); return m_nextToken++; } void UserStatisticsService::RemoveStatisticChangedHandler( XblFunctionContext token ) noexcept { std::lock_guard lock{ m_mutex }; auto removed{ m_statisticChangeHandlers.erase(token) }; // Remove subs if there are no more handlers if (removed && m_statisticChangeHandlers.empty()) { for (auto& userPair : m_trackedStatsByUser) { for (auto& statPair : userPair.second) { m_rtaManager->RemoveSubscription(m_user, statPair.second.subscription); statPair.second.subscription.reset(); } } } } HRESULT UserStatisticsService::TrackStatistics( _In_ const Vector xuids, _In_ const String& scid, _In_ const Vector& statNames ) noexcept { std::lock_guard lock{ m_mutex }; for (auto& xuid : xuids) { auto& userStats{ m_trackedStatsByUser[xuid] }; for (auto& statName : statNames) { auto iter{ userStats.find({ scid, statName }) }; if (iter == userStats.end()) { userStats[{scid, statName}] = SubscriptionHolder{ 1, nullptr }; // If there are existing handlers, add the new subs to RTA manager if (!m_statisticChangeHandlers.empty()) { auto sub{ MakeShared(xuid, scid, statName, shared_from_this()) }; userStats[{scid, statName}].subscription = sub; RETURN_HR_IF_FAILED(m_rtaManager->AddSubscription(m_user, sub)); } } else { ++(iter->second.refCount); } } } return S_OK; } HRESULT UserStatisticsService::StopTrackingStatistics( _In_ const Vector xuids, _In_ const String& scid, _In_ const Vector& statNames ) noexcept { std::lock_guard lock{ m_mutex }; for (auto& xuid : xuids) { auto& userStats{ m_trackedStatsByUser[xuid] }; for (auto& statName : statNames) { auto iter{ userStats.find({ scid, statName }) }; if (iter != userStats.end() && --(iter->second.refCount) == 0) { // Remove subs from RTA manager as necessary if (!m_statisticChangeHandlers.empty()) { RETURN_HR_IF_FAILED(m_rtaManager->RemoveSubscription(m_user, iter->second.subscription)); } userStats.erase(iter); } } } return S_OK; } HRESULT UserStatisticsService::StopTrackingUsers( _In_ const Vector& xuids ) noexcept { std::lock_guard lock{ m_mutex }; for (auto& xuid : xuids) { auto& userStats{ m_trackedStatsByUser[xuid] }; for (auto& statPair : userStats) { if (--(statPair.second.refCount) == 0) { // Remove the subs from RTA manager as necessary if (!m_statisticChangeHandlers.empty()) { RETURN_HR_IF_FAILED(m_rtaManager->RemoveSubscription(m_user, statPair.second.subscription)); statPair.second.subscription.reset(); } } } } return S_OK; } void UserStatisticsService::HandleStatisticChanged( const StatisticChangeEventArgs& args ) const noexcept { std::unique_lock lock{ m_mutex }; auto handlers{ m_statisticChangeHandlers }; lock.unlock(); for (auto& pair : handlers) { pair.second(args); } } void UserStatisticsService::HandleRTAResync() { std::unique_lock lock{ m_mutex }; // Get all stats tracked stats for all tracked users so that we can resync in a single request. // In the request callback, we will only invoke the stat changed handlers for the tracked users/stats Vector trackedUsers; Vector trackedStats; for (auto& pair : m_trackedStatsByUser) { trackedUsers.push_back(pair.first); } for (auto& pair : m_trackedStatsByScid) { trackedStats.push_back(RequestedStatistics{ pair.first, pair.second }); } auto weakThis = std::weak_ptr{ shared_from_this() }; auto getStatsCallback = [weakThis, this](Result> result) { auto sharedThis = weakThis.lock(); if (!sharedThis) { return; } std::unique_lock lock{ m_mutex }; if (Succeeded(result)) { Vector changeEvents; for (auto& userStatsResult : result.Payload()) { // Only invoke handler for tracked stats auto trackedUserIter = m_trackedStatsByUser.find(utils::internal_string_to_uint64(userStatsResult.XboxUserId())); if (trackedUserIter != m_trackedStatsByUser.end()) { for (auto& scidStats : userStatsResult.ServiceConfigurationStatistics()) { for (auto& stat : scidStats.Statistics()) { auto trackedStatIter = trackedUserIter->second.find({ scidStats.ServiceConfigurationId(), stat.StatisticName() }); if (trackedStatIter != trackedUserIter->second.end()) { changeEvents.emplace_back(trackedUserIter->first, scidStats.ServiceConfigurationId(), stat.StatisticName(), stat.StatisticType(), stat.Value()); } } } } } auto statChangedHandlers{ m_statisticChangeHandlers }; lock.unlock(); for (auto& pair : statChangedHandlers) { for (auto& eventArgs : changeEvents) { pair.second(eventArgs); } } } }; GetMultipleUserStatisticsForMultipleServiceConfigurations(trackedUsers, trackedStats, AsyncContext>>{ m_queue, std::move(getStatsCallback) }); } String UserStatisticsService::UserStatsSubpath( _In_ uint64_t xuid, _In_ const String& serviceConfigurationId, _In_ Vector statNames ) noexcept { Stringstream ss; ss << "/users/xuid(" << xuid << ")/scids/" << serviceConfigurationId << "/stats/"; for (const auto& statName : statNames) { if (statName != statNames.front()) { ss << ","; } ss << statName; } return ss.str(); } NAMESPACE_MICROSOFT_XBOX_SERVICES_USERSTATISTICS_CPP_END ================================================ FILE: Source/Services/StringVerify/string_service.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "string_service_internal.h" #include "xbox_live_context_internal.h" using namespace xbox::services; using namespace xbox::services::system; NAMESPACE_MICROSOFT_XBOX_SERVICES_SYSTEM_CPP_BEGIN StringService::StringService( _In_ User&& user, _In_ std::shared_ptr contextSettings ) : m_user{ std::move(user) }, m_contextSettings{ std::move(contextSettings) } { } HRESULT StringService::VerifyStrings( _In_ const xsapi_internal_vector stringsToVerify, _In_ AsyncContext>> async ) { if (stringsToVerify.size() == 0) { return E_INVALIDARG; } JsonDocument request(rapidjson::kObjectType); JsonValue stringsJson(rapidjson::kArrayType); // = request[_T("stringstoVerify")]; for (const auto& stringToVerify : stringsToVerify) { stringsJson.PushBack(JsonValue(stringToVerify.c_str(), request.GetAllocator()).Move(), request.GetAllocator()); } request.AddMember("stringsToVerify", stringsJson, request.GetAllocator()); xsapi_internal_vector result; Result userResult = m_user.Copy(); RETURN_HR_IF_FAILED(userResult.Hresult()); auto httpCall = MakeShared(userResult.ExtractPayload()); HRESULT hr = httpCall->Init( m_contextSettings, "POST", XblHttpCall::BuildUrl("client-strings", "/system/strings/validate"), xbox_live_api::verify_strings ); RETURN_HR_IF_FAILED(hr); RETURN_HR_IF_FAILED(httpCall->SetXblServiceContractVersion(2)); RETURN_HR_IF_FAILED(httpCall->SetRequestBody(JsonUtils::SerializeJson(request))); return httpCall->Perform({ async.Queue(), [ async, result ] (HttpResult httpResult) { HRESULT hr { Failed(httpResult) ? httpResult.Hresult() : httpResult.Payload()->Result() }; if (FAILED(hr)) { return async.Complete(hr); } return async.Complete(VerifyStringResult::DeserializeVerifyStringsResult( httpResult.Payload()->GetResponseBodyJson())); } }); } NAMESPACE_MICROSOFT_XBOX_SERVICES_SYSTEM_CPP_END STDAPI XblStringVerifyStringAsync( _In_ XblContextHandle xboxLiveContextHandle, _In_ const char* stringToVerify, _In_ XAsyncBlock* async ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(xboxLiveContextHandle == nullptr || stringToVerify == nullptr || async == nullptr); return XblStringVerifyStringsAsync(xboxLiveContextHandle, &stringToVerify, 1, async); } CATCH_RETURN() STDAPI XblStringVerifyStringResultSize( _In_ XAsyncBlock* asyncBlock, _Out_ size_t* resultSizeInBytes ) XBL_NOEXCEPT try { return XAsyncGetResultSize(asyncBlock, resultSizeInBytes); } CATCH_RETURN() STDAPI XblStringVerifyStringResult( _In_ XAsyncBlock* asyncBlock, _In_ size_t bufferSize, _Out_writes_bytes_to_(bufferSize, *bufferUsed) void* buffer, _Outptr_ XblVerifyStringResult** ptrToBuffer, _Out_opt_ size_t* bufferUsed ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(ptrToBuffer); auto hr = XAsyncGetResult(asyncBlock, nullptr, bufferSize, buffer, bufferUsed); if (SUCCEEDED(hr)) { *ptrToBuffer = static_cast(buffer); } return hr; } CATCH_RETURN() STDAPI XblStringVerifyStringsAsync( _In_ XblContextHandle xboxLiveContextHandle, _In_ const char** stringsToVerify, _In_ const uint64_t stringsCount, _In_ XAsyncBlock* async ) XBL_NOEXCEPT try { xsapi_internal_vector strings; for (uint32_t i = 0; i < stringsCount; i++) { strings.push_back(xsapi_internal_string(stringsToVerify[i])); } RETURN_HR_INVALIDARGUMENT_IF(xboxLiveContextHandle == nullptr || stringsToVerify == nullptr || async == nullptr); return RunAsync(async, __FUNCTION__, [ xboxLiveContext{ xboxLiveContextHandle->shared_from_this() }, strings, verifyStringResults = xsapi_internal_vector{} ] (XAsyncOp op, const XAsyncProviderData* data) mutable { switch (op) { case XAsyncOp::DoWork: { HRESULT hr = xboxLiveContext->StringService()->VerifyStrings( std::move(strings), AsyncContext>>{ data->async->queue, [ &verifyStringResults, asyncBlock{ data->async } ] (Result> result) { if (Succeeded(result)) { auto payload = result.ExtractPayload(); size_t bufferSize = 0; if (!payload.empty()) { for (size_t i = 0; i < payload.size(); i++) { verifyStringResults.push_back(payload[i]); bufferSize += payload[i].SizeOf(); } // Add some padding at the end of the buffer to store the number of XblVerifyStringResult // objects since there is no way to deduce it just from the buffer size bufferSize += sizeof(size_t); // size must be rounded up to support word aligned data because of the arbitrary length string packing // we won't actually use the extra space, but this will report that it is how much is needed bufferSize = static_cast((bufferSize + XBL_ALIGN_SIZE - 1) / XBL_ALIGN_SIZE) * XBL_ALIGN_SIZE; XAsyncComplete(asyncBlock, result.Hresult(), bufferSize); } else { XAsyncComplete(asyncBlock, E_FAIL, 0); } } else { XAsyncComplete(asyncBlock, result.Hresult(), 0); } } }); return SUCCEEDED(hr) ? E_PENDING : hr; } case XAsyncOp::GetResult: { char* buffer = static_cast(data->buffer); ZeroMemory(buffer, data->bufferSize); auto resultPtr = reinterpret_cast(buffer); auto stringPtr = buffer + (sizeof(XblVerifyStringResult)*verifyStringResults.size()); size_t bufferSize{ sizeof(size_t) }; for (auto& verifyStringResult : verifyStringResults) { bufferSize += verifyStringResult.SizeOf(); resultPtr->resultCode = verifyStringResult.ResultCode(); if (resultPtr->resultCode != XblVerifyStringResultCode::Success) { utils::strcpy(stringPtr, verifyStringResult.FirstOffendingSubstring().size() + 1, verifyStringResult.FirstOffendingSubstring().c_str()); resultPtr->firstOffendingSubstring = stringPtr; stringPtr += verifyStringResult.FirstOffendingSubstring().size() + 1; } else { resultPtr->firstOffendingSubstring = nullptr; } resultPtr++; } // Calculate buffer used plus padding (to stay word aligned, see above) bufferSize = static_cast((bufferSize + XBL_ALIGN_SIZE - 1) / XBL_ALIGN_SIZE) * XBL_ALIGN_SIZE; size_t* resultArrSize = reinterpret_cast(buffer + bufferSize) - 1; *resultArrSize = verifyStringResults.size(); } default: return S_OK; } }); } CATCH_RETURN() STDAPI XblStringVerifyStringsResultSize( _In_ XAsyncBlock* asyncBlock, _Out_ size_t* resultSizeInBytes ) XBL_NOEXCEPT try { return XAsyncGetResultSize(asyncBlock, resultSizeInBytes); } CATCH_RETURN() STDAPI XblStringVerifyStringsResult( _In_ XAsyncBlock* asyncBlock, _In_ size_t bufferSize, _Out_writes_bytes_to_(bufferSize, *bufferUsed) void* buffer, _Outptr_ XblVerifyStringResult** ptrToBuffer, _Out_ size_t* stringsCount, _Out_opt_ size_t* bufferUsed ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(ptrToBuffer); size_t bufferUsedTemp{}; if (bufferUsed == nullptr) { bufferUsed = &bufferUsedTemp; } auto hr = XAsyncGetResult(asyncBlock, nullptr, bufferSize, buffer, bufferUsed); if (SUCCEEDED(hr)) { *ptrToBuffer = static_cast(buffer); auto sizePtr = reinterpret_cast(static_cast(buffer) + *bufferUsed) - 1; *stringsCount = *sizePtr; } return hr; } CATCH_RETURN() ================================================ FILE: Source/Services/StringVerify/string_service_internal.h ================================================ #pragma once #include "xsapi-c/string_verify_c.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_SYSTEM_CPP_BEGIN class VerifyStringResult { public: VerifyStringResult(); VerifyStringResult( XblVerifyStringResultCode resultCode, xsapi_internal_string firstOffendingSubstring ); const XblVerifyStringResultCode ResultCode() const; const xsapi_internal_string& FirstOffendingSubstring() const; static Result> DeserializeVerifyStringsResult( _In_ const JsonValue& json ); size_t SizeOf() const; private: XblVerifyStringResultCode m_resultCode = XblVerifyStringResultCode::Success; xsapi_internal_string m_firstOffendingSubstring; static Result DeserializeVerifyStringResult( _In_ const JsonValue& json ); }; class StringService : public std::enable_shared_from_this { public: StringService( _In_ User&& user, _In_ std::shared_ptr contextSettings ); HRESULT VerifyStrings( _In_ const xsapi_internal_vector stringsToVerify, _In_ AsyncContext>> async ); private: User m_user; std::shared_ptr m_contextSettings; }; NAMESPACE_MICROSOFT_XBOX_SERVICES_SYSTEM_CPP_END ================================================ FILE: Source/Services/StringVerify/verify_string_result.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "string_service_internal.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_SYSTEM_CPP_BEGIN VerifyStringResult::VerifyStringResult() { } VerifyStringResult::VerifyStringResult( XblVerifyStringResultCode resultCode, xsapi_internal_string firstOffendingSubstring ) : m_resultCode(resultCode), m_firstOffendingSubstring(firstOffendingSubstring) { } const XblVerifyStringResultCode VerifyStringResult::ResultCode() const { return m_resultCode; } const xsapi_internal_string& VerifyStringResult::FirstOffendingSubstring() const { return m_firstOffendingSubstring; } size_t VerifyStringResult::SizeOf() const { size_t size = sizeof(XblVerifyStringResult); if (m_resultCode != XblVerifyStringResultCode::Success) { size += m_firstOffendingSubstring.length() + 1; } return size; } /*static*/ Result> VerifyStringResult::DeserializeVerifyStringsResult( _In_ const JsonValue& json ) { if (json.IsNull()) { return WEB_E_INVALID_JSON_STRING; } HRESULT errc = S_OK; xsapi_internal_vector resultVector; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonVector( VerifyStringResult::DeserializeVerifyStringResult, json, "verifyStringResult", resultVector, true )); return Result> {resultVector, errc}; } /*static*/ Result VerifyStringResult::DeserializeVerifyStringResult( _In_ const JsonValue& json ) { VerifyStringResult returnResult; if (json.IsNull()) { return returnResult; } int32_t resultCode = 0; xsapi_internal_string offendingString; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonInt(json, "resultCode", resultCode)); RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "offendingString", offendingString)); returnResult = VerifyStringResult( static_cast(resultCode), offendingString ); return Result(returnResult, S_OK); } NAMESPACE_MICROSOFT_XBOX_SERVICES_SYSTEM_CPP_END ================================================ FILE: Source/Services/TCUI/Android/title_callable_static_glue.h ================================================ #pragma once bool title_callable_ui_register_natives(JNIEnv *env, jobject clsLoader, jmethodID loadClass); ================================================ FILE: Source/Services/TCUI/Android/title_callable_ui_android.cpp ================================================ //********************************************************* // // Copyright (c) Microsoft. All rights reserved. // THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF // ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY // IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR // PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. // //********************************************************* #include "pch.h" #include "xsapi-cpp/title_callable_ui.h" #include "a/java_interop.h" #include "a/jni_utils.h" #include "TCUI/Android/title_callable_ui_jni.h" using namespace pplx; NAMESPACE_MICROSOFT_XBOX_SERVICES_SYSTEM_CPP_BEGIN bool title_callable_ui_internal::s_isTcuiRunning = false; pplx::task_completion_event title_callable_ui_internal::s_tcuiEventCompleted; void title_callable_ui_internal::tcui_completed_callback(JNIEnv *, jclass, int errorCode) { s_tcuiEventCompleted.set(errorCode); } xbox_live_result> tcui_init() { if (title_callable_ui_internal::s_isTcuiRunning) { return xbox_live_result>(xbox_live_result>( xbox_live_error_code::logic_error, _T("Previous tcui operation has not been completed")) ); } std::shared_ptr interop = java_interop::get_java_interop_singleton(); if (interop == nullptr) { return xbox_live_result>(xbox_live_result>( xbox_live_error_code::logic_error, _T("XSAPI has not been initialized with the JVM")) ); } title_callable_ui_internal::s_tcuiEventCompleted = task_completion_event(); return interop; } pplx::task> title_callable_ui::show_profile_card_ui( _In_ const string_t& targetXboxUserId, _In_ xbox_live_user_t user ) { XblAddServiceCallRoutedHandler( [](XblServiceCallRoutedArgs args, void* context) { LOG_DEBUG(args.fullResponseFormatted); }, nullptr ); auto interopResult = tcui_init(); if (interopResult.err()) { return pplx::task_from_result>( xbox_live_result( interopResult.err(), interopResult.err_message() ) ); } auto task = pplx::create_task(title_callable_ui_internal::s_tcuiEventCompleted) .then([](int32_t errorCode) { title_callable_ui_internal::s_isTcuiRunning = false; (void) java_interop::get_java_interop_singleton()->ExtractStoredUser(); return xbox_live_result(xbox_live_result()); }); auto interop = interopResult.payload(); auto jvm = interop->get_java_vm(); JVM_CHECK_RETURN_TASK_RESULT_VOID(jvm, "java interop not initialized properly") auto interopTcuiClass = interop->get_tcui_interop_class(); auto activity = interop->get_activity(); JNIEnv* jniEnv; JNI_ATTACH_THREAD(jvm, jniEnv); jmethodID showProfileCardUIMethodId = jniEnv->GetStaticMethodID(interopTcuiClass, "ShowProfileCardUI", "(Landroid/app/Activity;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); auto wrapUserResult = User::WrapHandle(user); if (showProfileCardUIMethodId != NULL && Succeeded(wrapUserResult)) { java_interop::get_java_interop_singleton()->StoreUser(wrapUserResult.ExtractPayload()); title_callable_ui_internal::s_isTcuiRunning = true; uint64_t xuid; XalUserGetId(user, &xuid); xsapi_internal_stringstream xuidSS; xuidSS << xuid; // The only privileges that are needed for TCUI are 254 (Communications) and 255 (AddFriends) so just query those here xsapi_internal_stringstream privilegesSS; bool hasPrivilege; XalUserCheckPrivilege(user, XalPrivilege::XalPrivilege_Comms, &hasPrivilege, nullptr); if (hasPrivilege) { privilegesSS << XalPrivilege::XalPrivilege_Comms; } XalUserCheckPrivilege(user, XalPrivilege::XalPrivilege_AddFriends, &hasPrivilege, nullptr); if (hasPrivilege) { privilegesSS << " " << XalPrivilege::XalPrivilege_AddFriends; } auto currentUserIdString = jniEnv->NewStringUTF(xuidSS.str().data()); auto targetUserIdString = jniEnv->NewStringUTF(targetXboxUserId.c_str()); auto currentUserPrivileges = jniEnv->NewStringUTF(privilegesSS.str().data()); jniEnv->CallStaticVoidMethod(interopTcuiClass, showProfileCardUIMethodId, activity, currentUserIdString, targetUserIdString, currentUserPrivileges, user); } else { pplx::task_from_result>(xbox_live_result( xbox_live_error_code::logic_error, _T("ShowProfileCardUI method not found")) ); } JNI_ERROR_CHECK(jniEnv) return task; } pplx::task> title_callable_ui::show_user_profile_ui( _In_ const string_t& targetXboxUserId ) { auto interopResult = tcui_init(); if (interopResult.err()) { return pplx::task_from_result>( xbox_live_result( interopResult.err(), interopResult.err_message() ) ); } auto task = pplx::create_task(title_callable_ui_internal::s_tcuiEventCompleted) .then([](int32_t errorCode) { title_callable_ui_internal::s_isTcuiRunning = false; return xbox_live_result(xbox_live_result()); }); auto interop = interopResult.payload(); auto jvm = interop->get_java_vm(); JVM_CHECK_RETURN_TASK_RESULT_VOID(jvm, "java interop not initialized properly") auto interopTcuiClass = interop->get_tcui_interop_class(); auto activity = interop->get_activity(); JNIEnv* jniEnv; JNI_ATTACH_THREAD(jvm, jniEnv); jmethodID showUserProfileMethodId = jniEnv->GetStaticMethodID(interopTcuiClass, "ShowUserProfile", "(Landroid/content/Context;Ljava/lang/String;J)V"); if (showUserProfileMethodId != NULL) { auto targetXuid = jniEnv->NewStringUTF(targetXboxUserId.c_str()); jniEnv->CallStaticVoidMethod(interopTcuiClass, showUserProfileMethodId, activity, targetXuid); } JNI_ERROR_CHECK(jniEnv); return task; } pplx::task> title_callable_ui::show_title_hub_ui() { auto interopResult = tcui_init(); if (interopResult.err()) { return pplx::task_from_result>( xbox_live_result( interopResult.err(), interopResult.err_message() ) ); } auto task = pplx::create_task(title_callable_ui_internal::s_tcuiEventCompleted) .then([](int32_t errorCode) { title_callable_ui_internal::s_isTcuiRunning = false; return xbox_live_result(xbox_live_result()); }); auto interop = interopResult.payload(); auto jvm = interop->get_java_vm(); JVM_CHECK_RETURN_TASK_RESULT_VOID(jvm, "java interop not initialized properly") auto interopTcuiClass = interop->get_tcui_interop_class(); auto activity = interop->get_activity(); auto titleId = AppConfig::Instance()->TitleId(); JNIEnv* jniEnv; JNI_ATTACH_THREAD(jvm, jniEnv); jmethodID showTitleHubMethodId = jniEnv->GetStaticMethodID(interopTcuiClass, "ShowTitleHub", "(Landroid/content/Context;Ljava/lang/String;)V"); if (showTitleHubMethodId != NULL) { stringstream_t titleIdStr; titleIdStr << titleId; auto jTitleId = jniEnv->NewStringUTF(titleIdStr.str().c_str()); jniEnv->CallStaticVoidMethod(interopTcuiClass, showTitleHubMethodId, activity, jTitleId); } JNI_ERROR_CHECK(jniEnv); return task; } pplx::task> title_callable_ui::show_title_achievements_ui( _In_ uint32_t titleId ) { auto interopResult = tcui_init(); if (interopResult.err()) { return pplx::task_from_result>( xbox_live_result( interopResult.err(), interopResult.err_message() ) ); } auto task = pplx::create_task(title_callable_ui_internal::s_tcuiEventCompleted) .then([](int32_t errorCode) { title_callable_ui_internal::s_isTcuiRunning = false; return xbox_live_result(xbox_live_result()); }); auto interop = interopResult.payload(); auto jvm = interop->get_java_vm(); JVM_CHECK_RETURN_TASK_RESULT_VOID(jvm, "java interop not initialized properly") auto interopTcuiClass = interop->get_tcui_interop_class(); auto activity = interop->get_activity(); JNIEnv* jniEnv; JNI_ATTACH_THREAD(jvm, jniEnv); jmethodID showTitleAchievementsMethodId = jniEnv->GetStaticMethodID(interopTcuiClass, "ShowTitleAchievements", "(Landroid/content/Context;Ljava/lang/String;)V"); if (showTitleAchievementsMethodId != NULL) { stringstream_t titleIdStr; titleIdStr << titleId; auto jTitleId = jniEnv->NewStringUTF(titleIdStr.str().c_str()); jniEnv->CallStaticVoidMethod(interopTcuiClass, showTitleAchievementsMethodId, activity, jTitleId); } JNI_ERROR_CHECK(jniEnv); return task; } pplx::task> title_callable_ui::show_user_settings_ui() { auto interopResult = tcui_init(); if (interopResult.err()) { return pplx::task_from_result>( xbox_live_result( interopResult.err(), interopResult.err_message() ) ); } auto task = pplx::create_task(title_callable_ui_internal::s_tcuiEventCompleted) .then([](int32_t errorCode) { title_callable_ui_internal::s_isTcuiRunning = false; return xbox_live_result(xbox_live_result()); }); auto interop = interopResult.payload(); auto jvm = interop->get_java_vm(); JVM_CHECK_RETURN_TASK_RESULT_VOID(jvm, "java interop not initialized properly") auto interopTcuiClass = interop->get_tcui_interop_class(); auto context = interop->get_context_object(); JNIEnv* jniEnv; JNI_ATTACH_THREAD(jvm, jniEnv); jmethodID showUserSettingsMethodId = jniEnv->GetStaticMethodID(interopTcuiClass, "ShowUserSettings", "(Landroid/content/Context;)V"); if (showUserSettingsMethodId != NULL) { jniEnv->CallStaticVoidMethod(interopTcuiClass, showUserSettingsMethodId, context); } JNI_ERROR_CHECK(jniEnv); return task; } pplx::task> title_callable_ui::show_add_friends_ui() { auto interopResult = tcui_init(); if (interopResult.err()) { return pplx::task_from_result>( xbox_live_result( interopResult.err(), interopResult.err_message() ) ); } auto task = pplx::create_task(title_callable_ui_internal::s_tcuiEventCompleted) .then([](int32_t errorCode) { title_callable_ui_internal::s_isTcuiRunning = false; return xbox_live_result(xbox_live_result()); }); auto interop = interopResult.payload(); auto jvm = interop->get_java_vm(); JVM_CHECK_RETURN_TASK_RESULT_VOID(jvm, "java interop not initialized properly") auto interopTcuiClass = interop->get_tcui_interop_class(); auto context = interop->get_context_object(); JNIEnv* jniEnv; JNI_ATTACH_THREAD(jvm, jniEnv); jmethodID showAddFriendsMethodId = jniEnv->GetStaticMethodID(interopTcuiClass, "ShowAddFriends", "(Landroid/content/Context;)V"); if (showAddFriendsMethodId != NULL) { jniEnv->CallStaticVoidMethod(interopTcuiClass, showAddFriendsMethodId, context); } JNI_ERROR_CHECK(jniEnv); return task; } pplx::task>> title_callable_ui::show_player_picker_ui( _In_ const string_t& promptDisplayText, _In_ const std::vector& xboxUserIds, _In_ const std::vector& preselectedXboxUserIds, _In_ uint32_t minSelectionCount, _In_ uint32_t maxSelectionCount ) { return pplx::task_from_result>>(xbox::services::xbox_live_result>(xbox_live_error_code::unsupported)); } pplx::task> title_callable_ui::show_game_invite_ui( _In_ const xbox::services::multiplayer::multiplayer_session_reference& sessionReference, _In_ const string_t& invitationDisplayText, _In_ const string_t& contextStringId ) { return pplx::task_from_result>(xbox::services::xbox_live_result(xbox_live_error_code::unsupported)); } pplx::task> title_callable_ui::show_change_friend_relationship_ui( _In_ const string_t& targetXboxUserId ) { return pplx::task_from_result>(xbox::services::xbox_live_result(xbox_live_error_code::unsupported)); } NAMESPACE_MICROSOFT_XBOX_SERVICES_SYSTEM_CPP_END ================================================ FILE: Source/Services/TCUI/Android/title_callable_ui_jni.h ================================================ #pragma once namespace xbox { namespace services { namespace system { class title_callable_ui_internal { public: static void tcui_completed_callback(JNIEnv *, jclass, int errorCode); static pplx::task_completion_event s_tcuiEventCompleted; static bool s_isTcuiRunning; }; } } } ================================================ FILE: Source/Services/TCUI/Android/title_callable_ui_static_glue.cpp ================================================ #include "pch.h" #include "TCUI/Android/title_callable_ui_jni.h" #include #include "a/jni_utils.h" #define LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, "XSAPI.Android", __VA_ARGS__)) #define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "XSAPI.Android", __VA_ARGS__)) JNINativeMethod tcuiMethods[] = { { "tcui_completed_callback", "(I)V", (void*)&xbox::services::system::title_callable_ui_internal::tcui_completed_callback }, }; bool title_callable_ui_register_natives(JNIEnv *env, jobject clsLoader, jmethodID loadClass) { jstring clsName = env->NewStringUTF("com/microsoft/xboxtcui/Interop"); JNI_ERROR_CHECK(env); jclass cls = (jclass)env->CallObjectMethod(clsLoader, loadClass, clsName); env->DeleteLocalRef(clsName); if (cls == NULL) { LOGE("Failed to load class com/microsoft/xboxtcui/Interop"); return false; } auto size = (sizeof tcuiMethods / sizeof *tcuiMethods); if (env->RegisterNatives(cls, tcuiMethods, size) != 0) { LOGE("Failed to register native tcuiMethods"); env->DeleteLocalRef(cls); return false; } env->DeleteLocalRef(cls); LOGD("Successfully registerered HttpCall tcuiMethods"); return true; } ================================================ FILE: Source/Services/TCUI/iOS/title_callable_ui.mm ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "xsapi-cpp/system.h" #include "xsapi-cpp/title_callable_ui.h" #import "XBLServiceManager.h" #import #import static NSString *const XboxAppStoreLink = @"itms-apps://itunes.apple.com/app/xbox-one-smartglass/id736179781?mt=8"; using namespace pplx; using namespace xbox::services; using namespace xbox::services::system; pplx::task> title_callable_ui::show_profile_card_ui( _In_ const string_t& targetXboxUserId, _In_ xbox_live_user_t user ) { auto completionEvent = pplx::task_completion_event>(); XBLServiceManager *manager = [[XBLServiceManager alloc] init]; [manager setUser:user]; [XBLService setUpWithServiceManager:manager]; [TCUIManager displayProfileCard:manager.userXuid youXuid:[NSString stringWithUTF8String:targetXboxUserId.c_str()] canAddFriend: manager.privilegeForAddFriend completionBlock:^{ completionEvent.set(xbox_live_result()); }]; return create_task(completionEvent); } pplx::task> title_callable_ui::show_user_profile_ui(_In_ const string_t& targetXboxUserId) { auto completionEvent = pplx::task_completion_event>(); auto task = create_task(completionEvent); /* TODO 1808 XLSCll *telemetry = [XLSCll sharedTelemetryManager]; NSString *deeplink = [NSString stringWithFormat:@"smartglass://profile?xuid=%@&deepLinkId=%@&deepLinkCaller=%@", [NSString stringWithUTF8String:targetXboxUserId.c_str()], [telemetry getAppSessionId], [telemetry getAppBundleName]]; if ([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:deeplink]]) { [telemetry pageActionEvent:IDP_PageAction_DeepLink_UserProfile withData:@{@"deepLinkId" : [telemetry getAppSessionId], @"deepLinkCaller" : [telemetry getAppBundleName], @"targetXUID" : [NSString stringWithFormat:@"x:%@",[NSString stringWithUTF8String:targetXboxUserId.c_str()]]}]; [[UIApplication sharedApplication] openURL:[NSURL URLWithString:deeplink]]; } else { [telemetry pageActionEvent:IDP_PageAction_DeepLink_SendToStore withData:@{@"deepLinkId" : [telemetry getAppSessionId], @"deepLinkCaller" : [telemetry getAppBundleName], @"intendedAction" : IDP_PageAction_DeepLink_UserProfile}]; [[UIApplication sharedApplication] openURL:[NSURL URLWithString:XboxAppStoreLink]]; } */ completionEvent.set(xbox_live_result()); return task; } pplx::task> title_callable_ui::show_title_hub_ui() { auto completionEvent = pplx::task_completion_event>(); auto task = create_task(completionEvent); /* TODO 1808 auto titleId = xbox_live_app_config::get_app_config_singleton()->title_id(); XLSCll *telemetry = [XLSCll sharedTelemetryManager]; NSString *deeplink = [NSString stringWithFormat:@"smartglass://game?titleid=%u&deepLinkId=%@&deepLinkCaller=%@", titleId, [telemetry getAppSessionId], [telemetry getAppBundleName]]; if ([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:deeplink]]) { [telemetry pageActionEvent:IDP_PageAction_DeepLink_TitleHub withData:@{@"deepLinkId" : [telemetry getAppSessionId], @"deepLinkCaller" : [telemetry getAppBundleName], @"targetTitleId" : [NSNumber numberWithInt:titleId]}]; [[UIApplication sharedApplication] openURL:[NSURL URLWithString:deeplink]]; } else { [telemetry pageActionEvent:IDP_PageAction_DeepLink_SendToStore withData:@{@"deepLinkId" : [telemetry getAppSessionId], @"deepLinkCaller" : [telemetry getAppBundleName], @"intendedAction" : IDP_PageAction_DeepLink_TitleHub}]; [[UIApplication sharedApplication] openURL:[NSURL URLWithString:XboxAppStoreLink]]; } */ completionEvent.set(xbox_live_result()); return task; } pplx::task> title_callable_ui::show_title_achievements_ui(_In_ uint32_t titleId) { auto completionEvent = pplx::task_completion_event>(); auto task = create_task(completionEvent); /* TODO 1808 XLSCll *telemetry = [XLSCll sharedTelemetryManager]; NSString *deeplink = [NSString stringWithFormat:@"smartglass://achievement?titleid=%u&deepLinkId=%@&deepLinkCaller=%@", titleId, [telemetry getAppSessionId], [telemetry getAppBundleName]]; if ([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:deeplink]]) { [telemetry pageActionEvent:IDP_PageAction_DeepLink_TitleAchievements withData:@{@"deepLinkId" : [telemetry getAppSessionId], @"deepLinkCaller" : [telemetry getAppBundleName], @"targetTitleId" : [NSNumber numberWithInt:titleId]}]; [[UIApplication sharedApplication] openURL:[NSURL URLWithString:deeplink]]; } else { [telemetry pageActionEvent:IDP_PageAction_DeepLink_SendToStore withData:@{@"deepLinkId" : [telemetry getAppSessionId], @"deepLinkCaller" : [telemetry getAppBundleName], @"intendedAction" : IDP_PageAction_DeepLink_TitleAchievements}]; [[UIApplication sharedApplication] openURL:[NSURL URLWithString:XboxAppStoreLink]]; } */ completionEvent.set(xbox_live_result()); return task; } pplx::task> title_callable_ui::show_user_settings_ui() { auto completionEvent = pplx::task_completion_event>(); auto task = create_task(completionEvent); /* TODO 1808 XLSCll *telemetry = [XLSCll sharedTelemetryManager]; NSString *deeplink = [NSString stringWithFormat:@"smartglass://settings?deepLinkId=%@&deepLinkCaller=%@", [telemetry getAppSessionId], [telemetry getAppBundleName]]; if ([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:deeplink]]) { [telemetry pageActionEvent:IDP_PageAction_DeepLink_UserSettings withData:@{@"deepLinkId" : [telemetry getAppSessionId], @"deepLinkCaller" : [telemetry getAppBundleName]}]; [[UIApplication sharedApplication] openURL:[NSURL URLWithString:deeplink]]; } else { [telemetry pageActionEvent:IDP_PageAction_DeepLink_SendToStore withData:@{@"deepLinkId" : [telemetry getAppSessionId], @"deepLinkCaller" : [telemetry getAppBundleName], @"intendedAction" : IDP_PageAction_DeepLink_UserSettings}]; [[UIApplication sharedApplication] openURL:[NSURL URLWithString:XboxAppStoreLink]]; } */ completionEvent.set(xbox_live_result()); return task; } pplx::task> title_callable_ui::show_add_friends_ui() { return pplx::task_from_result>(xbox::services::xbox_live_result(xbox_live_error_code::unsupported)); } pplx::task>> title_callable_ui::show_player_picker_ui( _In_ const string_t& promptDisplayText, _In_ const std::vector& xboxUserIds, _In_ const std::vector& preselectedXboxUserIds, _In_ uint32_t minSelectionCount, _In_ uint32_t maxSelectionCount ) { return pplx::task_from_result>>(xbox::services::xbox_live_result>(xbox_live_error_code::unsupported)); } pplx::task> title_callable_ui::show_game_invite_ui( _In_ const xbox::services::multiplayer::multiplayer_session_reference& sessionReference, _In_ const string_t& invitationDisplayText, _In_ const string_t& contextStringId ) { return pplx::task_from_result>(xbox::services::xbox_live_result(xbox_live_error_code::unsupported)); } pplx::task> title_callable_ui::show_change_friend_relationship_ui( _In_ const string_t& targetXboxUserId ) { return pplx::task_from_result>(xbox::services::xbox_live_result(xbox_live_error_code::unsupported)); } ================================================ FILE: Source/Services/TitleStorage/title_storage_api.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "xsapi-c/title_storage_c.h" #include "title_storage_internal.h" #include "xbox_live_context_internal.h" using namespace xbox::services; using namespace xbox::services::title_storage; STDAPI XblTitleStorageBlobMetadataResultGetItems( _In_ XblTitleStorageBlobMetadataResultHandle resultHandle, _Out_ const XblTitleStorageBlobMetadata** items, _Out_ size_t* itemsCount ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(resultHandle == nullptr || items == nullptr || itemsCount == nullptr); VERIFY_XBL_INITIALIZED(); *items = resultHandle->Items().data(); *itemsCount = resultHandle->Items().size(); return S_OK; } CATCH_RETURN() STDAPI XblTitleStorageBlobMetadataResultHasNext( _In_ XblTitleStorageBlobMetadataResultHandle resultHandle, _Out_ bool* hasNext ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(resultHandle == nullptr || hasNext == nullptr); VERIFY_XBL_INITIALIZED(); *hasNext = resultHandle->HasNext(); return S_OK; } CATCH_RETURN() STDAPI XblTitleStorageBlobMetadataResultGetNextAsync( _In_ XblTitleStorageBlobMetadataResultHandle resultHandle, _In_ uint32_t maxItems, _In_ XAsyncBlock* async ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(resultHandle == nullptr || async == nullptr); VERIFY_XBL_INITIALIZED(); std::shared_ptr cResult{ nullptr }; return RunAsync(async, __FUNCTION__, [ resultHandle, maxItems, async, cResult ] (XAsyncOp op, const XAsyncProviderData* data) mutable { if (op == XAsyncOp::DoWork) { HRESULT hr = resultHandle->GetNext( maxItems, AsyncContext>> (async->queue, [async, &cResult](Result> result) { if (Succeeded(result)) { cResult = result.ExtractPayload(); } XAsyncComplete(async, result.Hresult(), sizeof(XblTitleStorageBlobMetadataResultHandle)); } )); return SUCCEEDED(hr) ? E_PENDING : hr; } else if (op == XAsyncOp::GetResult) { auto resultHandlePtr = static_cast(data->buffer); *resultHandlePtr = cResult.get(); cResult->AddRef(); } return S_OK; }); } CATCH_RETURN() STDAPI XblTitleStorageBlobMetadataResultGetNextResult( _In_ XAsyncBlock* async, _Out_ XblTitleStorageBlobMetadataResultHandle* result ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(result == nullptr); VERIFY_XBL_INITIALIZED(); return XAsyncGetResult(async, nullptr, sizeof(XblTitleStorageBlobMetadataResultHandle), result, nullptr); } CATCH_RETURN() STDAPI XblTitleStorageBlobMetadataResultDuplicateHandle( _In_ XblTitleStorageBlobMetadataResultHandle handle, _Out_ XblTitleStorageBlobMetadataResultHandle* duplicatedHandle ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(handle == nullptr || duplicatedHandle == nullptr); VERIFY_XBL_INITIALIZED(); handle->AddRef(); *duplicatedHandle = handle; return S_OK; } CATCH_RETURN() STDAPI_(void) XblTitleStorageBlobMetadataResultCloseHandle( _In_ XblTitleStorageBlobMetadataResultHandle handle ) XBL_NOEXCEPT try { if (handle) { handle->DecRef(); } } CATCH_RETURN_WITH(;) STDAPI XblTitleStorageGetQuotaAsync( _In_ XblContextHandle xboxLiveContext, _In_z_ const char* serviceConfigurationId, _In_ XblTitleStorageType storageType, _In_ XAsyncBlock* async ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(xboxLiveContext == nullptr || serviceConfigurationId == nullptr || async == nullptr); VERIFY_XBL_INITIALIZED(); XblTitleStorageQuota cResult; return RunAsync(async, __FUNCTION__, [ xblContext{ xboxLiveContext->shared_from_this() }, scid{ xsapi_internal_string(serviceConfigurationId) }, storageType, async, cResult ] (XAsyncOp op, const XAsyncProviderData* data) mutable { if (op == XAsyncOp::DoWork) { HRESULT hr = xblContext->TitleStorageService()->GetQuota( scid, storageType, AsyncContext> (async->queue, [async, &cResult](Result result) { if (Succeeded(result)) { cResult = result.ExtractPayload(); } XAsyncComplete(async, result.Hresult(), sizeof(XblTitleStorageQuota)); } )); return SUCCEEDED(hr) ? E_PENDING : hr; } else if (op == XAsyncOp::GetResult) { auto resultHandle = static_cast(data->buffer); *resultHandle = std::move(cResult); } return S_OK; }); } CATCH_RETURN() STDAPI XblTitleStorageGetQuotaResult( _In_ XAsyncBlock* async, _Out_ size_t* usedBytes, _Out_ size_t* quotaBytes ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(async == nullptr || usedBytes == nullptr || quotaBytes == nullptr); VERIFY_XBL_INITIALIZED(); XblTitleStorageQuota quota{}; HRESULT hr = XAsyncGetResult(async, nullptr, sizeof(XblTitleStorageQuota), "a, nullptr); if (SUCCEEDED(hr)) { *usedBytes = quota.usedBytes; *quotaBytes = quota.quotaBytes; } return hr; } CATCH_RETURN() STDAPI XblTitleStorageGetBlobMetadataAsync( _In_ XblContextHandle xboxLiveContext, _In_z_ const char* serviceConfigurationId, _In_ XblTitleStorageType storageType, _In_z_ const char* blobPath, _In_ uint64_t xuid, _In_ uint32_t skipItems, _In_ uint32_t maxItems, _In_ XAsyncBlock* async ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(xboxLiveContext == nullptr || serviceConfigurationId == nullptr || async == nullptr); VERIFY_XBL_INITIALIZED(); std::shared_ptr cResult; return RunAsync(async, __FUNCTION__, [ xblContext{ xboxLiveContext->shared_from_this() }, scid{ xsapi_internal_string(serviceConfigurationId) }, storageType, blobPathStr{ xsapi_internal_string(blobPath) }, xuid, skipItems, maxItems, async, cResult ] (XAsyncOp op, const XAsyncProviderData* data) mutable { if (op == XAsyncOp::DoWork) { HRESULT hr = xblContext->TitleStorageService()->GetBlobMetadata( scid, storageType, blobPathStr, xuid, skipItems, maxItems, "", AsyncContext>> (async->queue, [async, &cResult](Result> result) { if (Succeeded(result)) { cResult = result.ExtractPayload(); } XAsyncComplete(async, result.Hresult(), sizeof(XblTitleStorageBlobMetadataResultHandle)); } )); return SUCCEEDED(hr) ? E_PENDING : hr; } else if (op == XAsyncOp::GetResult) { auto resultHandle = static_cast(data->buffer); *resultHandle = cResult.get(); cResult->AddRef(); } return S_OK; }); } CATCH_RETURN() STDAPI XblTitleStorageGetBlobMetadataResult( _In_ XAsyncBlock* async, _Out_ XblTitleStorageBlobMetadataResultHandle* result ) XBL_NOEXCEPT try { return XAsyncGetResult(async, nullptr, sizeof(XblTitleStorageBlobMetadataResultHandle), result, nullptr); } CATCH_RETURN() STDAPI XblTitleStorageDeleteBlobAsync( _In_ XblContextHandle xboxLiveContext, _In_ XblTitleStorageBlobMetadata blobMetadata, _In_ bool deleteOnlyIfEtagMatches, _In_ XAsyncBlock* async ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(xboxLiveContext == nullptr || async == nullptr); RETURN_HR_INVALIDARGUMENT_IF(blobMetadata.blobType == XblTitleStorageBlobType::Unknown); RETURN_HR_INVALIDARGUMENT_IF_EMPTY_STRING(blobMetadata.serviceConfigurationId); RETURN_HR_INVALIDARGUMENT_IF_EMPTY_STRING(blobMetadata.blobPath); VERIFY_XBL_INITIALIZED(); return RunAsync(async, __FUNCTION__, [ xblContext{ xboxLiveContext->shared_from_this() }, blobMetadata, deleteOnlyIfEtagMatches, async ] (XAsyncOp op, const XAsyncProviderData* data) mutable { UNREFERENCED_PARAMETER(data); if (op == XAsyncOp::DoWork) { HRESULT hr = xblContext->TitleStorageService()->DeleteBlob( blobMetadata, deleteOnlyIfEtagMatches, AsyncContext(async) ); return SUCCEEDED(hr) ? E_PENDING : hr; } return S_OK; }); } CATCH_RETURN() STDAPI XblTitleStorageDownloadBlobAsync( _In_ XblContextHandle xboxLiveContext, _In_ XblTitleStorageBlobMetadata blobMetadata, _Out_writes_(blobBufferCount) uint8_t* blobBuffer, _In_ size_t blobBufferCount, _In_ XblTitleStorageETagMatchCondition etagMatchCondition, _In_opt_z_ const char* selectQuery, _In_ size_t preferredDownloadBlockSize, _In_ XAsyncBlock* async ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(xboxLiveContext == nullptr || blobBuffer == nullptr || async == nullptr); RETURN_HR_INVALIDARGUMENT_IF(blobMetadata.blobType == XblTitleStorageBlobType::Unknown); RETURN_HR_INVALIDARGUMENT_IF_EMPTY_STRING(blobMetadata.serviceConfigurationId); RETURN_HR_INVALIDARGUMENT_IF_EMPTY_STRING(blobMetadata.blobPath); VERIFY_XBL_INITIALIZED(); XblTitleStorageBlobMetadata cResult; return RunAsync(async, __FUNCTION__, [ xblContext{ xboxLiveContext->shared_from_this() }, blobMetadata, blobBuffer, blobBufferCount, etagMatchCondition, selectQueryStr = selectQuery ? String{ selectQuery } : String{}, preferredDownloadBlockSize, async, cResult ] (XAsyncOp op, const XAsyncProviderData* data) mutable { if (op == XAsyncOp::DoWork) { HRESULT hr = xblContext->TitleStorageService()->DownloadBlob( blobMetadata, blobBuffer, blobBufferCount, etagMatchCondition, selectQueryStr, preferredDownloadBlockSize, AsyncContext> (async->queue, [async, &cResult](Result result) { if (Succeeded(result)) { cResult = result.ExtractPayload(); } XAsyncComplete(async, result.Hresult(), sizeof(XblTitleStorageBlobMetadata)); } )); return SUCCEEDED(hr) ? E_PENDING : hr; } else if (op == XAsyncOp::GetResult) { std::memcpy(data->buffer, &cResult, sizeof(XblTitleStorageBlobMetadata)); } return S_OK; }); } CATCH_RETURN() STDAPI XblTitleStorageDownloadBlobResult( _In_ XAsyncBlock* async, _Out_ XblTitleStorageBlobMetadata* blobMetadata ) XBL_NOEXCEPT try { return XAsyncGetResult(async, nullptr, sizeof(XblTitleStorageBlobMetadata), blobMetadata, nullptr); } CATCH_RETURN() STDAPI XblTitleStorageUploadBlobAsync( _In_ XblContextHandle xboxLiveContext, _In_ XblTitleStorageBlobMetadata blobMetadata, _In_ const uint8_t* blobBuffer, _In_ size_t blobBufferCount, _In_ XblTitleStorageETagMatchCondition etagMatchCondition, _In_ size_t preferredDownloadBlockSize, _In_ XAsyncBlock* async ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(xboxLiveContext == nullptr || blobBuffer == nullptr || async == nullptr); RETURN_HR_INVALIDARGUMENT_IF_EMPTY_STRING(blobMetadata.serviceConfigurationId); RETURN_HR_INVALIDARGUMENT_IF_EMPTY_STRING(blobMetadata.blobPath); VERIFY_XBL_INITIALIZED(); XblTitleStorageBlobMetadata cResult; return RunAsync(async, __FUNCTION__, [ xblContext{ xboxLiveContext->shared_from_this() }, blobMetadata, blobBuffer, blobBufferCount, etagMatchCondition, preferredDownloadBlockSize, async, cResult ] (XAsyncOp op, const XAsyncProviderData* data) mutable { if (op == XAsyncOp::DoWork) { HRESULT hr = xblContext->TitleStorageService()->UploadBlob( blobMetadata, blobBuffer, blobBufferCount, etagMatchCondition, preferredDownloadBlockSize, AsyncContext> (async->queue, [async, &cResult](Result result) { if (Succeeded(result)) { cResult = result.ExtractPayload(); } XAsyncComplete(async, result.Hresult(), sizeof(XblTitleStorageBlobMetadata)); } )); return SUCCEEDED(hr) ? E_PENDING : hr; } else if (op == XAsyncOp::GetResult) { std::memcpy(data->buffer, &cResult, sizeof(XblTitleStorageBlobMetadata)); } return S_OK; }); } CATCH_RETURN() STDAPI XblTitleStorageUploadBlobResult( _In_ XAsyncBlock* async, _Out_ XblTitleStorageBlobMetadata* blobMetadata ) XBL_NOEXCEPT try { return XAsyncGetResult(async, nullptr, sizeof(XblTitleStorageBlobMetadata), blobMetadata, nullptr); } CATCH_RETURN() ================================================ FILE: Source/Services/TitleStorage/title_storage_blob_metadata_result.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "title_storage_internal.h" using namespace xbox::services; using namespace xbox::services::title_storage; void XblTitleStorageBlobMetadataResult::Initialize( _In_ std::shared_ptr titleStorageService, _In_ xsapi_internal_string scid, _In_ uint64_t xuid, _In_ XblTitleStorageType storageType, _In_ xsapi_internal_string blobPath ) { m_titleStorageService = titleStorageService; m_scid = scid; m_xuid = xuid; m_storageType = storageType; m_blobPath = blobPath; for (auto& item : m_items) { utils::strcpy(item.serviceConfigurationId, XBL_SCID_LENGTH, m_scid.c_str()); item.xboxUserId = m_xuid; item.storageType = m_storageType; } } const xsapi_internal_vector& XblTitleStorageBlobMetadataResult::Items() const { return m_items; } bool XblTitleStorageBlobMetadataResult::HasNext() const { return !m_continuationToken.empty(); } HRESULT XblTitleStorageBlobMetadataResult::GetNext( _In_ uint32_t maxItems, _In_ AsyncContext>> async ) { return m_titleStorageService->GetBlobMetadata( m_scid, m_storageType, m_blobPath, m_xuid, 0, // use continuationToken, ignore skipItems. maxItems, m_continuationToken, async ); } std::shared_ptr XblTitleStorageBlobMetadataResult::GetSharedThis() { return shared_from_this(); } Result> XblTitleStorageBlobMetadataResult::Deserialize(_In_ const JsonValue& json) { if (json.IsNull()) { return Result>(nullptr); } auto titleStorageBlobMetadataResult = MakeShared(); if (json.IsObject() && json.HasMember("blobs")) { const JsonValue& blobs = json["blobs"]; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonVector( DeserializeXblTitleStorageBlobMetadata, blobs, titleStorageBlobMetadataResult->m_items )); } else { //required titleStorageBlobMetadataResult->m_items = xsapi_internal_vector(); return WEB_E_INVALID_JSON_STRING; } if (json.IsObject() && json.HasMember("pagingInfo")) { const JsonValue& pagingInfoJson = json["pagingInfo"]; if (!pagingInfoJson.IsNull()) { RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString( pagingInfoJson, "continuationToken", titleStorageBlobMetadataResult->m_continuationToken )); } } return Result>(titleStorageBlobMetadataResult, S_OK); } XblTitleStorageBlobType XblTitleStorageBlobMetadataResult::ConvertStringToTitleStorageBlobType( _In_ const xsapi_internal_string& value ) { if (utils::str_icmp_internal(value, "binary") == 0) { return XblTitleStorageBlobType::Binary; } else if (utils::str_icmp_internal(value, "json") == 0) { return XblTitleStorageBlobType::Json; } else if (utils::str_icmp_internal(value, "config") == 0) { return XblTitleStorageBlobType::Config; } return XblTitleStorageBlobType::Unknown; } Result XblTitleStorageBlobMetadataResult::DeserializeXblTitleStorageBlobMetadata(_In_ const JsonValue& json) { XblTitleStorageBlobMetadata returnObject{}; if (json.IsNull()) return Result(returnObject); HRESULT errc = S_OK; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonTimeT(json, "clientFileTime", returnObject.clientTimestamp)); xsapi_internal_string displayName; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "displayName", displayName)); utils::strcpy(returnObject.displayName, displayName.length() + 1, displayName.c_str()); xsapi_internal_string etag; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "etag", etag)); utils::strcpy(returnObject.eTag, etag.length() + 1, etag.c_str()); uint64_t size = 0; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonUInt64(json, "size", size)); returnObject.length = static_cast(size); xsapi_internal_string fileName; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "fileName", fileName)); if (!fileName.empty()) { auto nPos = fileName.find(','); if (nPos == std::string::npos) { return Result(returnObject, WEB_E_INVALID_JSON_STRING); } xsapi_internal_string smartBlobType = fileName.substr(nPos + 1); returnObject.blobType = ConvertStringToTitleStorageBlobType(smartBlobType); fileName.resize(nPos); utils::strcpy(returnObject.blobPath, fileName.length() + 1, fileName.c_str()); } return Result(returnObject, xbox::services::legacy::ConvertHr(errc)); } ================================================ FILE: Source/Services/TitleStorage/title_storage_internal.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "xsapi-c/title_storage_c.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_TITLE_STORAGE_CPP_BEGIN class TitleStorageService; NAMESPACE_MICROSOFT_XBOX_SERVICES_TITLE_STORAGE_CPP_END struct XblTitleStorageQuota { size_t usedBytes; size_t quotaBytes; }; struct XblTitleStorageBlobMetadataResult : public xbox::services::RefCounter, public std::enable_shared_from_this { public: XblTitleStorageBlobMetadataResult() = default; void Initialize(_In_ std::shared_ptr titleStorageService, _In_ xsapi_internal_string scid, _In_ uint64_t xuid, _In_ XblTitleStorageType storageType, _In_ xsapi_internal_string blobPath); const xsapi_internal_vector& Items() const; bool HasNext() const; HRESULT GetNext(_In_ uint32_t maxItems, _In_ xbox::services::AsyncContext>> async); static xbox::services::Result> Deserialize(_In_ const JsonValue& json); static xbox::services::Result DeserializeXblTitleStorageBlobMetadata(_In_ const JsonValue& json); static XblTitleStorageBlobType ConvertStringToTitleStorageBlobType(_In_ const xsapi_internal_string& value); protected: // RefCounter std::shared_ptr GetSharedThis() override; private: std::shared_ptr m_titleStorageService; xsapi_internal_string m_scid; uint64_t m_xuid{}; XblTitleStorageType m_storageType{}; xsapi_internal_string m_blobPath; xsapi_internal_vector m_items; xsapi_internal_string m_continuationToken; }; NAMESPACE_MICROSOFT_XBOX_SERVICES_TITLE_STORAGE_CPP_BEGIN class TitleStorageService : public std::enable_shared_from_this { public: TitleStorageService( _In_ User&& user, _In_ std::shared_ptr xboxLiveContextSettings ); HRESULT GetQuota( _In_ xsapi_internal_string scid, _In_ XblTitleStorageType storageType, _In_ AsyncContext> async ); HRESULT GetBlobMetadata( _In_ xsapi_internal_string scid, _In_ XblTitleStorageType storageType, _In_ xsapi_internal_string blobPath, _In_ uint64_t xboxUserId, _In_ uint32_t skipItems, _In_ uint32_t maxItems, _In_ xsapi_internal_string continuationToken, _In_ AsyncContext>> async ); HRESULT DeleteBlob( _In_ XblTitleStorageBlobMetadata blobMetadata, _In_ bool deleteOnlyIfEtagMatches, _In_ AsyncContext async ); HRESULT DownloadBlob( _In_ XblTitleStorageBlobMetadata blobMetadata, _In_ uint8_t* blobBuffer, _In_ size_t blobBufferSize, _In_ XblTitleStorageETagMatchCondition etagMatchCondition, _In_ xsapi_internal_string selectQuery, _In_ size_t preferredDownloadBlockSize, _In_ AsyncContext> async ); HRESULT UploadBlob( _In_ XblTitleStorageBlobMetadata blobMetadata, _In_ const uint8_t* blobBuffer, _In_ size_t blobBufferSize, _In_ XblTitleStorageETagMatchCondition etagMatchCondition, _In_ size_t preferredUploadBlockSize, _In_ AsyncContext> async ); private: struct BlobArgs { XblTitleStorageBlobMetadata blobMetadata{}; uint8_t* downloadBlobBuffer{ nullptr }; const uint8_t* uploadBlobBuffer{ nullptr }; size_t blobBufferSize{ 0 }; XblTitleStorageETagMatchCondition etagMatchCondition{}; xsapi_internal_string selectQuery; size_t preferredBlockSize{ 0 }; size_t startByte{ 0 }; AsyncContext> async; }; HRESULT DownloadBlobHelper( _In_ std::shared_ptr downloadBlobArgs ); HRESULT UploadBlobHelper( _In_ std::shared_ptr uploadBlobArgs, _In_ const xsapi_internal_string& continuationToken ); static Result TitleStorageQuotaSubpath( _In_ XblTitleStorageType storageType, _In_ const xsapi_internal_string& serviceConfigurationId, _In_ uint64_t xboxUserId ); static Result TitleStorageBlobMetadataSubpath( _In_ XblTitleStorageType storageType, _In_ const xsapi_internal_string& serviceConfigurationId, _In_ uint64_t xboxUserId, _In_ const xsapi_internal_string& blobPath, _In_ uint32_t skipItems, _In_ uint32_t maxItems, _In_ const xsapi_internal_string& continuationToken ); static Result TitleStorageDownloadBlobSubpath( _In_ const XblTitleStorageBlobMetadata& blobMetadata, _In_ const xsapi_internal_string& selectQuery ); static Result TitleStorageUploadBlobSubpath( _In_ const XblTitleStorageBlobMetadata& blobMetadata, _In_ const xsapi_internal_string& continuationToken, _In_ bool finalBlock ); HRESULT SetEtagHeader( _In_ std::shared_ptr httpCall, _In_ xsapi_internal_string etag, _In_ XblTitleStorageETagMatchCondition eTagMatchCondition ); HRESULT SetRangeHeader( _In_ std::shared_ptr httpCall, _In_ size_t startByte, _In_ size_t endByte ); // Deserialize Helpers static Result DeserializeTitleStorageQuota( _In_ const JsonValue& json ); User m_user; std::shared_ptr m_xboxLiveContextSettings; }; NAMESPACE_MICROSOFT_XBOX_SERVICES_TITLE_STORAGE_CPP_END ================================================ FILE: Source/Services/TitleStorage/title_storage_service.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "title_storage_internal.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_TITLE_STORAGE_CPP_BEGIN const char CONTENT_TYPE_HEADER_VALUE[] = "application/octet-stream"; const char E_TAG_HEADER_NAME[] = "ETag"; const char IF_MATCH_HEADER_NAME[] = "If-Match"; const char IF_NONE_HEADER_NAME[] = "If-None-Match"; const char E_TAG_INVALID_VALUE[] = "InvalidETagValue"; const char RANGE_HEADER_NAME[] = "Range"; TitleStorageService::TitleStorageService( _In_ User&& user, _In_ std::shared_ptr xboxLiveContextSettings ) : m_user{ std::move(user) }, m_xboxLiveContextSettings(std::move(xboxLiveContextSettings)) { } HRESULT TitleStorageService::GetQuota( _In_ xsapi_internal_string scid, _In_ XblTitleStorageType storageType, _In_ AsyncContext> async ) { RETURN_HR_INVALIDARGUMENT_IF_EMPTY_STRING(scid); auto subpath = TitleStorageQuotaSubpath(storageType, scid, m_user.Xuid()); RETURN_HR_INVALIDARGUMENT_IF(!Succeeded(subpath)); Result userResult = m_user.Copy(); RETURN_HR_IF_FAILED(userResult.Hresult()); auto httpCall = MakeShared(userResult.ExtractPayload()); HRESULT hr = httpCall->Init( m_xboxLiveContextSettings, "GET", XblHttpCall::BuildUrl("titlestorage", subpath.Payload()), xbox_live_api::get_quota ); RETURN_HR_IF_FAILED(hr); hr = httpCall->Perform( AsyncContext{ async.Queue(), [async](HttpResult httpResult) { HRESULT hr = httpResult.Hresult(); if (SUCCEEDED(hr)) { hr = httpResult.Payload()->Result(); if (SUCCEEDED(hr)) { async.Complete(DeserializeTitleStorageQuota(httpResult.Payload()->GetResponseBodyJson())); } } if (!SUCCEEDED(hr)) { async.Complete(hr); } }}); return hr; } Result TitleStorageService::DeserializeTitleStorageQuota( _In_ const JsonValue& json ) { XblTitleStorageQuota returnObject {}; if (json.IsNull()) { return returnObject; } HRESULT errc = S_OK; if (json.IsObject() && json.HasMember("quotaInfo")) { const JsonValue& quotaInfoJson = json["quotaInfo"]; if (!quotaInfoJson.IsNull()) { uint64_t usedBytes = 0; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonUInt64(quotaInfoJson, "usedBytes", usedBytes)); returnObject.usedBytes = (size_t)usedBytes; uint64_t quotaBytes = 0; RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonUInt64(quotaInfoJson, "quotaBytes", quotaBytes)); returnObject.quotaBytes = (size_t)quotaBytes; } } if (FAILED(errc)) { return E_FAIL; } return returnObject; } HRESULT TitleStorageService::GetBlobMetadata( _In_ xsapi_internal_string scid, _In_ XblTitleStorageType storageType, _In_ xsapi_internal_string blobPath, _In_ uint64_t xboxUserId, _In_ uint32_t skipItems, _In_ uint32_t maxItems, _In_ xsapi_internal_string continuationToken, _In_ AsyncContext>> async ) { RETURN_HR_INVALIDARGUMENT_IF_EMPTY_STRING(scid); uint64_t xuid = xboxUserId; if (xuid == 0 && (storageType == XblTitleStorageType::TrustedPlatformStorage || storageType == XblTitleStorageType::Universal)) { xuid = m_user.Xuid(); } auto subpath = TitleStorageBlobMetadataSubpath( storageType, scid, xuid, blobPath, skipItems, maxItems, continuationToken ); RETURN_HR_INVALIDARGUMENT_IF(!Succeeded(subpath)); Result userResult = m_user.Copy(); RETURN_HR_IF_FAILED(userResult.Hresult()); auto httpCall = MakeShared(userResult.ExtractPayload()); HRESULT hr = httpCall->Init( m_xboxLiveContextSettings, "GET", XblHttpCall::BuildUrl("titlestorage", subpath.Payload()), xbox_live_api::get_blob_metadata ); RETURN_HR_IF_FAILED(hr); hr = httpCall->Perform( AsyncContext{ async.Queue(), [ sharedThis{ shared_from_this() }, scid, xuid, storageType, blobPath, async ] (HttpResult httpResult) { HRESULT hr = httpResult.Hresult(); if (SUCCEEDED(hr)) { hr = httpResult.Payload()->Result(); if (SUCCEEDED(hr)) { auto result = XblTitleStorageBlobMetadataResult::Deserialize(httpResult.Payload()->GetResponseBodyJson()); if (Succeeded(result)) { result.Payload()->Initialize(sharedThis, scid, xuid, storageType, blobPath); } async.Complete(result); } } if (!SUCCEEDED(hr)) { async.Complete(hr); } }}); return hr; } HRESULT TitleStorageService::DeleteBlob( _In_ XblTitleStorageBlobMetadata blobMetadata, _In_ bool deleteOnlyIfEtagMatches, _In_ AsyncContext async ) { Result subpath = TitleStorageDownloadBlobSubpath(blobMetadata, ""); RETURN_HR_INVALIDARGUMENT_IF(!Succeeded(subpath)); XblTitleStorageETagMatchCondition etagMatchCondition = deleteOnlyIfEtagMatches ? XblTitleStorageETagMatchCondition::IfMatch : XblTitleStorageETagMatchCondition::NotUsed; Result userResult = m_user.Copy(); RETURN_HR_IF_FAILED(userResult.Hresult()); auto httpCall = MakeShared(userResult.ExtractPayload()); HRESULT hr = httpCall->Init( m_xboxLiveContextSettings, "DELETE", XblHttpCall::BuildUrl("titlestorage", subpath.Payload()), xbox_live_api::delete_blob ); RETURN_HR_IF_FAILED(hr); RETURN_HR_IF_FAILED(httpCall->SetHeader(CONTENT_TYPE_HEADER, CONTENT_TYPE_HEADER_VALUE)); hr = SetEtagHeader( httpCall, blobMetadata.eTag, etagMatchCondition ); RETURN_HR_IF_FAILED(hr); hr = httpCall->Perform( AsyncContext{ async.Queue(), [async](HttpResult httpResult) { HRESULT hr = httpResult.Hresult(); if (SUCCEEDED(hr)) { hr = httpResult.Payload()->Result(); } async.Complete(hr); }}); return hr; } HRESULT TitleStorageService::DownloadBlob( _In_ XblTitleStorageBlobMetadata blobMetadata, _In_ uint8_t* blobBuffer, _In_ size_t blobBufferSize, _In_ XblTitleStorageETagMatchCondition etagMatchCondition, _In_ xsapi_internal_string selectQuery, _In_ size_t preferredDownloadBlockSize, _In_ AsyncContext> async ) { RETURN_HR_INVALIDARGUMENT_IF_NULL(blobBuffer); RETURN_HR_INVALIDARGUMENT_IF(blobBufferSize < blobMetadata.length); Result subpath = TitleStorageDownloadBlobSubpath(blobMetadata, selectQuery); RETURN_HR_INVALIDARGUMENT_IF(!Succeeded(subpath)); if (preferredDownloadBlockSize == 0) { preferredDownloadBlockSize = XBL_TITLE_STORAGE_DEFAULT_DOWNLOAD_BLOCK_SIZE; } else { preferredDownloadBlockSize = preferredDownloadBlockSize < XBL_TITLE_STORAGE_MIN_DOWNLOAD_BLOCK_SIZE ? XBL_TITLE_STORAGE_MIN_DOWNLOAD_BLOCK_SIZE : preferredDownloadBlockSize; } auto args = MakeShared(); args->blobMetadata = { blobMetadata }; args->downloadBlobBuffer = blobBuffer; args->blobBufferSize = blobBufferSize; args->etagMatchCondition = etagMatchCondition; args->selectQuery = selectQuery; args->preferredBlockSize = preferredDownloadBlockSize; args->startByte = 0; args->async = std::move(async); return DownloadBlobHelper(args); } HRESULT TitleStorageService::DownloadBlobHelper( _In_ std::shared_ptr args ) { RETURN_HR_INVALIDARGUMENT_IF_NULL(args->downloadBlobBuffer); RETURN_HR_INVALIDARGUMENT_IF(args->blobBufferSize < args->blobMetadata.length); Result subpath = TitleStorageDownloadBlobSubpath(args->blobMetadata, args->selectQuery); RETURN_HR_INVALIDARGUMENT_IF(!Succeeded(subpath)); Result userResult = m_user.Copy(); RETURN_HR_IF_FAILED(userResult.Hresult()); auto httpCall = MakeShared(userResult.ExtractPayload()); HRESULT hr = httpCall->Init( m_xboxLiveContextSettings, "GET", XblHttpCall::BuildUrl("titlestorage", subpath.Payload()), xbox_live_api::download_blob ); RETURN_HR_IF_FAILED(hr); RETURN_HR_IF_FAILED(httpCall->SetHeader(CONTENT_TYPE_HEADER, CONTENT_TYPE_HEADER_VALUE)); httpCall->SetLongHttpCall(true); hr = SetEtagHeader( httpCall, args->blobMetadata.eTag, args->etagMatchCondition ); RETURN_HR_IF_FAILED(hr); if (args->blobMetadata.blobType == XblTitleStorageBlobType::Binary) { // Partial download is only used for binary blob types hr = SetRangeHeader( httpCall, args->startByte, args->startByte + args->preferredBlockSize - 1 ); RETURN_HR_IF_FAILED(hr); } return httpCall->Perform( AsyncContext{ args->async.Queue(), [ args, sharedThis{ shared_from_this() } ](HttpResult httpResult) { HRESULT hr = httpResult.Hresult(); if (SUCCEEDED(hr)) { hr = httpResult.Payload()->Result(); if (SUCCEEDED(hr)) { auto responseBody = httpResult.Payload()->GetResponseBodyBytes(); if (args->startByte + responseBody.size() > args->blobBufferSize) { args->async.Complete(E_NOT_SUFFICIENT_BUFFER); return; } memcpy(args->downloadBlobBuffer + args->startByte, responseBody.data(), responseBody.size()); args->startByte += responseBody.size(); // Check if there is more data to load // If not binary blob type then the service has returned the entire payload. // If binary blob type then check if the service returned less data than requested or // if we've loaded all the data defined by the blob metadata length property. if (args->blobMetadata.blobType != XblTitleStorageBlobType::Binary || responseBody.size() < args->preferredBlockSize || args->startByte == args->blobMetadata.length) { auto etag = httpResult.Payload()->GetResponseHeader(ETAG_HEADER); utils::strcpy(args->blobMetadata.eTag, etag.length() + 1, etag.c_str()); args->blobMetadata.length = args->startByte; args->async.Complete(std::move(args->blobMetadata)); } else { hr = sharedThis->DownloadBlobHelper(args); } } } if (!SUCCEEDED(hr)) { args->async.Complete(hr); } }}); } HRESULT TitleStorageService::UploadBlob( _In_ XblTitleStorageBlobMetadata blobMetadata, _In_ const uint8_t* blobBuffer, _In_ size_t blobBufferSize, _In_ XblTitleStorageETagMatchCondition etagMatchCondition, _In_ size_t preferredUploadBlockSize, _In_ AsyncContext> async ) { RETURN_HR_INVALIDARGUMENT_IF_NULL(blobBuffer); RETURN_HR_INVALIDARGUMENT_IF(blobBufferSize == 0); if (preferredUploadBlockSize == 0) { preferredUploadBlockSize = XBL_TITLE_STORAGE_DEFAULT_UPLOAD_BLOCK_SIZE; } else { preferredUploadBlockSize = preferredUploadBlockSize < XBL_TITLE_STORAGE_MIN_UPLOAD_BLOCK_SIZE ? XBL_TITLE_STORAGE_MIN_UPLOAD_BLOCK_SIZE : preferredUploadBlockSize; preferredUploadBlockSize = preferredUploadBlockSize > XBL_TITLE_STORAGE_MAX_UPLOAD_BLOCK_SIZE ? XBL_TITLE_STORAGE_MAX_UPLOAD_BLOCK_SIZE : preferredUploadBlockSize; } auto args = MakeShared(); args->blobMetadata = { blobMetadata }; args->uploadBlobBuffer = blobBuffer; args->blobBufferSize = blobBufferSize; args->etagMatchCondition = etagMatchCondition; args->preferredBlockSize = preferredUploadBlockSize; args->startByte = 0; args->async = async; if (args->blobMetadata.xboxUserId == 0) { args->blobMetadata.xboxUserId = m_user.Xuid(); } return UploadBlobHelper(args, ""); } HRESULT TitleStorageService::UploadBlobHelper( _In_ std::shared_ptr args, _In_ const xsapi_internal_string& continuationToken ) { RETURN_HR_INVALIDARGUMENT_IF_NULL(args->uploadBlobBuffer); RETURN_HR_INVALIDARGUMENT_IF(args->blobBufferSize == 0); bool isBinary = args->blobMetadata.blobType == XblTitleStorageBlobType::Binary; bool finalBlock = true; size_t dataChunkSize = isBinary ? args->blobBufferSize - args->startByte : args->blobBufferSize; if (isBinary) { if (dataChunkSize > args->preferredBlockSize) { dataChunkSize = args->preferredBlockSize; } size_t endByteOfChunk = args->startByte + dataChunkSize; finalBlock = (endByteOfChunk == args->blobBufferSize); } Result subpath = TitleStorageUploadBlobSubpath(args->blobMetadata, continuationToken, finalBlock); RETURN_HR_INVALIDARGUMENT_IF(!Succeeded(subpath)); Result userResult = m_user.Copy(); RETURN_HR_IF_FAILED(userResult.Hresult()); auto httpCall = MakeShared(userResult.ExtractPayload()); HRESULT hr = httpCall->Init( m_xboxLiveContextSettings, "PUT", XblHttpCall::BuildUrl("titlestorage", subpath.Payload()), xbox_live_api::upload_blob ); RETURN_HR_IF_FAILED(httpCall->SetHeader(CONTENT_TYPE_HEADER, CONTENT_TYPE_HEADER_VALUE)); httpCall->SetLongHttpCall(true); if (isBinary) { xsapi_internal_vector dataChunk(dataChunkSize); memcpy(dataChunk.data(), args->uploadBlobBuffer + args->startByte, dataChunkSize); RETURN_HR_IF_FAILED(httpCall->SetRequestBody(std::move(dataChunk))); args->startByte += dataChunkSize; // Now move the start byte forward } else { RETURN_HR_IF_FAILED(httpCall->SetRequestBody((const char*)std::move(args->uploadBlobBuffer))); } hr = SetEtagHeader( httpCall, args->blobMetadata.eTag, args->etagMatchCondition ); RETURN_HR_IF_FAILED(hr); return httpCall->Perform( AsyncContext{ args->async.Queue(), [ args, finalBlock, sharedThis{ shared_from_this() } ](HttpResult httpResult) { HRESULT hr = httpResult.Hresult(); if (SUCCEEDED(hr)) { hr = httpResult.Payload()->Result(); if (SUCCEEDED(hr)) { auto etag = httpResult.Payload()->GetResponseHeader(ETAG_HEADER); utils::strcpy(args->blobMetadata.eTag, etag.length() + 1, etag.c_str()); if (finalBlock) { args->async.Complete(std::move(args->blobMetadata)); } else { auto responseBody = httpResult.Payload()->GetResponseBodyJson(); xsapi_internal_string continuationToken; JsonUtils::ExtractJsonString(responseBody, "continuationToken", continuationToken); hr = sharedThis->UploadBlobHelper(args, continuationToken); } } } if (!SUCCEEDED(hr)) { args->async.Complete(hr); } }}); } HRESULT TitleStorageService::SetEtagHeader( _In_ std::shared_ptr httpCall, _In_ xsapi_internal_string etag, _In_ XblTitleStorageETagMatchCondition eTagMatchCondition ) { if (eTagMatchCondition == XblTitleStorageETagMatchCondition::NotUsed) { RETURN_HR_IF_FAILED(httpCall->SetHeader(E_TAG_HEADER_NAME, E_TAG_INVALID_VALUE)); RETURN_HR_IF_FAILED(httpCall->SetHeader(IF_NONE_HEADER_NAME, E_TAG_INVALID_VALUE)); } else { xsapi_internal_string etagValue = etag.empty() ? IF_NONE_HEADER_NAME : etag; RETURN_HR_IF_FAILED(httpCall->SetHeader(E_TAG_HEADER_NAME, etagValue)); if (eTagMatchCondition == XblTitleStorageETagMatchCondition::IfMatch) { RETURN_HR_IF_FAILED(httpCall->SetHeader(IF_MATCH_HEADER_NAME, etagValue)); } else { RETURN_HR_IF_FAILED(httpCall->SetHeader(IF_NONE_HEADER_NAME, etagValue)); } } return S_OK; } HRESULT TitleStorageService::SetRangeHeader( _In_ std::shared_ptr httpCall, _In_ size_t startByte, _In_ size_t endByte ) { xsapi_internal_stringstream byteRange; byteRange << "bytes="; byteRange << startByte; byteRange << "-"; byteRange << endByte; RETURN_HR_IF_FAILED(httpCall->SetHeader(RANGE_HEADER_NAME, byteRange.str())); return S_OK; } Result TitleStorageService::TitleStorageQuotaSubpath( _In_ XblTitleStorageType storageType, _In_ const xsapi_internal_string& serviceConfigurationId, _In_ uint64_t xboxUserId ) { xsapi_internal_stringstream path; switch (storageType) { case XblTitleStorageType::TrustedPlatformStorage: path << "/trustedplatform/users/xuid("; path << xboxUserId; path << ")/scids/"; path << serviceConfigurationId; break; case XblTitleStorageType::GlobalStorage: path << "/global/scids/"; path << serviceConfigurationId; break; case XblTitleStorageType::Universal: path << "/universalplatform/users/xuid("; path << xboxUserId; path << ")/scids/"; path << serviceConfigurationId; break; default: return Result(E_INVALIDARG); } return path.str(); } Result TitleStorageService::TitleStorageBlobMetadataSubpath( _In_ XblTitleStorageType storageType, _In_ const xsapi_internal_string& serviceConfigurationId, _In_ uint64_t xboxUserId, _In_ const xsapi_internal_string& blobPath, _In_ uint32_t skipItems, _In_ uint32_t maxItems, _In_ const xsapi_internal_string& continuationToken ) { xsapi_internal_stringstream path; switch (storageType) { case XblTitleStorageType::TrustedPlatformStorage: path << "/trustedplatform/users/xuid("; path << xboxUserId; path << ")/scids/"; path << serviceConfigurationId; break; case XblTitleStorageType::GlobalStorage: path << "/global/scids/"; path << serviceConfigurationId; break; case XblTitleStorageType::Universal: path << "/universalplatform/users/xuid("; path << xboxUserId; path << ")/scids/"; path << serviceConfigurationId; break; default: return Result(E_INVALIDARG); } path << "/data"; if (!blobPath.empty()) { path << "/"; path << utils::encode_uri(blobPath, xbox::services::uri::components::query); } xbox::services::uri_builder params; utils::append_paging_info( params, skipItems, maxItems, continuationToken ); xsapi_internal_string paramPath = params.query(); if (paramPath.size() > 1) { path << "?" << paramPath; } return path.str(); } Result TitleStorageService::TitleStorageDownloadBlobSubpath( _In_ const XblTitleStorageBlobMetadata& blobMetadata, _In_ const xsapi_internal_string& selectQuery ) { RETURN_HR_INVALIDARGUMENT_IF_EMPTY_STRING(blobMetadata.serviceConfigurationId); RETURN_HR_INVALIDARGUMENT_IF_EMPTY_STRING(blobMetadata.blobPath); xsapi_internal_stringstream path; xsapi_internal_string titleStorageBlobToString; switch (blobMetadata.blobType) { case XblTitleStorageBlobType::Binary: titleStorageBlobToString = "binary"; break; case XblTitleStorageBlobType::Json: titleStorageBlobToString = "json"; break; case XblTitleStorageBlobType::Config: titleStorageBlobToString = "config"; break; default: return Result(E_INVALIDARG); } switch (blobMetadata.storageType) { case XblTitleStorageType::TrustedPlatformStorage: path << "/trustedplatform/users/xuid("; path << blobMetadata.xboxUserId; path << ")/scids/"; break; case XblTitleStorageType::GlobalStorage: path << "/global/scids/"; break; case XblTitleStorageType::Universal: path << "/universalplatform/users/xuid("; path << blobMetadata.xboxUserId; path << ")/scids/"; break; default: return Result(E_INVALIDARG); } path << blobMetadata.serviceConfigurationId; path << "/data/"; path << blobMetadata.blobPath; path << ","; path << titleStorageBlobToString; xsapi_internal_vector params; if (!selectQuery.empty()) { if (blobMetadata.blobType == XblTitleStorageBlobType::Config) { xsapi_internal_stringstream param; param << "customSelector="; param << utils::encode_uri(selectQuery.c_str()); params.push_back(param.str()); } else if (blobMetadata.blobType == XblTitleStorageBlobType::Json) { xsapi_internal_stringstream param; param << "select="; param << utils::encode_uri(selectQuery.c_str()); params.push_back(param.str()); } } path << utils::get_query_from_params(params); return Result(path.str()); } Result TitleStorageService::TitleStorageUploadBlobSubpath( _In_ const XblTitleStorageBlobMetadata& blobMetadata, _In_ const xsapi_internal_string& continuationToken, _In_ bool finalBlock ) { xsapi_internal_stringstream source; Result titleStorageDlSubpath = TitleStorageDownloadBlobSubpath( blobMetadata, "" ); if (!Succeeded(titleStorageDlSubpath)) return titleStorageDlSubpath; source << titleStorageDlSubpath.Payload(); xsapi_internal_vector params; if (blobMetadata.clientTimestamp != 0) { // Format: Tue, 13 Aug 2019 09:01:23 GMT char clientTimeStamp[37]{}; #if HC_PLATFORM_IS_MICROSOFT struct tm timeinfo{}; if (gmtime_s(&timeinfo, &blobMetadata.clientTimestamp) == 0) { strftime(clientTimeStamp, 32, "%a, %d %b %Y %H:%M:%S GMT", &timeinfo); } #else struct tm* timeinfo{ nullptr }; timeinfo = gmtime(&blobMetadata.clientTimestamp); strftime(clientTimeStamp, 32, "%a, %d %b %Y %H:%M:%S GMT", timeinfo); #endif xsapi_internal_stringstream param; param << "clientFileTime="; param << utils::encode_uri(clientTimeStamp); params.push_back(param.str()); } if (strlen(blobMetadata.displayName) > 0) { xsapi_internal_stringstream param; param << "displayName="; param << utils::encode_uri(blobMetadata.displayName); params.push_back(param.str()); } if (!continuationToken.empty()) { xsapi_internal_stringstream param; param << "continuationToken="; param << utils::encode_uri(continuationToken); params.push_back(param.str()); } if (blobMetadata.blobType == XblTitleStorageBlobType::Binary) { xsapi_internal_string param = finalBlock ? "finalBlock=true" : "finalBlock=false"; params.push_back(param); } source << utils::get_query_from_params(params); return Result(source.str()); } NAMESPACE_MICROSOFT_XBOX_SERVICES_TITLE_STORAGE_CPP_END ================================================ FILE: Source/Shared/HookedUri/asyncrt_utils.h ================================================ /*** * Copyright (C) Microsoft. All rights reserved. * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. * * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ * * Various common utilities. * * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk * * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ****/ #pragma once #include #include #include #include #include #include #include "HookedUri/details/basic_types.h" #if !defined(_WIN32) || (_MSC_VER >= 1700) #include #endif #ifndef _WIN32 #include #endif #if HC_PLATFORM_IS_MICROSOFT #pragma warning( push ) #pragma warning( disable : 26444 ) // ignore various unnamed objects #endif namespace xbox { namespace services { namespace cppresturi { /// Various utilities for string conversions and date and time manipulation. namespace utility { // Left over from VS2010 support, remains to avoid breaking. typedef std::chrono::seconds seconds; /// Functions for converting to/from std::chrono::seconds to xml string. namespace timespan { /// /// Converts a timespan/interval in seconds to xml duration string as specified by /// http://www.w3.org/TR/xmlschema-2/#duration /// utility::string_t seconds_to_xml_duration(utility::seconds numSecs); /// /// Converts an xml duration to timespan/interval in seconds /// http://www.w3.org/TR/xmlschema-2/#duration /// utility::seconds xml_duration_to_seconds(const utility::string_t ×panString); } /// Functions for Unicode string conversions. namespace conversions { /// /// Converts a UTF-16 string to a UTF-8 string. /// /// A two byte character UTF-16 string. /// A single byte character UTF-8 string. xsapi_internal_string utf16_to_utf8_internal(const xsapi_internal_wstring &w); std::string utf16_to_utf8(const utf16string&w); /// /// Converts a UTF-8 string to a UTF-16 /// /// A single byte character UTF-8 string. /// A two byte character UTF-16 string. utf16string utf8_to_utf16(const std::string &s); /// /// Converts a ASCII (us-ascii) string to a UTF-16 string. /// /// A single byte character us-ascii string. /// A two byte character UTF-16 string. utf16string usascii_to_utf16(const std::string &s); /// /// Converts a Latin1 (iso-8859-1) string to a UTF-8 string. /// /// A single byte character UTF-8 string. /// A single byte character UTF-8 string. utf8string latin1_to_utf8(const std::string &s); /// /// Converts to a platform dependent Unicode string type. /// /// A single byte character UTF-8 string. /// A platform dependent string type. utility::string_t to_string_t(std::string &&s); /// /// Converts to a platform dependent Unicode string type. /// /// A two byte character UTF-16 string. /// A platform dependent string type. utility::string_t to_string_t(utf16string &&s); /// /// Converts to a platform dependent Unicode string type. /// /// A single byte character UTF-8 string. /// A platform dependent string type. utility::string_t to_string_t(const std::string &s); /// /// Converts to a platform dependent Unicode string type. /// /// A two byte character UTF-16 string. /// A platform dependent string type. utility::string_t to_string_t(const utf16string &s); /// /// Converts to a UTF-16 from string. /// /// A single byte character UTF-8 string. /// A two byte character UTF-16 string. utf16string to_utf16string(const std::string &value); /// /// Converts to a UTF-16 from string. /// /// A two byte character UTF-16 string. /// A two byte character UTF-16 string. utf16string to_utf16string(utf16string value); /// /// Converts to a UTF-8 string. /// /// A single byte character UTF-8 string. /// A single byte character UTF-8 string. xsapi_internal_string to_utf8string_internal(xsapi_internal_string value); /// /// Converts to a UTF-8 string. /// /// A two byte character UTF-16 string. /// A single byte character UTF-8 string. xsapi_internal_string to_utf8string_internal(const xsapi_internal_wstring &value); xsapi_internal_string latin1_to_utf8_internal(const xsapi_internal_string &s); xsapi_internal_wstring latin1_to_utf16_internal(const xsapi_internal_string &s); /// /// Encode the given byte array into a base64 string /// utility::string_t to_base64(const unsigned char* data, size_t dataSize); /// /// Encode the given byte array into a base64 string /// utility::string_t to_base64(const std::vector& data); /// /// Encode the given 8-byte integer into a base64 string /// utility::string_t to_base64(uint64_t data); /// /// Decode the given base64 string to a byte array /// std::vector from_base64(const utility::string_t& str); template utility::string_t print_string(const Source &val, const std::locale &loc) { utility::ostringstream_t oss; oss.imbue(loc); oss << val; if (oss.bad()) { throw std::bad_cast(); } return oss.str(); } template utility::string_t print_string(const Source &val) { return print_string(val, std::locale()); } inline utility::string_t print_string(const utility::string_t &val) { return val; } template Target scan_string(const utility::string_t &str, const std::locale &loc) { Target t; utility::istringstream_t iss(str); iss.imbue(loc); iss >> t; if (iss.bad()) { throw std::bad_cast(); } return t; } template Target scan_string(const utility::string_t &str) { return scan_string(str, std::locale()); } inline utility::string_t scan_string(const utility::string_t &str) { return str; } } namespace details { /// /// Cross platform RAII container for setting thread local locale. /// class scoped_c_thread_locale { public: _ASYNCRTIMP scoped_c_thread_locale(); _ASYNCRTIMP ~scoped_c_thread_locale(); #if !defined(ANDROID) && !defined(__ANDROID__) && !defined(PAVO) // CodePlex 269 #ifdef _WIN32 typedef _locale_t xplat_locale; #else typedef locale_t xplat_locale; #endif static _ASYNCRTIMP xplat_locale c_locale(); #endif private: #ifdef _WIN32 std::string m_prevLocale; int m_prevThreadSetting; #elif !(defined(ANDROID) || defined(__ANDROID__) || defined(PAVO)) locale_t m_prevLocale; #endif scoped_c_thread_locale(const scoped_c_thread_locale &); scoped_c_thread_locale & operator=(const scoped_c_thread_locale &); }; /// /// Our own implementation of alpha numeric instead of std::isalnum to avoid /// taking global lock for performance reasons. /// inline bool __cdecl is_alnum(char ch) { return (ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'); } /// /// Simplistic implementation of make_unique. A better implementation would be based on variadic templates /// and therefore not be compatible with Dev10. /// template std::unique_ptr<_Type> make_unique() { return std::unique_ptr<_Type>(new _Type()); } template std::unique_ptr<_Type> make_unique(_Arg1&& arg1) { return std::unique_ptr<_Type>(new _Type(std::forward<_Arg1>(arg1))); } template std::unique_ptr<_Type> make_unique(_Arg1&& arg1, _Arg2&& arg2) { return std::unique_ptr<_Type>(new _Type(std::forward<_Arg1>(arg1), std::forward<_Arg2>(arg2))); } template std::unique_ptr<_Type> make_unique(_Arg1&& arg1, _Arg2&& arg2, _Arg3&& arg3) { return std::unique_ptr<_Type>(new _Type(std::forward<_Arg1>(arg1), std::forward<_Arg2>(arg2), std::forward<_Arg3>(arg3))); } template std::unique_ptr<_Type> make_unique(_Arg1&& arg1, _Arg2&& arg2, _Arg3&& arg3, _Arg4&& arg4) { return std::unique_ptr<_Type>(new _Type(std::forward<_Arg1>(arg1), std::forward<_Arg2>(arg2), std::forward<_Arg3>(arg3), std::forward<_Arg4>(arg4))); } /// /// Cross platform utility function for performing case insensitive string comparision. /// /// First string to compare. /// Second strong to compare. /// true if the strings are equivalent, false otherwise inline bool str_icmp(const utility::string_t &left, const utility::string_t &right) { #ifdef _WIN32 return _wcsicmp(left.c_str(), right.c_str()) == 0; #else return strcasecmp(left.c_str(), right.c_str()); #endif } #ifdef _WIN32 /// /// Category error type for Windows OS errors. /// class windows_category_impl : public std::error_category { public: virtual const char *name() const CPPREST_NOEXCEPT { return "windows"; } _ASYNCRTIMP virtual std::string message(int errorCode) const CPPREST_NOEXCEPT; _ASYNCRTIMP virtual std::error_condition default_error_condition(int errorCode) const CPPREST_NOEXCEPT; }; /// /// Gets the one global instance of the windows error category. /// /// An error category instance. _ASYNCRTIMP const std::error_category & __cdecl windows_category(); #else /// /// Gets the one global instance of the linux error category. /// /// An error category instance. _ASYNCRTIMP const std::error_category & __cdecl linux_category(); #endif /// /// Gets the one global instance of the current platform's error category. /// _ASYNCRTIMP const std::error_category & __cdecl platform_category(); /// /// Creates an instance of std::system_error from a OS error code. /// inline std::system_error __cdecl create_system_error(unsigned long errorCode) { std::error_code code((int)errorCode, platform_category()); return std::system_error(code, code.message()); } /// /// Creates a std::error_code from a OS error code. /// inline std::error_code __cdecl create_error_code(unsigned long errorCode) { return std::error_code((int)errorCode, platform_category()); } /// /// Creates the corresponding error message from a OS error code. /// inline utility::string_t __cdecl create_error_message(unsigned long errorCode) { return utility::conversions::to_string_t(create_error_code(errorCode).message()); } } class datetime { public: typedef uint64_t interval_type; /// /// Defines the supported date and time string formats. /// enum date_format { RFC_1123, ISO_8601 }; /// /// Returns the current UTC time. /// static _ASYNCRTIMP datetime __cdecl utc_now(); /// /// An invalid UTC timestamp value. /// enum:interval_type { utc_timestamp_invalid = static_cast(-1) }; /// /// Returns seconds since Unix/POSIX time epoch at 01-01-1970 00:00:00. /// If time is before epoch, utc_timestamp_invalid is returned. /// static interval_type utc_timestamp() { const auto seconds = utc_now().to_interval() / _secondTicks; if (seconds >= 11644473600LL) { return seconds - 11644473600LL; } else { return utc_timestamp_invalid; } } datetime() : m_interval(0) { } /// /// Creates datetime from a string representing time in UTC in RFC 1123 format. /// /// Returns a datetime of zero if not successful. static _ASYNCRTIMP datetime __cdecl from_string(const utility::string_t& timestring, date_format format = RFC_1123); /// /// Returns a string representation of the datetime. /// _ASYNCRTIMP xsapi_internal_string to_string_internal(date_format format = RFC_1123) const; /// /// Returns the integral time value. /// interval_type to_interval() const { return m_interval; } datetime operator- (interval_type value) const { return datetime(m_interval - value); } datetime operator+ (interval_type value) const { return datetime(m_interval + value); } bool operator== (datetime dt) const { return m_interval == dt.m_interval; } bool operator!= (const datetime& dt) const { return !(*this == dt); } static interval_type from_milliseconds(unsigned int milliseconds) { return milliseconds*_msTicks; } static interval_type from_seconds(unsigned int seconds) { return seconds*_secondTicks; } static interval_type from_minutes(unsigned int minutes) { return minutes*_minuteTicks; } static interval_type from_hours(unsigned int hours) { return hours*_hourTicks; } static interval_type from_days(unsigned int days) { return days*_dayTicks; } bool is_initialized() const { return m_interval != 0; } private: friend int operator- (datetime t1, datetime t2); static const interval_type _msTicks = static_cast(10000); static const interval_type _secondTicks = 1000*_msTicks; static const interval_type _minuteTicks = 60*_secondTicks; static const interval_type _hourTicks = 60*60*_secondTicks; static const interval_type _dayTicks = 24*60*60*_secondTicks; #ifdef _WIN32 // void* to avoid pulling in windows.h static _ASYNCRTIMP bool __cdecl system_type_to_datetime(/*SYSTEMTIME*/ void* psysTime, uint64_t seconds, datetime * pdt); #else static datetime timeval_to_datetime(const timeval &time); #endif // Private constructor. Use static methods to create an instance. datetime(interval_type interval) : m_interval(interval) { } // Storing as hundreds of nanoseconds 10e-7, i.e. 1 here equals 100ns. interval_type m_interval; }; #ifndef _WIN32 // temporary workaround for the fact that // utf16char is not fully supported in GCC class cmp { public: static int icmp(std::string left, std::string right) { size_t i; for (i = 0; i < left.size(); ++i) { if (i == right.size()) return 1; auto l = cmp::tolower(left[i]); auto r = cmp::tolower(right[i]); if (l > r) return 1; if (l < r) return -1; } if (i < right.size()) return -1; return 0; } private: static char tolower(char c) { if (c >= 'A' && c <= 'Z') return static_cast(c - 'A' + 'a'); return c; } }; #endif inline int operator- (datetime t1, datetime t2) { auto diff = (t1.m_interval - t2.m_interval); // Round it down to seconds diff /= 10 * 1000 * 1000; return static_cast(diff); } /// /// Nonce string generator class. /// class nonce_generator { public: /// /// Define default nonce length. /// enum { default_length = 32 }; /// /// Nonce generator constructor. /// /// Length of the generated nonce string. nonce_generator(int length=default_length) : m_random(static_cast(utility::datetime::utc_timestamp())), m_length(length) {} /// /// Generate a nonce string containing random alphanumeric characters (A-Za-z0-9). /// Length of the generated string is set by length(). /// /// The generated nonce string. _ASYNCRTIMP utility::string_t generate(); /// /// Get length of generated nonce string. /// /// Nonce string length. int length() const { return m_length; } /// /// Set length of the generated nonce string. /// /// Lenght of nonce string. void set_length(int length) { m_length = length; } private: static const utility::char_t* c_allowed_chars; std::mt19937 m_random; int m_length; }; } // namespace utility; } } } #if HC_PLATFORM_IS_MICROSOFT #pragma warning( pop ) #endif ================================================ FILE: Source/Shared/HookedUri/base_uri.h ================================================ /*** * ==++== * * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * ==--== * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ * * Protocol independent support for URIs. * * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk * * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ****/ #pragma once #include #include #include #include #include #include "HookedUri/asyncrt_utils.h" #include "HookedUri/details/basic_types.h" namespace xbox { namespace services { namespace cppresturi { namespace web { namespace details { struct uri_components { uri_components() : m_path("/"), m_port(-1) {} uri_components(const uri_components &other) : m_scheme(other.m_scheme), m_host(other.m_host), m_user_info(other.m_user_info), m_path(other.m_path), m_query(other.m_query), m_fragment(other.m_fragment), m_port(other.m_port) {} uri_components & operator=(const uri_components &other) { if (this != &other) { m_scheme = other.m_scheme; m_host = other.m_host; m_user_info = other.m_user_info; m_path = other.m_path; m_query = other.m_query; m_fragment = other.m_fragment; m_port = other.m_port; } return *this; } uri_components(uri_components &&other) CPPREST_NOEXCEPT : m_scheme(std::move(other.m_scheme)), m_host(std::move(other.m_host)), m_user_info(std::move(other.m_user_info)), m_path(std::move(other.m_path)), m_query(std::move(other.m_query)), m_fragment(std::move(other.m_fragment)), m_port(other.m_port) {} uri_components & operator=(uri_components &&other) CPPREST_NOEXCEPT { if (this != &other) { m_scheme = std::move(other.m_scheme); m_host = std::move(other.m_host); m_user_info = std::move(other.m_user_info); m_path = std::move(other.m_path); m_query = std::move(other.m_query); m_fragment = std::move(other.m_fragment); m_port = other.m_port; } return *this; } xsapi_internal_string join(); xsapi_internal_string m_scheme; xsapi_internal_string m_host; xsapi_internal_string m_user_info; xsapi_internal_string m_path; xsapi_internal_string m_query; xsapi_internal_string m_fragment; int m_port; }; } /// /// A single exception type to represent errors in parsing, encoding, and decoding URIs. /// class uri_exception : public std::exception { public: uri_exception(std::string msg) : m_msg(std::move(msg)) {} ~uri_exception() CPPREST_NOEXCEPT {} const char* what() const CPPREST_NOEXCEPT { return m_msg.c_str(); } private: std::string m_msg; }; /// /// A flexible, protocol independent URI implementation. /// /// URI instances are immutable. Querying the various fields on an emtpy URI will return empty strings. Querying /// various diagnostic members on an empty URI will return false. /// /// /// This implementation accepts both URIs ('http://msn.com/path') and URI relative-references /// ('/path?query#frag'). /// /// This implementation does not provide any scheme-specific handling -- an example of this /// would be the following: 'http://path1/path'. This is a valid URI, but it's not a valid /// http-uri -- that is, it's syntactically correct but does not conform to the requirements /// of the http scheme (http requires a host). /// We could provide this by allowing a pluggable 'scheme' policy-class, which would provide /// extra capability for validating and canonicalizing a URI according to scheme, and would /// introduce a layer of type-safety for URIs of differing schemes, and thus differing semantics. /// /// One issue with implementing a scheme-independent URI facility is that of comparing for equality. /// For instance, these URIs are considered equal 'http://msn.com', 'http://msn.com:80'. That is -- /// the 'default' port can be either omitted or explicit. Since we don't have a way to map a scheme /// to it's default port, we don't have a way to know these are equal. This is just one of a class of /// issues with regard to scheme-specific behavior. /// class uri { public: /// /// The various components of a URI. This enum is used to indicate which /// URI component is being encoded to the encode_uri_component. This allows /// specific encoding to be performed. /// /// Scheme and port don't allow '%' so they don't need to be encoded. /// class components { public: enum component { user_info, host, path, query, fragment, full_uri }; }; /// /// Encodes a URI component according to RFC 3986. /// Note if a full URI is specified instead of an individual URI component all /// characters not in the unreserved set are escaped. /// /// The URI as a string. /// The encoded string. _ASYNCRTIMP static xsapi_internal_string __cdecl encode_uri(const xsapi_internal_string &raw, uri::components::component = components::full_uri); /// /// Encodes a string by converting all characters except for RFC 3986 unreserved characters to their /// hexadecimal representation. /// /// The UTF-8 string data. /// The encoded string. _ASYNCRTIMP static xsapi_internal_string __cdecl encode_data_string(const xsapi_internal_string &utf8data); /// /// Decodes an encoded string. /// /// The URI as a string. /// The decoded string. _ASYNCRTIMP static xsapi_internal_string __cdecl decode(const xsapi_internal_string &encoded); /// /// Splits a path into its hierarchical components. /// /// The path as a string /// A std::vector<xsapi_internal_string> containing the segments in the path. _ASYNCRTIMP static std::vector __cdecl split_path(const xsapi_internal_string &path); /// /// Splits a query into its key-value components. /// /// The query string /// A std::map<xsapi_internal_string, xsapi_internal_string> containing the key-value components of the query. _ASYNCRTIMP static std::map __cdecl split_query(const xsapi_internal_string &query); /// /// Validates a string as a URI. /// /// The URI string to be validated. /// true if the given string represents a valid URI, false otherwise. _ASYNCRTIMP static bool __cdecl validate(const xsapi_internal_string &uri_string); /// /// Creates an empty uri /// uri() { m_uri = "/";}; /// /// Creates a URI from the given URI components. /// /// A URI components object to create the URI instance. _ASYNCRTIMP uri(const details::uri_components &components); /// /// Creates a URI from the given encoded string. This will throw an exception if the string /// does not contain a valid URI. Use uri::validate if processing user-input. /// /// A pointer to an encoded string to create the URI instance. _ASYNCRTIMP uri(const char *uri_string); /// /// Creates a URI from the given encoded string. This will throw an exception if the string /// does not contain a valid URI. Use uri::validate if processing user-input. /// /// An encoded URI string to create the URI instance. _ASYNCRTIMP uri(const xsapi_internal_string &uri_string); /// /// Copy constructor. /// uri(const uri &other) : m_uri(other.m_uri), m_components(other.m_components) {} /// /// Copy assignment operator. /// uri & operator=(const uri &other) { if (this != &other) { m_uri = other.m_uri; m_components = other.m_components; } return *this; } /// /// Move constructor. /// uri(uri &&other) CPPREST_NOEXCEPT : m_uri(std::move(other.m_uri)), m_components(std::move(other.m_components)) {} /// /// Move assignment operator /// uri & operator=(uri &&other) CPPREST_NOEXCEPT { if (this != &other) { m_uri = std::move(other.m_uri); m_components = std::move(other.m_components); } return *this; } /// /// Get the scheme component of the URI as an encoded string. /// /// The URI scheme as a string. const xsapi_internal_string &scheme() const { return m_components.m_scheme; } /// /// Get the user information component of the URI as an encoded string. /// /// The URI user information as a string. const xsapi_internal_string &user_info() const { return m_components.m_user_info; } /// /// Get the host component of the URI as an encoded string. /// /// The URI host as a string. const xsapi_internal_string &host() const { return m_components.m_host; } /// /// Get the port component of the URI. Returns -1 if no port is specified. /// /// The URI port as an integer. int port() const { return m_components.m_port; } /// /// Get the path component of the URI as an encoded string. /// /// The URI path as a string. const xsapi_internal_string &path() const { return m_components.m_path; } /// /// Get the query component of the URI as an encoded string. /// /// The URI query as a string. const xsapi_internal_string &query() const { return m_components.m_query; } /// /// Get the fragment component of the URI as an encoded string. /// /// The URI fragment as a string. const xsapi_internal_string &fragment() const { return m_components.m_fragment; } /// /// Creates a new uri object with the same authority portion as this one, omitting the resource and query portions. /// /// The new uri object with the same authority. _ASYNCRTIMP uri authority() const; /// /// Gets the path, query, and fragment portion of this uri, which may be empty. /// /// The new URI object with the path, query and fragment portion of this URI. _ASYNCRTIMP uri resource() const; /// /// An empty URI specifies no components, and serves as a default value /// bool is_empty() const { return this->m_uri.empty() || this->m_uri == "/"; } /// /// A loopback URI is one which refers to a hostname or ip address with meaning only on the local machine. /// /// /// Examples include "locahost", or ip addresses in the loopback range (127.0.0.0/24). /// /// true if this URI references the local host, false otherwise. bool is_host_loopback() const { return !is_empty() && ((host() == "localhost") || (host().size() > 4 && host().substr(0,4) == "127.")); } /// /// A wildcard URI is one which refers to all hostnames that resolve to the local machine (using the * or +) /// /// /// http://*:80 /// bool is_host_wildcard() const { return !is_empty() && (this->host() == "*" || this->host() == "+"); } /// /// A portable URI is one with a hostname that can be resolved globally (used from another machine). /// /// true if this URI can be resolved globally (used from another machine), false otherwise. /// /// The hostname "localhost" is a reserved name that is guaranteed to resolve to the local machine, /// and cannot be used for inter-machine communication. Likewise the hostnames "*" and "+" on Windows /// represent wildcards, and do not map to a resolvable address. /// bool is_host_portable() const { return !(is_empty() || is_host_loopback() || is_host_wildcard()); } /// /// A default port is one where the port is unspecified, and will be determined by the operating system. /// The choice of default port may be dictated by the scheme (http -> 80) or not. /// /// true if this URI instance has a default port, false otherwise. bool is_port_default() const { return !is_empty() && this->port() == 0; } /// /// An "authority" URI is one with only a scheme, optional userinfo, hostname, and (optional) port. /// /// true if this is an "authority" URI, false otherwise. bool is_authority() const { return !is_empty() && is_path_empty() && query().empty() && fragment().empty(); } /// /// Returns whether the other URI has the same authority as this one /// /// The URI to compare the authority with. /// true if both the URI's have the same authority, false otherwise. bool has_same_authority(const uri &other) const { return !is_empty() && this->authority() == other.authority(); } /// /// Returns whether the path portion of this URI is empty /// /// true if the path portion of this URI is empty, false otherwise. bool is_path_empty() const { return path().empty() || path() == "/"; } /// /// Returns the full (encoded) URI as a string. /// /// The full encoded URI string. xsapi_internal_string to_string() const { return m_uri; } _ASYNCRTIMP bool operator == (const uri &other) const; bool operator < (const uri &other) const { return m_uri < other.m_uri; } bool operator != (const uri &other) const { return !(this->operator == (other)); } friend class uri_builder; // Encodes all characters not in given set determined by given function. _ASYNCRTIMP static xsapi_internal_string __cdecl encode_impl(const xsapi_internal_string& raw, const std::function& should_encode); xsapi_internal_string m_uri; details::uri_components m_components; }; } // namespace web } } } ================================================ FILE: Source/Shared/HookedUri/details/asyncrt_utils.hpp ================================================ /*** * ==++== * * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * ==--== * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ * * Utilities * * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk * * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ****/ #if defined(_WIN32) #if HC_PLATFORM != HC_PLATFORM_XDK #include #endif #else // _WIN32 #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-local-typedef" #endif // TODO 1808 #include // #include #if defined(__clang__) #pragma clang diagnostic pop #endif #endif // _WIN32 #if HC_PLATFORM_IS_MICROSOFT #pragma warning( push ) #pragma warning( disable : 26444 ) // ignore various unnamed objects #pragma warning( disable : 26498 ) // ignore eof warning #pragma warning( disable : 26812 ) // enum instead of enum class #endif // Could use C++ standard library if not __GLIBCXX__, // For testing purposes we just the handwritten on all platforms. #if defined(CPPREST_STDLIB_UNICODE_CONVERSIONS) #include #endif using namespace xbox::services::cppresturi::web; using namespace xbox::services::cppresturi::utility; using namespace xbox::services::cppresturi::utility::conversions; namespace xbox { namespace services { namespace cppresturi { namespace utility { namespace details { #if !defined(ANDROID) && !defined(__ANDROID__) && !defined(PAVO) std::once_flag g_c_localeFlag; std::unique_ptr g_c_locale(nullptr, [](scoped_c_thread_locale::xplat_locale *){}); scoped_c_thread_locale::xplat_locale scoped_c_thread_locale::c_locale() { std::call_once(g_c_localeFlag, [&]() { scoped_c_thread_locale::xplat_locale *clocale = new scoped_c_thread_locale::xplat_locale(); #ifdef _WIN32 if (clocale == nullptr) { throw std::runtime_error("Unable to create 'C' locale."); } *clocale = _create_locale(LC_ALL, "C"); if (clocale == nullptr || *clocale == nullptr) { throw std::runtime_error("Unable to create 'C' locale."); } auto deleter = [](scoped_c_thread_locale::xplat_locale *clocale) { _free_locale(*clocale); delete clocale; }; #else *clocale = newlocale(LC_ALL, "C", nullptr); if (clocale == nullptr || *clocale == nullptr) { throw std::runtime_error("Unable to create 'C' locale."); } auto deleter = [](scoped_c_thread_locale::xplat_locale *clocale) { freelocale(*clocale); delete clocale; }; #endif g_c_locale = std::unique_ptr(clocale, deleter); }); return *g_c_locale; } #endif #ifdef _WIN32 scoped_c_thread_locale::scoped_c_thread_locale() : m_prevLocale(), m_prevThreadSetting(-1) { char *prevLocale = setlocale(LC_ALL, nullptr); if (prevLocale == nullptr) { throw std::runtime_error("Unable to retrieve current locale."); } if (std::strcmp(prevLocale, "C") != 0) { m_prevLocale = prevLocale; m_prevThreadSetting = _configthreadlocale(_ENABLE_PER_THREAD_LOCALE); if (m_prevThreadSetting == -1) { throw std::runtime_error("Unable to enable per thread locale."); } if (setlocale(LC_ALL, "C") == nullptr) { _configthreadlocale(m_prevThreadSetting); throw std::runtime_error("Unable to set locale"); } } } scoped_c_thread_locale::~scoped_c_thread_locale() { if (m_prevThreadSetting != -1) { setlocale(LC_ALL, m_prevLocale.c_str()); _configthreadlocale(m_prevThreadSetting); } } #elif (defined(ANDROID) || defined(__ANDROID__) || defined(PAVO)) scoped_c_thread_locale::scoped_c_thread_locale() {} scoped_c_thread_locale::~scoped_c_thread_locale() {} #else scoped_c_thread_locale::scoped_c_thread_locale() : m_prevLocale(nullptr) { char *prevLocale = setlocale(LC_ALL, nullptr); if (prevLocale == nullptr) { throw std::runtime_error("Unable to retrieve current locale."); } if (std::strcmp(prevLocale, "C") != 0) { m_prevLocale = uselocale(c_locale()); if (m_prevLocale == nullptr) { throw std::runtime_error("Unable to set locale"); } } } scoped_c_thread_locale::~scoped_c_thread_locale() { if (m_prevLocale != nullptr) { uselocale(m_prevLocale); } } #endif } namespace details { const std::error_category & platform_category() { #ifdef _WIN32 return windows_category(); #else return linux_category(); #endif } #ifdef _WIN32 // Remove once VS 2013 is no longer supported. #if _MSC_VER < 1900 static details::windows_category_impl instance; #endif const std::error_category & windows_category() { #if _MSC_VER >= 1900 static details::windows_category_impl instance; #endif return instance; } std::string windows_category_impl::message(int errorCode) const CPPREST_NOEXCEPT { #if 0 // this returns a non-mem hooked string which can't be changed so commenting it out since its not really needed const size_t buffer_size = 4096; DWORD dwFlags = FORMAT_MESSAGE_FROM_SYSTEM; LPCVOID lpSource = NULL; #if !defined(__cplusplus_winrt) if (errorCode >= 12000) { dwFlags = FORMAT_MESSAGE_FROM_HMODULE; lpSource = GetModuleHandleA("winhttp.dll"); // this handle DOES NOT need to be freed } #endif std::wstring buffer; buffer.resize(buffer_size); const auto result = ::FormatMessageW( dwFlags, lpSource, errorCode, 0, &buffer[0], buffer_size, NULL); if (result == 0) { std::ostringstream os; os << "Unable to get an error message for error code: " << errorCode << "."; return os.str(); } return utility::conversions::to_utf8string(buffer); #else UNREFERENCED_PARAMETER(errorCode); return std::string(); #endif } std::error_condition windows_category_impl::default_error_condition(int errorCode) const CPPREST_NOEXCEPT { // First see if the STL implementation can handle the mapping for common cases. const std::error_condition errCondition = std::system_category().default_error_condition(errorCode); const std::string errConditionMsg = errCondition.message(); if(_stricmp(errConditionMsg.c_str(), "unknown error") != 0) { return errCondition; } switch(errorCode) { #ifndef __cplusplus_winrt case ERROR_WINHTTP_TIMEOUT: return std::errc::timed_out; case ERROR_WINHTTP_CANNOT_CONNECT: return std::errc::host_unreachable; case ERROR_WINHTTP_CONNECTION_ERROR: return std::errc::connection_aborted; #endif case INET_E_RESOURCE_NOT_FOUND: case INET_E_CANNOT_CONNECT: return std::errc::host_unreachable; case INET_E_CONNECTION_TIMEOUT: return std::errc::timed_out; case INET_E_DOWNLOAD_FAILURE: return std::errc::connection_aborted; default: break; } return std::error_condition(errorCode, *this); } #else const std::error_category & linux_category() { // On Linux we are using boost error codes which have the exact same // mapping and are equivalent with std::generic_category error codes. return std::generic_category(); } #endif } #define LOW_3BITS 0x7 #define LOW_4BITS 0xF #define LOW_5BITS 0x1F #define LOW_6BITS 0x3F #define BIT4 0x8 #define BIT5 0x10 #define BIT6 0x20 #define BIT7 0x40 #define BIT8 0x80 #define L_SURROGATE_START 0xDC00 #define L_SURROGATE_END 0xDFFF #define H_SURROGATE_START 0xD800 #define H_SURROGATE_END 0xDBFF #define SURROGATE_PAIR_START 0x10000 utf16string conversions::utf8_to_utf16(const std::string &s) { #if defined(CPPREST_STDLIB_UNICODE_CONVERSIONS) std::wstring_convert, utf16char> conversion; return conversion.from_bytes(s); #else utf16string dest; // Save repeated heap allocations, use less than source string size assuming some // of the characters are not just ASCII and collapse. dest.reserve(static_cast(static_cast(s.size()) * .70)); for (auto src = s.begin(); src != s.end(); ++src) { if ((*src & BIT8) == 0) // single byte character, 0x0 to 0x7F { dest.push_back(utf16string::value_type(*src)); } else { unsigned char numContBytes = 0; uint32_t codePoint; if ((*src & BIT7) == 0) { throw std::range_error("UTF-8 string character can never start with 10xxxxxx"); } else if ((*src & BIT6) == 0) // 2 byte character, 0x80 to 0x7FF { codePoint = *src & LOW_5BITS; numContBytes = 1; } else if ((*src & BIT5) == 0) // 3 byte character, 0x800 to 0xFFFF { codePoint = *src & LOW_4BITS; numContBytes = 2; } else if ((*src & BIT4) == 0) // 4 byte character, 0x10000 to 0x10FFFF { codePoint = *src & LOW_3BITS; numContBytes = 3; } else { throw std::range_error("UTF-8 string has invalid Unicode code point"); } for (unsigned char i = 0; i < numContBytes; ++i) { if (++src == s.end()) { throw std::range_error("UTF-8 string is missing bytes in character"); } if ((*src & BIT8) == 0 || (*src & BIT7) != 0) { throw std::range_error("UTF-8 continuation byte is missing leading byte"); } codePoint <<= 6; codePoint |= *src & LOW_6BITS; } if (codePoint >= SURROGATE_PAIR_START) { // In UTF-16 U+10000 to U+10FFFF are represented as two 16-bit code units, surrogate pairs. // - 0x10000 is subtracted from the code point // - high surrogate is 0xD800 added to the top ten bits // - low surrogate is 0xDC00 added to the low ten bits codePoint -= SURROGATE_PAIR_START; dest.push_back(utf16string::value_type((codePoint >> 10) | H_SURROGATE_START)); dest.push_back(utf16string::value_type((codePoint & 0x3FF) | L_SURROGATE_START)); } else { // In UTF-16 U+0000 to U+D7FF and U+E000 to U+FFFF are represented exactly as the Unicode code point value. // U+D800 to U+DFFF are not valid characters, for simplicity we assume they are not present but will encode // them if encountered. dest.push_back(utf16string::value_type(codePoint)); } } } return dest; #endif } xsapi_internal_string conversions::utf16_to_utf8_internal(const xsapi_internal_wstring &w) { #if defined(CPPREST_STDLIB_UNICODE_CONVERSIONS) std::wstring_convert, utf16char> conversion; return conversion.to_bytes(w); #else xsapi_internal_string dest; dest.reserve(w.size()); for (auto src = w.begin(); src != w.end(); ++src) { // Check for high surrogate. if (*src >= H_SURROGATE_START && *src <= H_SURROGATE_END) { const auto highSurrogate = *src++; if (src == w.end()) { throw std::range_error("UTF-16 string is missing low surrogate"); } const auto lowSurrogate = *src; if (lowSurrogate < L_SURROGATE_START || lowSurrogate > L_SURROGATE_END) { throw std::range_error("UTF-16 string has invalid low surrogate"); } // To get from surrogate pair to Unicode code point: // - subract 0xD800 from high surrogate, this forms top ten bits // - subract 0xDC00 from low surrogate, this forms low ten bits // - add 0x10000 // Leaves a code point in U+10000 to U+10FFFF range. uint32_t codePoint = highSurrogate - H_SURROGATE_START; codePoint <<= 10; codePoint |= lowSurrogate - L_SURROGATE_START; codePoint += SURROGATE_PAIR_START; // 4 bytes need using 21 bits dest.push_back(char((codePoint >> 18) | 0xF0)); // leading 3 bits dest.push_back(char(((codePoint >> 12) & LOW_6BITS) | BIT8)); // next 6 bits dest.push_back(char(((codePoint >> 6) & LOW_6BITS) | BIT8)); // next 6 bits dest.push_back(char((codePoint & LOW_6BITS) | BIT8)); // trailing 6 bits } else { if (*src <= 0x7F) // single byte character { dest.push_back(static_cast(*src)); } else if (*src <= 0x7FF) // 2 bytes needed (11 bits used) { dest.push_back(char((*src >> 6) | 0xC0)); // leading 5 bits dest.push_back(char((*src & LOW_6BITS) | BIT8)); // trailing 6 bits } else // 3 bytes needed (16 bits used) { dest.push_back(char((*src >> 12) | 0xE0)); // leading 4 bits dest.push_back(char(((*src >> 6) & LOW_6BITS) | BIT8)); // middle 6 bits dest.push_back(char((*src & LOW_6BITS) | BIT8)); // trailing 6 bits } } } return dest; #endif } std::string conversions::utf16_to_utf8(const utf16string&w) { #if defined(CPPREST_STDLIB_UNICODE_CONVERSIONS) std::wstring_convert, utf16char> conversion; return conversion.to_bytes(w); #else std::string dest; dest.reserve(w.size()); for (auto src = w.begin(); src != w.end(); ++src) { // Check for high surrogate. if (*src >= H_SURROGATE_START && *src <= H_SURROGATE_END) { const auto highSurrogate = *src++; if (src == w.end()) { throw std::range_error("UTF-16 string is missing low surrogate"); } const auto lowSurrogate = *src; if (lowSurrogate < L_SURROGATE_START || lowSurrogate > L_SURROGATE_END) { throw std::range_error("UTF-16 string has invalid low surrogate"); } // To get from surrogate pair to Unicode code point: // - subract 0xD800 from high surrogate, this forms top ten bits // - subract 0xDC00 from low surrogate, this forms low ten bits // - add 0x10000 // Leaves a code point in U+10000 to U+10FFFF range. uint32_t codePoint = highSurrogate - H_SURROGATE_START; codePoint <<= 10; codePoint |= lowSurrogate - L_SURROGATE_START; codePoint += SURROGATE_PAIR_START; // 4 bytes need using 21 bits dest.push_back(char((codePoint >> 18) | 0xF0)); // leading 3 bits dest.push_back(char(((codePoint >> 12) & LOW_6BITS) | BIT8)); // next 6 bits dest.push_back(char(((codePoint >> 6) & LOW_6BITS) | BIT8)); // next 6 bits dest.push_back(char((codePoint & LOW_6BITS) | BIT8)); // trailing 6 bits } else { if (*src <= 0x7F) // single byte character { dest.push_back(static_cast(*src)); } else if (*src <= 0x7FF) // 2 bytes needed (11 bits used) { dest.push_back(char((*src >> 6) | 0xC0)); // leading 5 bits dest.push_back(char((*src & LOW_6BITS) | BIT8)); // trailing 6 bits } else // 3 bytes needed (16 bits used) { dest.push_back(char((*src >> 12) | 0xE0)); // leading 4 bits dest.push_back(char(((*src >> 6) & LOW_6BITS) | BIT8)); // middle 6 bits dest.push_back(char((*src & LOW_6BITS) | BIT8)); // trailing 6 bits } } } return dest; #endif } utf16string conversions::usascii_to_utf16(const std::string &s) { // Ascii is a subset of UTF-8 so just convert to UTF-16 return utf8_to_utf16(s); } xsapi_internal_wstring conversions::latin1_to_utf16_internal(const xsapi_internal_string &s) { // Latin1 is the first 256 code points in Unicode. // In UTF-16 encoding each of these is represented as exactly the numeric code point. xsapi_internal_wstring dest; dest.resize(s.size()); for (size_t i = 0; i < s.size(); ++i) { dest[i] = utf16char(static_cast(s[i])); } return dest; } xsapi_internal_string conversions::latin1_to_utf8_internal(const xsapi_internal_string &s) { return utf16_to_utf8_internal(latin1_to_utf16_internal(s)); } utility::string_t conversions::to_string_t(utf16string &&s) { #ifdef _UTF16_STRINGS return std::move(s); #else return utf16_to_utf8(std::move(s)); #endif } utility::string_t conversions::to_string_t(std::string &&s) { #ifdef _UTF16_STRINGS return utf8_to_utf16(std::move(s)); #else return std::move(s); #endif } utility::string_t conversions::to_string_t(const utf16string &s) { #ifdef _UTF16_STRINGS return s; #else return utf16_to_utf8(s); #endif } utility::string_t conversions::to_string_t(const std::string &s) { #ifdef _UTF16_STRINGS return utf8_to_utf16(s); #else return s; #endif } xsapi_internal_string conversions::to_utf8string_internal(xsapi_internal_string value) { return value; } xsapi_internal_string conversions::to_utf8string_internal(const xsapi_internal_wstring &value) { return utf16_to_utf8_internal(value); } utf16string conversions::to_utf16string(const std::string &value) { return utf8_to_utf16(value); } utf16string conversions::to_utf16string(utf16string value) { return value; } #ifndef _WIN32 datetime datetime::timeval_to_datetime(const timeval &time) { const uint64_t epoch_offset = 11644473600LL; // diff between windows and unix epochs (seconds) uint64_t result = epoch_offset + time.tv_sec; result *= _secondTicks; // convert to 10e-7 result += time.tv_usec * 10; // convert and add microseconds, 10e-6 to 10e-7 return datetime(result); } #endif static bool is_digit(utility::char_t c) { return c >= _XPLATSTR('0') && c <= _XPLATSTR('9'); } datetime datetime::utc_now() { #ifdef _WIN32 ULARGE_INTEGER largeInt; FILETIME fileTime; GetSystemTimeAsFileTime(&fileTime); largeInt.LowPart = fileTime.dwLowDateTime; largeInt.HighPart = fileTime.dwHighDateTime; return datetime(largeInt.QuadPart); #else //LINUX timeval time {}; gettimeofday(&time, nullptr); return timeval_to_datetime(time); #endif } xsapi_internal_string datetime::to_string_internal(date_format format) const { #ifdef _WIN32 int status; ULARGE_INTEGER largeInt; largeInt.QuadPart = m_interval; FILETIME ft; ft.dwHighDateTime = largeInt.HighPart; ft.dwLowDateTime = largeInt.LowPart; SYSTEMTIME systemTime; if (!FileTimeToSystemTime((const FILETIME *)&ft, &systemTime)) { throw utility::details::create_system_error(GetLastError()); } constexpr size_t dateTimeMaxLength{ 256 }; wchar_t dateTimeBuffer[dateTimeMaxLength]{ 0 }; if (format == RFC_1123) { wchar_t dateStr[18] = { 0 }; #if _WIN32_WINNT < _WIN32_WINNT_VISTA status = GetDateFormatW(LOCALE_INVARIANT, 0, &systemTime, L"ddd',' dd MMM yyyy", dateStr, sizeof(dateStr) / sizeof(wchar_t)); #else status = GetDateFormatEx(LOCALE_NAME_INVARIANT, 0, &systemTime, L"ddd',' dd MMM yyyy", dateStr, sizeof(dateStr) / sizeof(wchar_t), NULL); #endif // _WIN32_WINNT < _WIN32_WINNT_VISTA if (status == 0) { throw utility::details::create_system_error(GetLastError()); } wchar_t timeStr[10] = { 0 }; #if _WIN32_WINNT < _WIN32_WINNT_VISTA status = GetTimeFormatW(LOCALE_INVARIANT, TIME_NOTIMEMARKER | TIME_FORCE24HOURFORMAT, &systemTime, L"HH':'mm':'ss", timeStr, sizeof(timeStr) / sizeof(wchar_t)); #else status = GetTimeFormatEx(LOCALE_NAME_INVARIANT, TIME_NOTIMEMARKER | TIME_FORCE24HOURFORMAT, &systemTime, L"HH':'mm':'ss", timeStr, sizeof(timeStr) / sizeof(wchar_t)); #endif // _WIN32_WINNT < _WIN32_WINNT_VISTA if (status == 0) { throw utility::details::create_system_error(GetLastError()); } _snwprintf_s(dateTimeBuffer, sizeof(dateTimeBuffer), L"%s %s GMT", dateStr, timeStr); } else if (format == ISO_8601) { const size_t buffSize = 64; wchar_t dateStr[buffSize] = { 0 }; #if _WIN32_WINNT < _WIN32_WINNT_VISTA status = GetDateFormatW(LOCALE_INVARIANT, 0, &systemTime, L"yyyy-MM-dd", dateStr, buffSize); #else status = GetDateFormatEx(LOCALE_NAME_INVARIANT, 0, &systemTime, L"yyyy-MM-dd", dateStr, buffSize, NULL); #endif // _WIN32_WINNT < _WIN32_WINNT_VISTA if (status == 0) { throw utility::details::create_system_error(GetLastError()); } wchar_t timeStr[buffSize] = { 0 }; #if _WIN32_WINNT < _WIN32_WINNT_VISTA status = GetTimeFormatW(LOCALE_INVARIANT, TIME_NOTIMEMARKER | TIME_FORCE24HOURFORMAT, &systemTime, L"HH':'mm':'ss", timeStr, buffSize); #else status = GetTimeFormatEx(LOCALE_NAME_INVARIANT, TIME_NOTIMEMARKER | TIME_FORCE24HOURFORMAT, &systemTime, L"HH':'mm':'ss", timeStr, buffSize); #endif // _WIN32_WINNT < _WIN32_WINNT_VISTA if (status == 0) { throw utility::details::create_system_error(GetLastError()); } wchar_t fracSecBuf[9] = { 0 }; uint64_t frac_sec = largeInt.QuadPart % _secondTicks; if (frac_sec > 0) { // Append fractional second, which is a 7-digit value with no trailing zeros // This way, '1200' becomes '00012' _snwprintf_s(fracSecBuf, sizeof(fracSecBuf), L".%07ld", (long int)frac_sec); // trim trailing zeros for (int i = 7; fracSecBuf[i] == '0'; i--) fracSecBuf[i] = '\0'; } _snwprintf_s(dateTimeBuffer, dateTimeMaxLength, L"%sT%sZ%s", dateStr, timeStr, fracSecBuf); } return conversions::to_utf8string_internal(dateTimeBuffer); #else //LINUX uint64_t input = m_interval; uint64_t frac_sec = input % _secondTicks; input /= _secondTicks; // convert to seconds time_t time = (time_t)input - (time_t)11644473600LL;// diff between windows and unix epochs (seconds) struct tm datetime; #if defined(PAVO) gmtime_s(&time, &datetime); #else gmtime_r(&time, &datetime); #endif // PAVO const int max_dt_length = 64; char output[max_dt_length+1] = {0}; if (format != RFC_1123 && frac_sec > 0) { // Append fractional second, which is a 7-digit value with no trailing zeros // This way, '1200' becomes '00012' char buf[9] = { 0 }; snprintf(buf, sizeof(buf), ".%07ld", (long int)frac_sec); // trim trailing zeros for (int i = 7; buf[i] == '0'; i--) buf[i] = '\0'; // format the datetime into a separate buffer char datetime_str[max_dt_length+1] = {0}; strftime(datetime_str, sizeof(datetime_str), "%Y-%m-%dT%H:%M:%S", &datetime); // now print this buffer into the output buffer snprintf(output, sizeof(output), "%s%sZ", datetime_str, buf); } else { strftime(output, sizeof(output), format == RFC_1123 ? "%a, %d %b %Y %H:%M:%S GMT" : "%Y-%m-%dT%H:%M:%SZ", &datetime); } return xsapi_internal_string(output); #endif } #ifdef _WIN32 bool datetime::system_type_to_datetime(void* pvsysTime, uint64_t seconds, datetime * pdt) { SYSTEMTIME* psysTime = (SYSTEMTIME*)pvsysTime; FILETIME fileTime; if (SystemTimeToFileTime(psysTime, &fileTime)) { ULARGE_INTEGER largeInt; largeInt.LowPart = fileTime.dwLowDateTime; largeInt.HighPart = fileTime.dwHighDateTime; // Add hundredths of nanoseconds largeInt.QuadPart += seconds; *pdt = datetime(largeInt.QuadPart); return true; } return false; } #endif // Take a string that represents a fractional second and return the number of ticks // This is equivalent to doing atof on the string and multiplying by 10000000, // but does not lose precision template uint64_t timeticks_from_second(StringIterator begin, StringIterator end) { int size = (int)(end - begin); _ASSERTE(begin[0] == U('.')); uint64_t ufrac_second = 0; for (int i = 1; i <= 7; ++i) { ufrac_second *= 10; int add = i < size ? begin[i] - U('0') : 0; ufrac_second += add; } return ufrac_second; } void extract_fractional_second(const utility::string_t& dateString, utility::string_t& resultString, uint64_t& ufrac_second) { resultString = dateString; // First, the string must be strictly longer than 2 characters, and the trailing character must be 'Z' if (resultString.size() > 2 && resultString[resultString.size() - 1] == U('Z')) { // Second, find the last non-digit by scanning the string backwards auto last_non_digit = std::find_if_not(resultString.rbegin() + 1, resultString.rend(), is_digit); if (last_non_digit < resultString.rend() - 1) { // Finally, make sure the last non-digit is a dot: auto last_dot = last_non_digit.base() - 1; if (*last_dot == U('.')) { // Got it! Now extract the fractional second auto last_before_Z = std::end(resultString) - 1; ufrac_second = timeticks_from_second(last_dot, last_before_Z); // And erase it from the string resultString.erase(last_dot, last_before_Z); } } } } datetime datetime::from_string(const utility::string_t& dateString, date_format format) { // avoid floating point math to preserve precision uint64_t ufrac_second = 0; #ifdef _WIN32 datetime result; if (format == RFC_1123) { SYSTEMTIME sysTime = {0}; std::wstring month(3, L'\0'); std::wstring unused(3, L'\0'); const wchar_t * formatString = L"%3c, %2d %3c %4d %2d:%2d:%2d %3c"; auto n = swscanf_s(dateString.c_str(), formatString, unused.data(), unused.size(), &sysTime.wDay, month.data(), month.size(), &sysTime.wYear, &sysTime.wHour, &sysTime.wMinute, &sysTime.wSecond, unused.data(), unused.size()); if (n == 8) { std::wstring monthnames[12] = {L"Jan", L"Feb", L"Mar", L"Apr", L"May", L"Jun", L"Jul", L"Aug", L"Sep", L"Oct", L"Nov", L"Dec"}; auto loc = std::find_if(monthnames, monthnames+12, [&month](const std::wstring& m) { return m == month;}); if (loc != monthnames+12) { sysTime.wMonth = (short) ((loc - monthnames) + 1); if (system_type_to_datetime(&sysTime, ufrac_second, &result)) { return result; } } } } else if (format == ISO_8601) { // Unlike FILETIME, SYSTEMTIME does not have enough precision to hold seconds in 100 nanosecond // increments. Therefore, start with seconds and milliseconds set to 0, then add them separately // Try to extract the fractional second from the timestamp utility::string_t input; extract_fractional_second(dateString, input, ufrac_second); { SYSTEMTIME sysTime = { 0 }; const wchar_t * formatString = L"%4d-%2d-%2dT%2d:%2d:%2dZ"; auto n = swscanf_s(input.c_str(), formatString, &sysTime.wYear, &sysTime.wMonth, &sysTime.wDay, &sysTime.wHour, &sysTime.wMinute, &sysTime.wSecond); if (n == 3 || n == 6) { if (system_type_to_datetime(&sysTime, ufrac_second, &result)) { return result; } } } { SYSTEMTIME sysTime = {0}; DWORD date = 0; const wchar_t * formatString = L"%8dT%2d:%2d:%2dZ"; auto n = swscanf_s(input.c_str(), formatString, &date, &sysTime.wHour, &sysTime.wMinute, &sysTime.wSecond); if (n == 1 || n == 4) { sysTime.wDay = date % 100; date /= 100; sysTime.wMonth = date % 100; date /= 100; sysTime.wYear = (WORD)date; if (system_type_to_datetime(&sysTime, ufrac_second, &result)) { return result; } } } { SYSTEMTIME sysTime = {0}; GetSystemTime(&sysTime); // Fill date portion with today's information sysTime.wSecond = 0; sysTime.wMilliseconds = 0; const wchar_t * formatString = L"%2d:%2d:%2dZ"; auto n = swscanf_s(input.c_str(), formatString, &sysTime.wHour, &sysTime.wMinute, &sysTime.wSecond); if (n == 3) { if (system_type_to_datetime(&sysTime, ufrac_second, &result)) { return result; } } } } return datetime(); #else std::string input(dateString); struct tm output = tm(); if (format == RFC_1123) { strptime(input.data(), "%a, %d %b %Y %H:%M:%S GMT", &output); } else { // Try to extract the fractional second from the timestamp utility::string_t input; extract_fractional_second(dateString, input, ufrac_second); auto result = strptime(input.data(), "%Y-%m-%dT%H:%M:%SZ", &output); if (result == nullptr) { result = strptime(input.data(), "%Y%m%dT%H:%M:%SZ", &output); } if (result == nullptr) { // Fill the date portion with the epoch, // strptime will do the rest memset(&output, 0, sizeof(struct tm)); output.tm_year = 70; output.tm_mon = 1; output.tm_mday = 1; result = strptime(input.data(), "%H:%M:%SZ", &output); } if (result == nullptr) { result = strptime(input.data(), "%Y-%m-%d", &output); } if (result == nullptr) { result = strptime(input.data(), "%Y%m%d", &output); } if (result == nullptr) { return datetime(); } } #if (defined(ANDROID) || defined(__ANDROID__)) // HACK: The (nonportable?) POSIX function timegm is not available in // bionic. As a workaround[1][2], we set the C library timezone to // UTC, call mktime, then set the timezone back. However, the C // environment is fundamentally a shared global resource and thread- // unsafe. We can protect our usage here, however any other code might // manipulate the environment at the same time. // // [1] http://linux.die.net/man/3/timegm // [2] http://www.gnu.org/software/libc/manual/html_node/Broken_002ddown-Time.html time_t time; static std::mutex env_var_lock; { std::lock_guard lock(env_var_lock); std::string prev_env; auto prev_env_cstr = getenv("TZ"); if (prev_env_cstr != nullptr) { prev_env = prev_env_cstr; } setenv("TZ", "UTC", 1); time = mktime(&output); if (prev_env_cstr) { setenv("TZ", prev_env.c_str(), 1); } else { unsetenv("TZ"); } tzset(); } #else time_t time = timegm(&output); #endif struct timeval tv = timeval(); tv.tv_sec = time; auto result = timeval_to_datetime(tv); // fractional seconds are already in correct format so just add them. result = result + ufrac_second; return result; #endif } /// /// Converts a timespan/interval in seconds to xml duration string as specified by /// http://www.w3.org/TR/xmlschema-2/#duration /// utility::string_t timespan::seconds_to_xml_duration(utility::seconds durationSecs) { auto numSecs = durationSecs.count(); // Find the number of minutes auto numMins = numSecs / 60; if (numMins > 0) { numSecs = numSecs % 60; } // Hours auto numHours = numMins / 60; if (numHours > 0) { numMins = numMins % 60; } // Days auto numDays = numHours / 24; if (numDays > 0) { numHours = numHours % 24; } // The format is: // PdaysDThoursHminutesMsecondsS utility::ostringstream_t oss; oss.imbue(std::locale::classic()); oss << _XPLATSTR("P"); if (numDays > 0) { oss << numDays << _XPLATSTR("D"); } oss << _XPLATSTR("T"); if (numHours > 0) { oss << numHours << _XPLATSTR("H"); } if (numMins > 0) { oss << numMins << _XPLATSTR("M"); } if (numSecs > 0) { oss << numSecs << _XPLATSTR("S"); } return oss.str(); } utility::seconds timespan::xml_duration_to_seconds(const utility::string_t ×panString) { // The format is: // PnDTnHnMnS // if n == 0 then the field could be omitted // The final S could be omitted int64_t numSecs = 0; utility::istringstream_t is(timespanString); is.imbue(std::locale::classic()); auto eof = std::char_traits::eof(); std::basic_istream::int_type c; c = is.get(); // P while (c != eof) { int val = 0; c = is.get(); while (is_digit((utility::char_t)c)) { val = val * 10 + (c - L'0'); c = is.get(); if (c == '.') { // decimal point is not handled do { c = is.get(); } while(is_digit((utility::char_t)c)); } } if (c == L'D') numSecs += static_cast(val) * 24 * 3600; // days if (c == L'H') numSecs += static_cast(val) * 3600; // Hours if (c == L'M') numSecs += static_cast(val) * 60; // Minutes if (c == L'S' || c == eof) { numSecs += val; // seconds break; } } return utility::seconds(numSecs); } const utility::char_t * nonce_generator::c_allowed_chars = _XPLATSTR("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); utility::string_t nonce_generator::generate() { std::uniform_int_distribution<> distr(0, static_cast(ustrlen(c_allowed_chars) - 1)); utility::string_t result; result.reserve(length()); std::generate_n(std::back_inserter(result), length(), [&]() { return c_allowed_chars[distr(m_random)]; } ); return result; } } } } } #if HC_PLATFORM_IS_MICROSOFT #pragma warning( pop ) #endif ================================================ FILE: Source/Shared/HookedUri/details/basic_types.h ================================================ /*** * Copyright (C) Microsoft. All rights reserved. * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. * * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ * * Platform-dependent type definitions * * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk * * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ****/ #pragma once #include #include #include #include #include "HookedUri/details/cpprest_compat.h" #include "pplx/pplxtasks.h" #ifndef _WIN32 # define __STDC_LIMIT_MACROS # include #else #include #endif namespace xbox { namespace services { namespace cppresturi { namespace utility { #ifdef _WIN32 #define _UTF16_STRINGS #endif // We should be using a 64-bit size type for most situations that do // not involve specifying the size of a memory allocation or buffer. typedef uint64_t size64_t; #ifdef _UTF16_STRINGS // // On Windows, all strings are wide // typedef wchar_t char_t ; typedef std::wstring string_t; #define _XPLATSTR(x) L ## x typedef std::wostringstream ostringstream_t; typedef std::wofstream ofstream_t; typedef std::wostream ostream_t; typedef std::wistream istream_t; typedef std::wifstream ifstream_t; typedef std::wistringstream istringstream_t; typedef std::wstringstream stringstream_t; #define ucout std::wcout #define ucin std::wcin #define ucerr std::wcerr #define ustrlen wcslen #else // // On POSIX platforms, all strings are narrow // typedef char char_t; typedef std::string string_t; #define _XPLATSTR(x) x typedef std::ostringstream ostringstream_t; typedef std::ofstream ofstream_t; typedef std::ostream ostream_t; typedef std::istream istream_t; typedef std::ifstream ifstream_t; typedef std::istringstream istringstream_t; typedef std::stringstream stringstream_t; #define ucout std::cout #define ucin std::cin #define ucerr std::cerr #define ustrlen strlen #endif // endif _UTF16_STRINGS #ifndef _TURN_OFF_PLATFORM_STRING #define U(x) _XPLATSTR(x) #endif // !_TURN_OFF_PLATFORM_STRING }// namespace utility } } } typedef char utf8char; typedef std::string utf8string; typedef std::stringstream utf8stringstream; typedef std::ostringstream utf8ostringstream; typedef std::ostream utf8ostream; typedef std::istream utf8istream; typedef std::istringstream utf8istringstream; #ifdef _UTF16_STRINGS typedef wchar_t utf16char; typedef std::wstring utf16string; typedef std::wstringstream utf16stringstream; typedef std::wostringstream utf16ostringstream; typedef std::wostream utf16ostream; typedef std::wistream utf16istream; typedef std::wistringstream utf16istringstream; #else typedef char16_t utf16char; typedef std::u16string utf16string; typedef std::basic_stringstream utf16stringstream; typedef std::basic_ostringstream utf16ostringstream; typedef std::basic_ostream utf16ostream; typedef std::basic_istream utf16istream; typedef std::basic_istringstream utf16istringstream; #endif #if defined(_WIN32) // Include on everything except Windows Desktop ARM, unless explicitly excluded. #if !defined(CPPREST_EXCLUDE_WEBSOCKETS) #if defined(WINAPI_FAMILY) #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && defined(_M_ARM) #define CPPREST_EXCLUDE_WEBSOCKETS #endif #else #if defined(_M_ARM) #define CPPREST_EXCLUDE_WEBSOCKETS #endif #endif #endif #endif ================================================ FILE: Source/Shared/HookedUri/details/cpprest_compat.h ================================================ /*** * ==++== * * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * ==--== * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ * * Standard macros and definitions. * This header has minimal dependency on windows headers and is safe for use in the public API * * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk * * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ****/ #pragma once #if defined(_WIN32) // Settings specific to Windows #if _MSC_VER >= 1900 #define CPPREST_NOEXCEPT noexcept #else #define CPPREST_NOEXCEPT #endif #define CASABLANCA_UNREFERENCED_PARAMETER(x) (x) #include #else // End settings specific to Windows // Settings common to all but Windows #define __declspec(x) __attribute__ ((x)) #define dllimport #define novtable /* no novtable equivalent */ #define __assume(x) do { if (!(x)) __builtin_unreachable(); } while (false) #define CASABLANCA_UNREFERENCED_PARAMETER(x) (void)x #define CPPREST_NOEXCEPT noexcept #include #define _ASSERTE(x) assert(x) // No SAL on non Windows platforms #include "HookedUri/details/nosal.h" #if not defined __cdecl #if defined cdecl #define __cdecl __attribute__ ((cdecl)) #else #define __cdecl #endif #if defined(__ANDROID__) // This is needed to disable the use of __thread inside the boost library. // Android does not support thread local storage -- if boost is included // without this macro defined, it will create references to __tls_get_addr // which (while able to link) will not be available at runtime and prevent // the .so from loading. #define BOOST_ASIO_DISABLE_THREAD_KEYWORD_EXTENSION #endif #ifdef __clang__ #include #endif #endif // defined(__APPLE__) #endif #ifdef _NO_ASYNCRTIMP #define _ASYNCRTIMP #else #ifdef _ASYNCRT_EXPORT #define _ASYNCRTIMP __declspec(dllexport) #else #define _ASYNCRTIMP __declspec(dllimport) #endif #endif #ifdef CASABLANCA_DEPRECATION_NO_WARNINGS #define CASABLANCA_DEPRECATED(x) #else #define CASABLANCA_DEPRECATED(x) __declspec(deprecated(x)) #endif ================================================ FILE: Source/Shared/HookedUri/details/nosal.h ================================================ /*** * ==++== * * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * ==--== * * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk * * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ ***/ #pragma once // selected MS SAL annotations #ifdef _In_ #undef _In_ #endif #define _In_ #ifdef _Inout_ #undef _Inout_ #endif #define _Inout_ #ifdef _Out_ #undef _Out_ #endif #define _Out_ #ifdef _In_z_ #undef _In_z_ #endif #define _In_z_ #ifdef _Out_z_ #undef _Out_z_ #endif #define _Out_z_ #ifdef _Inout_z_ #undef _Inout_z_ #endif #define _Inout_z_ #ifdef _In_opt_ #undef _In_opt_ #endif #define _In_opt_ #ifdef _Out_opt_ #undef _Out_opt_ #endif #define _Out_opt_ #ifdef _Inout_opt_ #undef _Inout_opt_ #endif #define _Inout_opt_ #ifdef _Out_writes_ #undef _Out_writes_ #endif #define _Out_writes_(x) #ifdef _Out_writes_opt_ #undef _Out_writes_opt_ #endif #define _Out_writes_opt_(x) #ifdef _In_reads_ #undef _In_reads_ #endif #define _In_reads_(x) #ifdef _Inout_updates_bytes_ #undef _Inout_updates_bytes_ #endif #define _Inout_updates_bytes_(x) ================================================ FILE: Source/Shared/HookedUri/details/uri.hpp ================================================ /*** * ==++== * * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * ==--== * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ * * Protocol independent support for URIs. * * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk * * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ****/ #pragma once using namespace xbox::services::cppresturi::utility::conversions; #if HC_PLATFORM_IS_MICROSOFT #pragma warning( push ) #pragma warning( disable : 26444 ) // ignore various unnamed objects #pragma warning( disable : 26812 ) // enum instead of enum class #endif namespace xbox { namespace services { namespace cppresturi { namespace web { namespace details { xsapi_internal_string uri_components::join() { // canonicalize components first // convert scheme to lowercase std::transform(m_scheme.begin(), m_scheme.end(), m_scheme.begin(), [](char c) { return (char)tolower(c); }); // convert host to lowercase std::transform(m_host.begin(), m_host.end(), m_host.begin(), [](char c) { return (char)tolower(c); }); // canonicalize the path to have a leading slash if it's a full uri if (!m_host.empty() && m_path.empty()) { m_path = "/"; } else if (!m_host.empty() && m_path[0] != '/') { m_path.insert(m_path.begin(), 1, '/'); } xsapi_internal_string ret; #if (defined(_MSC_VER) && (_MSC_VER >= 1800)) if (!m_scheme.empty()) { ret.append(m_scheme).append({ ':' }); } if (!m_host.empty()) { ret.append("//"); if (!m_user_info.empty()) { ret.append(m_user_info).append({ '@' }); } ret.append(m_host); if (m_port > 0) { char buf[16] = { 0 }; sprintf_s(buf, sizeof(buf), ":%d", m_port); ret.append(buf); } } if (!m_path.empty()) { // only add the leading slash when the host is present if (!m_host.empty() && m_path.front() != '/') { ret.append({ '/' }); } ret.append(m_path); } if (!m_query.empty()) { ret.append({ '?' }).append(m_query); } if (!m_fragment.empty()) { ret.append({ '#' }).append(m_fragment); } return ret; #else xsapi_internal_ostringstream_t os; os.imbue(std::locale::classic()); if (!m_scheme.empty()) { os << m_scheme << ':'; } if (!m_host.empty()) { os << "//"; if (!m_user_info.empty()) { os << m_user_info << '@'; } os << m_host; if (m_port > 0) { os << ':' << m_port; } } if (!m_path.empty()) { // only add the leading slash when the host is present if (!m_host.empty() && m_path.front() != '/') { os << '/'; } os << m_path; } if (!m_query.empty()) { os << '?' << m_query; } if (!m_fragment.empty()) { os << '#' << m_fragment; } return os.str(); #endif } } using namespace xbox::services::cppresturi::web::details; uri::uri(const details::uri_components &components) : m_components(components) { m_uri = m_components.join(); if (!details::uri_parser::validate(m_uri)) { throw uri_exception(""); // provided uri is invalid : " + utility::conversions::to_utf8string(m_uri)); } } uri::uri(const xsapi_internal_string &uri_string) { if (!details::uri_parser::parse(uri_string, m_components)) { throw uri_exception(""); // provided uri is invalid : " + utility::conversions::to_utf8string(uri_string)); } m_uri = m_components.join(); } uri::uri(const char *uri_string): m_uri(uri_string) { if (!details::uri_parser::parse(uri_string, m_components)) { #if 0 // this returns a non-mem hooked string which can't be changed so commenting it out since its not really needed throw uri_exception("provided uri is invalid: " + utility::conversions::to_utf8string_internal(uri_string)); #else throw uri_exception(std::string()); #endif } m_uri = m_components.join(); } xsapi_internal_string uri::encode_impl(const xsapi_internal_string &utf8raw, const std::function& should_encode) { const char * const hex = "0123456789ABCDEF"; xsapi_internal_string encoded; for (auto iter = utf8raw.begin(); iter != utf8raw.end(); ++iter) { // for utf8 encoded string, char ASCII can be greater than 127. int ch = static_cast(*iter); // ch should be same under both utf8 and utf16. if(should_encode(ch)) { encoded.push_back('%'); encoded.push_back(hex[(ch >> 4) & 0xF]); encoded.push_back(hex[ch & 0xF]); } else { // ASCII don't need to be encoded, which should be same on both utf8 and utf16. encoded.push_back((char)ch); } } return encoded; } /// /// Encodes a string by converting all characters except for RFC 3986 unreserved characters to their /// hexadecimal representation. /// xsapi_internal_string uri::encode_data_string(const xsapi_internal_string &raw) { return uri::encode_impl(raw, [](int ch) -> bool { return !uri_parser::is_unreserved(ch); }); } xsapi_internal_string uri::encode_uri(const xsapi_internal_string &raw, uri::components::component component) { // Note: we also encode the '+' character because some non-standard implementations // encode the space character as a '+' instead of %20. To better interoperate we encode // '+' to avoid any confusion and be mistaken as a space. switch(component) { case components::user_info: return uri::encode_impl(raw, [](int ch) -> bool { return !uri_parser::is_user_info_character(ch) || ch == '%' || ch == '+'; }); case components::host: return uri::encode_impl(raw, [](int ch) -> bool { // No encoding of ASCII characters in host name (RFC 3986 3.2.2) return ch > 127; }); case components::path: return uri::encode_impl(raw, [](int ch) -> bool { return !uri_parser::is_path_character(ch) || ch == '%' || ch == '+'; }); case components::query: return uri::encode_impl(raw, [](int ch) -> bool { return !uri_parser::is_query_character(ch) || ch == '%' || ch == '+'; }); case components::fragment: return uri::encode_impl(raw, [](int ch) -> bool { return !uri_parser::is_fragment_character(ch) || ch == '%' || ch == '+'; }); case components::full_uri: default: return uri::encode_impl(raw, [](int ch) -> bool { return !uri_parser::is_unreserved(ch) && !uri_parser::is_reserved(ch); }); }; } /// /// Helper function to convert a hex character digit to a decimal character value. /// Throws an exception if not a valid hex digit. /// static int hex_char_digit_to_decimal_char(int hex) { int decimal; if(hex >= '0' && hex <= '9') { decimal = hex - '0'; } else if(hex >= 'A' && hex <= 'F') { decimal = 10 + (hex - 'A'); } else if(hex >= 'a' && hex <= 'f') { decimal = 10 + (hex - 'a'); } else { throw uri_exception("Invalid hexadecimal digit"); } return decimal; } xsapi_internal_string uri::decode(const xsapi_internal_string &encoded) { xsapi_internal_string utf8raw; for(auto iter = encoded.begin(); iter != encoded.end(); ++iter) { if(*iter == '%') { if(++iter == encoded.end()) { throw uri_exception("Invalid URI string, two hexadecimal digits must follow '%'"); } int decimal_value = hex_char_digit_to_decimal_char(static_cast(*iter)) << 4; if(++iter == encoded.end()) { throw uri_exception("Invalid URI string, two hexadecimal digits must follow '%'"); } decimal_value += hex_char_digit_to_decimal_char(static_cast(*iter)); utf8raw.push_back(static_cast(decimal_value)); } else { // encoded string has to be ASCII. utf8raw.push_back(reinterpret_cast(*iter)); } } return utf8raw; } std::vector uri::split_path(const xsapi_internal_string &path) { std::vector results; xsapi_internal_istringstream iss(path); iss.imbue(std::locale::classic()); xsapi_internal_string s; while (std::getline(iss, s, '/')) { if (!s.empty()) { results.push_back(s); } } return results; } std::map uri::split_query(const xsapi_internal_string &query) { std::map results; // Split into key value pairs separated by '&'. size_t prev_amp_index = 0; while(prev_amp_index != xsapi_internal_string::npos) { size_t amp_index = query.find_first_of('&', prev_amp_index); if (amp_index == xsapi_internal_string::npos) amp_index = query.find_first_of(';', prev_amp_index); xsapi_internal_string key_value_pair = query.substr( prev_amp_index, amp_index == xsapi_internal_string::npos ? query.size() - prev_amp_index : amp_index - prev_amp_index); prev_amp_index = amp_index == xsapi_internal_string::npos ? xsapi_internal_string::npos : amp_index + 1; size_t equals_index = key_value_pair.find_first_of('='); if(equals_index == xsapi_internal_string::npos) { continue; } else if (equals_index == 0) { xsapi_internal_string value(key_value_pair.begin() + equals_index + 1, key_value_pair.end()); results[""] = value; } else { xsapi_internal_string key(key_value_pair.begin(), key_value_pair.begin() + equals_index); xsapi_internal_string value(key_value_pair.begin() + equals_index + 1, key_value_pair.end()); results[key] = value; } } return results; } bool uri::validate(const xsapi_internal_string &uri_string) { return uri_parser::validate(uri_string); } uri uri::authority() const { return uri_builder().set_scheme(this->scheme()).set_host(this->host()).set_port(this->port()).set_user_info(this->user_info()).to_uri(); } uri uri::resource() const { return uri_builder().set_path(this->path()).set_query(this->query()).set_fragment(this->fragment()).to_uri(); } bool uri::operator == (const uri &other) const { // Each individual URI component must be decoded before performing comparison. // TFS # 375865 if (this->is_empty() && other.is_empty()) { return true; } else if (this->is_empty() || other.is_empty()) { return false; } else if (this->scheme() != other.scheme()) { // scheme is canonicalized to lowercase return false; } else if(uri::decode(this->user_info()) != uri::decode(other.user_info())) { return false; } else if (uri::decode(this->host()) != uri::decode(other.host())) { // host is canonicalized to lowercase return false; } else if (this->port() != other.port()) { return false; } else if (uri::decode(this->path()) != uri::decode(other.path())) { return false; } else if (uri::decode(this->query()) != uri::decode(other.query())) { return false; } else if (uri::decode(this->fragment()) != uri::decode(other.fragment())) { return false; } return true; } } } } } #if HC_PLATFORM_IS_MICROSOFT #pragma warning( pop ) #endif ================================================ FILE: Source/Shared/HookedUri/details/uri_builder.hpp ================================================ /*** * ==++== * * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * ==--== * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ * * Builder for constructing URIs. * * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk * * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ****/ #pragma once namespace xbox { namespace services { namespace cppresturi { namespace web { namespace http { // URI class has been moved from web::http namespace to web namespace. // The below using declarations ensure we don't break existing code. // Please use the web::uri class going forward. using web::uri; using web::uri_builder; } uri_builder &uri_builder::append_path(const xsapi_internal_string &path, bool is_encode) { if(path.empty() || path == "/") { return *this; } auto encoded_path = is_encode ? uri::encode_uri(path, uri::components::path) : path; auto thisPath = this->path(); if(thisPath.empty() || thisPath == "/") { if(encoded_path.front() != '/') { set_path("/" + encoded_path); } else { set_path(encoded_path); } } else if(thisPath.back() == '/' && encoded_path.front() == '/') { thisPath.pop_back(); set_path(thisPath + encoded_path); } else if(thisPath.back() != '/' && encoded_path.front() != '/') { set_path(thisPath + "/" + encoded_path); } else { // Only one slash. set_path(thisPath + encoded_path); } return *this; } uri_builder &uri_builder::append_query(const xsapi_internal_string &query, bool is_encode) { if(query.empty()) { return *this; } auto encoded_query = is_encode ? uri::encode_uri(query, uri::components::query) : query; auto thisQuery = this->query(); if (thisQuery.empty()) { this->set_query(encoded_query); } else if(thisQuery.back() == '&' && encoded_query.front() == '&') { thisQuery.pop_back(); this->set_query(thisQuery + encoded_query); } else if(thisQuery.back() != '&' && encoded_query.front() != '&') { this->set_query(thisQuery + "&" + encoded_query); } else { // Only one ampersand. this->set_query(thisQuery + encoded_query); } return *this; } uri_builder &uri_builder::append(const http::uri &relative_uri) { append_path(relative_uri.path()); append_query(relative_uri.query()); this->set_fragment(this->fragment() + relative_uri.fragment()); return *this; } xsapi_internal_string uri_builder::to_string() { return to_uri().to_string(); } uri uri_builder::to_uri() { return uri(m_uri); } bool uri_builder::is_valid() { return uri::validate(m_uri.join()); } } // namespace web } } } ================================================ FILE: Source/Shared/HookedUri/details/uri_parser.h ================================================ /*** * ==++== * * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * ==--== * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ * * URI parsing implementation * * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk * * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ****/ #pragma once #include namespace xbox { namespace services { namespace cppresturi { namespace web { namespace details { namespace uri_parser { /// /// Parses the uri, attempting to determine its validity. /// /// This function accepts both uris ('http://msn.com') and uri relative-references ('path1/path2?query') /// bool validate(const xsapi_internal_string &encoded_string); /// /// Parses the uri, setting each provided string to the value of that component. Components /// that are not part of the provided text are set to the empty string. Component strings /// DO NOT contain their beginning or ending delimiters. /// /// This function accepts both uris ('http://msn.com') and uri relative-references ('path1/path2?query') /// bool parse(const xsapi_internal_string &encoded_string, uri_components &components); /// /// Unreserved characters are those that are allowed in a URI but do not have a reserved purpose. They include: /// - A-Z /// - a-z /// - 0-9 /// - '-' (hyphen) /// - '.' (period) /// - '_' (underscore) /// - '~' (tilde) /// inline bool is_unreserved(int c) { return xbox::services::cppresturi::utility::details::is_alnum((char)c) || c == '-' || c == '.' || c == '_' || c == '~'; } /// /// General delimiters serve as the delimiters between different uri components. /// General delimiters include: /// - All of these :/?#[]@ /// inline bool is_gen_delim(int c) { return c == ':' || c == '/' || c == '?' || c == '#' || c == '[' || c == ']' || c == '@'; } /// /// Subdelimiters are those characters that may have a defined meaning within component /// of a uri for a particular scheme. They do not serve as delimiters in any case between /// uri segments. sub_delimiters include: /// - All of these !$&'()*+,;= /// inline bool is_sub_delim(int c) { switch (c) { case '!': case '$': case '&': case '\'': case '(': case ')': case '*': case '+': case ',': case ';': case '=': return true; default: return false; } } /// /// Reserved characters includes the general delimiters and sub delimiters. Some characters /// are neither reserved nor unreserved, and must be percent-encoded. /// inline bool is_reserved(int c) { return is_gen_delim(c) || is_sub_delim(c); } /// /// Legal characters in the scheme portion include: /// - Any alphanumeric character /// - '+' (plus) /// - '-' (hyphen) /// - '.' (period) /// /// Note that the scheme must BEGIN with an alpha character. /// inline bool is_scheme_character(int c) { return xbox::services::cppresturi::utility::details::is_alnum((char)c) || c == '+' || c == '-' || c == '.'; } /// /// Legal characters in the user information portion include: /// - Any unreserved character /// - The percent character ('%'), and thus any percent-endcoded octet /// - The sub-delimiters /// - ':' (colon) /// inline bool is_user_info_character(int c) { return is_unreserved(c) || is_sub_delim(c) || c == '%' || c == ':'; } /// /// Legal characters in the host portion include: /// - Any unreserved character /// - The percent character ('%'), and thus any percent-endcoded octet /// - The sub-delimiters /// - ':' (colon) /// - '[' (open bracket) /// - ']' (close bracket) /// inline bool is_host_character(int c) { return is_unreserved(c) || is_sub_delim(c) || c == '%' || c == ':' || c == '[' || c == ']'; } /// /// Legal characters in the authority portion include: /// - Any unreserved character /// - The percent character ('%'), and thus any percent-endcoded octet /// - The sub-delimiters /// - ':' (colon) /// /// Note that we don't currently support: /// - IPv6 addresses (requires '[]') /// inline bool is_authority_character(int c) { return is_unreserved(c) || is_sub_delim(c) || c == '%' || c == '@' || c == ':'; } /// /// Legal characters in the path portion include: /// - Any unreserved character /// - The percent character ('%'), and thus any percent-endcoded octet /// - The sub-delimiters /// - ':' (colon) /// - '@' (ampersand) /// inline bool is_path_character(int c) { return is_unreserved(c) || is_sub_delim(c) || c == '%' || c == '/' || c == ':' || c == '@'; } /// /// Legal characters in the query portion include: /// - Any path character /// - '?' (question mark) /// inline bool is_query_character(int c) { return is_path_character(c) || c == '?'; } /// /// Legal characters in the fragment portion include: /// - Any path character /// - '?' (question mark) /// inline bool is_fragment_character(int c) { // this is intentional, they have the same set of legal characters return is_query_character(c); } /// /// Parses the uri, setting the given pointers to locations inside the given buffer. /// 'encoded' is expected to point to an encoded zero-terminated string containing a uri /// bool inner_parse( const char *encoded, const char **scheme_begin, const char **scheme_end, const char **uinfo_begin, const char **uinfo_end, const char **host_begin, const char **host_end, _Out_ int *port, const char **path_begin, const char **path_end, const char **query_begin, const char **query_end, const char **fragment_begin, const char **fragment_end); } }} } } } ================================================ FILE: Source/Shared/HookedUri/details/uri_parser.hpp ================================================ /*** * ==++== * * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * ==--== * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ * * URI parsing implementation * * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk * * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ****/ #pragma once #include namespace xbox { namespace services { namespace cppresturi { namespace web { namespace details { namespace uri_parser { bool validate(const xsapi_internal_string& encoded_string) { const char* scheme_begin = nullptr; const char* scheme_end = nullptr; const char* uinfo_begin = nullptr; const char* uinfo_end = nullptr; const char* host_begin = nullptr; const char* host_end = nullptr; int port_ptr = 0; const char* path_begin = nullptr; const char* path_end = nullptr; const char* query_begin = nullptr; const char* query_end = nullptr; const char* fragment_begin = nullptr; const char* fragment_end = nullptr; // perform a parse, but don't copy out the data return inner_parse( encoded_string.c_str(), &scheme_begin, &scheme_end, &uinfo_begin, &uinfo_end, &host_begin, &host_end, &port_ptr, &path_begin, &path_end, &query_begin, &query_end, &fragment_begin, &fragment_end); } bool parse(const xsapi_internal_string &encoded_string, uri_components &components) { const char* scheme_begin = nullptr; const char* scheme_end = nullptr; const char* host_begin = nullptr; const char* host_end = nullptr; const char* uinfo_begin = nullptr; const char* uinfo_end = nullptr; int port_ptr = 0; const char* path_begin = nullptr; const char* path_end = nullptr; const char* query_begin = nullptr; const char* query_end = nullptr; const char* fragment_begin = nullptr; const char* fragment_end = nullptr; if (inner_parse( encoded_string.c_str(), &scheme_begin, &scheme_end, &uinfo_begin, &uinfo_end, &host_begin, &host_end, &port_ptr, &path_begin, &path_end, &query_begin, &query_end, &fragment_begin, &fragment_end)) { if (scheme_begin) { components.m_scheme.assign(scheme_begin, scheme_end); // convert scheme to lowercase std::transform(components.m_scheme.begin(), components.m_scheme.end(), components.m_scheme.begin(), [](char c) { return (char)tolower(c); }); } else { components.m_scheme.clear(); } if (uinfo_begin) { components.m_user_info.assign(uinfo_begin, uinfo_end); } if (host_begin) { components.m_host.assign(host_begin, host_end); // convert host to lowercase std::transform(components.m_host.begin(), components.m_host.end(), components.m_host.begin(), [](char c) { return (char)tolower(c); }); } else { components.m_host.clear(); } if (port_ptr) { components.m_port = port_ptr; } else { components.m_port = 0; } if (path_begin) { components.m_path.assign(path_begin, path_end); } else { // default path to begin with a slash for easy comparison components.m_path = "/"; } if (query_begin) { components.m_query.assign(query_begin, query_end); } else { components.m_query.clear(); } if (fragment_begin) { components.m_fragment.assign(fragment_begin, fragment_end); } else { components.m_fragment.clear(); } return true; } else { return false; } } bool inner_parse( const char* encoded, const char** scheme_begin, const char** scheme_end, const char** uinfo_begin, const char** uinfo_end, const char** host_begin, const char** host_end, _Out_ int * port, const char** path_begin, const char** path_end, const char** query_begin, const char** query_end, const char** fragment_begin, const char** fragment_end) { *scheme_begin = nullptr; *scheme_end = nullptr; *uinfo_begin = nullptr; *uinfo_end = nullptr; *host_begin = nullptr; *host_end = nullptr; *port = 0; *path_begin = nullptr; *path_end = nullptr; *query_begin = nullptr; *query_end = nullptr; *fragment_begin = nullptr; *fragment_end = nullptr; const char*p = encoded; // IMPORTANT -- A uri may either be an absolute uri, or an relative-reference // Absolute: 'http://host.com' // Relative-Reference: '//:host.com', '/path1/path2?query', './path1:path2' // A Relative-Reference can be disambiguated by parsing for a ':' before the first slash bool is_relative_reference = true; const char*p2 = p; for (;*p2 != '/' && *p2 != '\0'; p2++) { if (*p2 == ':') { // found a colon, the first portion is a scheme is_relative_reference = false; break; } } if (!is_relative_reference) { // the first character of a scheme must be a letter if (!isalpha(*p)) { return false; } // start parsing the scheme, it's always delimited by a colon (must be present) *scheme_begin = p++; for (;*p != ':'; p++) { if (!is_scheme_character(*p)) { return false; } } *scheme_end = p; // skip over the colon p++; } // if we see two slashes next, then we're going to parse the authority portion // later on we'll break up the authority into the port and host const char*authority_begin = nullptr; const char*authority_end = nullptr; if (*p == '/' && p[1] == '/') { // skip over the slashes p += 2; authority_begin = p; // the authority is delimited by a slash (resource), question-mark (query) or octothorpe (fragment) // or by EOS. The authority could be empty ('file:///C:\file_name.txt') for (;*p != '/' && *p != '?' && *p != '#' && *p != '\0'; p++) { // We're NOT currently supporting IPv6, IPvFuture or username/password in authority if (!is_authority_character(*p)) { return false; } } authority_end = p; // now lets see if we have a port specified -- by working back from the end if (authority_begin != authority_end) { // the port is made up of all digits const char*port_begin = authority_end - 1; for (;isdigit(*port_begin) && port_begin != authority_begin; port_begin--) { } if (*port_begin == ':') { // has a port *host_begin = authority_begin; *host_end = port_begin; //skip the colon port_begin++; *port = utility::conversions::scan_string(utility::string_t(port_begin, authority_end), std::locale::classic()); } else { // no port *host_begin = authority_begin; *host_end = authority_end; } // look for a user_info component const char*u_end = *host_begin; for (;is_user_info_character(*u_end) && u_end != *host_end; u_end++) { } if (*u_end == '@') { *host_begin = u_end+1; *uinfo_begin = authority_begin; *uinfo_end = u_end; } else { uinfo_end = uinfo_begin = nullptr; } } } // if we see a path character or a slash, then the // if we see a slash, or any other legal path character, parse the path next if (*p == '/' || is_path_character(*p)) { *path_begin = p; // the path is delimited by a question-mark (query) or octothorpe (fragment) or by EOS for (;*p != '?' && *p != '#' && *p != '\0'; p++) { if (!is_path_character(*p)) { return false; } } *path_end = p; } // if we see a ?, then the query is next if (*p == '?') { // skip over the question mark p++; *query_begin = p; // the query is delimited by a '#' (fragment) or EOS for (;*p != '#' && *p != '\0'; p++) { if (!is_query_character(*p)) { return false; } } *query_end = p; } // if we see a #, then the fragment is next if (*p == '#') { // skip over the hash mark p++; *fragment_begin = p; // the fragment is delimited by EOS for (;*p != '\0'; p++) { if (!is_fragment_character(*p)) { return false; } } *fragment_end = p; } return true; } }}} } } } ================================================ FILE: Source/Shared/HookedUri/uri.h ================================================ /*** * ==++== * * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * ==--== * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ * * Protocol independent support for URIs. * * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk * * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ****/ #pragma once #ifndef _CASA_URI_H #define _CASA_URI_H #include "HookedUri/base_uri.h" #include "HookedUri/uri_builder.h" #endif ================================================ FILE: Source/Shared/HookedUri/uri_builder.h ================================================ /*** * ==++== * * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * ==--== * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ * * Builder style class for creating URIs. * * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk * * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ****/ #pragma once #include #include #include #include "HookedUri/base_uri.h" #include "HookedUri/details/uri_parser.h" namespace xbox { namespace services { namespace cppresturi { namespace web { /// /// Builder for constructing URIs incrementally. /// class uri_builder { public: /// /// Creates a builder with an initially empty URI. /// uri_builder() {} /// /// Creates a builder with a existing URI object. /// /// Encoded string containing the URI. uri_builder(const uri &uri_str): m_uri(uri_str.m_components) {} /// /// Get the scheme component of the URI as an encoded string. /// /// The URI scheme as a string. const xsapi_internal_string &scheme() const { return m_uri.m_scheme; } /// /// Get the user information component of the URI as an encoded string. /// /// The URI user information as a string. const xsapi_internal_string &user_info() const { return m_uri.m_user_info; } /// /// Get the host component of the URI as an encoded string. /// /// The URI host as a string. const xsapi_internal_string &host() const { return m_uri.m_host; } /// /// Get the port component of the URI. Returns -1 if no port is specified. /// /// The URI port as an integer. int port() const { return m_uri.m_port; } /// /// Get the path component of the URI as an encoded string. /// /// The URI path as a string. const xsapi_internal_string &path() const { return m_uri.m_path; } /// /// Get the query component of the URI as an encoded string. /// /// The URI query as a string. const xsapi_internal_string &query() const { return m_uri.m_query; } /// /// Get the fragment component of the URI as an encoded string. /// /// The URI fragment as a string. const xsapi_internal_string &fragment() const { return m_uri.m_fragment; } /// /// Set the scheme of the URI. /// /// Uri scheme. /// A reference to this uri_builder to support chaining. uri_builder & set_scheme(const xsapi_internal_string &scheme) { m_uri.m_scheme = scheme; return *this; } /// /// Set the user info component of the URI. /// /// User info as a decoded string. /// Specify whether to apply URI encoding to the given string. /// A reference to this uri_builder to support chaining. uri_builder & set_user_info(const xsapi_internal_string &user_info, bool do_encoding = false) { m_uri.m_user_info = do_encoding ? uri::encode_uri(user_info, uri::components::user_info) : user_info; return *this; } /// /// Set the host component of the URI. /// /// Host as a decoded string. /// Specify whether to apply URI encoding to the given string. /// A reference to this uri_builder to support chaining. uri_builder & set_host(const xsapi_internal_string &host, bool do_encoding = false) { m_uri.m_host = do_encoding ? uri::encode_uri(host, uri::components::host) : host; return *this; } /// /// Set the port component of the URI. /// /// Port as an integer. /// A reference to this uri_builder to support chaining. uri_builder & set_port(int port) { m_uri.m_port = port; return *this; } /// /// Set the port component of the URI. /// /// Port as a string. /// A reference to this uri_builder to support chaining. /// When string can't be converted to an integer the port is left unchanged. uri_builder & set_port(const xsapi_internal_string &port) { xsapi_internal_istringstream portStream(port); int port_tmp; portStream >> port_tmp; if(portStream.fail() || portStream.bad()) { throw std::invalid_argument("invalid port argument, must be non empty string containing integer value"); } m_uri.m_port = port_tmp; return *this; } /// /// Set the path component of the URI. /// /// Path as a decoded string. /// Specify whether to apply URI encoding to the given string. /// A reference to this uri_builder to support chaining. uri_builder & set_path(const xsapi_internal_string &path, bool do_encoding = false) { m_uri.m_path = do_encoding ? uri::encode_uri(path, uri::components::path) : path; return *this; } /// /// Set the query component of the URI. /// /// Query as a decoded string. /// Specify whether apply URI encoding to the given string. /// A reference to this uri_builder to support chaining. uri_builder & set_query(const xsapi_internal_string &query, bool do_encoding = false) { m_uri.m_query = do_encoding ? uri::encode_uri(query, uri::components::query) : query; return *this; } /// /// Set the fragment component of the URI. /// /// Fragment as a decoded string. /// Specify whether to apply URI encoding to the given string. /// A reference to this uri_builder to support chaining. uri_builder & set_fragment(const xsapi_internal_string &fragment, bool do_encoding = false) { m_uri.m_fragment = do_encoding ? uri::encode_uri(fragment, uri::components::fragment) : fragment; return *this; } /// /// Clears all components of the underlying URI in this uri_builder. /// void clear() { m_uri = details::uri_components(); } /// /// Appends another path to the path of this uri_builder. /// /// Path to append as a already encoded string. /// Specify whether to apply URI encoding to the given string. /// A reference to this uri_builder to support chaining. _ASYNCRTIMP uri_builder &append_path(const xsapi_internal_string &path, bool do_encoding = false); /// /// Appends another query to the query of this uri_builder. /// /// Query to append as a decoded string. /// Specify whether to apply URI encoding to the given string. /// A reference to this uri_builder to support chaining. _ASYNCRTIMP uri_builder &append_query(const xsapi_internal_string &query, bool do_encoding = false); /// /// Appends an relative uri (Path, Query and fragment) at the end of the current uri. /// /// The relative uri to append. /// A reference to this uri_builder to support chaining. _ASYNCRTIMP uri_builder &append(const uri &relative_uri); /// /// Appends another query to the query of this uri_builder, encoding it first. This overload is useful when building a query segment of /// the form "element=10", where the right hand side of the query is stored as a type other than a string, for instance, an integral type. /// /// The name portion of the query string /// The value portion of the query string /// A reference to this uri_builder to support chaining. template uri_builder &append_query(const xsapi_internal_string &name, const T &value, bool do_encoding = true) { auto encodedName = name; auto encodedValue = ::utility::conversions::print_string(value, std::locale::classic()); if (do_encoding) { auto encodingCheck = [](int ch) { switch (ch) { // Encode '&', ';', and '=' since they are used // as delimiters in query component. case '&': case ';': case '=': case '%': case '+': return true; default: return !::web::details::uri_parser::is_query_character(ch); } }; encodedName = uri::encode_impl(encodedName, encodingCheck); encodedValue = uri::encode_impl(encodedValue, encodingCheck); } auto encodedQuery = encodedName; encodedQuery.append(_XPLATSTR("=")); encodedQuery.append(encodedValue); // The query key value pair was already encoded by us or the user separately. return append_query(encodedQuery, false); } /// /// Combine and validate the URI components into a encoded string. An exception will be thrown if the URI is invalid. /// /// The created URI as a string. _ASYNCRTIMP xsapi_internal_string to_string(); /// /// Combine and validate the URI components into a URI class instance. An exception will be thrown if the URI is invalid. /// /// The create URI as a URI class instance. _ASYNCRTIMP uri to_uri(); /// /// Validate the generated URI from all existing components of this uri_builder. /// /// Whether the URI is valid. _ASYNCRTIMP bool is_valid(); private: details::uri_components m_uri; }; } // namespace web } } } ================================================ FILE: Source/Shared/Logger/log.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "log.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN std::shared_ptr logger::get_logger() { auto state = GlobalState::Get(); if (state) { return state->Logger(); } return nullptr; } void logger::add_log_output(std::shared_ptr output) { m_log_outputs.emplace_back(output); }; void logger::set_log_level(HCTraceLevel level) { HCSettingsSetTraceLevel(level); } bool logger::is_log_enabled(HCTraceLevel level) { for (const auto& output : m_log_outputs) { if (output->log_level_enabled(level)) { return true; } } return false; } void logger::add_log(const log_entry& logEntry) { for(const auto& output : m_log_outputs) { if (output->log_level_enabled(logEntry.get_log_level())) { output->add_log(logEntry); } } } void logger::operator+=(const log_entry& logEntry) { add_log(logEntry); } NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END ================================================ FILE: Source/Shared/Logger/log.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "internal_mem.h" #if XSAPI_ANDROID_STUDIO #include "trace.h" #endif #define DEFAULT_LOGGER xbox::services::logger::get_logger() #if HC_PLATFORM_IS_MICROSOFT #define LOG(logger, level, category, msg) \ __pragma(warning( push )) \ __pragma(warning( disable : 26444 )) \ { auto logInst = logger; if (logInst) { logInst->add_log({ level, category, msg }); } } \ __pragma(warning( pop )) #else #define LOG(logger, level, category, msg) \ { auto logInst = logger; if (logInst) { logInst->add_log({ level, category, msg }); } } #endif #define LOGS(level, category) xbox::services::logger_raii() += xbox::services::log_entry(level, category) // default logging macro const char defaultCategory[] = ""; #define IF_LOG_ERROR_ENABLED(x) { auto logger = DEFAULT_LOGGER; if (logger && logger->is_log_enabled(HCTraceLevel::Error)) { x; } } #define LOG_ERROR(msg) LOG(DEFAULT_LOGGER, HCTraceLevel::Error, defaultCategory, msg) #define LOG_ERROR_IF(boolean_expression, msg) if(boolean_expression) LOG_ERROR(msg) #define LOGS_ERROR LOGS(HCTraceLevel::Error, defaultCategory) #define LOGS_ERROR_IF(boolean_expression) if(boolean_expression) LOGS_ERROR #define IF_LOG_WARN_ENABLED(x) { auto logger = DEFAULT_LOGGER; if (logger && logger->is_log_enabled(HCTraceLevel::Warning)) { x; } } #define LOG_WARN(msg) LOG(DEFAULT_LOGGER, HCTraceLevel::Warning, defaultCategory, msg) #define LOG_WARN_IF(boolean_expression, msg) if(boolean_expression) LOG_WARN(msg) #define LOGS_WARN LOGS(HCTraceLevel::Warning, defaultCategory) #define LOGS_WARN_IF(boolean_expression) if(boolean_expression) LOGS_WARN #define IF_LOG_INFO_ENABLED(x) { auto logger = DEFAULT_LOGGER; if (logger && logger->is_log_enabled(HCTraceLevel::Information)) { x; } } #define LOG_INFO(msg) LOG(DEFAULT_LOGGER, HCTraceLevel::Information, defaultCategory, msg) #define LOG_INFO_IF(boolean_expression, msg) if(boolean_expression) LOG_INFO(msg) #define LOGS_INFO LOGS(HCTraceLevel::Information, defaultCategory) #define LOGS_INFO_IF(boolean_expression) if(boolean_expression) LOGS_INFO #define IF_LOG_DEBUG_ENABLED(x) { auto logger = DEFAULT_LOGGER; if (logger && logger->is_log_enabled(HCTraceLevel::Verbose)) { x; } } #define LOG_DEBUG(msg) LOG(DEFAULT_LOGGER, HCTraceLevel::Verbose, defaultCategory, msg) #define LOG_DEBUG_IF(boolean_expression, msg) if(boolean_expression) LOG_DEBUG(msg) #define LOGS_DEBUG LOGS(HCTraceLevel::Verbose, defaultCategory) #define LOGS_DEBUG_IF(boolean_expression) if(boolean_expression) LOGS_DEBUG NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN class log_entry { public: log_entry(HCTraceLevel level, xsapi_internal_string category); log_entry(HCTraceLevel level, xsapi_internal_string category, xsapi_internal_string msg); xsapi_internal_string level_to_string() const; const xsapi_internal_stringstream& msg_stream() const { return m_message; } const xsapi_internal_string& category() const { return m_category; } HCTraceLevel get_log_level() const { return m_logLevel; } log_entry& operator<<(const char* data) { m_message << data; return *this; } log_entry& operator<<(const xsapi_internal_string& data) { m_message << data; return *this; } #if HC_PLATFORM_IS_MICROSOFT log_entry& operator<<(const wchar_t* data) { m_message << xbox::services::convert::to_utf8string(data); return *this; } log_entry& operator<<(const xsapi_internal_wstring& data) { m_message << xbox::services::convert::to_utf8string(data); return *this; } #endif template log_entry& operator<<(const T& data) { m_message << data; return *this; } private: HCTraceLevel m_logLevel; xsapi_internal_string m_category; xsapi_internal_stringstream m_message; }; class log_output { public: log_output(); virtual void add_log(_In_ const log_entry& entry); bool log_level_enabled(HCTraceLevel level) const { return get_log_level() >= level; } HCTraceLevel get_log_level() const { HCTraceLevel traceLevel = HCTraceLevel::Off; HCSettingsGetTraceLevel(&traceLevel); return traceLevel; } virtual ~log_output() = default; protected: // This function is to write the string to the final output, don't need to be thread safe. virtual void write(_In_ HCTraceLevel level, _In_ const xsapi_internal_string& msg); virtual xsapi_internal_string format_log(_In_ const log_entry& entry); private: mutable std::mutex m_mutex; }; class logger { public: logger() {} static std::shared_ptr get_logger(); void set_log_level(HCTraceLevel level); void add_log_output(std::shared_ptr output); void add_log(const log_entry& entry); bool is_log_enabled(HCTraceLevel level); void operator+=(const log_entry& record); private: Vector> m_log_outputs; }; class logger_raii { public: logger_raii() { m_logger = xbox::services::logger::get_logger(); } void set_log_level(HCTraceLevel level) { if (m_logger) m_logger->set_log_level(level); } void add_log_output(std::shared_ptr output) { if (m_logger) m_logger->add_log_output(output); } void add_log(const log_entry& entry) { if (m_logger) m_logger->add_log(entry); } void operator+=(const log_entry& record) { add_log(record); } private: std::shared_ptr m_logger; }; NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END ================================================ FILE: Source/Shared/Logger/log_entry.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "log.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN log_entry::log_entry(HCTraceLevel level, xsapi_internal_string category) : m_logLevel(level), m_category(std::move(category)) { } log_entry::log_entry(HCTraceLevel level, xsapi_internal_string category, xsapi_internal_string msg) : m_logLevel(level), m_category(std::move(category)) { m_message << msg; } xsapi_internal_string log_entry::level_to_string() const { switch (m_logLevel) { case HCTraceLevel::Error: return "L1"; case HCTraceLevel::Warning: return "L2"; case HCTraceLevel::Important: return "L3"; case HCTraceLevel::Information: return "L4"; case HCTraceLevel::Verbose: return "L5"; default: break; } return ""; } NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END ================================================ FILE: Source/Shared/Logger/log_hc_output.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "log_hc_output.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN void log_hc_output::write(_In_ HCTraceLevel level, _In_ const String& msg) { constexpr char escapedFormatSpecifier[]{ "%%" }; String const* msgPtr{ &msg }; String escapedMsg{}; size_t index = msg.find('%'); if (index != std::string::npos) { // Escape format string before passing to HC_TRACE escapedMsg = msg; while ((index = escapedMsg.find('%', index)) != std::string::npos) { escapedMsg.replace(index, 1, escapedFormatSpecifier); index += (sizeof(escapedFormatSpecifier) - 1); } msgPtr = &escapedMsg; } HC_TRACE_MESSAGE(XSAPI, level, msgPtr->data()); } NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END ================================================ FILE: Source/Shared/Logger/log_hc_output.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "log.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN class log_hc_output : public log_output { public: log_hc_output() : log_output() {} void write(_In_ HCTraceLevel level, _In_ const xsapi_internal_string& msg) override; }; NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END ================================================ FILE: Source/Shared/Logger/log_output.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "Logger/log.h" #include NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN log_output::log_output() { } void log_output::add_log(_In_ const log_entry& entry) { xsapi_internal_string msg = format_log(entry); { std::lock_guard lock(m_mutex); write(entry.get_log_level(), msg); } } void log_output::write(_In_ HCTraceLevel level, _In_ const xsapi_internal_string& msg) { UNREFERENCED_PARAMETER(level); UNREFERENCED_PARAMETER(msg); } xsapi_internal_string log_output::format_log(_In_ const log_entry& entry) { return entry.msg_stream().str(); } NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END ================================================ FILE: Source/Shared/a/android_utils.cpp ================================================ #include "android_utils.h" #include #include "Logger/log.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN JString::JString(JNIEnv* env, jstring string) noexcept : m_env{ env }, m_string{ string }, m_cStr{ nullptr } { assert(env); assert(string); } JString::~JString() noexcept { if (m_cStr != nullptr) { assert(m_env); assert(m_string); m_env->ReleaseStringUTFChars(m_string, m_cStr); } } char const* JString::c_str() { assert(m_env); assert(m_string); if (m_cStr == nullptr) { m_cStr = m_env->GetStringUTFChars(m_string, nullptr); if (m_cStr == nullptr) { LOG_ERROR("GetStringUTFChars failed"); } } return m_cStr; } NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END ================================================ FILE: Source/Shared/a/android_utils.h ================================================ #pragma once #include #if XSAPI_ANDROID_STUDIO #include "types.h" #endif NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN /// /// Thread unsafe wrapper for the jni jstring object to ensure we don't leak memory /// class JString { public: JString(JNIEnv* env, jstring string) noexcept; ~JString() noexcept; JString(JString const&) = delete; JString(JString&&) = delete; JString& operator=(JString const&) = delete; JString& operator=(JString&&) = delete; char const* c_str(); private: JNIEnv * m_env; jstring m_string; char const* m_cStr; }; NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END ================================================ FILE: Source/Shared/a/guid.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "xbl_guid.h" #include "android_utils.h" #include "a/java_interop.h" #include "a/jni_utils.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN #define UNREFERENCED_LOCAL(x) (void)(x); template void FormatHelper(TBuffer& buffer, _In_z_ _Printf_format_string_ char const* format, va_list args) { va_list args1{}; va_copy(args1, args); int required = vsnprintf(nullptr, 0, format, args1); va_end(args1); assert(required > 0); size_t originalSize = buffer.size(); va_list args2{}; va_copy(args2, args); buffer.resize(originalSize + static_cast(required) + 1); // add space for null terminator int written = vsnprintf(reinterpret_cast(&buffer[originalSize]), buffer.size(), format, args2); va_end(args2); assert(written == required); UNREFERENCED_LOCAL(written); buffer.resize(buffer.size() - 1); // drop null terminator } inline xsapi_internal_string Format(_In_z_ _Printf_format_string_ char const* format, ...) { xsapi_internal_string s; va_list args{}; va_start(args, format); FormatHelper(s, format, args); va_end(args); return s; } xsapi_internal_string generate_guid() { auto javaInterop = java_interop::get_java_interop_singleton(); auto javaVM = javaInterop->get_java_vm(); if (javaVM == nullptr) { LOG_ERROR("java interop not initialized properly"); return ""; } if (javaVM != nullptr) { JNIEnv* jniEnv; JNI_ATTACH_THREAD(javaVM, jniEnv); jclass guidClass = jniEnv->FindClass("java/util/UUID"); if (guidClass == nullptr) { LOG_ERROR("Could not find UUID class"); return ""; } jclass m_guidClass = reinterpret_cast(jniEnv->NewGlobalRef(guidClass)); jmethodID generateUuidMethodId = jniEnv->GetStaticMethodID(m_guidClass, "randomUUID", "()Ljava/util/UUID;"); jmethodID uuidToStringMethodId = jniEnv->GetMethodID(m_guidClass, "toString", "()Ljava/lang/String;"); jobject uuidObject = jniEnv->CallStaticObjectMethod(m_guidClass, generateUuidMethodId); JString rawUuid{ jniEnv, static_cast(jniEnv->CallObjectMethod(uuidObject, uuidToStringMethodId)) }; return Format("{%s}", rawUuid.c_str()); } return ""; } NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END ================================================ FILE: Source/Shared/a/http_call_jni.cpp ================================================ #include "pch.h" #include "http_call_jni.h" #include #include #include "jni_utils.h" #include "http_call_legacy.h" #include "a/java_interop.h" #define LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, "HttpCall", __VA_ARGS__)) #define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "HttpCall", __VA_ARGS__)) #define JENV_CHECK_MSG(var, env, msg) \ if (var == nullptr) \ { \ clear_and_log_exception(env); \ LOG_ERROR(msg); \ return; \ } #define JENV_CHECK_MSG_RET(var, env, msg, ret) \ if (var == nullptr) \ { \ clear_and_log_exception(env); \ LOG_ERROR(msg); \ return ret; \ } static xbox::services::legacy::http_call* getHttpCall(JNIEnv * env, jobject httpCall) { jclass cls = env->GetObjectClass(httpCall); jfieldID id = env->GetFieldID(cls, "id", "J"); if (id == nullptr) { XSAPI_ASSERT(false); } return reinterpret_cast*>(env->GetLongField(httpCall, id))->get(); } static bool clear_and_log_exception(JNIEnv* env) { bool ret = false; if (env->ExceptionCheck()) { jthrowable e = env->ExceptionOccurred(); env->ExceptionClear(); jclass cls = env->GetObjectClass(e); jmethodID getMessage = env->GetMethodID(cls, "getMessage", "()Ljava/lang/String;"); jstring_t msg(env, static_cast(env->CallObjectMethod(e, getMessage))); #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wformat-security" LOGE(msg); #pragma clang diagnostic pop ret = true; } return ret; } static bool prepare_stream(JNIEnv * myEnv, std::shared_ptr response, jobject& stream, jmethodID& closeStream) { if(!response) { return false; } auto v = response->response_body_vector(); auto sz = v.size(); LOGD("Received %zu bytes", sz); jbyteArray array = myEnv->NewByteArray(sz); JENV_CHECK_MSG_RET(array, myEnv, "Failed creating Java byte array", false) jbyte* buf = myEnv->GetByteArrayElements(array, nullptr); JENV_CHECK_MSG_RET(buf, myEnv, "Failed getting array elements", false) memcpy(buf, v.data(), sz); myEnv->ReleaseByteArrayElements(array, buf, 0); jclass clsStream = myEnv->FindClass("java/io/ByteArrayInputStream"); JENV_CHECK_MSG_RET(clsStream, myEnv, "Could not find ByteArrayInputStream class", false) jmethodID initStream = myEnv->GetMethodID(clsStream, "", "([B)V"); JENV_CHECK_MSG_RET(initStream, myEnv, "Failed getting method ID", false) closeStream = myEnv->GetMethodID(clsStream, "close", "()V"); JENV_CHECK_MSG_RET(closeStream, myEnv, "Failed getting close method ID", false) stream = myEnv->NewObject(clsStream, initStream, array); JENV_CHECK_MSG_RET(stream, myEnv, "Failed creating ByteArrayInputStream instance", false) return true; } static bool prepare_headers(JNIEnv* myEnv, std::shared_ptr response, jclass clsHeaders, jobject& headers) { if (!response) { return false; } jmethodID initHeaders = myEnv->GetMethodID(clsHeaders, "", "()V"); JENV_CHECK_MSG_RET(initHeaders, myEnv, "Failed getting method ID", false) jmethodID addHeader = myEnv->GetMethodID(clsHeaders, "add", "(Ljava/lang/String;Ljava/lang/String;)V"); JENV_CHECK_MSG_RET(addHeader, myEnv, "Failed getting add() method ID", false) headers = myEnv->NewObject(clsHeaders, initHeaders); JENV_CHECK_MSG_RET(headers, myEnv, "Failed creating ByteArrayInputStream instance", false) web::http::http_headers hdr = response->response_headers(); for (web::http::http_headers::iterator itr = hdr.begin(); itr != hdr.end(); ++itr) { jstring key = myEnv->NewStringUTF(itr->first.c_str()); JENV_CHECK_MSG_RET(key, myEnv, "Error allocating string for a key", false) local_ref_holder lrKey(std::pair(myEnv, key)); jstring value = myEnv->NewStringUTF(itr->second.c_str()); JENV_CHECK_MSG_RET(value, myEnv, "Error allocating string for a value", false) local_ref_holder lrValue(std::pair(myEnv, value)); myEnv->CallVoidMethod(headers, addHeader, key, value); } return true; } /* * Class: com_microsoft_xbox_idp_util_HttpCall * Method: setRequestBody * Signature: (Ljava/lang/String;)V */ JNIEXPORT void JNICALL Java_com_microsoft_xbox_idp_util_HttpCall_setRequestBody__Ljava_lang_String_2( JNIEnv * env, jobject httpCall, jstring value) { if (value != nullptr) { jstring_t strValue(env, value); getHttpCall(env, httpCall)->set_request_body(strValue); } } /* * Class: com_microsoft_xbox_idp_util_HttpCall * Method: setRequestBody * Signature: ([B)V */ JNIEXPORT void JNICALL Java_com_microsoft_xbox_idp_util_HttpCall_setRequestBody___3B( JNIEnv * env, jobject httpCall, jbyteArray value) { jbyte* buf = env->GetByteArrayElements(value, nullptr); std::vector v(reinterpret_cast(buf), reinterpret_cast(buf) + env->GetArrayLength(value)); getHttpCall(env, httpCall)->set_request_body(v); env->ReleaseByteArrayElements(value, buf, JNI_ABORT); } /* * Class: com_microsoft_xbox_idp_util_HttpCall * Method: setCustomHeader * Signature: (Ljava/lang/String;Ljava/lang/String;)V */ JNIEXPORT void JNICALL Java_com_microsoft_xbox_idp_util_HttpCall_setCustomHeader( JNIEnv * env, jobject httpCall, jstring name, jstring value) { if (name != nullptr && value != nullptr) { jstring_t strName(env, name); jstring_t strValue(env, value); getHttpCall(env, httpCall)->set_custom_header(strName, strValue); } } /* * Class: com_microsoft_xbox_idp_util_HttpCall * Method: setRetryAllowed * Signature: (Z)V */ JNIEXPORT void JNICALL Java_com_microsoft_xbox_idp_util_HttpCall_setRetryAllowed( JNIEnv * env, jobject httpCall, jboolean value) { getHttpCall(env, httpCall)->set_retry_allowed(value); } /* * Class: com_microsoft_xbox_idp_util_HttpCall * Method: setLongHttpCall * Signature: (Z)V */ JNIEXPORT void JNICALL Java_com_microsoft_xbox_idp_util_HttpCall_setLongHttpCall( JNIEnv * env, jobject httpCall, jboolean value) { getHttpCall(env, httpCall)->set_long_http_call(value); } /* * Class: com_microsoft_xbox_idp_util_HttpCall * Method: setContentTypeHeaderValue * Signature: (Ljava/lang/String;)V */ JNIEXPORT void JNICALL Java_com_microsoft_xbox_idp_util_HttpCall_setContentTypeHeaderValue( JNIEnv * env, jobject httpCall, jstring value) { if (value != nullptr) { jstring_t strValue(env, value); getHttpCall(env, httpCall)->set_content_type_header_value(strValue); } } /* * Class: com_microsoft_xbox_idp_util_HttpCall * Method: setXboxContractVersionHeaderValue * Signature: (Ljava/lang/String;)V */ JNIEXPORT void JNICALL Java_com_microsoft_xbox_idp_util_HttpCall_setXboxContractVersionHeaderValue( JNIEnv * env, jobject httpCall, jstring value) { if (value != nullptr) { jstring_t strValue(env, value); getHttpCall(env, httpCall)->set_xbox_contract_version_header_value(strValue); } } /* * Class: com_microsoft_xbox_idp_util_HttpCall * Method: getResponseAsync * Signature: (Lcom/microsoft/xbox/idp/util/HttpCall/Callback;)V */ JNIEXPORT void JNICALL Java_com_microsoft_xbox_idp_util_HttpCall_getResponseAsync__Lcom_microsoft_xbox_idp_util_HttpCall_Callback_2 (JNIEnv * env, jobject httpCall, jobject callback) { jclass clsHeaders = env->FindClass("com/microsoft/xbox/idp/util/HttpHeaders"); if (clsHeaders == nullptr) { LOG_ERROR("Could not find HttpHeaders class"); return; } jclass myClsHeaders = (jclass)env->NewGlobalRef(clsHeaders); JavaVM* jvm; env->GetJavaVM(&jvm); jobject myCallback = env->NewGlobalRef(callback); xbox::services::legacy::http_call* http_call = getHttpCall(env, httpCall); auto task = http_call->get_response_with_auth( xbox::services::legacy::http_call_response_body_type::vector_body ) .then([jvm, myCallback, myClsHeaders](std::shared_ptr response) { if (response) { LOGD("HttpCall response error code %d", response->http_status()); } JNIEnv* myEnv; jvm->AttachCurrentThread(&myEnv, nullptr); JNI_ERROR_CHECK(myEnv); thread_holder th(jvm); global_ref_holder grCallback(std::pair(myEnv, myCallback)); global_ref_holder grClsHeaders(std::pair(myEnv, myClsHeaders)); jclass clsCallback = myEnv->GetObjectClass(myCallback); jmethodID processResponse = myEnv->GetMethodID(clsCallback, "processResponse", "(ILjava/io/InputStream;Lcom/microsoft/xbox/idp/util/HttpHeaders;)V"); if (processResponse == nullptr) { LOG_ERROR("Failed getting processResponse method ID"); return; } jobject stream; jmethodID closeStream; if (!prepare_stream(myEnv, response, stream, closeStream)) { LOG_ERROR("prepare_stream error"); return; } local_ref_holder lrStream(std::pair(myEnv, stream)); jobject headers = nullptr; if (!prepare_headers(myEnv, response, myClsHeaders, headers)) { clear_and_log_exception(myEnv); LOG_ERROR("prepare_headers error"); return; } local_ref_holder lrHeaders(std::pair(myEnv, headers)); jint httpStatus = response->http_status(); myEnv->CallVoidMethod(myCallback, processResponse, httpStatus, stream, headers); clear_and_log_exception(myEnv); myEnv->CallVoidMethod(stream, closeStream); clear_and_log_exception(myEnv); }); } /* * Class: com_microsoft_xbox_idp_util_HttpCall * Method: create * Signature: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)J */ JNIEXPORT jlong JNICALL Java_com_microsoft_xbox_idp_util_HttpCall_create (JNIEnv * env, jclass cls, jstring method, jstring endpoint, jstring pathAndQuery, jboolean addDefaultHeaders) { jstring_t strMethod(env, method); jstring_t strEndpoint(env, endpoint); jstring_t strPathAndQuery(env, pathAndQuery); LOGD("Create HttpCall with uri %s", strEndpoint.get()); std::shared_ptr* legacyHttpCallPtr{ nullptr }; auto user = java_interop::get_java_interop_singleton()->GetStoredUser(); if (user) { auto userCopyResult = user->Copy(); if (Succeeded(userCopyResult)) { String fullUrl{ strEndpoint.get() }; xbox::services::uri path{ strPathAndQuery.get() }; if (!path.is_empty()) { fullUrl += path.to_string(); } auto httpCall = MakeShared(userCopyResult.ExtractPayload()); HRESULT hr = httpCall->Init( std::shared_ptr{ new xbox::services::XboxLiveContextSettings }, strMethod.get(), fullUrl, xbox::services::xbox_live_api::unspecified ); if (SUCCEEDED(hr)) { // legacy http_call doesn't duplicate the provided XblHttpCall handle so do it before passing. // This seems like a poor design, but keeping behavior same for now httpCall->AddRef(); web::uri_builder uriBuilder; uriBuilder.append(strPathAndQuery.get()); auto legacyHttpCall = std::make_shared(httpCall.get(), strMethod.get(), strEndpoint.get(), uriBuilder.to_uri()); legacyHttpCallPtr = new std::shared_ptr(std::move(legacyHttpCall)); (*legacyHttpCallPtr)->set_add_default_headers(addDefaultHeaders); } } } return reinterpret_cast(legacyHttpCallPtr); } /* * Class: com_microsoft_xbox_idp_util_HttpCall * Method: delete * Signature: (J)V */ JNIEXPORT void JNICALL Java_com_microsoft_xbox_idp_util_HttpCall_delete(JNIEnv * env, jclass cls, jlong id) { delete reinterpret_cast*>(id); } ================================================ FILE: Source/Shared/a/http_call_jni.h ================================================ /* DO NOT EDIT THIS FILE - it is machine generated */ #include /* Header for class com_microsoft_xbox_idp_util_HttpCall */ #ifndef _Included_com_microsoft_xbox_idp_util_HttpCall #define _Included_com_microsoft_xbox_idp_util_HttpCall #ifdef __cplusplus extern "C" { #endif /* * Class: com_microsoft_xbox_idp_util_HttpCall * Method: setRequestBody * Signature: (Ljava/lang/String;)V */ JNIEXPORT void JNICALL Java_com_microsoft_xbox_idp_util_HttpCall_setRequestBody__Ljava_lang_String_2 (JNIEnv *, jobject, jstring); /* * Class: com_microsoft_xbox_idp_util_HttpCall * Method: setRequestBody * Signature: ([B)V */ JNIEXPORT void JNICALL Java_com_microsoft_xbox_idp_util_HttpCall_setRequestBody___3B (JNIEnv *, jobject, jbyteArray); /* * Class: com_microsoft_xbox_idp_util_HttpCall * Method: setCustomHeader * Signature: (Ljava/lang/String;Ljava/lang/String;)V */ JNIEXPORT void JNICALL Java_com_microsoft_xbox_idp_util_HttpCall_setCustomHeader (JNIEnv *, jobject, jstring, jstring); /* * Class: com_microsoft_xbox_idp_util_HttpCall * Method: setRetryAllowed * Signature: (Z)V */ JNIEXPORT void JNICALL Java_com_microsoft_xbox_idp_util_HttpCall_setRetryAllowed (JNIEnv *, jobject, jboolean); /* * Class: com_microsoft_xbox_idp_util_HttpCall * Method: setLongHttpCall * Signature: (Z)V */ JNIEXPORT void JNICALL Java_com_microsoft_xbox_idp_util_HttpCall_setLongHttpCall (JNIEnv *, jobject, jboolean); /* * Class: com_microsoft_xbox_idp_util_HttpCall * Method: setContentTypeHeaderValue * Signature: (Ljava/lang/String;)V */ JNIEXPORT void JNICALL Java_com_microsoft_xbox_idp_util_HttpCall_setContentTypeHeaderValue (JNIEnv *, jobject, jstring); /* * Class: com_microsoft_xbox_idp_util_HttpCall * Method: setXboxContractVersionHeaderValue * Signature: (Ljava/lang/String;)V */ JNIEXPORT void JNICALL Java_com_microsoft_xbox_idp_util_HttpCall_setXboxContractVersionHeaderValue (JNIEnv *, jobject, jstring); /* * Class: com_microsoft_xbox_idp_util_HttpCall * Method: getResponseAsync * Signature: (Lcom/microsoft/xbox/idp/util/HttpCall/Callback;)V */ JNIEXPORT void JNICALL Java_com_microsoft_xbox_idp_util_HttpCall_getResponseAsync__Lcom_microsoft_xbox_idp_util_HttpCall_Callback_2 (JNIEnv *, jobject, jobject); /* * Class: com_microsoft_xbox_idp_util_HttpCall * Method: create * Signature: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)J */ JNIEXPORT jlong JNICALL Java_com_microsoft_xbox_idp_util_HttpCall_create (JNIEnv *, jclass, jstring, jstring, jstring, jboolean); /* * Class: com_microsoft_xbox_idp_util_HttpCall * Method: delete * Signature: (J)V */ JNIEXPORT void JNICALL Java_com_microsoft_xbox_idp_util_HttpCall_delete (JNIEnv *, jclass, jlong); #ifdef __cplusplus } #endif #endif ================================================ FILE: Source/Shared/a/http_call_static_glue.cpp ================================================ #include "pch.h" #include "http_call_jni.h" #include #define LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, "HttpCallStaticGlue", __VA_ARGS__)) #define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "HttpCallStaticGlue", __VA_ARGS__)) static JNINativeMethod methods[] = { { "setRequestBody", "(Ljava/lang/String;)V", (void*)Java_com_microsoft_xbox_idp_util_HttpCall_setRequestBody__Ljava_lang_String_2 }, { "setRequestBody", "([B)V", (void*)Java_com_microsoft_xbox_idp_util_HttpCall_setRequestBody___3B }, { "setCustomHeader", "(Ljava/lang/String;Ljava/lang/String;)V", (void*)Java_com_microsoft_xbox_idp_util_HttpCall_setCustomHeader }, { "setRetryAllowed", "(Z)V", (void*)Java_com_microsoft_xbox_idp_util_HttpCall_setRetryAllowed }, { "setLongHttpCall", "(Z)V", (void*)Java_com_microsoft_xbox_idp_util_HttpCall_setLongHttpCall }, { "setContentTypeHeaderValue", "(Ljava/lang/String;)V", (void*)Java_com_microsoft_xbox_idp_util_HttpCall_setContentTypeHeaderValue }, { "setXboxContractVersionHeaderValue", "(Ljava/lang/String;)V", (void*)Java_com_microsoft_xbox_idp_util_HttpCall_setXboxContractVersionHeaderValue }, { "getResponseAsync", "(Lcom/microsoft/xbox/idp/util/HttpCall$Callback;)V", (void*)Java_com_microsoft_xbox_idp_util_HttpCall_getResponseAsync__Lcom_microsoft_xbox_idp_util_HttpCall_Callback_2 }, { "create", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)J", (void*)Java_com_microsoft_xbox_idp_util_HttpCall_create }, { "delete", "(J)V", (void*)Java_com_microsoft_xbox_idp_util_HttpCall_delete } }; bool http_call_register_natives(JNIEnv *env, jobject clsLoader, jmethodID loadClass) { jstring clsName = env->NewStringUTF("com/microsoft/xbox/idp/util/HttpCall"); jclass cls = (jclass)env->CallObjectMethod(clsLoader, loadClass, clsName); env->DeleteLocalRef(clsName); if (cls == NULL) { LOGE("Failed to load class com/microsoft/xbox/idp/util/HttpCall"); return false; } if (env->RegisterNatives(cls, methods, sizeof methods / sizeof *methods) != 0) { LOGE("Failed to register native methods"); env->DeleteLocalRef(cls); return false; } env->DeleteLocalRef(cls); LOGD("Successfully registerered HttpCall methods"); return true; } ================================================ FILE: Source/Shared/a/http_call_static_glue.h ================================================ #pragma once extern bool http_call_register_natives(JNIEnv *env, jobject clsLoader, jmethodID loadClass); ================================================ FILE: Source/Shared/a/interop_jni.cpp ================================================ #include "pch.h" #include "a/java_interop.h" #include #include "uri_impl.h" #ifdef __cplusplus extern "C" { #endif JNIEXPORT jboolean JNICALL Java_com_microsoft_xbox_idp_interop_Interop_initializeInterop(JNIEnv* env, jclass clsInterop, jobject context) { xbox::services::xbl_result result = xbox::services::java_interop::get_java_interop_singleton()->initialize(env, clsInterop, context); return result.err() ? 0 : 1; } JNIEXPORT void JNICALL Java_com_microsoft_xbox_idp_interop_Interop_deinitializeInterop(JNIEnv* env, jclass clsInterop) { xbox::services::java_interop::get_java_interop_singleton()->deinitialize(); } #ifdef __cplusplus } #endif ================================================ FILE: Source/Shared/a/interop_jni.h ================================================ #pragma once #include #ifdef __cplusplus extern "C" { #endif JNIEXPORT jboolean JNICALL Java_com_microsoft_xbox_idp_interop_Interop_initializeInterop(JNIEnv* env, jclass clsInterop, jobject context); JNIEXPORT void JNICALL Java_com_microsoft_xbox_idp_interop_Interop_deinitializeInterop(JNIEnv* env, jclass clsInterop); #ifdef __cplusplus } #endif ================================================ FILE: Source/Shared/a/jni_utils.h ================================================ #pragma once #include #include "xsapi_utils.h" template struct ref_holder { ref_holder(T ref) : m_ref(ref) { } operator T() const { return m_ref; } protected: T m_ref; }; struct thread_holder : ref_holder { thread_holder() : ref_holder(nullptr) {} thread_holder(JavaVM* jvm) : ref_holder(jvm) {} ~thread_holder() { if (m_ref != nullptr) { LOG_INFO(_T("thread detached")); m_ref->DetachCurrentThread(); } } void set_ref(JavaVM* ref) { m_ref = ref; } }; struct global_ref_holder : ref_holder> { global_ref_holder(std::pair ref) : ref_holder>(ref) { } ~global_ref_holder() { m_ref.first->DeleteGlobalRef(m_ref.second); } }; struct local_ref_holder : ref_holder> { local_ref_holder(std::pair ref) : ref_holder>(ref) { } ~local_ref_holder() { m_ref.first->DeleteLocalRef(m_ref.second); } }; class jstring_deleter : ref_holder { jstring m_s; public: jstring_deleter(JNIEnv * env, jstring s) : ref_holder(env), m_s(s) { } void operator()(const char* s) const { m_ref->ReleaseStringUTFChars(m_s, s); } }; struct jstring_t : std::shared_ptr { public: jstring_t(JNIEnv* env, jstring s) : std::shared_ptr(env->GetStringUTFChars(s, NULL), jstring_deleter(env, s)) { } operator string_t() const { return get(); } operator xbox::services::uri() const { return get(); } operator const char*() const { return get(); } }; #define JNI_ATTACH_THREAD(jvm, env) \ jvm->GetEnv((void **)&env, JNI_VERSION_1_6); \ thread_holder th; \ if (env == nullptr) \ { \ jvm->AttachCurrentThread(&env, nullptr); \ th.set_ref(jvm); \ } #define JNI_ERROR_CHECK(env) \ if (env->ExceptionCheck()) \ { \ env->ExceptionDescribe(); \ env->ExceptionClear(); \ } #define JVM_CHECK_RETURN_TASK_RESULT_VOID(jvm, msg) \ if (jvm == nullptr) \ { \ LOG_ERROR(msg); \ return pplx::task_from_result>(xbox_live_result(xbox_live_error_code::runtime_error, msg)); \ } #define JVM_CHECK_RETURN_RESULT_VOID(jvm, msg) \ if (jvm == nullptr) \ { \ LOG_ERROR(msg); \ return xbox_live_result(xbox_live_error_code::runtime_error, msg); \ } ================================================ FILE: Source/Shared/a/rwlock_guard.cpp ================================================ #include "a/rwlock_guard.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN rwlock_guard::rwlock_guard(pthread_rwlock_t& lock, bool exclusive) : rwlock(&lock) { if (exclusive) { pthread_rwlock_wrlock(rwlock); } else { pthread_rwlock_rdlock(rwlock); } } rwlock_guard::~rwlock_guard() { pthread_rwlock_unlock(rwlock); } NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END ================================================ FILE: Source/Shared/a/rwlock_guard.h ================================================ #pragma once #include #if XSAPI_ANDROID_STUDIO #include "types.h" #endif NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN /// /// Wrapper class to use pthread_rwlock_t in an RAII style /// We cannot use std::shared_mutex as it was not introduced until c++17 /// class rwlock_guard { public: /// /// Constructs a rwlock_guard and locks the given pthread_rwlock /// rwlock_guard(pthread_rwlock_t& lock, bool exclusive); /// /// Destructs the rwlock_guard and unlocks the pthread_rwlock /// ~rwlock_guard(); rwlock_guard(const rwlock_guard&) = delete; rwlock_guard& operator=(const rwlock_guard&) = delete; private: pthread_rwlock_t* rwlock; }; NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END ================================================ FILE: Source/Shared/a/utils_a.cpp ================================================ //********************************************************* // // Copyright (c) Microsoft. All rights reserved. // THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF // ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY // IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR // PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. // //********************************************************* #include "pch.h" #include "utils_a.h" #include NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN #define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "XSAPI.Android", __VA_ARGS__)) #define LOGCAT_BUFFER_SIZE 746 void utils_a::log_output( _In_ const string_t& logMessage ) { std::vector logMessageList; auto logMessageSize = logMessage.size(); if (logMessageSize > LOGCAT_BUFFER_SIZE) { uint32_t arraySize = logMessageSize / LOGCAT_BUFFER_SIZE; for (uint32_t i = 0; i <= arraySize; ++i) { string_t logSplitMessage; logSplitMessage = logMessage.substr(i * LOGCAT_BUFFER_SIZE, LOGCAT_BUFFER_SIZE); logMessageList.push_back(logSplitMessage); } } else { logMessageList.push_back(logMessage); } for (const auto& entry : logMessageList) { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wformat-security" LOGI(entry.c_str()); #pragma clang diagnostic pop } } NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END ================================================ FILE: Source/Shared/a/utils_a.h ================================================ //********************************************************* // // Copyright (c) Microsoft. All rights reserved. // THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF // ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY // IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR // PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. // //********************************************************* #pragma once NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN class utils_a { public: static void log_output(_In_ const string_t& logMessage); }; NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END ================================================ FILE: Source/Shared/a/xbox_live_app_config_jni.cpp ================================================ #include "pch.h" #include "jni_utils.h" #include "xbox_live_app_config_static_glue.h" #include "xbox_live_app_config_internal.h" #include #include #define LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, "XboxLiveAppConfig", __VA_ARGS__)) #define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "XboxLiveAppConfig", __VA_ARGS__)) using namespace xbox::services; using namespace xbox::services::system; #ifdef __cplusplus extern "C" { #endif /* * Class: com_microsoft_xbox_idp_interop_XboxLiveAppConfig * Method: create * Signature: ()J */ JNIEXPORT jlong JNICALL Java_com_microsoft_xbox_idp_interop_XboxLiveAppConfig_create (JNIEnv * env, jclass cls) { return reinterpret_cast(new std::shared_ptr(xbox::services::AppConfig::Instance())); } /* * Class: com_microsoft_xbox_idp_interop_XboxLiveAppConfig * Method: delete * Signature: (J)V */ JNIEXPORT void JNICALL Java_com_microsoft_xbox_idp_interop_XboxLiveAppConfig_delete (JNIEnv * env, jclass cls, jlong id) { delete reinterpret_cast*>(id); } /* * Class: com_microsoft_xbox_idp_interop_XboxLiveAppConfig * Method: getTitleId * Signature: (J)I */ JNIEXPORT jint JNICALL Java_com_microsoft_xbox_idp_interop_XboxLiveAppConfig_getTitleId (JNIEnv * env, jclass cls, jlong id) { std::shared_ptr* cfg = reinterpret_cast*>(id); return static_cast((*cfg)->TitleId()); } /* * Class: com_microsoft_xbox_idp_interop_XboxLiveAppConfig * Method: getOverrideTitleId * Signature: (J)I */ JNIEXPORT jint JNICALL Java_com_microsoft_xbox_idp_interop_XboxLiveAppConfig_getOverrideTitleId (JNIEnv * env, jclass cls, jlong id) { std::shared_ptr* cfg = reinterpret_cast*>(id); return static_cast(std::stoi((*cfg)->OverrideScid().data())); } /* * Class: com_microsoft_xbox_idp_interop_XboxLiveAppConfig * Method: getScid * Signature: (J)Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_com_microsoft_xbox_idp_interop_XboxLiveAppConfig_getScid (JNIEnv * env, jclass cls, jlong id) { std::shared_ptr* cfg = reinterpret_cast*>(id); string_t scid = (*cfg)->Scid().c_str(); return scid.empty() ? nullptr : env->NewStringUTF(scid.c_str()); } /* * Class: com_microsoft_xbox_idp_interop_XboxLiveAppConfig * Method: getEnvironment * Signature: (J)Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_com_microsoft_xbox_idp_interop_XboxLiveAppConfig_getEnvironment (JNIEnv * env, jclass cls, jlong id) { return nullptr; } /* * Class: com_microsoft_xbox_idp_interop_XboxLiveAppConfig * Method: getSandbox * Signature: (J)Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_com_microsoft_xbox_idp_interop_XboxLiveAppConfig_getSandbox (JNIEnv * env, jclass cls, jlong id) { std::shared_ptr* cfg = reinterpret_cast*>(id); auto& sandbox = (*cfg)->Sandbox(); return sandbox.empty() ? nullptr : env->NewStringUTF(sandbox.c_str()); } #ifdef __cplusplus } #endif static JNINativeMethod methods[] = { { "create", "()J", (void*)Java_com_microsoft_xbox_idp_interop_XboxLiveAppConfig_create }, { "delete", "(J)V", (void*)Java_com_microsoft_xbox_idp_interop_XboxLiveAppConfig_delete }, { "getTitleId", "(J)I", (void*)Java_com_microsoft_xbox_idp_interop_XboxLiveAppConfig_getTitleId }, { "getOverrideTitleId", "(J)I", (void*)Java_com_microsoft_xbox_idp_interop_XboxLiveAppConfig_getOverrideTitleId }, { "getScid", "(J)Ljava/lang/String;", (void*)Java_com_microsoft_xbox_idp_interop_XboxLiveAppConfig_getScid }, { "getEnvironment", "(J)Ljava/lang/String;", (void*)Java_com_microsoft_xbox_idp_interop_XboxLiveAppConfig_getEnvironment }, { "getSandbox", "(J)Ljava/lang/String;", (void*)Java_com_microsoft_xbox_idp_interop_XboxLiveAppConfig_getSandbox } }; bool xbox_live_app_config_register_natives(JNIEnv *env, jobject clsLoader, jmethodID loadClass) { jstring clsName = env->NewStringUTF("com/microsoft/xbox/idp/interop/XboxLiveAppConfig"); jclass cls = (jclass)env->CallObjectMethod(clsLoader, loadClass, clsName); env->DeleteLocalRef(clsName); if (cls == NULL) { LOGE("Failed to load class com/microsoft/xbox/idp/interop/XboxLiveAppConfig"); return false; } if (env->RegisterNatives(cls, methods, sizeof methods / sizeof *methods) != 0) { LOGE("Failed to register native methods"); env->DeleteLocalRef(cls); return false; } env->DeleteLocalRef(cls); LOGD("Successfully registerered XboxLiveAppConfig methods"); return true; } ================================================ FILE: Source/Shared/a/xbox_live_app_config_static_glue.h ================================================ #pragma once extern bool xbox_live_app_config_register_natives(JNIEnv *env, jobject clsLoader, jmethodID loadClass); ================================================ FILE: Source/Shared/async_helpers.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN TaskQueue::TaskQueue() noexcept { auto state{ GlobalState::Get() }; if (state) { *this = state->Queue(); } } TaskQueue::TaskQueue(XTaskQueueHandle handle) noexcept { if (handle) { auto hr = XTaskQueueDuplicateHandle(handle, &m_handle); assert(SUCCEEDED(hr)); UNREFERENCED_PARAMETER(hr); } } TaskQueue::TaskQueue(const TaskQueue& other) noexcept { if (other.m_handle) { auto hr = XTaskQueueDuplicateHandle(other.m_handle, &m_handle); assert(SUCCEEDED(hr)); UNREFERENCED_PARAMETER(hr); } } TaskQueue::TaskQueue(TaskQueue&& other) noexcept { m_handle = other.m_handle; other.m_handle = nullptr; } TaskQueue& TaskQueue::operator=(TaskQueue other) noexcept { std::swap(other.m_handle, m_handle); return *this; } TaskQueue::~TaskQueue() noexcept { if (m_handle) { XTaskQueueCloseHandle(m_handle); } } TaskQueue TaskQueue::DeriveWorkerQueue() const noexcept { return DeriveWorkerQueue(m_handle); } XTaskQueueHandle TaskQueue::GetHandle() const noexcept { return m_handle; } HRESULT TaskQueue::Terminate( _In_ bool wait, _In_opt_ Callback<> queueTerminatedCallback ) const noexcept { auto context{ MakeUnique>(std::move(queueTerminatedCallback)) }; HRESULT hr = XTaskQueueTerminate(m_handle, wait, context.get(), [](void* context) { // Be sure to retake ownership of the callback here UniquePtr> callback{ reinterpret_cast*>(context) }; (*callback)(); }); RETURN_HR_IF_FAILED(hr); context.release(); return S_OK; } HRESULT TaskQueue::RunWork( _In_ AsyncWork&& work, _In_ uint64_t delayInMs ) const noexcept { return RunOnPort(XTaskQueuePort::Work, std::move(work), delayInMs); } HRESULT TaskQueue::RunCompletion( _In_ AsyncWork&& work, _In_ uint64_t delayInMs ) const noexcept { return RunOnPort(XTaskQueuePort::Completion, std::move(work), delayInMs); } HRESULT TaskQueue::RunOnPort( _In_ XTaskQueuePort port, _In_ AsyncWork&& work, _In_ uint64_t delayInMs ) const noexcept { auto context{ MakeUnique(std::move(work)) }; HRESULT hr = XTaskQueueSubmitDelayedCallback(m_handle, port, static_cast(delayInMs), context.get(), [](void* context, bool canceled) { UniquePtr work{ static_cast(context) }; if (!canceled) { (*work)(); } }); RETURN_HR_IF_FAILED(hr); context.release(); return S_OK; } TaskQueue TaskQueue::DeriveWorkerQueue(XTaskQueueHandle handle) noexcept { TaskQueue derivedQueue{ nullptr }; TaskQueue queue{ handle }; // If handle is null, derive a queue from XSAPI global queue if (!queue.m_handle) { queue = TaskQueue{}; } // If queue is still null, try to derive from the process default queue if (!queue.m_handle) { TaskQueue processQueue{ nullptr }; bool haveProcessQueue = XTaskQueueGetCurrentProcessTaskQueue(&processQueue.m_handle); if (haveProcessQueue) { queue = processQueue; } } assert(queue.m_handle); XTaskQueuePortHandle worker{ nullptr }; auto hr = XTaskQueueGetPort(queue.m_handle, XTaskQueuePort::Work, &worker); assert(SUCCEEDED(hr)); hr = XTaskQueueCreateComposite(worker, worker, &derivedQueue.m_handle); assert(SUCCEEDED(hr)); UNREFERENCED_PARAMETER(hr); return derivedQueue; } PeriodicTask::PeriodicTask( const TaskQueue& queue, uint32_t interval, std::function task ) noexcept : m_queue{ queue.DeriveWorkerQueue() }, m_interval{ interval }, m_task{ std::move(task) } { } std::shared_ptr PeriodicTask::MakeAndRun( const TaskQueue& queue, uint32_t interval, std::function task ) noexcept { auto periodicTask = std::shared_ptr( new (Alloc(sizeof(PeriodicTask))) PeriodicTask{ queue, interval, std::move(task) }, Deleter(), Allocator() ); periodicTask->m_queue.RunWork([weakThis = std::weak_ptr{ periodicTask }] { if (auto sharedThis{ weakThis.lock() }) { sharedThis->Run(); } }); return periodicTask; } PeriodicTask::~PeriodicTask() noexcept { m_queue.Terminate(false); } HRESULT PeriodicTask::ScheduleImmediately() noexcept { std::lock_guard lock{ mutex }; // Every time the task is scheduled manually, skip the next scheduled run m_skipCount++; return m_queue.RunWork([weakThis = std::weak_ptr{ shared_from_this() }] { if (auto sharedThis{ weakThis.lock() }) { sharedThis->Run(); } }); } void PeriodicTask::Run() noexcept { try { m_task(); } catch (...) { LOGS_ERROR << __FUNCTION__ << " Failed unexpectedly with exception!"; assert(false); } // Schedule the next run m_queue.RunWork([this, weakThis = std::weak_ptr{ shared_from_this() }] { if (auto sharedThis{ weakThis.lock() }) { std::unique_lock lock{ mutex }; if (m_skipCount > 0) { LOGS_DEBUG << __FUNCTION__ << ": Skipping scheduled PeriodicTask, m_skipCount=" << m_skipCount; --m_skipCount; } else { lock.unlock(); Run(); } } }, m_interval); } struct AsyncProviderContext { AsyncProviderContext( AsyncProvider&& _provider, const char* _identityName, uint64_t _delay ) noexcept : provider{ std::move(_provider) }, identityName{ _identityName }, delay{ _delay } { } AsyncProvider provider; const char* identityName; uint64_t delay; std::weak_ptr globalState; }; HRESULT RunAsync( XAsyncBlock* async, const char* identityName, AsyncProvider&& provider, uint64_t delayInMs ) noexcept { auto context = MakeUnique(std::move(provider), identityName, delayInMs); HRESULT hr = XAsyncBegin(async, context.get(), nullptr, identityName, [](_In_ XAsyncOp op, _In_ const XAsyncProviderData* data) noexcept { auto context{ static_cast(data->context) }; HC_TRACE_VERBOSE(XSAPI, "RunAsync::XAsyncOp::%s: IdentityName=%s", EnumName(op).data(), context->identityName); switch (op) { case XAsyncOp::Begin: try { // Should we invoke provider on XAsyncOp::Begin? RETURN_HR_IF_FAILED(XAsyncSchedule(data->async, static_cast(context->delay))); #if TRACK_ASYNC auto state = GlobalState::Get(); if (state) { std::lock_guard lock{ state->asyncBlocksMutex }; state->asyncBlocks[data->async] = context->identityName; context->globalState = state; } else { HC_TRACE_VERBOSE(XSAPI, "XAsync operation running after GlobalState has been destroyed"); return E_UNEXPECTED; } #endif return S_OK; } catch (...) { DISABLE_WARNING_PUSH; SUPPRESS_WARNING_UNNAMED_CUSTOM_OBJ; LOGS_ERROR << "Unexpected exception in " << __FUNCTION__ << ", completing XAsyncOperation."; DISABLE_WARNING_POP; return E_UNEXPECTED; } // For DoWork, GetResult, and Cancel, we have nothing to do. Invoke provider and handle any exceptions. case XAsyncOp::DoWork: case XAsyncOp::GetResult: case XAsyncOp::Cancel: try { return context->provider(op, data); } catch (...) { LOGS_ERROR << "Unexpected provider exception in " << __FUNCTION__ << ", completing XAsyncOperation."; return E_FAIL; } case XAsyncOp::Cleanup: { #if TRACK_ASYNC if (auto state{ context->globalState.lock() }) { std::lock_guard lock{ state->asyncBlocksMutex }; state->asyncBlocks.erase(data->async); } #endif // Cleanup should only fail in catostrophic cases. Can't pass result to client // at this point so die with exception. HRESULT hr = context->provider(op, data); Delete(context); return hr; } default: { assert(false); return S_OK; } } }); RETURN_HR_IF_FAILED(hr); context.release(); return S_OK; } NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END ================================================ FILE: Source/Shared/async_helpers.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "internal_errors.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN // Memhook aware function class with type erasure template class Function; template class Function { public: Function() noexcept = default; ~Function() = default; Function(std::nullptr_t) noexcept : m_callable{ nullptr } { } template Function(Functor functor) noexcept { m_callable = UniquePtr{ MakeUnique>(std::move(functor)).release() }; } Function(const Function& rhs) noexcept { *this = rhs; } Function(Function&& rhs) noexcept { *this = std::move(rhs); } template Function& operator=(Functor f) noexcept { m_callable = UniquePtr{ MakeUnique>(std::move(f)).release() }; return *this; } Function& operator=(const Function& rhs) noexcept { if (rhs.m_callable != nullptr) { m_callable = rhs.m_callable->Copy(); } else { m_callable.reset(); } return *this; } Function& operator=(Function&& rhs) noexcept { m_callable = std::move(rhs.m_callable); return *this; } Function& operator=(std::nullptr_t) noexcept { m_callable.reset(); return *this; } Ret operator()(Args... args) const { if (m_callable != nullptr) { return (*m_callable)(args...); } else { return Ret(); } } bool operator==(std::nullptr_t) const noexcept { return m_callable == nullptr; } bool operator!=(std::nullptr_t) const noexcept { return m_callable != nullptr; } private: struct ICallable { virtual ~ICallable() = default; virtual Ret operator()(Args...) = 0; virtual UniquePtr Copy() = 0; }; template struct Callable : public ICallable { Callable(Functor functor) : m_functor{ std::move(functor) } { } ~Callable() override = default; Ret operator()(Args... args) override { return m_functor(args...); } UniquePtr Copy() override { return UniquePtr{ MakeUnique>(m_functor).release() }; } Functor m_functor; }; UniquePtr m_callable{ nullptr }; }; template using xbox_live_callback = Function; template using Callback = Function; using AsyncWork = Function; // RAII wrapper around XTaskQueueHandle class TaskQueue { public: TaskQueue() noexcept; TaskQueue(XTaskQueueHandle handle) noexcept; TaskQueue(const TaskQueue& other) noexcept; TaskQueue(TaskQueue&& other) noexcept; TaskQueue& operator=(TaskQueue other) noexcept; ~TaskQueue() noexcept; TaskQueue DeriveWorkerQueue() const noexcept; static TaskQueue DeriveWorkerQueue(XTaskQueueHandle handle) noexcept; XTaskQueueHandle GetHandle() const noexcept; HRESULT Terminate( _In_ bool wait, _In_opt_ Callback<> queueTerminatedCallback = nullptr ) const noexcept; HRESULT RunWork( _In_ AsyncWork&& work, _In_ uint64_t delayInMs = 0 ) const noexcept; HRESULT RunCompletion( _In_ AsyncWork&& work, _In_ uint64_t delayInMs = 0 ) const noexcept; private: HRESULT RunOnPort( _In_ XTaskQueuePort port, _In_ AsyncWork&& work, _In_ uint64_t delayInMs = 0 ) const noexcept; XTaskQueueHandle m_handle{ nullptr }; }; // PeriodicTask class. Periodically runs synchronous work at fixed intervals or when explicitly requested. // Each time the task is run (manually or otherwise), it will be reschduled. Once started, a periodic // task will continue to repeat for its lifetime. class PeriodicTask : public std::enable_shared_from_this { public: // Create a PeriodTask. The task will be immediately scheduled to the provided queue. static std::shared_ptr MakeAndRun( const TaskQueue& queue, uint32_t interval, std::function task ) noexcept; PeriodicTask(const PeriodicTask&) = delete; PeriodicTask& operator=(PeriodicTask) = delete; ~PeriodicTask() noexcept; // Schedules task to the queue immediately. HRESULT ScheduleImmediately() noexcept; private: PeriodicTask( const TaskQueue& queue, uint32_t interval, std::function work ) noexcept; void Run() noexcept; TaskQueue m_queue; uint32_t const m_interval; std::function const m_task; int32_t m_skipCount{ 0 }; std::mutex mutex; }; template class AsyncContext { public: AsyncContext() noexcept = default; AsyncContext(Function&& callback) noexcept : m_callback{ callback } { } AsyncContext(TaskQueue queue, Function&& callback) noexcept : m_queue{ std::move(queue) }, m_callback{ callback } { } AsyncContext(XTaskQueueHandle queueHandle, Function&& callback) noexcept : m_queue{ queueHandle }, m_callback{ callback } { } AsyncContext(TaskQueue queue) noexcept : m_queue{ std::move(queue) } { } AsyncContext(XAsyncBlock* asyncBlock) noexcept { #if HC_PLATFORM_IS_MICROSOFT // Clang seems to compile this and assert even with no invalid uses of this template. // Keep this assert here to catch invalid uses on Microsoft platforms. static_assert(false, "Constructor only valid for Args... = "); #endif } AsyncContext(const AsyncContext& other) = default; AsyncContext(AsyncContext&& other) = default; AsyncContext& operator=(const AsyncContext& other) = default; ~AsyncContext() = default; void Complete(Args... args) const noexcept { m_callback(args...); } const TaskQueue& Queue() const noexcept { return m_queue; } static AsyncContext Collapse( Vector> _contexts ) noexcept { return AsyncContext( [ contexts{ std::move(_contexts) } ] (Args... args) { for (auto& context : contexts) { context.Complete(args...); } }); } private: TaskQueue m_queue{ nullptr }; Function m_callback{ nullptr }; }; template<> inline AsyncContext::AsyncContext(XAsyncBlock* asyncBlock) noexcept : m_queue{ asyncBlock->queue } { m_callback = [asyncBlock](HRESULT hr) { XAsyncComplete(asyncBlock, hr, 0); }; } template<> inline AsyncContext>::AsyncContext(XAsyncBlock* asyncBlock) noexcept : m_queue{ asyncBlock->queue } { m_callback = [asyncBlock](Result result) { XAsyncComplete(asyncBlock, result.Hresult(), 0); }; } typedef Function AsyncProvider; // Helper method for writing XAsync Providers. // AsyncProvider type allows for capture enabled lambdas. XAsyncProvider context lifetime is managed automatically. HRESULT RunAsync( XAsyncBlock* async, const char* identityName, AsyncProvider&& provider, uint64_t delayInMs = 0 ) noexcept; NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END ================================================ FILE: Source/Shared/build_version.h ================================================ //********************************************************* // // Copyright (c) Microsoft. All rights reserved. // THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF // ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY // IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR // PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. // //********************************************************* #pragma once #define XBOX_SERVICES_API_VERSION_STRING "2025.10.20251000.0" ================================================ FILE: Source/Shared/enum_traits.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #define DEFAULT_ENUM_MIN 0u #define DEFAULT_ENUM_MAX 30u #define ENUM_RANGE_MAX 1000u NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN namespace detail { template class StaticString { public: DISABLE_WARNING_PUSH; SUPPRESS_WARNING_UNINITIALIZED_MEMBER; constexpr StaticString(const char* name) noexcept : StaticString{ name, std::make_index_sequence{} } { } DISABLE_WARNING_POP; constexpr operator const char*() const noexcept { return chars; } private: template constexpr StaticString(const char* name, std::index_sequence) noexcept : chars{ name[I]..., 0 } { } const char chars[n + 1]{}; }; template<> class StaticString<0> { public: constexpr StaticString(const char*) noexcept {}; constexpr operator const char*() const noexcept { return nullptr; } }; using StringView = std::pair; constexpr StringView ParseName(const char* funcName) noexcept { #if defined(_MSC_VER) constexpr auto delim{ '>' }; #elif defined(__clang__) constexpr auto delim{ ']' }; #endif size_t end{ 0 }; for (; funcName[end] && funcName[end] != delim; ++end) {} size_t begin{ end }; for (; begin > 0; --begin) { if (!((funcName[begin - 1] >= '0' && funcName[begin - 1] <= '9') || (funcName[begin - 1] >= 'a' && funcName[begin - 1] <= 'z') || (funcName[begin - 1] >= 'A' && funcName[begin - 1] <= 'Z') || (funcName[begin - 1] == '_'))) { break; } } // Symbol names cannot begin with a number if (funcName[begin] >= '0' && funcName[begin] <= '9') { // Invalid enum value return StringView{ nullptr, 0 }; } return StringView{ funcName + begin, end - begin }; } template static constexpr auto N() noexcept { static_assert(std::is_enum::value, "E must be an enum type."); #if defined(_MSC_VER) constexpr auto name = ParseName(__FUNCSIG__); #elif defined(__clang__) constexpr auto name = ParseName(__PRETTY_FUNCTION__); #else static_assert(false, "Unrecognized platform"); #endif return StaticString{ name.first }; } template constexpr auto EnumValueName = N(); template class EnumTraits { private: static_assert(std::is_enum::value, "EnumTraits can only be instantiated for enum types."); static_assert(std::is_same, uint32_t>::value, "EnumTraits can only be instantiated for enums with uint32_t as their underlying type"); static constexpr uint32_t range = MAX_E - MIN_E + 1; static_assert(range < ENUM_RANGE_MAX, "EnumTraits only supports a maximum value range of ENUM_RANGE_MAX"); template static constexpr auto Names(std::index_sequence) noexcept { return std::array{ { EnumValueName(I + MIN_E)>... } }; } static constexpr std::array names = Names(std::make_index_sequence()); public: static String Name(E v) noexcept { #if HC_PLATFORM == HC_PLATFORM_ANDROID // This gets around the ODR-use rule on clang when compiling without C++17 support constexpr auto names_{ names }; #else auto& names_{ names }; #endif auto i{ static_cast(v) }; if (i < MIN_E || i > MAX_E) { // Value outside the specified range. Return empty string. return String{}; } return String{ names_[i - MIN_E] }; } static E Value(const char* n) noexcept { #if HC_PLATFORM == HC_PLATFORM_ANDROID constexpr auto names_{ names }; #else auto& names_{ names }; #endif for (size_t i = 0; i < names_.size(); ++i) { if (names_[i] && xbox::services::legacy::Stricmp(n, names_[i]) == 0) { return static_cast(i + MIN_E); } } // Provided string didn't map to an enum value. Return default value. return E{}; } }; } template constexpr auto EnumName(E value) noexcept -> std::enable_if_t::value, String> { return detail::EnumTraits::Name(value); } template constexpr auto EnumValue(const char* name) noexcept -> std::enable_if_t::value, E> { return detail::EnumTraits::Value(name); } NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END ================================================ FILE: Source/Shared/errors.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN namespace legacy { static xbox_services_error_code_category_impl s_error_code_category_instance; const xbox_services_error_code_category_impl& xbox_services_error_code_category() { return s_error_code_category_instance; } static xbox_services_error_condition_category_impl s_error_condition_category_instance; const xbox_services_error_condition_category_impl& xbox_services_error_condition_category() { return s_error_condition_category_instance; } std::string xbox_services_error_code_category_impl::message(_In_ int errorCode) const _NOEXCEPT { #if 0 // this returns a non-mem hooked string which can't be changed so commenting it out since its not really needed xbl_error_code code = static_cast(errorCode); switch (code) { case xbl_error_code::no_error: return "No error"; case xbl_error_code::bad_alloc: return "bad_alloc"; case xbl_error_code::bad_cast: return "bad_cast"; case xbl_error_code::invalid_argument: return "invalid_argument"; case xbl_error_code::out_of_range: return "out_of_range"; case xbl_error_code::length_error: return "length_error"; case xbl_error_code::range_error: return "range_error"; case xbl_error_code::logic_error: return "logic_error"; case xbl_error_code::runtime_error: return "runtime_error"; case xbl_error_code::json_error: return "json_error"; case xbl_error_code::websocket_error: return "websocket_error"; case xbl_error_code::uri_error: return "uri_error"; case xbl_error_code::generic_error: return "generic_error"; case xbl_error_code::rta_generic_error: return "rta_generic_error"; case xbl_error_code::rta_access_denied: return "rta_access_denied"; case xbl_error_code::rta_subscription_limit_reached: return "rta_subscription_limit_reached"; case xbl_error_code::auth_user_interaction_required: return "auth_user_interaction_required"; case xbl_error_code::auth_user_switched: return "auth_user_switched"; case xbl_error_code::auth_user_cancel: return "auth_user_cancel"; case xbl_error_code::auth_unknown_error: return "auth_unknown_error"; case xbl_error_code::auth_user_not_signed_in: return "auth_user_not_signed_in"; case xbl_error_code::auth_runtime_error: return "auth_runtime_error"; case xbl_error_code::auth_no_token_error: return "auth_no_token_error"; case xbl_error_code::invalid_config: return "invalid_config"; case xbl_error_code::unsupported: return "unsupported"; case xbl_error_code::HR_ERROR_INTERNET_TIMEOUT: return "ERROR_INTERNET_TIMEOUT"; case xbl_error_code::AM_E_XASD_UNEXPECTED: return "AM_E_XASD_UNEXPECTED"; case xbl_error_code::AM_E_XASU_UNEXPECTED: return "AM_E_XASU_UNEXPECTED"; case xbl_error_code::AM_E_XAST_UNEXPECTED: return "AM_E_XAST_UNEXPECTED"; case xbl_error_code::AM_E_XSTS_UNEXPECTED: return "AM_E_XSTS_UNEXPECTED"; case xbl_error_code::AM_E_XDEVICE_UNEXPECTED: return "AM_E_XDEVICE_UNEXPECTED"; case xbl_error_code::AM_E_DEVMODE_NOT_AUTHORIZED: return "AM_E_DEVMODE_NOT_AUTHORIZED"; case xbl_error_code::AM_E_NOT_AUTHORIZED: return "AM_E_NOT_AUTHORIZED"; case xbl_error_code::AM_E_FORBIDDEN: return "AM_E_FORBIDDEN"; case xbl_error_code::AM_E_UNKNOWN_TARGET: return "AM_E_UNKNOWN_TARGET"; case xbl_error_code::AM_E_INVALID_NSAL_DATA: return "AM_E_INVALID_NSAL_DATA"; case xbl_error_code::AM_E_TITLE_NOT_AUTHENTICATED: return "AM_E_TITLE_NOT_AUTHENTICATED"; case xbl_error_code::AM_E_TITLE_NOT_AUTHORIZED: return "AM_E_TITLE_NOT_AUTHORIZED"; case xbl_error_code::AM_E_USER_HASH_MISSING: return "AM_E_USER_HASH_MISSING"; case xbl_error_code::AM_E_USER_NOT_FOUND: return "AM_E_USER_NOT_FOUND"; case xbl_error_code::AM_E_INVALID_ENVIRONMENT: return "AM_E_INVALID_ENVIRONMENT"; case xbl_error_code::AM_E_XASD_TIMEOUT: return "AM_E_XASD_TIMEOUT"; case xbl_error_code::AM_E_XASU_TIMEOUT: return "AM_E_XASU_TIMEOUT"; case xbl_error_code::AM_E_XAST_TIMEOUT: return "AM_E_XAST_TIMEOUT"; case xbl_error_code::AM_E_XSTS_TIMEOUT: return "AM_E_XSTS_TIMEOUT"; case xbl_error_code::AM_E_LIVE_CONNECTION_REQUIRED: return "AM_E_LIVE_CONNECTION_REQUIRED"; case xbl_error_code::AM_E_NO_NETWORK: return "AM_E_NO_NETWORK"; case xbl_error_code::AM_E_XTITLE_UNEXPECTED: return "AM_E_XTITLE_UNEXPECTED"; case xbl_error_code::AM_E_NO_TOKEN_REQUIRED: return "AM_E_NO_TOKEN_REQUIRED"; case xbl_error_code::AM_E_XTITLE_TIMEOUT: return "AM_E_XTITLE_TIMEOUT"; case xbl_error_code::XO_E_DEVMODE_NOT_AUTHORIZED: return "XO_E_DEVMODE_NOT_AUTHORIZED"; case xbl_error_code::XO_E_SYSTEM_UPDATE_REQUIRED: return "XO_E_SYSTEM_UPDATE_REQUIRED"; case xbl_error_code::XO_E_CONTENT_UPDATE_REQUIRED: return "XO_E_CONTENT_UPDATE_REQUIRED"; case xbl_error_code::XO_E_ENFORCEMENT_BAN: return "XO_E_ENFORCEMENT_BAN"; case xbl_error_code::XO_E_THIRD_PARTY_BAN: return "XO_E_THIRD_PARTY_BAN"; case xbl_error_code::XO_E_ACCOUNT_PARENTALLY_RESTRICTED: return "XO_E_ACCOUNT_PARENTALLY_RESTRICTED"; case xbl_error_code::XO_E_ACCOUNT_BILLING_MAINTENANCE_REQUIRED: return "XO_E_ACCOUNT_BILLING_MAINTENANCE_REQUIRED"; case xbl_error_code::XO_E_ACCOUNT_TERMS_OF_USE_NOT_ACCEPTED: return "XO_E_ACCOUNT_TERMS_OF_USE_NOT_ACCEPTED"; case xbl_error_code::XO_E_ACCOUNT_COUNTRY_NOT_AUTHORIZED: return "XO_E_ACCOUNT_COUNTRY_NOT_AUTHORIZED"; case xbl_error_code::XO_E_ACCOUNT_AGE_VERIFICATION_REQUIRED: return "XO_E_ACCOUNT_AGE_VERIFICATION_REQUIRED"; case xbl_error_code::XO_E_ACCOUNT_CURFEW: return "XO_E_ACCOUNT_CURFEW"; case xbl_error_code::XO_E_ACCOUNT_CHILD_NOT_IN_FAMILY: return "XO_E_ACCOUNT_CHILD_NOT_IN_FAMILY"; case xbl_error_code::XO_E_ACCOUNT_CSV_TRANSITION_REQUIRED: return "XO_E_ACCOUNT_CSV_TRANSITION_REQUIRED"; case xbl_error_code::XO_E_ACCOUNT_MAINTENANCE_REQUIRED: return "XO_E_ACCOUNT_MAINTENANCE_REQUIRED"; case xbl_error_code::XO_E_ACCOUNT_TYPE_NOT_ALLOWED: return "XO_E_ACCOUNT_TYPE_NOT_ALLOWED"; case xbl_error_code::XO_E_CONTENT_ISOLATION: return "XO_E_CONTENT_ISOLATION"; case xbl_error_code::XO_E_ACCOUNT_NAME_CHANGE_REQUIRED: return "XO_E_ACCOUNT_NAME_CHANGE_REQUIRED"; case xbl_error_code::XO_E_DEVICE_CHALLENGE_REQUIRED: return "XO_E_DEVICE_CHALLENGE_REQUIRED"; case xbl_error_code::XO_E_SIGNIN_COUNT_BY_DEVICE_TYPE_EXCEEDED: return "XO_E_SIGNIN_COUNT_BY_DEVICE_TYPE_EXCEEDED"; case xbl_error_code::XO_E_PIN_CHALLENGE_REQUIRED: return "XO_E_PIN_CHALLENGE_REQUIRED"; case xbl_error_code::XO_E_RETAIL_ACCOUNT_NOT_ALLOWED: return "XO_E_RETAIL_ACCOUNT_NOT_ALLOWED"; case xbl_error_code::XO_E_SANDBOX_NOT_ALLOWED: return "XO_E_SANDBOX_NOT_ALLOWED"; case xbl_error_code::XO_E_ACCOUNT_SERVICE_UNAVAILABLE_UNKNOWN_USER: return "XO_E_ACCOUNT_SERVICE_UNAVAILABLE_UNKNOWN_USER"; case xbl_error_code::XO_E_GREEN_SIGNED_CONTENT_NOT_AUTHORIZED: return "XO_E_GREEN_SIGNED_CONTENT_NOT_AUTHORIZED"; case xbl_error_code::XO_E_CONTENT_NOT_AUTHORIZED: return "XO_E_CONTENT_NOT_AUTHORIZED"; case xbl_error_code::http_status_204_resource_data_not_found: return "204 - ResourceDataNotFound"; case xbl_error_code::http_status_400_bad_request: return "400 - BadRequest"; case xbl_error_code::http_status_401_unauthorized: return "401 - Unauthorized"; case xbl_error_code::http_status_403_forbidden: return "403 - Forbidden"; case xbl_error_code::http_status_404_not_found: return "404 - NotFound"; case xbl_error_code::http_status_405_method_not_allowed: return "405 - MethodNotAllowed"; case xbl_error_code::http_status_406_not_acceptable: return "406 - NotAcceptable"; case xbl_error_code::http_status_407_proxy_authentication_required: return "407 - ProxyAuthenticationRequired"; case xbl_error_code::http_status_408_request_timeout: return "408 - RequestTimeout"; case xbl_error_code::http_status_409_conflict: return "409 - Conflict"; case xbl_error_code::http_status_410_gone: return "410 - Gone"; case xbl_error_code::http_status_411_length_required: return "411 - LengthRequired"; case xbl_error_code::http_status_412_precondition_failed: return "412 - PreconditionFailed"; case xbl_error_code::http_status_413_request_entity_too_large: return "413 - RequestEntityTooLarge"; case xbl_error_code::http_status_414_request_uri_too_long: return "414 - RequestUriTooLong"; case xbl_error_code::http_status_415_unsupported_media_type: return "415 - UnsupportedMediaType"; case xbl_error_code::http_status_416_requested_range_not_satisfiable: return "416 - RequestedRangeNotSatisfiable"; case xbl_error_code::http_status_417_expectation_failed: return "417 - Expectation Failed"; case xbl_error_code::http_status_421_misdirected_request: return "421 - Misdirected Request"; case xbl_error_code::http_status_422_unprocessable_entity: return "422 - Unprocessable Entity"; case xbl_error_code::http_status_423_locked: return "423 - Locked"; case xbl_error_code::http_status_424_failed_dependency: return "424 - Failed Dependency"; case xbl_error_code::http_status_426_upgrade_required: return "426 - Upgrade Required"; case xbl_error_code::http_status_428_precondition_required: return "428 - Precondition Required"; case xbl_error_code::http_status_429_too_many_requests: return "429 - TooManyRequests"; case xbl_error_code::http_status_431_request_header_fields_too_large: return "431 - Request Header Fields Too Large"; case xbl_error_code::http_status_451_unavailable_for_legal_reasons: return "451 - Unavailable For Legal Reasons"; case xbl_error_code::http_status_500_internal_server_error: return "500 - InternalServerError"; case xbl_error_code::http_status_501_not_implemented: return "501 - Not Implemented"; case xbl_error_code::http_status_502_bad_gateway: return "502 - Bad Gateway"; case xbl_error_code::http_status_503_service_unavailable: return "503 - ServiceUnavailable"; case xbl_error_code::http_status_504_gateway_timeout: return "504 - GatewayTimeout"; case xbl_error_code::http_status_505_http_version_not_supported: return "505 - HttpVersionNotSupported"; case xbl_error_code::http_status_506_variant_also_negotiates: return "506 - Variant Also Negotiates"; case xbl_error_code::http_status_507_insufficient_storage: return "507 - Insufficient Storage"; case xbl_error_code::http_status_508_loop_detected: return "508 - Loop Detected"; case xbl_error_code::http_status_510_not_extended: return "510 - Not Extended"; case xbl_error_code::http_status_511_network_authentication_required: return "511 - Network Authentication Required"; case xbl_error_code::HR_INET_E_INVALID_URL: return "INET_E_INVALID_URL"; case xbl_error_code::HR_INET_E_NO_SESSION: return "INET_E_NO_SESSION"; case xbl_error_code::HR_INET_E_CANNOT_CONNECT: return "INET_E_CANNOT_CONNECT"; case xbl_error_code::HR_INET_E_RESOURCE_NOT_FOUND: return "INET_E_RESOURCE_NOT_FOUND"; case xbl_error_code::HR_INET_E_OBJECT_NOT_FOUND: return "INET_E_OBJECT_NOT_FOUND"; case xbl_error_code::HR_INET_E_DATA_NOT_AVAILABLE: return "INET_E_DATA_NOT_AVAILABLE"; case xbl_error_code::HR_INET_E_DOWNLOAD_FAILURE: return "INET_E_DOWNLOAD_FAILURE"; case xbl_error_code::HR_INET_E_AUTHENTICATION_REQUIRED: return "INET_E_AUTHENTICATION_REQUIRED"; case xbl_error_code::HR_INET_E_NO_VALID_MEDIA: return "INET_E_NO_VALID_MEDIA"; case xbl_error_code::HR_INET_E_CONNECTION_TIMEOUT: return "INET_E_CONNECTION_TIMEOUT"; case xbl_error_code::HR_INET_E_INVALID_REQUEST: return "INET_E_INVALID_REQUEST"; case xbl_error_code::HR_INET_E_UNKNOWN_PROTOCOL: return "INET_E_UNKNOWN_PROTOCOL"; case xbl_error_code::HR_INET_E_SECURITY_PROBLEM: return "INET_E_SECURITY_PROBLEM"; case xbl_error_code::HR_INET_E_CANNOT_LOAD_DATA: return "INET_E_CANNOT_LOAD_DATA"; case xbl_error_code::HR_INET_E_CANNOT_INSTANTIATE_OBJECT: return "INET_E_CANNOT_INSTANTIATE_OBJECT"; case xbl_error_code::HR_INET_E_INVALID_CERTIFICATE: return "INET_E_INVALID_CERTIFICATE"; case xbl_error_code::HR_INET_E_REDIRECT_FAILED: return "INET_E_REDIRECT_FAILED"; case xbl_error_code::HR_INET_E_REDIRECT_TO_DIR: return "INET_E_REDIRECT_TO_DIR"; case xbl_error_code::HR_ERROR_NETWORK_UNREACHABLE: return "ERROR_NETWORK_UNREACHABLE"; default: { std::stringstream msg; msg << "Unknown error: 0x" << std::hex << errorCode; return msg.str(); } } #else UNREFERENCED_PARAMETER(errorCode); return std::string(); #endif } std::string xbox_services_error_condition_category_impl::message(_In_ int errorCode) const _NOEXCEPT { #if 0 // this returns a non-mem hooked string which can't be changed so commenting it out since its not really needed xbl_error_condition code = static_cast(errorCode); switch (code) { case xbl_error_condition::no_error: return "No error"; case xbl_error_condition::generic_error: return "Generic Error"; case xbl_error_condition::generic_out_of_range: return "Out of Range"; case xbl_error_condition::auth: return "Authorization Error"; case xbl_error_condition::http: return "HTTP"; case xbl_error_condition::http_404_not_found: return "404 - Not Found"; case xbl_error_condition::http_412_precondition_failed: return "412 - PreconditionFailed"; case xbl_error_condition::http_429_too_many_requests: return "429- Too Many Requests"; case xbl_error_condition::http_service_timeout: return "Service Timeout"; case xbl_error_condition::network: return "Network Error"; case xbl_error_condition::rta: return "Real Time Activity"; default: { std::stringstream msg; msg << "Unknown error: 0x" << std::hex << errorCode; return msg.str(); } } #else UNREFERENCED_PARAMETER(errorCode); return std::string(); #endif } bool xbox_services_error_condition_category_impl::equivalent( _In_ const std::error_code& arbitraryErrorCode, _In_ int xboxLiveErrorCondition) const _NOEXCEPT { if (arbitraryErrorCode.category() != xbox_services_error_code_category()) { return false; } xbl_error_code code = static_cast(arbitraryErrorCode.value()); xbl_error_condition condition = static_cast(xboxLiveErrorCondition); // range 0x80860000 - 0x8086FFFF is reserved for other OnlineId error code // put is under auth_msa error condition if ((arbitraryErrorCode.value() & 0x80860000) == 0x80860000) { return condition == xbl_error_condition::auth; } switch (code) { case xbl_error_code::no_error: return (condition == xbl_error_condition::no_error); case xbl_error_code::http_status_404_not_found: return (condition == xbl_error_condition::http_404_not_found || condition == xbl_error_condition::http); case xbl_error_code::http_status_412_precondition_failed: return (condition == xbl_error_condition::http_412_precondition_failed || condition == xbl_error_condition::http); case xbl_error_code::http_status_408_request_timeout: case xbl_error_code::http_status_503_service_unavailable: case xbl_error_code::http_status_504_gateway_timeout: return (condition == xbl_error_condition::http_service_timeout || condition == xbl_error_condition::http); case xbl_error_code::http_status_429_too_many_requests: return (condition == xbl_error_condition::http_429_too_many_requests || condition == xbl_error_condition::http); case xbl_error_code::http_status_500_internal_server_error: return (condition == xbl_error_condition::http); case xbl_error_code::http_status_204_resource_data_not_found: case xbl_error_code::http_status_400_bad_request: case xbl_error_code::http_status_401_unauthorized: case xbl_error_code::http_status_402_payment_required: case xbl_error_code::http_status_403_forbidden: case xbl_error_code::http_status_405_method_not_allowed: case xbl_error_code::http_status_406_not_acceptable: case xbl_error_code::http_status_407_proxy_authentication_required: case xbl_error_code::http_status_409_conflict: case xbl_error_code::http_status_410_gone: case xbl_error_code::http_status_411_length_required: case xbl_error_code::http_status_413_request_entity_too_large: case xbl_error_code::http_status_414_request_uri_too_long: case xbl_error_code::http_status_415_unsupported_media_type: case xbl_error_code::http_status_416_requested_range_not_satisfiable: case xbl_error_code::http_status_417_expectation_failed: case xbl_error_code::http_status_421_misdirected_request: case xbl_error_code::http_status_422_unprocessable_entity: case xbl_error_code::http_status_423_locked: case xbl_error_code::http_status_424_failed_dependency: case xbl_error_code::http_status_426_upgrade_required: case xbl_error_code::http_status_428_precondition_required: case xbl_error_code::http_status_431_request_header_fields_too_large: case xbl_error_code::http_status_449_retry_with: case xbl_error_code::http_status_451_unavailable_for_legal_reasons: case xbl_error_code::http_status_501_not_implemented: case xbl_error_code::http_status_502_bad_gateway: case xbl_error_code::http_status_505_http_version_not_supported: case xbl_error_code::http_status_506_variant_also_negotiates: case xbl_error_code::http_status_507_insufficient_storage: case xbl_error_code::http_status_508_loop_detected: case xbl_error_code::http_status_510_not_extended: case xbl_error_code::http_status_511_network_authentication_required: return (condition == xbl_error_condition::http); case xbl_error_code::auth_user_interaction_required: case xbl_error_code::auth_user_switched: case xbl_error_code::auth_unknown_error: case xbl_error_code::auth_user_cancel: case xbl_error_code::auth_user_not_signed_in: case xbl_error_code::auth_runtime_error: case xbl_error_code::auth_no_token_error: return condition == xbl_error_condition::auth; case xbl_error_code::invalid_config: case xbl_error_code::unsupported: return condition == xbl_error_condition::generic_error; case xbl_error_code::AM_E_XASD_UNEXPECTED: case xbl_error_code::AM_E_XASU_UNEXPECTED: case xbl_error_code::AM_E_XAST_UNEXPECTED: case xbl_error_code::AM_E_XSTS_UNEXPECTED: case xbl_error_code::AM_E_XDEVICE_UNEXPECTED: case xbl_error_code::AM_E_DEVMODE_NOT_AUTHORIZED: case xbl_error_code::AM_E_NOT_AUTHORIZED: case xbl_error_code::AM_E_FORBIDDEN: case xbl_error_code::AM_E_UNKNOWN_TARGET: case xbl_error_code::AM_E_INVALID_NSAL_DATA: case xbl_error_code::AM_E_TITLE_NOT_AUTHENTICATED: case xbl_error_code::AM_E_TITLE_NOT_AUTHORIZED: case xbl_error_code::AM_E_USER_HASH_MISSING: case xbl_error_code::AM_E_USER_NOT_FOUND: case xbl_error_code::AM_E_INVALID_ENVIRONMENT: case xbl_error_code::AM_E_XASD_TIMEOUT: case xbl_error_code::AM_E_XASU_TIMEOUT: case xbl_error_code::AM_E_XAST_TIMEOUT: case xbl_error_code::AM_E_XSTS_TIMEOUT: case xbl_error_code::AM_E_LIVE_CONNECTION_REQUIRED: case xbl_error_code::AM_E_NO_NETWORK: case xbl_error_code::AM_E_XTITLE_UNEXPECTED: case xbl_error_code::AM_E_NO_TOKEN_REQUIRED: case xbl_error_code::AM_E_XTITLE_TIMEOUT: case xbl_error_code::XO_E_DEVMODE_NOT_AUTHORIZED: case xbl_error_code::XO_E_SYSTEM_UPDATE_REQUIRED: case xbl_error_code::XO_E_CONTENT_UPDATE_REQUIRED: case xbl_error_code::XO_E_ENFORCEMENT_BAN: case xbl_error_code::XO_E_THIRD_PARTY_BAN: case xbl_error_code::XO_E_ACCOUNT_PARENTALLY_RESTRICTED: case xbl_error_code::XO_E_ACCOUNT_BILLING_MAINTENANCE_REQUIRED: case xbl_error_code::XO_E_ACCOUNT_TERMS_OF_USE_NOT_ACCEPTED: case xbl_error_code::XO_E_ACCOUNT_COUNTRY_NOT_AUTHORIZED: case xbl_error_code::XO_E_ACCOUNT_AGE_VERIFICATION_REQUIRED: case xbl_error_code::XO_E_ACCOUNT_CURFEW: case xbl_error_code::XO_E_ACCOUNT_CHILD_NOT_IN_FAMILY: case xbl_error_code::XO_E_ACCOUNT_CSV_TRANSITION_REQUIRED: case xbl_error_code::XO_E_ACCOUNT_MAINTENANCE_REQUIRED: case xbl_error_code::XO_E_ACCOUNT_TYPE_NOT_ALLOWED: case xbl_error_code::XO_E_CONTENT_ISOLATION: case xbl_error_code::XO_E_ACCOUNT_NAME_CHANGE_REQUIRED: case xbl_error_code::XO_E_DEVICE_CHALLENGE_REQUIRED: case xbl_error_code::XO_E_SIGNIN_COUNT_BY_DEVICE_TYPE_EXCEEDED: case xbl_error_code::XO_E_PIN_CHALLENGE_REQUIRED: case xbl_error_code::XO_E_RETAIL_ACCOUNT_NOT_ALLOWED: case xbl_error_code::XO_E_SANDBOX_NOT_ALLOWED: case xbl_error_code::XO_E_ACCOUNT_SERVICE_UNAVAILABLE_UNKNOWN_USER: case xbl_error_code::XO_E_GREEN_SIGNED_CONTENT_NOT_AUTHORIZED: case xbl_error_code::XO_E_CONTENT_NOT_AUTHORIZED: case xbl_error_code::HR_ERROR_INTERNET_TIMEOUT: return (condition == xbl_error_condition::auth); case xbl_error_code::HR_INET_E_INVALID_URL: case xbl_error_code::HR_INET_E_NO_SESSION: case xbl_error_code::HR_INET_E_CANNOT_CONNECT: case xbl_error_code::HR_INET_E_RESOURCE_NOT_FOUND: case xbl_error_code::HR_INET_E_OBJECT_NOT_FOUND: case xbl_error_code::HR_INET_E_DATA_NOT_AVAILABLE: case xbl_error_code::HR_INET_E_DOWNLOAD_FAILURE: case xbl_error_code::HR_INET_E_AUTHENTICATION_REQUIRED: case xbl_error_code::HR_INET_E_NO_VALID_MEDIA: case xbl_error_code::HR_INET_E_CONNECTION_TIMEOUT: case xbl_error_code::HR_INET_E_INVALID_REQUEST: case xbl_error_code::HR_INET_E_UNKNOWN_PROTOCOL: case xbl_error_code::HR_INET_E_SECURITY_PROBLEM: case xbl_error_code::HR_INET_E_CANNOT_LOAD_DATA: case xbl_error_code::HR_INET_E_CANNOT_INSTANTIATE_OBJECT: case xbl_error_code::HR_INET_E_INVALID_CERTIFICATE: case xbl_error_code::HR_INET_E_REDIRECT_FAILED: case xbl_error_code::HR_INET_E_REDIRECT_TO_DIR: case xbl_error_code::HR_ERROR_NETWORK_UNREACHABLE: return (condition == xbl_error_condition::network); case xbl_error_code::out_of_range: return (condition == xbl_error_condition::generic_out_of_range || condition == xbl_error_condition::generic_error); case xbl_error_code::bad_alloc: case xbl_error_code::bad_cast: case xbl_error_code::invalid_argument: case xbl_error_code::json_error: case xbl_error_code::length_error: case xbl_error_code::range_error: case xbl_error_code::logic_error: case xbl_error_code::runtime_error: case xbl_error_code::uri_error: case xbl_error_code::rta_generic_error: case xbl_error_code::rta_access_denied: case xbl_error_code::rta_subscription_limit_reached: case xbl_error_code::generic_error: default: return (condition == xbl_error_condition::generic_error); } } } NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END ================================================ FILE: Source/Shared/errors_legacy.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include #ifndef _NOEXCEPT #define _NOEXCEPT noexcept #endif NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN /// /// Enumeration values that define the Xbox Live API error conditions. /// /// /// A best practice is to test the returned std::error_code against these error conditions. /// For more detail about std::error_code vs std::error_condition, see /// http://en.cppreference.com/w/cpp/error/error_condition /// /// /// For example: /// /// if( result.err() == xbox::services::legacy::xbox_live_error_condition::auth ) /// { /// // ... /// } /// /// or /// /// switch (result.err().default_error_condition().value()) /// { /// case xbox::services::legacy::xbox_live_error_condition::auth: /// // ... /// break; /// } /// /// enum class xbl_error_condition { /// /// No error. /// no_error = 0, /// /// A generic error condition. /// generic_error, /// /// An error condition related to an object being out of range. /// generic_out_of_range, /// /// An error condition related to attempting to authenticate. /// auth, /// /// An error condition related to network connectivity. /// network, /// /// An error condition related to an HTTP method call. /// http, /// /// The requested resource was not found. /// http_404_not_found, /// /// The precondition given in one or more of the request-header fields evaluated /// to false when it was tested on the server. /// http_412_precondition_failed, /// /// Client is sending too many requests /// http_429_too_many_requests, /// /// The service timed out while attempting to process the request. /// http_service_timeout, /// /// An error related to real time activity. /// rta }; /// /// These are XSAPI specific error codes. /// Only the HTTP codes and errors that are commonly seen when calling Xbox LIVE are called out with guidance about the cause. /// The best practice to test for and react to using the xbox_live_error_condition enum instead of these error codes. /// enum class xbl_error_code { /// /// 0 /// No error /// no_error = 0, ////////////////////////////////////////////////////////////////////////// // HTTP errors ////////////////////////////////////////////////////////////////////////// /// /// The 204 response indicates that the content data was not found. /// This code is returned when you are trying to access or write to a session that has been deleted. /// http_status_204_resource_data_not_found = 204, /// /// 0x8019012C /// The 300 response indicates there are multiple choices. Not returned by Xbox Live services /// http_status_300_multiple_choices = 300, /// /// 0x8019012D /// The 301 response indicates that the request should be directed at a new URI /// http_status_301_moved_permanently = 301, /// /// 0x8019012E /// The 302 response indicates found. /// http_status_302_found = 302, /// /// 0x8019012F /// The 303 response indicates see other. Not returned by Xbox Live services /// http_status_303_see_other = 303, /// /// 0x80190130 /// The 304 response indicates resource has not been modified. /// http_status_304_not_modified = 304, /// /// 0x80190131 /// The 305 response indicates you must a use proxy. Not typically returned by Xbox Live services /// http_status_305_use_proxy = 305, /// /// 0x80190133 /// The 307 response indicates a temporary redirect. /// http_status_307_temporary_redirect = 307, /// /// 0x80190190 /// The request could not be understood by the server due to malformed syntax. /// The client should not repeat this request without modification. /// /// This code is returned by the following methods: /// Social services /// GetUserProfileAsync: /// The list of XUIDs returned from a leaderboard read operation and passed to this method has failed /// due to one or more invalid XUID values. /// /// Data services /// GetSingleUserStatisticsAsync: /// Corresponds to a general HTTP error code: HTTP 400 Bad Request. /// /// Server Platform services /// AllocateClusterAsync: /// Matchmaking request failure, typically due to QoS (where the requirement is all-or-nothing). /// /// Marketplace services /// GetCatalogItemDetailsAsync: /// The Details service can only support 10 CatalogItems per request (MAX_DETAILS_ITEMS). /// BrowseCatalogAsync: Typically an error of setting maxItems too large. The catalog service only supports a max of /// 25 items per call. /// http_status_400_bad_request = 400, /// /// 0x80190191 /// The 401 response typically indicates that authorization has been refused for provided credentials. /// This code is returned when the user(s) or the device is not authorized to access the requested data /// or perform the requested action. /// http_status_401_unauthorized = 401, /// /// 0x80190192 /// 402 Payment Required /// http_status_402_payment_required = 402, /// /// 0x80190193 /// Corresponds to general HTTP error code HTTP 403 - Forbidden. /// The server understood the request but is refusing to fulfill. Authorization will not resolve the issue /// and the request should not be repeated. /// /// General /// This is typically a service configuration issue or a malformed HTTP request. To resolve, make sure that /// your service configuration is correct, that your console is in the right sandbox, and that you are using /// the correct title id and SCID. Using fiddler to examine the failing HTTP request will often help root cause the issue. /// /// Multiplayer services /// The user doesn't have multiplayer privileges, or the session is private/reserved and the user isn't a member. /// http_status_403_forbidden = 403, /// /// 0x80190194 /// This typically indicates that the server has not found anything matching the provided URI. /// The root cause of the 404 will depend on the API you are using. /// For example many of the "Get Leaderboard" /// APIs return a 404 error if you request a stat that does not exist, or if the leader board is empty, /// CreateSessionAsync returns a 404 error if you call it with an invalid Session Template name, and /// GetPartyViewAsync fails with a 404 if the multiplayer session times out. Using fiddler to examine the failing /// HTTP request will often help root cause the issue. /// http_status_404_not_found = 404, /// /// 0x80190195 /// The method specified in the Request-Line is not allowed for the resource identified by the Request-URI. /// For example, the request is a POST where a PUT is needed. /// http_status_405_method_not_allowed = 405, /// /// 0x80190196 /// The resource identified by the request is only capable for generating response entities which have /// content characteristics not acceptable according to the accept headers sent in the Request. /// http_status_406_not_acceptable = 406, /// /// 0x80190197 /// This code is similar to HTTP 401 (Unauthorized), but indicates that the client must first /// authenticate itself with the proxy. /// http_status_407_proxy_authentication_required = 407, /// /// 0x80190198 /// The client did not produce a request within the time the server was prepared to wait. The client MAY /// repeat the request without modifications at a later time. /// http_status_408_request_timeout = 408, /// /// 0x80190199 /// This typically indicates that the request could not be completed due to a conflict with the current /// state of the resource. This code is only allowed in situations where it is expected that a user might /// be able to resolve the conflict and re-submit the request. /// /// Conflicts are most likely to occur in response to a PUT request. For example, if versioning were being /// used and the entity being PUT included changes to a resource which conflict with those made by an earlier /// (third-party) request, the server might use the 409 response to indicate that it can't complete the request. /// In this case, the response entity would likely contain a list of the differences between the two versions in a /// format defined by the Response-ContentType. /// /// Multiplayer services: /// Incorrect session or matchmaking query syntax or re-setting properties that are already pre-defined in the session template. /// The session couldn't be updated because the request is incompatible with the session. For example: /// Constants in the request conflict with constants in the session or session template. /// Members other than the caller are added to, or removed, from a large session. /// http_status_409_conflict = 409, /// /// 0x8019019A /// This typically indicates that the requested resource is no longer available at the server and no forwarding address /// is known. The expectation is that this condition is considered to be permanent. /// http_status_410_gone = 410, /// /// 0x8019019B /// The server refuses to accept the request without a defined Content-Length. The client MAY repeat the request if it /// adds a valid Content-Length header field containing the length of the message-body in the request. /// http_status_411_length_required = 411, /// /// 0x8019019C /// The precondition given in one or more of the request-header fields evaluated to false when it was tested on the /// server. This response code allows the client to place preconditions on the current resource meta information /// (header field data) to prevent the requested method from being applied to a resource other than the one intended. /// /// Multiplayer services /// The If-Match header, or (other than on a GET) the If-None-Match header couldn't be satisfied. /// The If-Match header couldn't be satisfied on a PUT or DELETE to an existing session. The current state of the session /// is returned along with the current ETag. /// http_status_412_precondition_failed = 412, /// /// 0x8019019D /// The server is refusing to process a request because the request entity is larger than the server is willing, or able, /// to process. The server MAY close the connection to prevent the client from continuing with the request. /// http_status_413_request_entity_too_large = 413, /// /// 0x8019019E /// The server is refusing to service the request because the Request-URI is longer than the server is willing to interpret. /// This rare condition is only likely to occur when a client has improperly converted a POST Request to a GET Request with long query information. /// http_status_414_request_uri_too_long = 414, /// /// 0x8019019F /// The server is refusing to service the request because the entity of the request is in a format not supported by the /// requested resource for the requested method. /// http_status_415_unsupported_media_type = 415, /// /// 0x801901A0 /// A server SHOULD return a response with this status code if a requested included in a Range request-header field, and none of /// the range-specifier values in this field overlap the current extent of the selected resource, and the request did not /// include an If-Range request-header field. /// http_status_416_requested_range_not_satisfiable = 416, /// /// 0x801901A1 /// Expect-request header failure /// http_status_417_expectation_failed = 417, /// /// 0x801901A5 /// The request was misdirected /// http_status_421_misdirected_request = 421, /// /// 0x801901A6 /// The request was not processable /// http_status_422_unprocessable_entity = 422, /// /// 0x801901A7 /// The resource was locked /// http_status_423_locked = 423, /// /// 0x801901A8 /// The request failed due to dependency /// http_status_424_failed_dependency = 424, /// /// 0x801901AA /// The client should upgrade to later protocol /// http_status_426_upgrade_required = 426, /// /// 0x801901AC /// The server requires a precondition /// http_status_428_precondition_required = 428, /// /// 0x801901AD /// Client is sending too many requests /// http_status_429_too_many_requests = 429, /// /// 0x801901AF /// The request headers are too large /// http_status_431_request_header_fields_too_large = 431, /// /// 0x801901C1 /// The request should be retried after doing the appropriate action /// http_status_449_retry_with = 449, /// /// 0x801901C3 /// The request was unavailable for legal reasons /// http_status_451_unavailable_for_legal_reasons = 451, /// /// 0x801901F4 /// Corresponds to HTTP 500 Internal Server Error.This error can occur when invalid parameters are provided to the web service /// via a RESTful API call, or if the web service encounters a crash or other unexpected error state. Using fiddler to examine /// the failing HTTP request will often help root cause the issue. /// http_status_500_internal_server_error = 500, /// /// 0x801901F5 /// The requested service is not implemented. /// http_status_501_not_implemented = 501, /// /// 0x801901F6 /// The request got an invalid response to the gateway /// http_status_502_bad_gateway = 502, /// /// 0x801901F7 /// The requested service is not available. /// http_status_503_service_unavailable = 503, /// /// 0x801901F8 /// The HTTP gateway has timed out. /// http_status_504_gateway_timeout = 504, /// /// 0x801901F9 /// This version of HTTP is not supported by the endpoint. /// http_status_505_http_version_not_supported = 505, /// /// 0x801901FA /// Internal configuration error /// http_status_506_variant_also_negotiates = 506, /// /// 0x801901FB /// The service was unable complete the request due to storage /// http_status_507_insufficient_storage = 507, /// /// 0x801901FC /// The service detected an infinite loop while processing /// http_status_508_loop_detected = 508, /// /// 0x801901FE /// The request requires more extensions to complete /// http_status_510_not_extended = 510, /// /// 0x801901FF /// The client needs to authenticate to gain network access. Not used by Xbox Live services. /// http_status_511_network_authentication_required = 511, ////////////////////////////////////////////////////////////////////////// // Errors from exception enabled components such as Casablanca ////////////////////////////////////////////////////////////////////////// /// /// 0x8007000e /// xbl_error_code 1000 /// Bad alloc /// bad_alloc = 1000, /// /// 0x80004002 /// xbl_error_code 1001 /// Bad cast /// bad_cast, /// /// 0x80070057 /// xbl_error_code 1002 /// Invalid argument /// invalid_argument, /// /// 0x8000000b /// xbl_error_code 1003 /// Out of range /// out_of_range, /// /// 0x80070018 /// xbl_error_code 1004 /// Length error /// length_error, /// /// 0x8000000b /// xbl_error_code 1005 /// Range error /// range_error, /// /// 0x8000ffff /// xbl_error_code 1006 /// Logic error /// logic_error, /// /// 0x89235200 /// xbl_error_code 1007 /// Runtime error /// runtime_error, /// /// 0x83750007 /// xbl_error_code 1008 /// JSON error /// json_error, /// /// 0x83750005 /// xbl_error_code 1009 /// Websocket error /// websocket_error, /// /// 0x83750005 /// xbl_error_code 1010 /// URI error /// uri_error, /// /// 0x80004005 /// xbl_error_code 1011 /// Generic error /// generic_error, ////////////////////////////////////////////////////////////////////////// // RTA errors ////////////////////////////////////////////////////////////////////////// /// /// 0x89235201 /// xbl_error_code 1500 /// RTA generic error /// rta_generic_error = 1500, /// /// 0x89235202 /// xbl_error_code 1501 /// RTA subscription limit reached /// rta_subscription_limit_reached, /// /// 0x89235203 /// xbl_error_code 1502 /// RTA access denied /// rta_access_denied, /// /// 0x89235209 /// xbl_error_code 1503 /// RTA not activated /// rta_not_activated, ////////////////////////////////////////////////////////////////////////// // Auth errors ////////////////////////////////////////////////////////////////////////// /// /// 0x89235204 /// xbl_error_code 2000 /// Unknown auth error /// auth_unknown_error = 2000, /// /// 0x8086000c /// xbl_error_code 2001 /// User interaction required /// auth_user_interaction_required, /// /// 0x80070525 /// xbl_error_code 2002 /// User interaction required /// auth_user_switched, /// /// 0x80070525 /// xbl_error_code 2003 /// User cancelled /// auth_user_cancel, /// /// 0x80070525 /// xbl_error_code 2004 /// User not signed in /// auth_user_not_signed_in, /// /// 0x89235205 /// xbl_error_code 2005 /// Auth runtime error /// auth_runtime_error, /// /// 0x89235206 /// xbl_error_code 2006 /// Auth no token error /// auth_no_token_error, ////////////////////////////////////////////////////////////////////////// // Xbox Live SDK errors ////////////////////////////////////////////////////////////////////////// /// /// 0x8007064a /// xbl_error_code 3000 /// Could not read the xboxservices.config. Ensure it is deployed in the package at /// Windows::ApplicationModel::Package::Current->InstalledLocation + "\xboxservices.config" /// invalid_config = 3000, /// /// 0x80004001 /// xbl_error_code 3001 /// API is not supported /// unsupported = 3001, ////////////////////////////////////////////////////////////////////////// // xbox live auth errors ////////////////////////////////////////////////////////////////////////// /// /// 0x80072EE2 /// This error is typically returned by Xbox::Services APIs, GetTokenAndSignatureAsync, /// and CheckLicense. It indicates that the Internet connection or a server response was /// interrupted; possibly the result of a incorrectly configured NSAL. Pending requests /// should be retried when this error is encountered. /// HR_ERROR_INTERNET_TIMEOUT = (int)0x80072EE2, /// /// 0x87DD0003 /// XASD returned an unexpected response. /// AM_E_XASD_UNEXPECTED = (int)0x87DD0003, /// /// 0x87DD0004 /// XASU returned an unexpected response. /// AM_E_XASU_UNEXPECTED = (int)0x87DD0004, /// /// 0x87DD0005 /// XAST returned an unexpected response. /// Cause: Your NSAL configuration is set up correctly for your service, however the /// Relying Party certificate is not yet trusted by Xbox Live. /// /// Resolution : Relying Party configuration on XDP and provide the Relying Party /// certificate in the XSTS token certificate for your endpoint. /// If you are using the NSAL.json file, double check that the Relying Party name is /// correct in the 'target' field of your NSAL.json file on the console and ensure that /// the certificate has been specified in XDP. /// /// If you are developing a UWP title and performing service configuration in XDP, then /// you may have not completed the Application ID binding step correctly. For more /// information, please see the "Troubleshooting Sign-in" article in the Xbox Live documentation. /// AM_E_XAST_UNEXPECTED = (int)0x87DD0005, /// /// 0x87DD0006 /// Cause : Your NSAL configuration is set up correctly for your service, however the /// Relying Party certificate is not yet trusted by Xbox Live. /// /// Resolution : Relying Party configuration on XDP and provide the Relying Party /// certificate in the XSTS token certificate for your endpoint. /// If you are using the NSAL.json file, double check that the Relying Party name is /// correct in the 'target' field of your NSAL.json file on the console and ensure /// that the certificate has been specified in XDP. /// AM_E_XSTS_UNEXPECTED = (int)0x87DD0006, /// /// 0x87DD0007 /// XDevice returned an unexpected response. /// AM_E_XDEVICE_UNEXPECTED = (int)0x87DD0007, /// /// 0x87DD0008 /// The console is not authorized to enable development mode. /// AM_E_DEVMODE_NOT_AUTHORIZED = (int)0x87DD0008, /// /// 0x87DD0009 /// The operation was not authorized. /// Cause: The user is not authorized to get an XSTS token with the current TitleID or /// SandboxID setup on the console for the provided URL. /// /// Resolution: /// Ensure that your console is in the proper SandboxID for the TitleID you are using. /// See this forum thread for instructions on changing your SandboxID and which SandboxID /// to use for samples. /// /// Ensure that the test account you are using is partitioned to a group in XDP that has /// access to the TitleID and SandboxID for your title(Note that all accounts are able to /// access the samples while in the sample SandboxID). /// AM_E_NOT_AUTHORIZED = (int)0x87DD0009, /// /// 0x87DD000A /// The operation was forbidden. The server responded with a 403, and a more detailed error is not available. /// AM_E_FORBIDDEN = (int)0x87DD000A, /// /// 0x87DD000B /// The URL specified does not match a known target. /// /// Cause: /// The URL you are attempting to get a token for was not found within your NSAL configuration. /// /// Resolution: /// Ensure that the Relying Party and server name are properly set up as an endpoint /// on your console and in your NSAL. /// /// Try calling a known Xbox Service such as https ://social.xboxlive.com/users/xuid(user's xuid)/people /// to ensure that you can get tokens for the Xbox services. If you are using the NSAL.json file, make sure /// that the 'target' field in the file has your Relying Party name ending with '/' and /// that the server name is set up correctly. /// AM_E_UNKNOWN_TARGET = (int)0x87DD000B, /// /// 0x87DD000C /// There were problems with the JSON data downloaded from the server. /// AM_E_INVALID_NSAL_DATA = (int)0x87DD000C, /// /// 0x87DD000D /// The title has not yet been successfully authenticated. /// Cause: This will be returned when a title token is required, but has not been cached. For example, /// attempting to retrieve an X token with title claims will fail with this error if AuthenticateTitle /// hasn't been called successfully. /// /// Resolution: Check your TitleID, SOCID, and SandboxID, or check your title's configuration on XDP. /// AM_E_TITLE_NOT_AUTHENTICATED = (int)0x87DD000D, /// /// 0x87DD000E /// XAST returned 401 when attempting to retrieve the T token. Double-check that your device is set to the /// proper development sandbox and that the user has access to the sandbox. /// /// For more information, please see the "Troubleshooting Sign-in" article in the Xbox Live documentation. /// AM_E_TITLE_NOT_AUTHORIZED = (int)0x87DD000E, /// /// 0x87DD0011 /// The user hash value for the specified user hasn't been recorded, so a valid token can't be generated. /// AM_E_USER_HASH_MISSING = (int)0x87DD0011, /// /// 0x87DD0013 /// The Authentication Manager can't find the user for which an authentication token is being retrieved. /// One possible scenario is that the User signed out, but the title still has a reference to the User object. /// AM_E_USER_NOT_FOUND = (int)0x87DD0013, /// /// 0x87DD0015 /// The environment configured or specified is not valid. /// AM_E_INVALID_ENVIRONMENT = (int)0x87DD0015, /// /// 0x87DD0016 /// The XASD authentication server has timed out. /// AM_E_XASD_TIMEOUT = (int)0x87DD0016, /// /// 0x87DD0017 /// The XASU authentication server has timed out. /// AM_E_XASU_TIMEOUT = (int)0x87DD0017, /// /// 0x87DD0018 /// The XAST authentication server has timed out. /// AM_E_XAST_TIMEOUT = (int)0x87DD0018, /// /// 0x87DD0019 /// The XSTS authentication server has timed out. /// AM_E_XSTS_TIMEOUT = (int)0x87DD0019, /// /// 0x87DD001A /// Title authentication failed because a connection to Xbox Live is required, by policy, and none is present. /// AM_E_LIVE_CONNECTION_REQUIRED = (int)0x87DD001A, /// /// 0x87dd001e /// There is no network connection. /// AM_E_NO_NETWORK = (int)0x87dd001e, /// /// 0x87dd0020 /// The Network Security Authorization List(NSAL) returned an unexpected response. /// AM_E_XTITLE_UNEXPECTED = (int)0x87dd0020, /// /// 0x87dd0021 /// The endpoint does not require an authorization token, but the application is attempting to /// retrieve a token via GetTokenAndSignatureAsync. /// AM_E_NO_TOKEN_REQUIRED = (int)0x87dd0021, /// /// 0x87dd0022 /// Timeouts were received from the various authorization servers. /// AM_E_XTITLE_TIMEOUT = (int)0x87dd0022, /// /// 0x8015DC00 /// Developer mode is not authorized for the client device. /// XO_E_DEVMODE_NOT_AUTHORIZED = (int)0x8015DC00, /// /// 0x8015DC01 /// A system update is required before this action can be performed. /// XO_E_SYSTEM_UPDATE_REQUIRED = (int)0x8015DC01, /// /// 0x8015DC02 /// A content update is required before this action can be performed. /// XO_E_CONTENT_UPDATE_REQUIRED = (int)0x8015DC02, /// /// 0x8015DC03 /// The device or user was banned. /// XO_E_ENFORCEMENT_BAN = (int)0x8015DC03, /// /// 0x8015DC04 /// The device or user was banned. /// XO_E_THIRD_PARTY_BAN = (int)0x8015DC04, /// /// 0x8015DC05 /// Access to this resource has been parentally restricted. /// XO_E_ACCOUNT_PARENTALLY_RESTRICTED = (int)0x8015DC05, /// /// 0x8015DC08 /// Access to this resource requires that the account billing information /// is updated. /// XO_E_ACCOUNT_BILLING_MAINTENANCE_REQUIRED = (int)0x8015DC08, /// /// 0x8015DC0A /// The user has not accepted the terms of use for this resource. /// XO_E_ACCOUNT_TERMS_OF_USE_NOT_ACCEPTED = (int)0x8015DC0A, /// /// 0x8015DC0B /// This resource is not available in the country associated with the user. /// XO_E_ACCOUNT_COUNTRY_NOT_AUTHORIZED = (int)0x8015DC0B, /// /// 0x8015DC0C /// Access to this resource requires age verification. /// XO_E_ACCOUNT_AGE_VERIFICATION_REQUIRED = (int)0x8015DC0C, /// /// 0x8015DC0D /// XO_E_ACCOUNT_CURFEW = (int)0x8015DC0D, /// /// 0x8015DC0E /// XO_E_ACCOUNT_CHILD_NOT_IN_FAMILY = (int)0x8015DC0E, /// /// 0x8015DC0F /// XO_E_ACCOUNT_CSV_TRANSITION_REQUIRED = (int)0x8015DC0F, /// /// 0x8015DC09 /// XO_E_ACCOUNT_CREATION_REQUIRED = (int)0x8015DC09, /// /// 0x8015DC10 /// XO_E_ACCOUNT_MAINTENANCE_REQUIRED = (int)0x8015DC10, /// /// 0x8015DC11 /// The call was blocked because there was a conflict with the sandbox, console, application, or /// your account.Verify your account, console and title settings in XDP, and check the current /// Sandbox on the device. /// XO_E_ACCOUNT_TYPE_NOT_ALLOWED = (int)0x8015DC11, /// /// 0x8015DC12 /// Your device does not have access to the Sandbox it is set to, or the account you are signed /// in with does not have access to the Sandbox.Check that you are using the correct Sandbox. /// /// Note: All XDK samples use XDKS.1 SandboxID, which allow all user accounts to access and run /// the samples.SandboxID's are case sensitive- Not matching the case of your SandboxID exactly may /// result in errors. If you are still having issues running the sample, please work with your /// Developer Account Manager and provide a fiddler trace to help with troubleshooting. /// /// For more information on handling this error, please see the "Troubleshooting Sign-in" article /// in the Xbox Live documentation /// XO_E_CONTENT_ISOLATION = (int)0x8015DC12, /// /// 0x8015DC13 /// XO_E_ACCOUNT_NAME_CHANGE_REQUIRED = (int)0x8015DC13, /// /// 0x8015DC14 /// XO_E_DEVICE_CHALLENGE_REQUIRED = (int)0x8015DC14, /// /// 0x8015DC16 /// The account was signed in on another device. /// XO_E_SIGNIN_COUNT_BY_DEVICE_TYPE_EXCEEDED = (int)0x8015DC16, /// /// 0x8015DC17 /// XO_E_PIN_CHALLENGE_REQUIRED = (int)0x8015DC17, /// /// 0x8015DC18 /// XO_E_RETAIL_ACCOUNT_NOT_ALLOWED = (int)0x8015DC18, /// /// 0x8015DC19 /// The current sandbox is not allowed to access the SCID. Please ensure that your current /// sandbox is set to your development sandbox. If you are running on a Windows 10 PC, then /// you can change your current sandbox using the SwitchSandbox.cmd script in the Xbox Live SDK /// tools directory. If you are using an Xbox One, you can switch the sandbox using Xbox One /// Manager. /// /// For more information on handling this error, please see the "Troubleshooting Sign-in" article /// in the Xbox Live documentation. /// XO_E_SANDBOX_NOT_ALLOWED = (int)0x8015DC19, /// /// 0x8015DC1A /// XO_E_ACCOUNT_SERVICE_UNAVAILABLE_UNKNOWN_USER = (int)0x8015DC1A, /// /// 0x8015DC1B /// XO_E_GREEN_SIGNED_CONTENT_NOT_AUTHORIZED = (int)0x8015DC1B, /// /// 0x8015DC1C /// XO_E_CONTENT_NOT_AUTHORIZED = (int)0x8015DC1C, ////////////////////////////////////////////////////////////////////////// // Generic errors ////////////////////////////////////////////////////////////////////////// /// /// 0x800C0002 /// The URL is invalid. /// HR_INET_E_INVALID_URL = (int)0x800C0002, /// /// 0x800C0003 /// No session. /// HR_INET_E_NO_SESSION = (int)0x800C0003, /// /// 0x800C0004 /// WinINet cannot connect to the requested resource. /// HR_INET_E_CANNOT_CONNECT = (int)0x800C0004, /// /// 0x800C0005 /// The requested resource was not found. /// HR_INET_E_RESOURCE_NOT_FOUND = (int)0x800C0005, /// /// 0x800C0006 /// The requested resource was not found. /// HR_INET_E_OBJECT_NOT_FOUND = (int)0x800C0006, /// /// 0x800C0007 /// The requested resource was not found. /// HR_INET_E_DATA_NOT_AVAILABLE = (int)0x800C0007, /// /// 0x800C0008 /// Operation restricted by the current inability to discover or join the multiplayer session, or /// the player does not have the multiplayer privilege. /// HR_INET_E_DOWNLOAD_FAILURE = (int)0x800C0008, /// /// 0x800C0009 /// Authentication is required to access this resource. /// HR_INET_E_AUTHENTICATION_REQUIRED = (int)0x800C0009, /// /// 0x800C000A /// No valid media /// HR_INET_E_NO_VALID_MEDIA = (int)0x800C000A, /// /// 0x800C000B /// The connection has timed out. /// HR_INET_E_CONNECTION_TIMEOUT = (int)0x800C000B, /// /// 0x800C000C /// Invalid request /// HR_INET_E_INVALID_REQUEST = (int)0x800C000C, /// /// 0x800C000D /// The requested protocol is unknown. /// HR_INET_E_UNKNOWN_PROTOCOL = (int)0x800C000D, /// /// 0x800C000E /// Security problem /// HR_INET_E_SECURITY_PROBLEM = (int)0x800C000E, /// /// 0x800C000F /// Cannot load data /// HR_INET_E_CANNOT_LOAD_DATA = (int)0x800C000F, /// /// 0x800C0010 /// Cannot instantiate object /// HR_INET_E_CANNOT_INSTANTIATE_OBJECT = (int)0x800C0010, /// /// 0x800C0019 /// The certificate presented for the request is invalid. /// HR_INET_E_INVALID_CERTIFICATE = (int)0x800C0019, /// /// 0x800C0014 /// The redirect to a different endpoint has failed. /// HR_INET_E_REDIRECT_FAILED = (int)0x800C0014, /// /// 0x800C0015 /// A resource request has been directed to a directory rather than an individual resource. /// HR_INET_E_REDIRECT_TO_DIR = (int)0x800C0015, /// /// 0x800704cf /// The network location cannot be reached. /// HR_ERROR_NETWORK_UNREACHABLE = (int)0x800704cf, /// /// 0x89235007 /// The network has not been initialized. /// HR_HC_NETWORK_NOT_INTIALIZED = (int)0x89235007 }; namespace legacy { /// /// Category error type for XSAPI error codes. /// class xbox_services_error_code_category_impl : public std::error_category { public: /// /// Gets the error category name. /// /// A string identifying the category. _XSAPIIMP const char* name() const _NOEXCEPT override { return "XBL"; } /// /// Converts an error value into a string that describes the error. /// /// A string that describes the error. _XSAPIIMP std::string message(_In_ int errorCode) const _NOEXCEPT override; }; /// /// Category error type for XSAPI error conditions. /// class xbox_services_error_condition_category_impl : public std::error_category { public: /// /// Gets the error category name. /// /// A string identifying the category. _XSAPIIMP const char* name() const _NOEXCEPT override { return "XBL CONDITION"; } /// /// Converts an error value into a string that describes the error. /// /// A string that describes the error. _XSAPIIMP std::string message(_In_ int errorCode) const _NOEXCEPT override; /// /// Used to establish equivalence between arbitrary error_codes in /// the current category with arbitrary error_conditions. /// /// An error code. /// An error condition. /// True if the error code and the error condition are related; otherwise false. _XSAPIIMP bool equivalent( _In_ const std::error_code& arbitraryErrorCode, _In_ int xboxLiveErrorCondition) const _NOEXCEPT override; }; /// /// Gets the one global instance of the error code category. /// /// An error category instance. _XSAPIIMP const xbox_services_error_code_category_impl& xbox_services_error_code_category(); /// /// Gets the one global instance of the error condition category. /// /// An error category instance. _XSAPIIMP const xbox_services_error_condition_category_impl& xbox_services_error_condition_category(); } NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END namespace xbox { namespace services { inline std::error_code make_error_code(xbl_error_code e) { return std::error_code(static_cast(e), legacy::xbox_services_error_code_category()); } inline std::error_condition make_error_condition(xbl_error_condition e) { return std::error_condition(static_cast(e), legacy::xbox_services_error_condition_category()); } } } namespace std { template <> struct is_error_code_enum : public std::true_type {}; template <> struct is_error_condition_enum : public std::true_type {}; } NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN template class xbl_result { public: xbl_result(); xbl_result(_In_ T payload); xbl_result(_In_ std::error_code errorCode, _In_ xsapi_internal_string errorMessage = xsapi_internal_string()); xbl_result(_In_ T payload, _In_ std::error_code errorCode, _In_ xsapi_internal_string errorMessage = xsapi_internal_string()); xbl_result(_In_ const xbl_result&); xbl_result& operator=(_In_ const xbl_result&); xbl_result(_In_ xbl_result&& other) noexcept; xbl_result& operator=(_In_ xbl_result&& other) noexcept; /// /// The payload of the result. It may be empty if there was an error /// T& payload(); /// /// The payload of the result. It may be empty if there was an error /// const T& payload() const; /// /// Sets the payload. /// void set_payload(T payload); /// /// The error returned by the operation. /// To test for and potentially react to in your code, use the values in xbox_error_condition /// To see the specific error value returned by the operation, use err_code().value() but testing for these values is discouraged. /// For more detail about std::error_code vs std::error_condition, see /// http://en.cppreference.com/w/cpp/error/error_condition /// const std::error_code& err() const; /// /// Sets the error. /// To test for and potentially react to in your code, use the values in xbox_error_condition /// To see the specific error value returned by the operation, use err_code().value() but testing for these values is discouraged. /// For more detail about std::error_code vs std::error_condition, see /// http://en.cppreference.com/w/cpp/error/error_condition /// void _Set_err(std::error_code errc); /// /// Returns call specific debug information. /// It is not localized, so only use for debugging purposes. /// const xsapi_internal_string& err_message() const; /// /// Sets error code debug information. /// It is not localized, so only use for debugging purposes. /// void _Set_err_message(xsapi_internal_string errMessage); private: T m_payload{}; std::error_code m_errorCode{}; xsapi_internal_string m_errorMessage; }; template<> class xbl_result { public: xbl_result() : m_errorMessage(std::string()) { //m_errorCode = std::make_error_code(xbl_error_code::no_error); m_errorCode = make_error_code(xbl_error_code::no_error); } xbl_result(_In_ std::error_code errorCode, _In_ std::string errorMessage = std::string()) : m_errorCode(std::move(errorCode)), m_errorMessage(std::move(errorMessage)) { } xbl_result(_In_ const xbl_result& other) { *this = other; } xbl_result& operator=(_In_ const xbl_result& other) { m_errorCode = other.m_errorCode; m_errorMessage = other.m_errorMessage; return *this; } xbl_result(_In_ xbl_result&& other) noexcept { *this = std::move(other); } xbl_result& operator=(_In_ xbl_result&& other) noexcept { if (this != &other) { m_errorCode = std::move(other.m_errorCode); m_errorMessage = std::move(other.m_errorMessage); } return *this; } /// /// The error returned by the operation. /// To test for and potentially react to in your code, use the values in xbox_error_condition /// To see the specific error value returned by the operation, use err_code().value() but testing for these values is discouraged. /// For more detail about std::error_code vs std::error_condition, see /// http://en.cppreference.com/w/cpp/error/error_condition /// const std::error_code& err() const { return m_errorCode; } /// /// Returns call specific debug information. /// It is not localized, so only use for debugging purposes. /// const std::string& err_message() const { return m_errorMessage; } /// /// Returns call specific debug information. /// It is not localized, so only use for debugging purposes. /// void _Set_err(std::error_code errCode) { m_errorCode = std::move(errCode); } /// /// Sets error code debug information. /// It is not localized, so only use for debugging purposes. /// void _Set_err_message(std::string errMessage) { m_errorMessage = std::move(errMessage); } private: std::error_code m_errorCode; std::string m_errorMessage; }; template xbl_result::xbl_result() : m_errorMessage(xsapi_internal_string()) { m_errorCode = make_error_code(xbl_error_code::no_error); } template xbl_result::xbl_result(_In_ T payload) : m_payload(std::move(payload)), m_errorMessage(xsapi_internal_string()) { m_errorCode = make_error_code(xbl_error_code::no_error); } template xbl_result::xbl_result( _In_ std::error_code errorCode, _In_ xsapi_internal_string errorMessage ) : m_errorCode(std::move(errorCode)), m_errorMessage(std::move(errorMessage)) { } template xbl_result::xbl_result( _In_ T payload, _In_ std::error_code errorCode, _In_ xsapi_internal_string errorMessage ) : m_payload(std::move(payload)), m_errorCode(std::move(errorCode)), m_errorMessage(std::move(errorMessage)) { } template T& xbl_result::payload() { return m_payload; } template const T& xbl_result::payload() const { return m_payload; } template void xbl_result::set_payload(T payload) { m_payload = std::move(payload); } template const std::error_code& xbl_result::err() const { return m_errorCode; } template void xbl_result::_Set_err(std::error_code errCode) { m_errorCode = std::move(errCode); } template const xsapi_internal_string& xbl_result::err_message() const { return m_errorMessage; } template void xbl_result::_Set_err_message(xsapi_internal_string errorMsg) { m_errorMessage = std::move(errorMsg); } template xbl_result::xbl_result( _In_ const xbl_result& other ) : m_payload{ other.m_payload }, m_errorCode{ other.m_errorCode }, m_errorMessage{ other.m_errorMessage } { } template xbl_result& xbl_result::operator=(_In_ const xbl_result& other) { m_payload = other.m_payload; m_errorCode = other.m_errorCode; m_errorMessage = other.m_errorMessage; return *this; } template xbl_result::xbl_result( _In_ xbl_result&& other ) noexcept : m_payload{ std::move(other.m_payload) }, m_errorCode{ other.m_errorCode }, m_errorMessage{ std::move(other.m_errorMessage) } { } template xbl_result& xbl_result::operator=(_In_ xbl_result&& other) noexcept { if (this != &other) { m_payload = std::move(other.m_payload); m_errorCode = std::move(other.m_errorCode); m_errorMessage = std::move(other.m_errorMessage); } return *this; } NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END ================================================ FILE: Source/Shared/fault_injection.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "fault_injection.h" #if _DEBUG // fault injection only enabled on debug bits so as not to impact retail titles static fault_injection g_faultInjection; // not hanging this off of global state since it needs to keep between XblCleanups so long test runs are useful #endif STDAPI_(void) XblEnableFaultInjection(_In_ uint64_t featureId) XBL_NOEXCEPT try { #if _DEBUG // fault injection only enabled on debug bits so as not to impact retail titles g_faultInjection.m_enabledFeatureMask |= featureId; #else UNREFERENCED_PARAMETER(featureId); #endif } CATCH_RETURN_WITH(;) STDAPI_(uint64_t) XblGetFaultCounter() XBL_NOEXCEPT try { #if _DEBUG // testing feature only enabled on debug bits so as not to impact retail titles return g_faultInjection.m_failTotalCounter; #else return 0; #endif } CATCH_RETURN_WITH(0) STDAPI_(void) XblSetFaultInjectOptions(int64_t failFreq, uint64_t freqChangeSpeed, int64_t freqChangeAmount) XBL_NOEXCEPT try { #if _DEBUG // testing feature only enabled on debug bits so as not to impact retail titles g_faultInjection.m_failFreq = failFreq; g_faultInjection.m_freqChangeSpeed = freqChangeSpeed; g_faultInjection.m_freqChangeAmount = freqChangeAmount; #else UNREFERENCED_PARAMETER(failFreq); UNREFERENCED_PARAMETER(freqChangeSpeed); UNREFERENCED_PARAMETER(freqChangeAmount); #endif } CATCH_RETURN_WITH(;) STDAPI_(bool) XblShouldFaultInject(_In_ uint64_t featureId) XBL_NOEXCEPT try { #if _DEBUG // testing feature only enabled on debug bits so as not to impact retail titles if ((g_faultInjection.m_enabledFeatureMask & featureId) == featureId) { g_faultInjection.m_freqChangeCounter++; g_faultInjection.m_freqChangeCounter %= g_faultInjection.m_freqChangeSpeed; if (g_faultInjection.m_freqChangeCounter == 0) { // Alter the fail freq every so often so it doesn't just fall into testing a repeated calling pattern g_faultInjection.m_failFreq += g_faultInjection.m_freqChangeAmount; g_faultInjection.m_failFreq = std::max(0, g_faultInjection.m_failFreq); } g_faultInjection.m_failTotalCounter++; g_faultInjection.m_failCounter++; g_faultInjection.m_failCounter %= g_faultInjection.m_failFreq; return (g_faultInjection.m_failCounter == 0); } #else UNREFERENCED_PARAMETER(featureId); #endif return false; } CATCH_RETURN() ================================================ FILE: Source/Shared/fault_injection.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #define INJECTION_FEATURE_USER 0x0001 #define INJECTION_FEATURE_HTTP 0x0002 extern "C" { STDAPI_(void) XblEnableFaultInjection(_In_ uint64_t featureId) XBL_NOEXCEPT; STDAPI_(bool) XblShouldFaultInject(_In_ uint64_t featureId) XBL_NOEXCEPT; STDAPI_(uint64_t) XblGetFaultCounter() XBL_NOEXCEPT; STDAPI_(void) XblSetFaultInjectOptions(int64_t failFreq, uint64_t freqChangeSpeed, int64_t freqChangeAmount) XBL_NOEXCEPT; } NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN struct fault_injection { uint64_t m_enabledFeatureMask{ 0 }; uint64_t m_failCounter{ 0 }; uint64_t m_failTotalCounter{ 0 }; int64_t m_failFreq{ 3 }; // Alter the fail freq every so often so it doesn't just fall into testing a repeated calling pattern uint64_t m_freqChangeCounter{ 0 }; uint64_t m_freqChangeSpeed{ 7 }; int64_t m_freqChangeAmount{ 1 }; }; NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END ================================================ FILE: Source/Shared/global_state.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "global_state.h" #include "Achievements/Manager/achievements_manager_internal.h" #include "multiplayer_manager_internal.h" #include "social_manager_internal.h" #include "real_time_activity_manager.h" #include "Logger/log_hc_output.h" #if HC_PLATFORM == HC_PLATFORM_ANDROID #include "a/utils_a.h" #include "a/java_interop.h" #endif HC_DEFINE_TRACE_AREA(XSAPI, HCTraceLevel::Verbose); NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN // Local Storage in Android is dependent on the JNI Initialization done in GlobalState::Create // Only on Android the initialization of m_localStorage is done outside of the constructor. GlobalState::GlobalState( _In_ const XblInitArgs* args ) : m_taskQueue{ args ? TaskQueue::DeriveWorkerQueue(args->queue) : nullptr }, m_achievementsManager{ MakeShared() }, m_multiplayerManager{ MakeShared() }, m_socialManager{ MakeShared() }, m_rtaManager{ MakeShared(m_taskQueue) }, #if HC_PLATFORM != HC_PLATFORM_ANDROID m_localStorage{ MakeShared(m_taskQueue) }, #endif m_appConfig{ MakeShared() }, m_logger{ MakeShared() } { #if HC_PLATFORM_IS_MICROSOFT HCTraceSetEtwEnabled(true); #endif m_logger->add_log_output(MakeShared()); #if _DEBUG && defined(XSAPI_UNIT_TESTS) && XSAPI_PROFILE _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); #endif } HRESULT GlobalState::Create( _In_ const XblInitArgs* args ) { RETURN_HR_INVALIDARGUMENT_IF_NULL(args); #if !(HC_PLATFORM == HC_PLATFORM_XDK || HC_PLATFORM == HC_PLATFORM_UWP) RETURN_HR_INVALIDARGUMENT_IF_NULL(args->scid); RETURN_HR_INVALIDARGUMENT_IF_EMPTY_STRING(args->scid); #endif XTaskQueueHandle processQueue{ nullptr }; bool haveProcessQueue = XTaskQueueGetCurrentProcessTaskQueue(&processQueue); if (!haveProcessQueue && args->queue == nullptr) { return E_NO_TASK_QUEUE; } if (AccessHelper(AccessMode::GET)) { return E_XBL_ALREADY_INITIALIZED; } // Global state depends on libHttpClient state so call HCInitialize before creating our state. #if HC_PLATFORM == HC_PLATFORM_ANDROID HCInitArgs hcArgs{}; hcArgs.javaVM = args->javaVM; hcArgs.applicationContext = args->applicationContext; RETURN_HR_IF_FAILED(HCInitialize(&hcArgs)); #else RETURN_HR_IF_FAILED(HCInitialize(nullptr)); #endif auto state = std::shared_ptr( new (Alloc(sizeof(GlobalState))) GlobalState(args), Deleter(), Allocator() ); #if HC_PLATFORM == HC_PLATFORM_ANDROID // TODO Should make the java interop singleton a member of GlobalState rather than a separate static auto javaInteropInitResult = xbox::services::java_interop::get_java_interop_singleton()->initialize( args->javaVM, args->applicationContext ); if (javaInteropInitResult.err()) { return utils::convert_xbox_live_error_code_to_hresult(javaInteropInitResult.err()); } #endif #if HC_PLATFORM == HC_PLATFORM_ANDROID // Local Storage in Android is dependent on the JNI Initialization so create LocalStorage now. state->m_localStorage = MakeShared(state->m_taskQueue); #endif #if HC_PLATFORM == HC_PLATFORM_WIN32 // On Win32, set Local Storage path using init args RETURN_HR_IF_FAILED(state->m_localStorage->SetStoragePath(args->localStoragePath)); #endif #if HC_PLATFORM != HC_PLATFORM_XDK && HC_PLATFORM != HC_PLATFORM_UWP RETURN_HR_INVALIDARGUMENT_IF_NULL(args->scid); RETURN_HR_IF_FAILED(state->m_appConfig->Initialize(args->scid)); #else RETURN_HR_IF_FAILED(state->m_appConfig->Initialize()); #endif #if HC_PLATFORM_IS_EXTERNAL state->m_appConfig->SetAppId(args->appId); state->m_appConfig->SetAppVer(args->appVer); state->m_appConfig->SetOsName(args->osName); state->m_appConfig->SetOsVersion(args->osVersion); state->m_appConfig->SetOsLocale(args->osLocale); state->m_appConfig->SetDeviceClass(args->deviceClass); state->m_appConfig->SetDeviceId(args->deviceId); #endif #if HC_PLATFORM == HC_PLATFORM_IOS if (args->apnsEnvironment) { state->m_appConfig->SetAPNSEnvironment(args->apnsEnvironment); } #endif #if HC_PLATFORM == HC_PLATFORM_XDK // Initialize achievements provider properties RETURN_HR_IF_FAILED(CoCreateGuid(&state->m_achievementsSessionId)); CHAR strTitleId[16] = ""; sprintf_s(strTitleId, "%0.8X", state->m_appConfig->TitleId()); Stringstream achievementsProviderName; achievementsProviderName << "XSAPI_" << strTitleId; state->m_achivementsEventProviderName = achievementsProviderName.str(); #endif state->m_locales = utils::generate_locales(); // GlobalState object has been created and initialized successfully at this point so store it. (void)AccessHelper(AccessMode::SET, state); #if HC_PLATFORM == HC_PLATFORM_GDK RegisterAppStateChangeNotification(AppStateChangeNotificationReceived, nullptr, &state->m_registrationID); #endif // Leak a ref until Cleanup is called state->AddRef(); return S_OK; } struct HCCleanupContext { XAsyncBlock* xblCleanupAsyncBlock; TaskQueue hcCleanupQueue; XAsyncBlock hcCleanupAsyncBlock; }; HRESULT GlobalState::CleanupAsync( _In_ XAsyncBlock* async ) noexcept { auto state{ AccessHelper(AccessMode::SET, nullptr) }; if (!state) { return E_XBL_NOT_INITIALIZED; } #if HC_PLATFORM == HC_PLATFORM_GDK // In case of GDK, make sure to unregister from App Change Notifications before disconnecting UnregisterAppStateChangeNotification(state->m_registrationID); #endif return XAsyncBegin( async, state.get(), reinterpret_cast(CleanupAsync), __FUNCTION__, [](XAsyncOp op, const XAsyncProviderData* data) { switch(op) { case XAsyncOp::Begin: { return XAsyncSchedule(data->async, 0); } case XAsyncOp::DoWork: { //Limit shared_ptr scope so that it never lives beyond the XTaskQueueTerminate callback, //even if XTaskQueueTerminate completes synchronously. This ensures the call to DecRef //in the callback releases the last remaining reference. XTaskQueueHandle stateTaskQueueHandle; { // Create a shared_ptr to the state so we can track the use_count here auto state{ static_cast(data->context)->shared_from_this() }; stateTaskQueueHandle = state->Queue().GetHandle(); // Wait for all other references to the GlobalState to go away. // Note that the use count check here is only valid because we never create // a weak_ptr to the singleton. If we did that could cause the use count // to increase even though we are the only strong reference if (state.use_count() > 2) { // Wait for async tasks which require the GlobalState to finish. RETURN_HR_IF_FAILED(XAsyncSchedule(data->async, 10)); return E_PENDING; } #ifdef XSAPI_UNIT_TESTS // As a sanity check, ensure there aren't other references to RTA manager. // Don't always block on this - unclosed XblContexts would then prevent XblCleanup from completing. auto rtaManagerUseCount{ state->m_rtaManager.use_count() }; if (rtaManagerUseCount > 1) { HC_TRACE_VERBOSE(XSAPI, "RTAManager still in use (use_count=%d)", rtaManagerUseCount); RETURN_HR_IF_FAILED(XAsyncSchedule(data->async, 10)); return E_PENDING; } #if TRACK_ASYNC // Wait for any outstanding XAsync operations to be cleaned up. XAsync makes no guarantee // that XAsyncOp::Cleanup will be called & completed before prior to signaling to the client that the // operation has completed. This leads to a race condition where titles could be told XblCleanupAsync // has finished while we still have XAsync provider contexts that need to be cleaned up. We don't want // to always block on this since XAsync operations whose result is never consumed by the client would // stop XblCleanupAsync from ever returning. { std::lock_guard lock{ state->asyncBlocksMutex }; if (!state->asyncBlocks.empty()) { Stringstream ss; ss << "Awaiting outstanding XAsync operations:"; for (auto& pair : state->asyncBlocks) { ss << "\nXAsyncBlock[" << pair.first << "] Identity=" << pair.second; } HC_TRACE_VERBOSE(XSAPI, ss.str().data()); RETURN_HR_IF_FAILED(XAsyncSchedule(data->async, 10)); return E_PENDING; } } #endif // TRACK_ASYNC #endif // XSAPI_UNIT_TESTS // Cleanup RTA state and open connections state->m_rtaManager->Cleanup(); } // Terminate all pending/running async tasks on the global background queue. // We don't use TaskQueue::Terminate here because control will return to the client before // the Callback is cleaned up, giving them freedom to clean up custom memory hooks. auto hr = XTaskQueueTerminate(stateTaskQueueHandle, false, const_cast(data), [](void* context) { HC_TRACE_VERBOSE(XSAPI, "Cleaning up GlobalState"); auto data{ static_cast(context) }; auto state{ static_cast(data->context) }; #if TRACK_ASYNC { // Log warning for unfinished async operations std::lock_guard lock{ state->asyncBlocksMutex }; for (auto& pair : state->asyncBlocks) { HC_TRACE_WARNING(XSAPI, "Unfinished XAsyncBlock[%llu], Identity=%s", pair.first, pair.second); } } #endif // Release the leaked reference from Create state->DecRef(); // Don't call HCCleanup until after GlobalState is destroyed since some of our state // depends on HC state. auto hcCleanupContext = MakeUnique(); hcCleanupContext->xblCleanupAsyncBlock = data->async; hcCleanupContext->hcCleanupQueue = TaskQueue::DeriveWorkerQueue(data->async->queue); hcCleanupContext->hcCleanupAsyncBlock.queue = hcCleanupContext->hcCleanupQueue.GetHandle(); hcCleanupContext->hcCleanupAsyncBlock.context = hcCleanupContext.get(); hcCleanupContext->hcCleanupAsyncBlock.callback = GlobalState::HCCleanupComplete; HRESULT hr = HCCleanupAsync(&hcCleanupContext->hcCleanupAsyncBlock); if (FAILED(hr)) { HC_TRACE_ERROR_HR(XSAPI, hr, "HCCleanupAsync Failed"); XAsyncComplete(data->async, hr, 0); } hcCleanupContext.release(); }); RETURN_HR_IF_FAILED(hr); return E_PENDING; } default: { return S_OK; } } }); } void GlobalState::HCCleanupComplete(XAsyncBlock* async) noexcept { UniquePtr context{ static_cast(async->context) }; XAsyncBlock* xblCleanupAsyncBlock = context->xblCleanupAsyncBlock; HRESULT hr = XAsyncGetStatus(async, true); if (hr == E_HC_INTERNAL_STILLINUSE) { // If something else is still referencing libHttpClient, consider that a success hr = S_OK; } context.reset(); // Cleanup context before returning to caller XAsyncComplete(xblCleanupAsyncBlock, hr, 0); } std::shared_ptr GlobalState::Get() noexcept { return AccessHelper(AccessMode::GET); } std::shared_ptr GlobalState::AccessHelper( _In_ AccessMode mode, _In_opt_ std::shared_ptr state ) noexcept { static std::mutex s_mutex; static std::shared_ptr s_state{ nullptr }; std::lock_guard lock{ s_mutex }; switch (mode) { case AccessMode::GET: { assert(!state); return s_state; } case AccessMode::SET: { auto previousValue{ s_state }; s_state = state; return previousValue; } } return nullptr; } const TaskQueue& GlobalState::Queue() const noexcept { return m_taskQueue; } std::shared_ptr GlobalState::MultiplayerManager() const noexcept { return m_multiplayerManager; } std::shared_ptr GlobalState::SocialManager() const noexcept { return m_socialManager; } std::shared_ptr GlobalState::AchievementsManager() const noexcept { return m_achievementsManager; } std::shared_ptr GlobalState::RTAManager() const noexcept { return m_rtaManager; } void GlobalState::SetUserChangeHandler(uint64_t token, std::shared_ptr context) noexcept { std::lock_guard lock{ m_mutex }; m_userChangeHandlers[token] = context; } size_t GlobalState::EraseUserChangeHandler(uint64_t token) noexcept { std::lock_guard lock{ m_mutex }; return m_userChangeHandlers.erase(token); } size_t GlobalState::EraseUserExpiredToken(uint64_t xuid) noexcept { std::lock_guard lock{ m_mutex }; return m_userExpiredTokens.erase(xuid); } void GlobalState::InsertUserExpiredToken(uint64_t xuid) noexcept { std::lock_guard lock{ m_mutex }; m_userExpiredTokens.insert(xuid); } XblFunctionContext GlobalState::AddServiceCallRoutedHandler( _In_ XblCallRoutedHandler callback, _In_opt_ void* context ) noexcept { std::lock_guard lock{ m_mutex }; m_callRoutedHandlers.emplace(m_nextHandlerToken, MakeShared(callback, context)); return m_nextHandlerToken++; } void GlobalState::RemoveServiceCallRoutedHandler( _In_ XblFunctionContext token ) noexcept { std::lock_guard lock{ m_mutex }; m_callRoutedHandlers.erase(token); } std::shared_ptr GlobalState::LocalStorage() const noexcept { return m_localStorage; } std::shared_ptr GlobalState::AppConfig() const noexcept { return m_appConfig; } std::shared_ptr GlobalState::Logger() const noexcept { return m_logger; } std::shared_ptr GlobalState::GetSharedThis() { return shared_from_this(); } #if HC_PLATFORM == HC_PLATFORM_GDK XblFunctionContext GlobalState::AddAppChangeNotificationHandler( _In_ AppChangeNotificationHandler appChangeNotificationHandler ) noexcept { std::lock_guard lock{ m_mutex }; m_appChangeNotificationHandlers.emplace(m_nextAppChangeHandlerToken, appChangeNotificationHandler); return m_nextAppChangeHandlerToken++; } /* static */ void GlobalState::AppStateChangeNotificationReceived( BOOLEAN quiesced, PVOID context ) { UNREFERENCED_PARAMETER(context); auto state{ GlobalState::Get() }; if (state) { // Before moving to the handlers, we need to unlock the mutex or run into a deadlock inside // the erase_token_and_signature function. For now, copying the handlers locally and unlocking std::unique_lock lock{ state->m_mutex }; auto localAppChangeNotificationHandlers = state->m_appChangeNotificationHandlers; lock.unlock(); for (const auto& pair : localAppChangeNotificationHandlers) { auto handler = pair.second; handler(quiesced); } } } void GlobalState::RemoveAppChangeNotificationHandler( _In_ XblFunctionContext token ) noexcept { std::lock_guard lock{ m_mutex }; m_appChangeNotificationHandlers.erase(token); } #endif #if HC_PLATFORM == HC_PLATFORM_XDK const String& GlobalState::AchievementsProviderName() const noexcept { return m_achivementsEventProviderName; } const GUID& GlobalState::AchievementsSessionId() const noexcept { return m_achievementsSessionId; } #endif const String& GlobalState::Locales() const noexcept { return m_locales; } void GlobalState::OverrideLocale(const xsapi_internal_string& locale) noexcept { m_locales = utils::generate_locales(locale); } NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END ================================================ FILE: Source/Shared/global_state.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "service_call_routed_handler.h" #include "local_storage.h" #include "fault_injection.h" #if HC_PLATFORM == HC_PLATFORM_GDK #include #endif // When TRACK_ASYNC is enabled, we will monitor the progress of each XAsync operation, asserting // if tasks are started or outstanding when XblCleanup completes. By default only do async tracking // for UnitTests, but it can be enabled for debugging in other situations as well. #define TRACK_ASYNC XSAPI_UNIT_TESTS // Forward declarations NAMESPACE_MICROSOFT_XBOX_SERVICES_ACHIEVEMENTS_MANAGER_CPP_BEGIN class AchievementsManager; NAMESPACE_MICROSOFT_XBOX_SERVICES_ACHIEVEMENTS_MANAGER_CPP_END NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_MANAGER_CPP_BEGIN class MultiplayerManager; NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_MANAGER_CPP_END NAMESPACE_MICROSOFT_XBOX_SERVICES_SOCIAL_MANAGER_CPP_BEGIN class SocialManager; NAMESPACE_MICROSOFT_XBOX_SERVICES_SOCIAL_MANAGER_CPP_END NAMESPACE_MICROSOFT_XBOX_SERVICES_RTA_CPP_BEGIN class RealTimeActivityManager; NAMESPACE_MICROSOFT_XBOX_SERVICES_RTA_CPP_END NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN #if HC_PLATFORM == HC_PLATFORM_GDK typedef Function AppChangeNotificationHandler; #endif class GlobalState : public RefCounter, public std::enable_shared_from_this { public: virtual ~GlobalState() noexcept = default; static HRESULT Create(_In_ const XblInitArgs* args); static HRESULT CleanupAsync(_In_ XAsyncBlock* async) noexcept; static std::shared_ptr Get() noexcept; const TaskQueue& Queue() const noexcept; std::shared_ptr AchievementsManager() const noexcept; std::shared_ptr MultiplayerManager() const noexcept; std::shared_ptr SocialManager() const noexcept; std::shared_ptr RTAManager() const noexcept; void SetUserChangeHandler(uint64_t token, std::shared_ptr context) noexcept; size_t EraseUserChangeHandler(uint64_t token) noexcept; size_t EraseUserExpiredToken(uint64_t xuid) noexcept; void InsertUserExpiredToken(uint64_t xuid) noexcept; XblFunctionContext AddServiceCallRoutedHandler( _In_ XblCallRoutedHandler handler, _In_opt_ void* context ) noexcept; void RemoveServiceCallRoutedHandler( _In_ XblFunctionContext token ) noexcept; std::shared_ptr LocalStorage() const noexcept; std::shared_ptr AppConfig() const noexcept; std::shared_ptr Logger() const noexcept; #if HC_PLATFORM == HC_PLATFORM_GDK XblFunctionContext AddAppChangeNotificationHandler( _In_ AppChangeNotificationHandler routine ) noexcept; void RemoveAppChangeNotificationHandler( _In_ XblFunctionContext token ) noexcept; #endif #if HC_PLATFORM == HC_PLATFORM_XDK // On XDK, offline achievements are driven by ETX. // The AchievementsService will create one ETX provider per XSAPI session (when needed) using these properties. const String& AchievementsProviderName() const noexcept; const GUID& AchievementsSessionId() const noexcept; #endif const String& Locales() const noexcept; void OverrideLocale(const xsapi_internal_string& locales) noexcept; // API Type to be used in HTTP requests so they are identifiable in traces. // TODO consider configuring this with XblInitArgs XblApiType ApiType{ XblApiType::XblCApi }; #if TRACK_ASYNC std::mutex asyncBlocksMutex{}; UnorderedMap asyncBlocks{}; #endif private: GlobalState(_In_ const XblInitArgs* args); GlobalState(const GlobalState&) = delete; GlobalState& operator=(const GlobalState&) = delete; static void CALLBACK HCCleanupComplete(XAsyncBlock* async) noexcept; mutable std::mutex m_mutex; TaskQueue m_taskQueue{ nullptr }; std::shared_ptr m_achievementsManager; std::shared_ptr m_multiplayerManager; std::shared_ptr m_socialManager; std::shared_ptr m_rtaManager; std::shared_ptr m_localStorage; Set m_userExpiredTokens; UnorderedMap> m_userChangeHandlers; XblFunctionContext m_nextHandlerToken{ 1 }; UnorderedMap> m_callRoutedHandlers; String m_locales{ "en-US" }; // from Shared\xbox_live_app_config.cpp const std::shared_ptr m_appConfig; // from Shared\Logger\log.cpp const std::shared_ptr m_logger; #if HC_PLATFORM == HC_PLATFORM_XDK String m_achivementsEventProviderName; GUID m_achievementsSessionId{}; #endif #if HC_PLATFORM == HC_PLATFORM_GDK XblFunctionContext m_nextAppChangeHandlerToken{ 1 }; xsapi_internal_unordered_map m_appChangeNotificationHandlers; #endif enum class AccessMode { GET, SET, }; // In order to avoid uncertainty of global static initialization order, keep the static state pointer to a function scope static std::shared_ptr AccessHelper( _In_ AccessMode mode, _In_opt_ std::shared_ptr state = nullptr ) noexcept; // RefCounter std::shared_ptr GetSharedThis() override; #if HC_PLATFORM == HC_PLATFORM_GDK // Holds the registration ID for receiving App State Notifications (aka Quick Resume) PAPPSTATE_REGISTRATION m_registrationID; static void AppStateChangeNotificationReceived( BOOLEAN quiesced, PVOID context ); #endif }; NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END ================================================ FILE: Source/Shared/http_call_api.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "xsapi_utils.h" #include "xbox_live_context_internal.h" #include "xsapi-c/http_call_c.h" using namespace xbox::services; using namespace xbox::services::system; // Http APIs STDAPI XblHttpCallCreate( _In_ XblContextHandle xblContext, _In_z_ const char* method, _In_z_ const char* url, _Out_ XblHttpCallHandle* call ) XBL_NOEXCEPT try { VERIFY_XBL_INITIALIZED(); auto userResult = xblContext->User().Copy(); RETURN_HR_IF_FAILED(userResult.Hresult()); auto httpCall = MakeShared(userResult.ExtractPayload()); HRESULT hr = httpCall->Init(xblContext->Settings(), method, url, xbox_live_api::unspecified); if (SUCCEEDED(hr)) { httpCall->AddRef(); *call = httpCall.get(); } return hr; } CATCH_RETURN() STDAPI XblHttpCallPerformAsync( _In_ XblHttpCallHandle call, _In_ XblHttpCallResponseBodyType type, _Inout_ XAsyncBlock* asyncBlock ) XBL_NOEXCEPT try { UNREFERENCED_PARAMETER(type); RETURN_HR_INVALIDARGUMENT_IF_NULL(call); VERIFY_XBL_INITIALIZED(); return RunAsync(asyncBlock, __FUNCTION__, [ call{ std::dynamic_pointer_cast(call->shared_from_this()) } ] (XAsyncOp op, const XAsyncProviderData* data) { switch (op) { case XAsyncOp::DoWork: { RETURN_HR_IF_FAILED(call->Perform(AsyncContext{ TaskQueue::DeriveWorkerQueue(data->async->queue), [data](HttpResult httpResult) { auto hr = httpResult.Hresult(); if (SUCCEEDED(hr)) { hr = httpResult.Payload()->Result(); } XAsyncComplete(data->async, hr, 0); } })); return E_PENDING; } default: { return S_OK; } } }); } CATCH_RETURN() STDAPI XblHttpCallDuplicateHandle( _In_ XblHttpCallHandle call, _Out_ XblHttpCallHandle* duplicatedHandle ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF(call == nullptr || duplicatedHandle == nullptr); call->AddRef(); *duplicatedHandle = call; return S_OK; } CATCH_RETURN() STDAPI_(void) XblHttpCallCloseHandle( _In_ XblHttpCallHandle call ) XBL_NOEXCEPT try { if (call) { call->DecRef(); } return; } CATCH_RETURN_WITH(;) STDAPI XblHttpCallSetTracing( _In_ XblHttpCallHandle call, _In_ bool traceCall ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(call); return call->SetTracing(traceCall); } CATCH_RETURN() STDAPI XblHttpCallGetRequestUrl( _In_ XblHttpCallHandle call, _Out_ const char** url ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(call); RETURN_HR_INVALIDARGUMENT_IF_NULL(url); return call->GetRequestUrl(url); } CATCH_RETURN() // HttpCallRequest Set APIs STDAPI XblHttpCallRequestSetRequestBodyBytes( _In_ XblHttpCallHandle call, _In_reads_bytes_(requestBodySize) const uint8_t* requestBodyBytes, _In_ uint32_t requestBodySize ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(call); return call->SetRequestBody(requestBodyBytes, requestBodySize); } CATCH_RETURN() STDAPI XblHttpCallRequestSetRequestBodyString( _In_ XblHttpCallHandle call, _In_z_ const char* requestBodyString ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(call); return call->SetRequestBody(requestBodyString); } CATCH_RETURN() STDAPI XblHttpCallRequestSetHeader( _In_ XblHttpCallHandle call, _In_z_ const char* headerName, _In_z_ const char* headerValue, _In_ bool allowTracing ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(call); RETURN_HR_INVALIDARGUMENT_IF_NULL(headerName); RETURN_HR_INVALIDARGUMENT_IF_NULL(headerValue); const xsapi_internal_string headerNameString{ headerName }; const xsapi_internal_string headerValueString{ headerValue }; return call->SetHeader(headerNameString, headerValueString, allowTracing); } CATCH_RETURN() STDAPI XblHttpCallRequestSetRetryAllowed( _In_ XblHttpCallHandle call, _In_ bool retryAllowed ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(call); return call->SetRetryAllowed(retryAllowed); } CATCH_RETURN() STDAPI XblHttpCallRequestSetRetryCacheId( _In_ XblHttpCallHandle call, _In_ uint32_t retryAfterCacheId ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(call); // values 1-1000 are reserved for XSAPI if (retryAfterCacheId <= 1000) { return E_INVALIDARG; } return call->SetRetryCacheId(retryAfterCacheId); } CATCH_RETURN() STDAPI XblHttpCallRequestSetLongHttpCall( _In_ XblHttpCallHandle call, _In_ bool longHttpCall ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(call); call->SetLongHttpCall(longHttpCall); return S_OK; } CATCH_RETURN() // HttpCallResponse Get APIs STDAPI XblHttpCallGetResponseString( _In_ XblHttpCallHandle call, _Out_ const char** responseString ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(call); return call->GetResponseString(responseString); } CATCH_RETURN() STDAPI XblHttpCallGetResponseBodyBytesSize( _In_ XblHttpCallHandle call, _Out_ size_t* bufferSize ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(call); return call->GetResponseBodyBytesSize(bufferSize); } CATCH_RETURN() STDAPI XblHttpCallGetResponseBodyBytes( _In_ XblHttpCallHandle call, _In_ size_t bufferSize, _Out_writes_bytes_to_(bufferSize, *bufferUsed) uint8_t* buffer, _Out_opt_ size_t* bufferUsed ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(call); return call->GetResponseBodyBytes(bufferSize, buffer, bufferUsed); } CATCH_RETURN() STDAPI XblHttpCallGetStatusCode( _In_ XblHttpCallHandle call, _Out_ uint32_t* statusCode ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(call); RETURN_HR_INVALIDARGUMENT_IF_NULL(statusCode); *statusCode = call->HttpStatus(); return S_OK; } CATCH_RETURN() STDAPI XblHttpCallGetNetworkErrorCode( _In_ XblHttpCallHandle call, _Out_ HRESULT* networkErrorCode, _Out_ uint32_t* platformNetworkErrorCode ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(call); return call->GetNetworkErrorCode(networkErrorCode, platformNetworkErrorCode); } CATCH_RETURN() STDAPI XblHttpCallGetPlatformNetworkErrorMessage( _In_ XblHttpCallHandle call, _Out_ const char** platformNetworkErrorMessage ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(call); return call->GetPlatformNetworkErrorMessage(platformNetworkErrorMessage); } CATCH_RETURN() STDAPI XblHttpCallGetHeader( _In_ XblHttpCallHandle call, _In_z_ const char* headerName, _Out_ const char** headerValue ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(call); return call->ResponseGetHeader(headerName, headerValue); } CATCH_RETURN() STDAPI XblHttpCallGetNumHeaders( _In_ XblHttpCallHandle call, _Out_ uint32_t* numHeaders ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(call); return call->ResponseGetNumHeaders(numHeaders); } CATCH_RETURN() STDAPI XblHttpCallGetHeaderAtIndex( _In_ XblHttpCallHandle call, _In_ uint32_t headerIndex, _Out_ const char** headerName, _Out_ const char** headerValue ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(call); return call->ResponseGetHeaderAtIndex(headerIndex, headerName, headerValue); } CATCH_RETURN() ================================================ FILE: Source/Shared/http_call_legacy.cpp ================================================ #include "pch.h" #include "http_call_legacy.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN namespace legacy { http_call_response::http_call_response( _In_ XblHttpCallHandle call, _In_ XblHttpCallResponseBodyType type ) { XblHttpCallDuplicateHandle(call, &m_handle); m_bodyType = static_cast(type); } http_call_response_body_type http_call_response::body_type() const { return m_bodyType; } string_t http_call_response::response_body_string() { if (m_responseBodyString != string_t()) { return m_responseBodyString; } const char* responseString; HRESULT hr = XblHttpCallGetResponseString(m_handle, &responseString); if (FAILED(hr)) { return string_t(); } m_responseBodyString = StringTFromUtf8(responseString); return m_responseBodyString; } web::json::value http_call_response::response_body_json() { auto str = response_body_string(); m_responseBodyJson = web::json::value::parse(str); return m_responseBodyJson; } std::vector http_call_response::response_body_vector() { if (!m_responseBodyVector.empty()) { return m_responseBodyVector; } size_t bufferSize; HRESULT hr = XblHttpCallGetResponseBodyBytesSize(m_handle, &bufferSize); if (FAILED(hr)) { return {}; } uint8_t* buffer = new uint8_t[bufferSize]; size_t bufferUsed; hr = XblHttpCallGetResponseBodyBytes(m_handle, bufferSize, buffer, &bufferUsed); if (FAILED(hr)) { return {}; } auto bufferInput = std::vector(buffer, buffer + bufferUsed); m_responseBodyVector = std::vector(bufferUsed); std::transform(bufferInput.begin(), bufferInput.end(), m_responseBodyVector.begin(), [](uint8_t c) { return static_cast(c); }); return m_responseBodyVector; } web::http::http_headers http_call_response::response_headers() { if (!m_responseHeaders.empty()) { return m_responseHeaders; } uint32_t numHeader; HRESULT hr = XblHttpCallGetNumHeaders(m_handle, &numHeader); if (FAILED(hr)) { return web::http::http_headers(); } const char* headerName; const char* headerValue; auto headers = web::http::http_headers(); for (auto i = 0u; i < numHeader; i++) { hr = XblHttpCallGetHeaderAtIndex(m_handle, i, &headerName, &headerValue); if (SUCCEEDED(hr)) { headers.add(StringTFromUtf8(headerName), StringTFromUtf8(headerValue)); } } m_responseHeaders = headers; return headers; } uint32_t http_call_response::http_status() { uint32_t statusCode; HRESULT hr = XblHttpCallGetStatusCode(m_handle, &statusCode); if (FAILED(hr)) { return 0; } m_httpStatus = statusCode; return m_httpStatus; } std::error_code http_call_response::err_code() { if (m_errorCode == std::error_code()) { return m_errorCode; } HRESULT networkErrorCode; uint32_t platformNetworkErrorCode; HRESULT hr = XblHttpCallGetNetworkErrorCode(m_handle, &networkErrorCode, &platformNetworkErrorCode); if (FAILED(hr)) { return make_error_code(ConvertHrToXblErrorCode(hr)); } m_errorCode = make_error_code(ConvertHrToXblErrorCode(networkErrorCode)); return m_errorCode; } std::string http_call_response::err_message() { if (m_errMessage != std::string()) { return m_errMessage; } const char* errorMessage; HRESULT hr = XblHttpCallGetPlatformNetworkErrorMessage(m_handle, &errorMessage); if (FAILED(hr)) { return std::string(); } m_errMessage = std::string(errorMessage); return m_errMessage; } string_t http_call_response::e_tag() const { // return default value because there is no matching method return string_t(); } string_t http_call_response::response_date() const { // return default value because there is no matching method return string_t(); } std::chrono::seconds http_call_response::retry_after() const { // return default value because there is no matching method return std::chrono::seconds(); } http_call_response::http_call_response(const http_call_response& other) : m_bodyType(other.m_bodyType), m_responseBodyString(other.m_responseBodyString), m_responseBodyJson(other.m_responseBodyJson), m_responseBodyVector(other.m_responseBodyVector), m_responseHeaders(other.m_responseHeaders), m_httpStatus(other.m_httpStatus), m_errorCode(other.m_errorCode), m_errMessage(other.m_errMessage) { XblHttpCallDuplicateHandle(other.m_handle, &m_handle); } http_call_response& http_call_response::operator=(http_call_response other) { std::swap(m_handle, other.m_handle); m_responseBodyString = other.m_responseBodyString; m_responseBodyJson = other.m_responseBodyJson; m_responseBodyVector = other.m_responseBodyVector; m_httpStatus = other.m_httpStatus; m_responseHeaders = other.m_responseHeaders; m_errorCode = other.m_errorCode; m_errMessage = other.m_errMessage; return *this; } http_call_response::~http_call_response() { XblHttpCallCloseHandle(m_handle); } http_call::http_call( _In_ XblHttpCallHandle callHandle, _In_ string_t httpMethod, _In_ string_t serverName, _In_ web::uri pathQueryFragment ) : m_callHandle(std::move(callHandle)), m_httpMethod(std::move(httpMethod)), m_serverName(std::move(serverName)), m_pathQueryFragment(std::move(pathQueryFragment)) { } #if !XSAPI_NO_PPL pplx::task> http_call::get_response_with_auth( _In_ http_call_response_body_type httpCallResponseBodyType ) { XblHttpCallHandle xblHttpCall = m_callHandle; auto asyncWrapper = new AsyncWrapper>( [xblHttpCall, httpCallResponseBodyType]( _In_ XAsyncBlock* async, _In_ std::shared_ptr& result) { HRESULT hr = XAsyncGetStatus(async, false); if (SUCCEEDED(hr)) { result = std::make_shared(xblHttpCall, static_cast(httpCallResponseBodyType)); } return hr; }); auto hr = XblHttpCallPerformAsync( m_callHandle, static_cast(httpCallResponseBodyType), &asyncWrapper->async ); pplx::task_completion_event> taskCompletionEvent; auto response = asyncWrapper->Task(hr).then([taskCompletionEvent](xbl_result> result) { taskCompletionEvent.set(result.payload()); }); return pplx::task>(taskCompletionEvent); } pplx::task> http_call::get_response_with_auth( _In_ XalUserHandle user, _In_ http_call_response_body_type httpCallResponseBodyType, _In_ bool allUsersAuthRequired ) { UNREFERENCED_PARAMETER(user); UNREFERENCED_PARAMETER(allUsersAuthRequired); auto xblHttpCall = m_callHandle; auto asyncWrapper = new AsyncWrapper>( [xblHttpCall, httpCallResponseBodyType](XAsyncBlock* async, std::shared_ptr& response) { HRESULT hr = XAsyncGetStatus(async, false); if (SUCCEEDED(hr)) { response = std::make_shared(xblHttpCall, static_cast(httpCallResponseBodyType)); } return hr; }); auto hr = XblHttpCallPerformAsync( m_callHandle, static_cast(httpCallResponseBodyType), &asyncWrapper->async ); pplx::task_completion_event> taskCompletionEvent; auto response = asyncWrapper->Task(hr).then([taskCompletionEvent](xbl_result> result) { taskCompletionEvent.set(result.payload()); }); return pplx::task>(taskCompletionEvent); } pplx::task> http_call::get_response( _In_ http_call_response_body_type httpCallResponseBodyType ) { // no matching function UNREFERENCED_PARAMETER(httpCallResponseBodyType); return pplx::task>(); } #endif // !XSAPI_NO_PPL void http_call::set_request_body( _In_ const string_t& value ) { XblHttpCallRequestSetRequestBodyString(m_callHandle, StringFromStringT(value).c_str()); } void http_call::set_request_body( _In_ const rapidjson::Value& value ) { XblHttpCallRequestSetRequestBodyString(m_callHandle, SerializeJson(value).c_str()); } void http_call::set_request_body( _In_ const std::vector& value ) { uint8_t* buffer{ nullptr }; auto neededSize = value.size(); std::copy(value.begin(), value.end(), buffer); if (buffer == nullptr) { return; } XblHttpCallRequestSetRequestBodyBytes(m_callHandle, buffer, static_cast(neededSize)); } void http_call::set_custom_header( _In_ const string_t& Name, _In_ const string_t& Value ) { XblHttpCallRequestSetHeader( m_callHandle, StringFromStringT(Name).c_str(), StringFromStringT(Value).c_str(), false ); } void http_call::set_long_http_call( _In_ bool value ) { XblHttpCallRequestSetLongHttpCall(m_callHandle, value); m_longHttpCall = value; } bool http_call::long_http_call() const { return m_longHttpCall; } void http_call::set_retry_allowed( _In_ bool value ) { XblHttpCallRequestSetLongHttpCall(m_callHandle, value); m_retryAllowed = value; } bool http_call::retry_allowed() const { return m_retryAllowed; } void http_call::set_content_type_header_value( _In_ const string_t& value ) { XblHttpCallRequestSetHeader(m_callHandle, "Content-Type", StringFromStringT(value).c_str(), true); } string_t http_call::content_type_header_value() const { //no matching method return string_t(); } void http_call::set_xbox_contract_version_header_value( _In_ const string_t& value ) { XblHttpCallRequestSetHeader(m_callHandle, "x-xbl-contract-version", StringFromStringT(value).c_str(), true); } string_t http_call::xbox_contract_version_header_value() const { //no matching method return string_t(); } string_t http_call::server_name() const { return m_serverName; } const web::uri& http_call::path_query_fragment() const { return m_pathQueryFragment; } string_t http_call::http_method() const { return m_httpMethod; } void http_call::set_add_default_headers( _In_ bool value ) { UNREFERENCED_PARAMETER(value); //no matching method } bool http_call::add_default_headers() const { //no matching method return false; } http_call::~http_call() { XblHttpCallCloseHandle(m_callHandle); } } NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END ================================================ FILE: Source/Shared/http_call_legacy.h ================================================ #pragma once #include "xsapi-c/http_call_c.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN namespace legacy { /// /// Enumerates the type of structured data contained in the http response body. /// enum class http_call_response_body_type { /// /// The response body consists of a string. /// string_body, /// /// The response body consists of a vector of bytes. /// vector_body, /// /// The response body consists of a JavaScript Object Notation (JSON) object. /// json_body }; /// /// Represents an http response from the Xbox Live service. /// class http_call_response { public: #ifndef DEFAULT_MOVE_ENABLED http_call_response(http_call_response&& other); http_call_response& operator=(http_call_response&& other); #endif http_call_response( _In_ XblHttpCallHandle call, _In_ XblHttpCallResponseBodyType type ); /// /// Gets the body type of the response. /// http_call_response_body_type body_type() const; /// /// Gets the response body of the response as a string. /// string_t response_body_string(); /// /// Gets the response body of the response as a JSON value. /// web::json::value response_body_json(); /// /// Gets the response body of the response as a byte vector. /// std::vector response_body_vector(); /// /// Gets the http headers of the response. /// web::http::http_headers response_headers(); /// /// Gets the http status of the response. /// uint32_t http_status(); /// /// Gets the error code of the response. /// std::error_code err_code(); /// /// Gets the error message of the response. /// std::string err_message(); /// /// Gets the eTag of the response. /// string_t e_tag() const; /// /// Gets the response date of the response. /// string_t response_date() const; /// /// Gets the "retry after" value found in the response. /// std::chrono::seconds retry_after() const; http_call_response(const http_call_response&); http_call_response& operator=(http_call_response other); ~http_call_response(); private: XblHttpCallHandle m_handle; http_call_response_body_type m_bodyType; string_t m_responseBodyString; web::json::value m_responseBodyJson; std::vector m_responseBodyVector; web::http::http_headers m_responseHeaders; uint32_t m_httpStatus{ 0 }; std::error_code m_errorCode; std::string m_errMessage; http_call_response() = delete; }; class http_call { public: http_call( _In_ XblHttpCallHandle callHandle, _In_ string_t httpMethod, _In_ string_t serverName, _In_ web::uri pathQueryFragment ); #if !XSAPI_NO_PPL /// /// Attach the Xbox Live token, sign the request, send the request to the service, and return the response. /// pplx::task> get_response_with_auth( _In_ http_call_response_body_type httpCallResponseBodyType = http_call_response_body_type::json_body ); pplx::task> get_response_with_auth( _In_ XalUserHandle user, _In_ http_call_response_body_type httpCallResponseBodyType = http_call_response_body_type::json_body, _In_ bool allUsersAuthRequired = false ); /// /// Send the request without authentication and get the response. /// pplx::task< std::shared_ptr > get_response( _In_ http_call_response_body_type httpCallResponseBodyType ); #endif // !XSAPI_NO_PPL /// /// Sets the request body using a string. /// void set_request_body(_In_ const string_t& value); /// /// Sets the request body using a JSON value. /// void set_request_body(_In_ const rapidjson::Value& value); /// /// Sets the request body using a byte array value. /// void set_request_body(_In_ const std::vector& value); /// /// Sets a custom header. /// void set_custom_header( _In_ const string_t& headerName, _In_ const string_t& headerValue); /// /// Sets if this is a long http call, and should use the long_http_timeout setting. /// void set_long_http_call(_In_ bool value); /// /// Gets if this is a long http call, and should use the long_http_timeout setting. /// bool long_http_call() const; /// /// Sets if retry is allowed during this call. /// void set_retry_allowed(_In_ bool value); /// /// Get if retry is allowed during this call. /// bool retry_allowed() const; /// /// Sets the content type header value for this call. /// void set_content_type_header_value(_In_ const string_t& value); /// /// Gets the content type header value for this call. /// string_t content_type_header_value() const; /// /// Sets the Xbox Live contract version header value for this call. /// void set_xbox_contract_version_header_value(_In_ const string_t& value); /// /// Gets the Xbox Live contract version header value for this call. /// string_t xbox_contract_version_header_value() const; /// /// Gets the server name for this call. /// string_t server_name() const; /// /// Gets the path for this call. /// const web::uri& path_query_fragment() const; /// /// Gets the http method for this call. /// string_t http_method() const; /// /// Sets a flag indicating if default headers should be added or not. /// void set_add_default_headers(_In_ bool value); /// /// Internal function /// bool add_default_headers() const; ~http_call(); private: XblHttpCallHandle m_callHandle; string_t m_httpMethod; string_t m_serverName; web::uri m_pathQueryFragment; bool m_longHttpCall{ false }; bool m_retryAllowed{ false }; }; } NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END ================================================ FILE: Source/Shared/http_call_request_message.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "http_call_request_message_internal.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN namespace legacy { http_call_request_message::http_call_request_message() : m_httpRequestMessageType(http_request_message_type::empty_message) { } http_call_request_message::http_call_request_message( _In_ const http_call_request_message_internal *internalObj ) : m_httpRequestMessageType(internalObj->get_http_request_message_type()) { m_requestMessageString = internalObj->request_message_string(); m_requestMessageVector = internalObj->request_message_vector(); } const xsapi_internal_string& http_call_request_message::request_message_string() const { return m_requestMessageString; } const xsapi_internal_vector& http_call_request_message::request_message_vector() const { return m_requestMessageVector; } http_request_message_type http_call_request_message::get_http_request_message_type() const { return m_httpRequestMessageType; } http_call_request_message_internal::http_call_request_message_internal() : m_httpRequestMessageType(http_request_message_type::empty_message) { } http_call_request_message_internal::http_call_request_message_internal( _In_ xsapi_internal_string messageString ) : m_requestMessageString(std::move(messageString)), m_httpRequestMessageType(http_request_message_type::string_message) { } http_call_request_message_internal::http_call_request_message_internal( _In_ xsapi_internal_vector messageVector ) : m_requestMessageVector(std::move(messageVector)), m_httpRequestMessageType(http_request_message_type::vector_message) { } const xsapi_internal_string& http_call_request_message_internal::request_message_string() const { return m_requestMessageString; } const xsapi_internal_vector& http_call_request_message_internal::request_message_vector() const { return m_requestMessageVector; } http_request_message_type http_call_request_message_internal::get_http_request_message_type() const { return m_httpRequestMessageType; } } NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END ================================================ FILE: Source/Shared/http_call_request_message_internal.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN namespace legacy { /// /// Enumerates the type of data contained in the http request body. /// enum http_request_message_type { /// /// No message. /// empty_message, /// /// The message is of type string. /// string_message, /// /// The message is of type vector, and acts as a memory buffer. /// vector_message }; class http_call_request_message_internal { public: http_call_request_message_internal(); http_call_request_message_internal(_In_ xsapi_internal_string messageString); http_call_request_message_internal(_In_ xsapi_internal_vector messageVector); _XSAPIIMP const xsapi_internal_string& request_message_string() const; _XSAPIIMP const xsapi_internal_vector& request_message_vector() const; _XSAPIIMP http_request_message_type get_http_request_message_type() const; private: xsapi_internal_vector m_requestMessageVector; xsapi_internal_string m_requestMessageString; http_request_message_type m_httpRequestMessageType; }; /// /// Represents an http request message. /// class http_call_request_message { public: /// /// Internal function /// inline http_call_request_message(); /// /// Internal function /// inline http_call_request_message(_In_ const http_call_request_message_internal* internalObj); /// /// The http request message if it is a string type. /// inline _XSAPIIMP const xsapi_internal_string& request_message_string() const; /// /// The http request message if it is a buffer. /// inline _XSAPIIMP const xsapi_internal_vector& request_message_vector() const; /// /// The type of message. /// inline _XSAPIIMP http_request_message_type get_http_request_message_type() const; private: xsapi_internal_vector m_requestMessageVector; xsapi_internal_string m_requestMessageString; http_request_message_type m_httpRequestMessageType; }; } NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END ================================================ FILE: Source/Shared/http_call_wrapper_internal.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "xbox_live_context_internal.h" #include using namespace xbox::services; #define DEFAULT_USER_AGENT "XboxServicesAPI/" XBOX_SERVICES_API_VERSION_STRING NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN HttpCall::~HttpCall() { if (m_callHandle) { HCHttpCallCloseHandle(m_callHandle); } } HRESULT HttpCall::Init( _In_ const xsapi_internal_string& httpMethod, _In_ const xsapi_internal_string& fullUrl ) { assert(m_step == Step::Uninitialized); RETURN_HR_IF_FAILED( HCHttpCallCreate(&m_callHandle) ); m_step = Step::Pending; RETURN_HR_IF_FAILED( HCHttpCallRequestSetUrl(m_callHandle, httpMethod.data(), fullUrl.data()) ); // Add default User-Agent header xsapi_internal_string userAgent = DEFAULT_USER_AGENT; return SetHeader(USER_AGENT_HEADER, userAgent); } HRESULT HttpCall::SetHeader( _In_ const xsapi_internal_string& key, _In_ const xsapi_internal_string& value, _In_ bool allowTracing ) { assert(m_step == Step::Pending); return HCHttpCallRequestSetHeader(m_callHandle, key.data(), value.data(), allowTracing); } HRESULT HttpCall::SetRequestBody(const xsapi_internal_vector& bytes) { assert(m_step == Step::Pending); return HCHttpCallRequestSetRequestBodyBytes(m_callHandle, bytes.data(), static_cast(bytes.size())); } HRESULT HttpCall::SetRequestBody(const xsapi_internal_string& bodyString) { assert(m_step == Step::Pending); return HCHttpCallRequestSetRequestBodyString(m_callHandle, bodyString.data()); } HRESULT HttpCall::SetRequestBody(const JsonValue& bodyJson) { assert(m_step == Step::Pending); return SetRequestBody(JsonUtils::SerializeJson(bodyJson)); } HRESULT HttpCall::SetRetryAllowed(bool retryAllowed) { assert(m_step == Step::Pending); return HCHttpCallRequestSetRetryAllowed(m_callHandle, retryAllowed); } HRESULT HttpCall::SetRetryCacheId(uint32_t retryAfterCacheId) { assert(m_step == Step::Pending); return HCHttpCallRequestSetRetryCacheId(m_callHandle, retryAfterCacheId); } HRESULT HttpCall::SetRetryDelay(uint32_t retryDelayInSeconds) { assert(m_step == Step::Pending); return HCHttpCallRequestSetRetryDelay(m_callHandle, retryDelayInSeconds); } HRESULT HttpCall::SetTimeout(uint32_t timeoutInSeconds) { assert(m_step == Step::Pending); return HCHttpCallRequestSetTimeout(m_callHandle, timeoutInSeconds); } HRESULT HttpCall::SetTimeoutWindow(uint32_t timeoutWindowInSeconds) { assert(m_step == Step::Pending); return HCHttpCallRequestSetTimeoutWindow(m_callHandle, timeoutWindowInSeconds); } HRESULT HttpCall::Perform( AsyncContext async, bool forceRefresh ) { UNREFERENCED_PARAMETER(forceRefresh); assert(m_step == Step::Pending); m_asyncContext = std::move(async); m_asyncBlock.queue = m_asyncContext.Queue().GetHandle(); m_asyncBlock.context = this; m_asyncBlock.callback = &HttpCall::CompletionCallback; HRESULT hr = S_OK; if (m_performAlreadyCalled) { hr = CopyHttpCallHandle(); } if (SUCCEEDED(hr)) { m_performAlreadyCalled = true; m_step = Step::Running; if (XblShouldFaultInject(INJECTION_FEATURE_HTTP)) { LOGS_ERROR << "FAULT INJECTION: HttpCall::Perform ID:" << XblGetFaultCounter(); hr = E_FAIL; } else { hr = HCHttpCallPerformAsync(m_callHandle, &m_asyncBlock); } if (SUCCEEDED(hr)) { AddRef(); // Keep HttpCall object alive until call completes } else { m_step = Step::Done; } } return hr; } struct RAIIHttpCallHandle { public: ~RAIIHttpCallHandle() { if(h) HCHttpCallCloseHandle(h); } HCCallHandle h{ nullptr }; }; HRESULT HttpCall::CopyHttpCallHandle() { RAIIHttpCallHandle newCallHandle; RETURN_HR_IF_FAILED(HCHttpCallCreate(&newCallHandle.h)); const char* method{ nullptr }; const char* url{ nullptr }; RETURN_HR_IF_FAILED(HCHttpCallRequestGetUrl(m_callHandle, &method, &url)); RETURN_HR_IF_FAILED(HCHttpCallRequestSetUrl(newCallHandle.h, method, url)); bool retryAllowed{ false }; RETURN_HR_IF_FAILED(HCHttpCallRequestGetRetryAllowed(m_callHandle, &retryAllowed)); RETURN_HR_IF_FAILED(HCHttpCallRequestSetRetryAllowed(newCallHandle.h, retryAllowed)); uint32_t retryAfterCacheId{ 0 }; RETURN_HR_IF_FAILED(HCHttpCallRequestGetRetryCacheId(m_callHandle, &retryAfterCacheId)); RETURN_HR_IF_FAILED(HCHttpCallRequestSetRetryCacheId(newCallHandle.h, retryAfterCacheId)); uint32_t retryDelayInSeconds{ 0 }; RETURN_HR_IF_FAILED(HCHttpCallRequestGetRetryDelay(m_callHandle, &retryDelayInSeconds)); RETURN_HR_IF_FAILED(HCHttpCallRequestSetRetryDelay(newCallHandle.h, retryDelayInSeconds)); uint32_t timeoutInSeconds{ 0 }; RETURN_HR_IF_FAILED(HCHttpCallRequestGetTimeout(m_callHandle, &timeoutInSeconds)); RETURN_HR_IF_FAILED(HCHttpCallRequestSetTimeout(newCallHandle.h, timeoutInSeconds)); uint32_t timeoutWindowInSeconds{ 0 }; RETURN_HR_IF_FAILED(HCHttpCallRequestGetTimeoutWindow(m_callHandle, &timeoutWindowInSeconds)); RETURN_HR_IF_FAILED(HCHttpCallRequestSetTimeoutWindow(newCallHandle.h, timeoutWindowInSeconds)); uint32_t headerCount{ 0 }; RETURN_HR_IF_FAILED(HCHttpCallRequestGetNumHeaders(m_callHandle, &headerCount)); for (uint32_t i = 0; i < headerCount; ++i) { const char* headerName{ nullptr }; const char* headerValue{ nullptr }; RETURN_HR_IF_FAILED(HCHttpCallRequestGetHeaderAtIndex(m_callHandle, i, &headerName, &headerValue)); RETURN_HR_IF_FAILED(HCHttpCallRequestSetHeader(newCallHandle.h, headerName, headerValue, false)); } const uint8_t* requestBodyBytes{ nullptr }; uint32_t requestBodySize{ 0 }; RETURN_HR_IF_FAILED(HCHttpCallRequestGetRequestBodyBytes(m_callHandle, &requestBodyBytes, &requestBodySize)); if (requestBodyBytes != nullptr && requestBodySize > 0) { RETURN_HR_IF_FAILED(HCHttpCallRequestSetRequestBodyBytes(newCallHandle.h, requestBodyBytes, requestBodySize)); } HCHttpCallCloseHandle(m_callHandle); m_callHandle = HCHttpCallDuplicateHandle(newCallHandle.h); return S_OK; } HRESULT xbox::services::HttpCall::ResetAndCopyForRetry() { HRESULT hr = CopyHttpCallHandle(); if (SUCCEEDED(hr)) { m_step = Step::Pending; m_performAlreadyCalled = false; } return hr; } HRESULT HttpCall::ConvertHttpStatusToHRESULT(_In_ uint32_t httpStatusCode) { xbox::services::xbl_error_code errCode = static_cast(httpStatusCode); HRESULT hr = HTTP_E_STATUS_UNEXPECTED; // 2xx are http success codes if ((httpStatusCode >= 200) && (httpStatusCode < 300)) { hr = S_OK; } // MSXML XHR bug: get_status() returns HTTP/1223 for HTTP/204: // http://blogs.msdn.com/b/ieinternals/archive/2009/07/23/the-ie8-native-xmlhttprequest-object.aspx // treat it as success code as well else if (httpStatusCode == 1223) { hr = S_OK; } else { switch (errCode) { case xbl_error_code::http_status_300_multiple_choices: hr = HTTP_E_STATUS_AMBIGUOUS; break; case xbl_error_code::http_status_301_moved_permanently: hr = HTTP_E_STATUS_MOVED; break; case xbl_error_code::http_status_302_found: hr = HTTP_E_STATUS_REDIRECT; break; case xbl_error_code::http_status_303_see_other: hr = HTTP_E_STATUS_REDIRECT_METHOD; break; case xbl_error_code::http_status_304_not_modified: hr = HTTP_E_STATUS_NOT_MODIFIED; break; case xbl_error_code::http_status_305_use_proxy: hr = HTTP_E_STATUS_USE_PROXY; break; case xbl_error_code::http_status_307_temporary_redirect: hr = HTTP_E_STATUS_REDIRECT_KEEP_VERB; break; case xbl_error_code::http_status_400_bad_request: hr = HTTP_E_STATUS_BAD_REQUEST; break; case xbl_error_code::http_status_401_unauthorized: hr = HTTP_E_STATUS_DENIED; break; case xbl_error_code::http_status_402_payment_required: hr = HTTP_E_STATUS_PAYMENT_REQ; break; case xbl_error_code::http_status_403_forbidden: hr = HTTP_E_STATUS_FORBIDDEN; break; case xbl_error_code::http_status_404_not_found: hr = HTTP_E_STATUS_NOT_FOUND; break; case xbl_error_code::http_status_405_method_not_allowed: hr = HTTP_E_STATUS_BAD_METHOD; break; case xbl_error_code::http_status_406_not_acceptable: hr = HTTP_E_STATUS_NONE_ACCEPTABLE; break; case xbl_error_code::http_status_407_proxy_authentication_required: hr = HTTP_E_STATUS_PROXY_AUTH_REQ; break; case xbl_error_code::http_status_408_request_timeout: hr = HTTP_E_STATUS_REQUEST_TIMEOUT; break; case xbl_error_code::http_status_409_conflict: hr = HTTP_E_STATUS_CONFLICT; break; case xbl_error_code::http_status_410_gone: hr = HTTP_E_STATUS_GONE; break; case xbl_error_code::http_status_411_length_required: hr = HTTP_E_STATUS_LENGTH_REQUIRED; break; case xbl_error_code::http_status_412_precondition_failed: hr = HTTP_E_STATUS_PRECOND_FAILED; break; case xbl_error_code::http_status_413_request_entity_too_large: hr = HTTP_E_STATUS_REQUEST_TOO_LARGE; break; case xbl_error_code::http_status_414_request_uri_too_long: hr = HTTP_E_STATUS_URI_TOO_LONG; break; case xbl_error_code::http_status_415_unsupported_media_type: hr = HTTP_E_STATUS_UNSUPPORTED_MEDIA; break; case xbl_error_code::http_status_416_requested_range_not_satisfiable: hr = HTTP_E_STATUS_RANGE_NOT_SATISFIABLE; break; case xbl_error_code::http_status_417_expectation_failed: hr = HTTP_E_STATUS_EXPECTATION_FAILED; break; case xbl_error_code::http_status_421_misdirected_request: hr = MAKE_HTTP_HRESULT(421); break; case xbl_error_code::http_status_422_unprocessable_entity: hr = MAKE_HTTP_HRESULT(422); break; case xbl_error_code::http_status_423_locked: hr = MAKE_HTTP_HRESULT(423); break; case xbl_error_code::http_status_424_failed_dependency: hr = MAKE_HTTP_HRESULT(424); break; case xbl_error_code::http_status_426_upgrade_required: hr = MAKE_HTTP_HRESULT(426); break; case xbl_error_code::http_status_428_precondition_required: hr = MAKE_HTTP_HRESULT(428); break; case xbl_error_code::http_status_429_too_many_requests: hr = MAKE_HTTP_HRESULT(429); break; case xbl_error_code::http_status_431_request_header_fields_too_large: hr = MAKE_HTTP_HRESULT(431); break; case xbl_error_code::http_status_449_retry_with:hr = MAKE_HTTP_HRESULT(449); break; case xbl_error_code::http_status_451_unavailable_for_legal_reasons: hr = MAKE_HTTP_HRESULT(451); break; case xbl_error_code::http_status_500_internal_server_error: hr = HTTP_E_STATUS_SERVER_ERROR; break; case xbl_error_code::http_status_501_not_implemented: hr = HTTP_E_STATUS_NOT_SUPPORTED; break; case xbl_error_code::http_status_502_bad_gateway: hr = HTTP_E_STATUS_BAD_GATEWAY; break; case xbl_error_code::http_status_503_service_unavailable: hr = HTTP_E_STATUS_SERVICE_UNAVAIL; break; case xbl_error_code::http_status_504_gateway_timeout: hr = HTTP_E_STATUS_GATEWAY_TIMEOUT; break; case xbl_error_code::http_status_505_http_version_not_supported: hr = HTTP_E_STATUS_VERSION_NOT_SUP; break; case xbl_error_code::http_status_506_variant_also_negotiates: hr = MAKE_HTTP_HRESULT(506); break; case xbl_error_code::http_status_507_insufficient_storage: hr = MAKE_HTTP_HRESULT(507); break; case xbl_error_code::http_status_508_loop_detected: hr = MAKE_HTTP_HRESULT(508); break; case xbl_error_code::http_status_510_not_extended: hr = MAKE_HTTP_HRESULT(510); break; case xbl_error_code::http_status_511_network_authentication_required: hr = MAKE_HTTP_HRESULT(511); break; default: hr = HTTP_E_STATUS_UNEXPECTED; break; } } return hr; } HRESULT HttpCall::Result() const { HRESULT hrNetworkError{ S_OK }; uint32_t platformNetworkResult{ 0 }; RETURN_HR_IF_FAILED(HCHttpCallResponseGetNetworkErrorCode(m_callHandle, &hrNetworkError, &platformNetworkResult)); if (XblShouldFaultInject(INJECTION_FEATURE_HTTP)) { LOGS_ERROR << "FAULT INJECTION: HttpCall::Result" << XblGetFaultCounter(); hrNetworkError = E_FAIL; } if (SUCCEEDED(hrNetworkError)) { HRESULT hrHttpStatus = ConvertHttpStatusToHRESULT(HttpStatus()); return hrHttpStatus; } else { LOGS_ERROR << "HttpCall failed due to network error " << platformNetworkResult; return hrNetworkError; } } HRESULT HttpCall::GetErrorMessage(const char** errorMessage) const { return HCHttpCallResponseGetPlatformNetworkErrorMessage(m_callHandle, errorMessage); } uint32_t HttpCall::HttpStatus() const { assert(m_step == Step::Done); uint32_t status{ 0 }; auto hr = HCHttpCallResponseGetStatusCode(m_callHandle, &status); assert(SUCCEEDED(hr)); UNREFERENCED_PARAMETER(hr); return status; } xsapi_internal_vector HttpCall::GetResponseBodyBytes() const { assert(m_step == Step::Done); size_t bodySize{ 0 }; xsapi_internal_vector body; HRESULT hr = HCHttpCallResponseGetResponseBodyBytesSize(m_callHandle, &bodySize); if (SUCCEEDED(hr)) { body.resize(bodySize); hr = HCHttpCallResponseGetResponseBodyBytes(m_callHandle, body.size(), body.data(), nullptr); assert(SUCCEEDED(hr)); } return body; } xsapi_internal_string HttpCall::GetResponseBodyString() const { assert(m_step == Step::Done); const char* bodyString{ nullptr }; auto hr = HCHttpCallResponseGetResponseString(m_callHandle, &bodyString); assert(SUCCEEDED(hr)); UNREFERENCED_PARAMETER(hr); return bodyString != nullptr ? xsapi_internal_string{ bodyString } : xsapi_internal_string(); } //TODO: consider keeping a JsonDocument member instead of parsing every time JsonDocument HttpCall::GetResponseBodyJson() const { assert(m_step == Step::Done); const char* bodyString{ nullptr }; auto hr = HCHttpCallResponseGetResponseString(m_callHandle, &bodyString); assert(SUCCEEDED(hr)); UNREFERENCED_PARAMETER(hr); JsonDocument json; if (bodyString != nullptr) { json.Parse(bodyString); if (!json.HasParseError()) { return json; } } return JsonDocument(rapidjson::kNullType); } xsapi_internal_string HttpCall::GetResponseHeader(const xsapi_internal_string& key) const { assert(m_step == Step::Done); const char* headerValue{ nullptr }; auto hr = HCHttpCallResponseGetHeader(m_callHandle, key.data(), &headerValue); assert(SUCCEEDED(hr)); UNREFERENCED_PARAMETER(hr); return headerValue ? xsapi_internal_string{ headerValue } : xsapi_internal_string{}; } HRESULT HttpCall::SetRequestBody( _In_reads_bytes_(requestBodySize) const uint8_t* requestBodyBytes, _In_ uint32_t requestBodySize ) { return HCHttpCallRequestSetRequestBodyBytes(m_callHandle, requestBodyBytes, requestBodySize); } HRESULT HttpCall::SetRequestBody( _In_z_ const char* requestBodyString ) { return HCHttpCallRequestSetRequestBodyString(m_callHandle, requestBodyString); } HRESULT HttpCall::GetResponseString( _Out_ const char** responseString ) { return HCHttpCallResponseGetResponseString(m_callHandle, responseString); } HRESULT HttpCall::GetResponseBodyBytesSize( _Out_ size_t* bufferSize ) { return HCHttpCallResponseGetResponseBodyBytesSize(m_callHandle, bufferSize); } HRESULT HttpCall::GetResponseBodyBytes( _In_ size_t bufferSize, _Out_writes_bytes_to_(bufferSize, *bufferUsed) uint8_t* buffer, _Out_opt_ size_t* bufferUsed ) { return HCHttpCallResponseGetResponseBodyBytes(m_callHandle, bufferSize, buffer, bufferUsed); } HRESULT HttpCall::GetNetworkErrorCode( _Out_ HRESULT* networkErrorCode, _Out_ uint32_t* platformNetworkErrorCode ) { return HCHttpCallResponseGetNetworkErrorCode(m_callHandle, networkErrorCode, platformNetworkErrorCode); } HRESULT HttpCall::GetPlatformNetworkErrorMessage( _Out_ const char** platformNetworkErrorMessage ) { return HCHttpCallResponseGetPlatformNetworkErrorMessage(m_callHandle, platformNetworkErrorMessage); } HRESULT HttpCall::ResponseGetHeader( _In_z_ const char* headerName, _Out_ const char** headerValue ) { return HCHttpCallResponseGetHeader(m_callHandle, headerName, headerValue); } HRESULT HttpCall::ResponseGetNumHeaders( _Out_ uint32_t* numHeaders ) { return HCHttpCallResponseGetNumHeaders(m_callHandle, numHeaders); } HRESULT HttpCall::ResponseGetHeaderAtIndex( _In_ uint32_t headerIndex, _Out_ const char** headerName, _Out_ const char** headerValue ) { return HCHttpCallResponseGetHeaderAtIndex(m_callHandle, headerIndex, headerName, headerValue); } HRESULT HttpCall::SetTracing(bool traceCall) { return HCHttpCallSetTracing(m_callHandle, traceCall); } HRESULT HttpCall::GetRequestUrl(const char** url) const { return HCHttpCallGetRequestUrl(m_callHandle, url); } HttpHeaders HttpCall::GetResponseHeaders() const { assert(m_step == Step::Done); uint32_t headerCount{ 0 }; auto hr = HCHttpCallResponseGetNumHeaders(m_callHandle, &headerCount); assert(SUCCEEDED(hr)); HttpHeaders headers{}; for (uint32_t i = 0; i < headerCount; ++i) { const char* headerName{ nullptr }; const char* headerValue{ nullptr }; hr = HCHttpCallResponseGetHeaderAtIndex(m_callHandle, i, &headerName, &headerValue); assert(SUCCEEDED(hr)); headers[headerName] = headerValue; } UNREFERENCED_PARAMETER(hr); return headers; } void HttpCall::CompletionCallback(_In_ XAsyncBlock* async) { auto sharedThis{ static_cast(async->context)->shared_from_this() }; sharedThis->DecRef(); sharedThis->m_step = Step::Done; sharedThis->m_asyncContext.Complete(HttpResult{ sharedThis }); } std::shared_ptr HttpCall::GetSharedThis() { return shared_from_this(); } NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END using namespace xbox::services; HRESULT XblHttpCall::SetXblServiceContractVersion(uint32_t contractVersion) { xsapi_internal_stringstream ss; ss << contractVersion; return SetHeader(CONTRACT_VERSION_HEADER, ss.str()); } void XblHttpCall::SetLongHttpCall(_In_ bool longHttpCall) { m_longHttpCall = longHttpCall; } XblHttpCall::XblHttpCall(_In_ User&& user) : m_user{ std::move(user) } { } HRESULT XblHttpCall::Init( _In_ std::shared_ptr contextSettings, _In_ const xsapi_internal_string& httpMethod, _In_ const xsapi_internal_string& fullUrl, _In_ xbox_live_api xboxLiveApi ) { m_httpMethod = httpMethod; m_fullUrl = fullUrl; m_longHttpTimeout = contextSettings->LongHttpTimeout(); RETURN_HR_IF_FAILED(HttpCall::Init(httpMethod, fullUrl)); RETURN_HR_IF_FAILED(SetRetryCacheId(static_cast(xboxLiveApi))); m_httpTimeoutWindowInSeconds = contextSettings->HttpTimeoutWindow(); RETURN_HR_IF_FAILED(SetTimeoutWindow(contextSettings->HttpTimeoutWindow())); RETURN_HR_IF_FAILED(SetRetryDelay(contextSettings->HttpRetryDelay())); RETURN_HR_IF_FAILED(SetHeader(CONTRACT_VERSION_HEADER, "1")); RETURN_HR_IF_FAILED(SetHeader(CONTENT_TYPE_HEADER, "application/json; charset=utf-8")); RETURN_HR_IF_FAILED(SetHeader(ACCEPT_LANGUAGE_HEADER, utils::get_locales())); RETURN_HR_IF_FAILED(SetUserAgent(contextSettings->HttpUserAgent())); return S_OK; } HRESULT XblHttpCall::CalcHttpTimeout() { if (m_longHttpCall) { // Long calls such as Title Storage upload/download ignore http_timeout_window so they act as expected with // requiring the game developer to manually adjust http_timeout_window before calling them. return SetTimeout(m_longHttpTimeout); } else { // For all other calls, set the timeout to be how much time left before hitting the http_timeout_window setting with a min of 5 second std::chrono::milliseconds timeElapsedSinceFirstCall = std::chrono::duration_cast(m_requestStartTime - m_firstCallStartTime); std::chrono::seconds remainingTimeBeforeTimeout = std::chrono::duration_cast(std::chrono::seconds(m_httpTimeoutWindowInSeconds) - timeElapsedSinceFirstCall); uint64_t secondsLeft = __min(DEFAULT_HTTP_TIMEOUT_SECONDS, static_cast(remainingTimeBeforeTimeout.count())); uint64_t secondsLeftCapped = __max(MIN_HTTP_TIMEOUT_SECONDS, secondsLeft); return SetTimeout(static_cast(secondsLeftCapped)); } } HRESULT XblHttpCall::SetHeader( _In_ const xsapi_internal_string& key, _In_ const xsapi_internal_string& value, _In_ bool allowTracing ) { m_requestHeaders[key] = value; return HttpCall::SetHeader(key, value, allowTracing); } HRESULT XblHttpCall::SetUserAgent(_In_ HttpCallAgent userAgent) { String headerValue{ DEFAULT_USER_AGENT }; if (userAgent != HttpCallAgent::Title) { headerValue += EnumName(userAgent); } XblApiType apiType{ XblApiType::XblCApi }; { auto state{ GlobalState::Get() }; if (state) { apiType = state->ApiType; } } switch (apiType) { case XblApiType::XblCApi: { headerValue += " c"; break; } case XblApiType::XblCPPApi: { headerValue += " cpp"; break; } } return SetHeader(USER_AGENT_HEADER, headerValue); } HRESULT XblHttpCall::SetRequestBody(const xsapi_internal_vector& bytes) { m_requestBody = bytes; return HttpCall::SetRequestBody(xsapi_internal_vector{ bytes }); } HRESULT XblHttpCall::SetRequestBody(_In_reads_bytes_(requestBodySize) const uint8_t* requestBodyBytes, _In_ uint32_t requestBodySize) { m_requestBody = xsapi_internal_vector{ requestBodyBytes, requestBodyBytes + requestBodySize }; return HttpCall::SetRequestBody(requestBodyBytes, requestBodySize); } HRESULT XblHttpCall::SetRequestBody(_In_z_ const char* requestBodyString) { xsapi_internal_string requestBody{ requestBodyString }; return XblHttpCall::SetRequestBody(requestBody); } HRESULT XblHttpCall::SetRequestBody(const xsapi_internal_string& bodyString) { m_requestBody = xsapi_internal_vector{ bodyString.begin(), bodyString.end() }; return HttpCall::SetRequestBody(bodyString); } HRESULT XblHttpCall::SetRequestBody(const JsonValue& bodyJson) { return SetRequestBody(JsonUtils::SerializeJson(bodyJson)); } void XblHttpCall::SetAuthRetryAllowed(bool authRetryAllowed) { m_authRetryExplicitlyAllowed = authRetryAllowed; } HRESULT XblHttpCall::Perform( AsyncContext async, bool forceRefresh ) { m_asyncContext = std::move(async); std::shared_ptr sharedThis = { std::dynamic_pointer_cast(shared_from_this()) }; auto now = chrono_clock_t::now(); if (m_iterationNumber == 0) { m_firstCallStartTime = now; } m_iterationNumber++; m_requestStartTime = now; if (forceRefresh) { m_user.SetTokenExpired(m_user.Xuid()); } return m_user.GetTokenAndSignature( m_httpMethod, m_fullUrl, m_requestHeaders, m_requestBody.data(), m_requestBody.size(), false, // allUsersAuthRequired AsyncContext>{ m_asyncContext.Queue(), [ sharedThis ] (xbox::services::Result authResult) { if (Failed(authResult)) { sharedThis->IntermediateHttpCallCompleteCallback(authResult.Hresult()); } else { const auto& authPayload = authResult.Payload(); HRESULT hr = S_OK; if (!authPayload.token.empty()) { hr = sharedThis->HttpCall::SetHeader(AUTH_HEADER, authPayload.token, false); } if (SUCCEEDED(hr) && !authPayload.signature.empty()) { hr = sharedThis->HttpCall::SetHeader(SIG_HEADER, authPayload.signature, false); } if (SUCCEEDED(hr)) { hr = sharedThis->CalcHttpTimeout(); if (SUCCEEDED(hr)) { hr = sharedThis->HttpCall::Perform(AsyncContext{ sharedThis->m_asyncContext.Queue(), [ // Don't store a shared ref here since this lambda will be stored in HttpCall object, creating a self reference that would never // be cleaned up. HttpCallPerform already guarantees lifetime until this callback is called. rawThis{ sharedThis.get() } ] (HttpResult result) { rawThis->IntermediateHttpCallCompleteCallback(result); }}); } } if (FAILED(hr)) { sharedThis->IntermediateHttpCallCompleteCallback(HttpResult{ hr }); } } } }); } void XblHttpCall::IntermediateHttpCallCompleteCallback(HttpResult result) { auto httpCall = result.Payload(); if (httpCall) { bool wasHandled{ false }; HRESULT hr = HandleAuthError(httpCall, wasHandled); if (wasHandled) { return; } if (FAILED(hr)) { m_asyncContext.Complete(HttpResult{ hr }); return; } HandleThrottleError(httpCall); } m_asyncContext.Complete(std::move(result)); } HRESULT XblHttpCall::HandleAuthError(_In_ std::shared_ptr httpCall, _Out_ bool& wasHandled) { if (httpCall->HttpStatus() != 401) { wasHandled = false; return S_OK; } bool retryAllowed{ false }; HCHttpCallRequestGetRetryAllowed(m_callHandle, &retryAllowed); if (!retryAllowed && !m_authRetryExplicitlyAllowed) { wasHandled = false; return S_OK; } if (m_hasPerformedRetryOn401) { // Ignore 401 retrying if we already have retried a 401 wasHandled = false; return S_OK; } m_hasPerformedRetryOn401 = true; HRESULT hr = ResetAndCopyForRetry(); if (SUCCEEDED(hr)) { hr = XblHttpCall::Perform(m_asyncContext, true); } if (SUCCEEDED(hr)) { wasHandled = true; return S_OK; } wasHandled = false; return hr; } void XblHttpCall::HandleThrottleError(_In_ std::shared_ptr httpCall) { if (httpCall->HttpStatus() != 429) { return; } // Assert if we were throttled by the service so the game dev knows that they are calling Xbox Live to agressively auto appConfig = AppConfig::Instance(); if (appConfig && utils::str_icmp_internal(appConfig->Sandbox(), "RETAIL") != 0) { if (!appConfig->IsDisableAssertsForXboxLiveThrottlingInDevSandboxes()) { LOGS_ERROR << "Xbox Live service call to " << m_fullUrl << " was throttled"; LOGS_ERROR << GetResponseBodyString(); LOGS_ERROR << "You can temporarily disable the assert by calling"; LOGS_ERROR << "XblDisableAssertsForXboxLiveThrottlingInDevSandboxes()"; LOGS_ERROR << "Note that this will only disable this assert. You will still be throttled in all sandboxes."; #ifndef XSAPI_UNIT_TESTS assert(false && "Xbox Live service call was throttled. See Output for more detail"); #endif } } } xsapi_internal_string XblHttpCall::BuildUrl( xsapi_internal_string&& serviceName, const xsapi_internal_string& pathQueryFragment ) { xsapi_internal_stringstream source; source << "https://" << serviceName << ".xboxlive.com" << pathQueryFragment; return source.str(); } ================================================ FILE: Source/Shared/http_call_wrapper_internal.h ================================================ #pragma once #include "httpClient/httpClient.h" #include "shared_macros.h" #include "xbox_live_context_settings_internal.h" const char CONTENT_TYPE_HEADER[] = "Content-Type"; const char ACCEPT_LANGUAGE_HEADER[] = "Accept-Language"; const char CONTRACT_VERSION_HEADER[] = "x-xbl-contract-version"; const char USER_AGENT_HEADER[] = "User-Agent"; NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN enum class xbox_live_api { unspecified, add_user_to_club, add_club_role, allocate_cluster, allocate_cluster_inline, allocate_session_host, browse_catalog_bundles_helper, browse_catalog_helper, check_multiple_permissions_with_multiple_target_users, check_permission_with_target_user, clear_activity, clear_search_handle, consume_inventory_item, create_club, create_match_ticket, delete_blob, delete_club, delete_match_ticket, download_blob, events_upload, get_achievement, get_achievements, get_activities_for_social_group, get_activities_for_users, get_avoid_or_mute_list, get_blob_metadata, get_broadcasts, get_catalog_item_details, get_club, get_club_batch, get_clubs_owned, get_configuration, get_current_session, get_current_session_by_handle, get_game_clips, get_game_server_metadata, get_hopper_statistics, get_inventory_item, get_inventory_items, get_leaderboard_for_social_group_internal, get_leaderboard_internal, get_match_ticket_details, get_multiple_user_statistics_for_multiple_service_configurations, get_presence, get_presence_for_multiple_users, get_presence_for_social_group, get_quality_of_service_servers, get_quota, get_quota_for_session_storage, get_search_handles, delete_search_handle, get_session_host_allocation_status, get_sessions, get_single_user_statistics, get_social_graph, get_social_relationships, get_stats_value_document, get_ticket_status, get_teams, get_team_details, get_user_profiles, get_user_profiles_for_social_group, get_users_club_associations, recommend_clubs, register_team, remove_user_from_club, remove_club_role, rename_club, search_clubs, send_invites, set_activity, set_presence_helper, set_search_handle, set_transfer_handle, set_user_presence_within_club, submit_batch_reputation_feedback, submit_reputation_feedback, subscribe_to_notifications, suggest_clubs, update_achievement, patch_stats_value_document, post_stats_value_document, upload_blob, verify_strings, write_session_using_subpath, xbox_one_pins_add_item, xbox_one_pins_contains_item, xbox_one_pins_remove_item, post_recent_players, get_activity_batch, delete_activity, mpa_send_invites }; using HttpHeaders = xsapi_internal_map; using HttpResult = Result>; // RAII wrapper around HCHttpCall. No Xbox Live specific logic. class HttpCall : public RefCounter, public std::enable_shared_from_this { public: HttpCall() = default; virtual ~HttpCall(); HRESULT Init( _In_ const xsapi_internal_string& httpMethod, _In_ const xsapi_internal_string& fullUrl ); virtual HRESULT SetHeader( _In_ const xsapi_internal_string& key, _In_ const xsapi_internal_string& value, _In_ bool allowTracing = true ); virtual HRESULT SetTracing(bool traceCall); virtual HRESULT SetRequestBody(const xsapi_internal_vector& bytes); virtual HRESULT SetRequestBody(_In_reads_bytes_(requestBodySize) const uint8_t* requestBodyBytes, _In_ uint32_t requestBodySize); virtual HRESULT SetRequestBody(const xsapi_internal_string& bodyString); virtual HRESULT SetRequestBody(const JsonValue& bodyJson); virtual HRESULT SetRequestBody(_In_z_ const char* requestBodyString); virtual HRESULT SetRetryAllowed(bool retryAllowed); virtual HRESULT SetRetryCacheId(uint32_t retryAfterCacheId); virtual HRESULT SetRetryDelay(uint32_t retryDelayInSeconds); virtual HRESULT SetTimeout(uint32_t timeoutInSeconds); virtual HRESULT SetTimeoutWindow(uint32_t timeoutWindowInSeconds); virtual HRESULT Perform( AsyncContext async, bool forceRefresh = false ); virtual HRESULT GetRequestUrl(const char** url) const; virtual HRESULT GetErrorMessage(const char** errorMessage) const; virtual HRESULT Result() const; virtual uint32_t HttpStatus() const; virtual xsapi_internal_string GetResponseHeader(const xsapi_internal_string& key) const; virtual HttpHeaders GetResponseHeaders() const; virtual xsapi_internal_vector GetResponseBodyBytes() const; virtual HRESULT GetResponseBodyBytesSize(_Out_ size_t* bufferSize); virtual HRESULT GetResponseBodyBytes(_In_ size_t bufferSize, _Out_writes_bytes_to_(bufferSize, *bufferUsed) uint8_t* buffer, _Out_opt_ size_t* bufferUsed); virtual xsapi_internal_string GetResponseBodyString() const; virtual HRESULT GetResponseString(_Out_ const char** responseString); virtual JsonDocument GetResponseBodyJson() const; virtual HRESULT GetNetworkErrorCode(_Out_ HRESULT* networkErrorCode, _Out_ uint32_t* platformNetworkErrorCode); virtual HRESULT GetPlatformNetworkErrorMessage(_Out_ const char** platformNetworkErrorMessage); virtual HRESULT ResponseGetHeader(_In_z_ const char* headerName, _Out_ const char** headerValue); virtual HRESULT ResponseGetNumHeaders(_Out_ uint32_t* numHeaders); virtual HRESULT ResponseGetHeaderAtIndex(_In_ uint32_t headerIndex, _Out_ const char** headerName, _Out_ const char** headerValue); private: HttpCall(const HttpCall&) = delete; HttpCall& operator=(HttpCall) = delete; static void CALLBACK CompletionCallback(_In_ XAsyncBlock* async); std::shared_ptr GetSharedThis() override; static HRESULT ConvertHttpStatusToHRESULT(_In_ uint32_t httpStatusCode); HRESULT CopyHttpCallHandle(); XAsyncBlock m_asyncBlock{}; AsyncContext m_asyncContext; bool m_performAlreadyCalled{ false }; enum class Step { Uninitialized, Pending, Running, Done } m_step{ Step::Uninitialized }; protected: HCCallHandle m_callHandle{ nullptr }; HRESULT ResetAndCopyForRetry(); }; NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END using namespace xbox::services; // Xbox Live specific http call wrapper that gets and attaches the Xbox Live token, // signs the request, and adds Xbox Live specific headers automatically. // Handles 401 token refresh logic, and 429 throttling logic struct XblHttpCall : public HttpCall { public: XblHttpCall(User&& user); virtual HRESULT Init( _In_ std::shared_ptr contextSettings, _In_ const xsapi_internal_string& httpMethod, _In_ const xsapi_internal_string& fullUrl, _In_ xbox_live_api xboxLiveApi ); HRESULT SetHeader( _In_ const xsapi_internal_string& key, _In_ const xsapi_internal_string& value, _In_ bool allowTracing = true ) override; // Override the UserAgent that was specified in the contextSettings HRESULT SetUserAgent(_In_ HttpCallAgent userAgent); void SetLongHttpCall(_In_ bool longHttpCall); HRESULT SetXblServiceContractVersion(uint32_t contractVersion); HRESULT SetRequestBody(const xsapi_internal_vector& bytes) override; HRESULT SetRequestBody(_In_reads_bytes_(requestBodySize) const uint8_t* requestBodyBytes, _In_ uint32_t requestBodySize) override; HRESULT SetRequestBody(const xsapi_internal_string& bodyString) override; HRESULT SetRequestBody(const JsonValue& bodyJson) override; HRESULT SetRequestBody(_In_z_ const char* requestBodyString) override; void SetAuthRetryAllowed(bool authRetryAllowed); HRESULT Perform( AsyncContext async, bool forceRefresh = false ) override; static xsapi_internal_string BuildUrl( xsapi_internal_string&& serviceName, const xsapi_internal_string& pathQueryFragment ); private: XblHttpCall(const XblHttpCall&) = delete; XblHttpCall& operator=(XblHttpCall) = delete; void IntermediateHttpCallCompleteCallback(HttpResult result); HRESULT HandleAuthError(_In_ std::shared_ptr httpCall, _Out_ bool& wasHandled); void HandleThrottleError(_In_ std::shared_ptr httpCall); HRESULT CalcHttpTimeout(); User m_user; xsapi_internal_vector m_requestBody; HttpHeaders m_requestHeaders; xsapi_internal_string m_httpMethod; xsapi_internal_string m_fullUrl; bool m_longHttpCall{ false }; uint32_t m_longHttpTimeout{ 0 }; uint32_t m_httpTimeoutWindowInSeconds{ 0 }; chrono_clock_t::time_point m_firstCallStartTime; chrono_clock_t::time_point m_requestStartTime; uint32_t m_iterationNumber{ 0 }; AsyncContext m_asyncContext; bool m_authRetryExplicitlyAllowed{ false }; bool m_hasPerformedRetryOn401{ false }; }; ================================================ FILE: Source/Shared/http_headers.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN typedef xsapi_internal_map xsapi_internal_http_headers; NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END ================================================ FILE: Source/Shared/http_utils.cpp ================================================ /*** * ==++== * * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * ==--== * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ * * Utilities * * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk * * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ****/ #include "pch.h" #include #if defined(_WIN32) #if HC_PLATFORM != HC_PLATFORM_XDK #include #endif #else // _WIN32 #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-local-typedef" #endif // TODO 1808 #include // #include #if defined(__clang__) #pragma clang diagnostic pop #endif #endif // _WIN32 #if HC_PLATFORM_IS_MICROSOFT #pragma warning( push ) #pragma warning( disable : 26444 ) // ignore various unnamed objects #pragma warning( disable : 26498 ) // ignore eof warning #pragma warning( disable : 26812 ) // enum instead of enum class #endif // Could use C++ standard library if not __GLIBCXX__, // For testing purposes we just the handwritten on all platforms. #if defined(CPPREST_STDLIB_UNICODE_CONVERSIONS) #include #endif namespace xbox { namespace services { namespace detail { xsapi_internal_string _to_base64(const unsigned char *ptr, size_t size); std::vector _from_base64(const xsapi_internal_string& str); struct _triple_byte { unsigned char _1_1 : 2; unsigned char _0 : 6; unsigned char _2_1 : 4; unsigned char _1_2 : 4; unsigned char _3 : 6; unsigned char _2_2 : 2; }; static const char* _base64_enctbl = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; const std::array _base64_dectbl = { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, 255, 254, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255, 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 255, 255, 255, 255, 255 } }; xsapi_internal_string _to_base64(const unsigned char *ptr, size_t size) { xsapi_internal_string result; for (; size >= 3; ) { const _triple_byte* record = reinterpret_cast(ptr); unsigned char idx0 = record->_0; unsigned char idx1 = (record->_1_1 << 4) | record->_1_2; unsigned char idx2 = (record->_2_1 << 2) | record->_2_2; unsigned char idx3 = record->_3; result.push_back(char(_base64_enctbl[idx0])); result.push_back(char(_base64_enctbl[idx1])); result.push_back(char(_base64_enctbl[idx2])); result.push_back(char(_base64_enctbl[idx3])); size -= 3; ptr += 3; } switch (size) { case 1: { const _triple_byte* record = reinterpret_cast(ptr); unsigned char idx0 = record->_0; unsigned char idx1 = (record->_1_1 << 4); result.push_back(char(_base64_enctbl[idx0])); result.push_back(char(_base64_enctbl[idx1])); result.push_back('='); result.push_back('='); break; } case 2: { const _triple_byte* record = reinterpret_cast(ptr); unsigned char idx0 = record->_0; unsigned char idx1 = (record->_1_1 << 4) | record->_1_2; unsigned char idx2 = (record->_2_1 << 2); result.push_back(char(_base64_enctbl[idx0])); result.push_back(char(_base64_enctbl[idx1])); result.push_back(char(_base64_enctbl[idx2])); result.push_back('='); break; } } return result; } // // A note on the implementation of BASE64 encoding and decoding: // // This is a fairly basic and naive implementation; there is probably a lot of room for // performance improvement, as well as for adding options such as support for URI-safe base64, // ignoring CRLF, relaxed validation rules, etc. The decoder is currently pretty strict. // #ifdef __GNUC__ // gcc is concerned about the bitfield uses in the code, something we simply need to ignore. #pragma GCC diagnostic ignored "-Wconversion" #endif std::vector _from_base64(const xsapi_internal_string& input) { std::vector result; if (input.empty()) return result; size_t padding = 0; // Validation { auto size = input.size(); if ((size % 4) != 0) { throw std::runtime_error("length of base64 string is not an even multiple of 4"); } for (auto iter = input.begin(); iter != input.end(); ++iter, --size) { const size_t ch_sz = static_cast(*iter); if (ch_sz >= _base64_dectbl.size() || _base64_dectbl[ch_sz] == 255) { throw std::runtime_error("invalid character found in base64 string"); } if (_base64_dectbl[ch_sz] == 254) { padding++; // padding only at the end if (size > 2) { throw std::runtime_error("invalid padding character found in base64 string"); } if (size == 2) { const size_t ch2_sz = static_cast(*(iter + 1)); if (ch2_sz >= _base64_dectbl.size() || _base64_dectbl[ch2_sz] != 254) { throw std::runtime_error("invalid padding character found in base64 string"); } } } } } auto size = input.size(); const char* ptr = &input[0]; auto outsz = (size / 4) * 3; outsz -= padding; result.resize(outsz); size_t idx = 0; for (; size > 4; ++idx) { unsigned char target[3]; memset(target, 0, sizeof(target)); _triple_byte* record = reinterpret_cast<_triple_byte*>(target); unsigned char val0 = _base64_dectbl[ptr[0]]; unsigned char val1 = _base64_dectbl[ptr[1]]; unsigned char val2 = _base64_dectbl[ptr[2]]; unsigned char val3 = _base64_dectbl[ptr[3]]; record->_0 = val0; record->_1_1 = val1 >> 4; result[idx] = target[0]; record->_1_2 = val1 & 0xF; record->_2_1 = val2 >> 2; result[++idx] = target[1]; record->_2_2 = val2 & 0x3; record->_3 = val3 & 0x3F; result[++idx] = target[2]; ptr += 4; size -= 4; } // Handle the last four bytes separately, to avoid having the conditional statements // in all the iterations (a performance issue). { unsigned char target[3]; memset(target, 0, sizeof(target)); _triple_byte* record = reinterpret_cast<_triple_byte*>(target); DISABLE_WARNING_PUSH; SUPPRESS_WARNING_EXPRESSION_NOT_TRUE; unsigned char val0 = _base64_dectbl[ptr[0]]; DISABLE_WARNING_POP; unsigned char val1 = _base64_dectbl[ptr[1]]; unsigned char val2 = _base64_dectbl[ptr[2]]; unsigned char val3 = _base64_dectbl[ptr[3]]; record->_0 = val0; record->_1_1 = val1 >> 4; result[idx] = target[0]; record->_1_2 = val1 & 0xF; if (val2 != 254) { record->_2_1 = val2 >> 2; result[++idx] = target[1]; } else { // There shouldn't be any information (ones) in the unused bits, if (record->_1_2 != 0) { throw std::runtime_error("Invalid end of base64 string"); } return result; } record->_2_2 = val2 & 0x3; if (val3 != 254) { record->_3 = val3 & 0x3F; result[++idx] = target[2]; } else { // There shouldn't be any information (ones) in the unused bits. if (record->_2_2 != 0) { throw std::runtime_error("Invalid end of base64 string"); } return result; } } return result; } } #ifndef _WIN32 datetime datetime::timeval_to_datetime(const timeval& time) { const uint64_t epoch_offset = 11644473600LL; // diff between windows and unix epochs (seconds) uint64_t result = epoch_offset + time.tv_sec; result *= _secondTicks; // convert to 10e-7 result += time.tv_usec * 10; // convert and add microseconds, 10e-6 to 10e-7 return datetime(result); } #endif static bool is_digit(char c) { return c >= '0' && c <= '9'; } datetime datetime::utc_now() { #ifdef _WIN32 ULARGE_INTEGER largeInt; FILETIME fileTime; GetSystemTimeAsFileTime(&fileTime); largeInt.LowPart = fileTime.dwLowDateTime; largeInt.HighPart = fileTime.dwHighDateTime; return datetime(largeInt.QuadPart); #else //LINUX timeval time{}; gettimeofday(&time, nullptr); return timeval_to_datetime(time); #endif } xsapi_internal_string datetime::to_string(date_format format) const { #ifdef _WIN32 int status; ULARGE_INTEGER largeInt; largeInt.QuadPart = m_interval; FILETIME ft; ft.dwHighDateTime = largeInt.HighPart; ft.dwLowDateTime = largeInt.LowPart; SYSTEMTIME systemTime; if (!FileTimeToSystemTime((const FILETIME*)&ft, &systemTime)) { throw details::create_system_error(GetLastError()); } xsapi_internal_wostringstream outStream; outStream.imbue(std::locale::classic()); if (format == RFC_1123) { #if _WIN32_WINNT < _WIN32_WINNT_VISTA TCHAR dateStr[18] = { 0 }; status = GetDateFormat(LOCALE_INVARIANT, 0, &systemTime, __TEXT("ddd',' dd MMM yyyy"), dateStr, sizeof(dateStr) / sizeof(TCHAR)); #else wchar_t dateStr[18] = { 0 }; status = GetDateFormatEx(LOCALE_NAME_INVARIANT, 0, &systemTime, L"ddd',' dd MMM yyyy", dateStr, sizeof(dateStr) / sizeof(wchar_t), NULL); #endif // _WIN32_WINNT < _WIN32_WINNT_VISTA if (status == 0) { throw details::create_system_error(GetLastError()); } #if _WIN32_WINNT < _WIN32_WINNT_VISTA TCHAR timeStr[10] = { 0 }; status = GetTimeFormat(LOCALE_INVARIANT, TIME_NOTIMEMARKER | TIME_FORCE24HOURFORMAT, &systemTime, __TEXT("HH':'mm':'ss"), timeStr, sizeof(timeStr) / sizeof(TCHAR)); #else wchar_t timeStr[10] = { 0 }; status = GetTimeFormatEx(LOCALE_NAME_INVARIANT, TIME_NOTIMEMARKER | TIME_FORCE24HOURFORMAT, &systemTime, L"HH':'mm':'ss", timeStr, sizeof(timeStr) / sizeof(wchar_t)); #endif // _WIN32_WINNT < _WIN32_WINNT_VISTA if (status == 0) { throw details::create_system_error(GetLastError()); } outStream << dateStr << " " << timeStr << " " << "GMT"; } else if (format == ISO_8601) { const size_t buffSize = 64; #if _WIN32_WINNT < _WIN32_WINNT_VISTA TCHAR dateStr[buffSize] = { 0 }; status = GetDateFormat(LOCALE_INVARIANT, 0, &systemTime, __TEXT("yyyy-MM-dd"), dateStr, buffSize); #else wchar_t dateStr[buffSize] = { 0 }; status = GetDateFormatEx(LOCALE_NAME_INVARIANT, 0, &systemTime, L"yyyy-MM-dd", dateStr, buffSize, NULL); #endif // _WIN32_WINNT < _WIN32_WINNT_VISTA if (status == 0) { throw details::create_system_error(GetLastError()); } #if _WIN32_WINNT < _WIN32_WINNT_VISTA TCHAR timeStr[buffSize] = { 0 }; status = GetTimeFormat(LOCALE_INVARIANT, TIME_NOTIMEMARKER | TIME_FORCE24HOURFORMAT, &systemTime, __TEXT("HH':'mm':'ss"), timeStr, buffSize); #else wchar_t timeStr[buffSize] = { 0 }; status = GetTimeFormatEx(LOCALE_NAME_INVARIANT, TIME_NOTIMEMARKER | TIME_FORCE24HOURFORMAT, &systemTime, L"HH':'mm':'ss", timeStr, buffSize); #endif // _WIN32_WINNT < _WIN32_WINNT_VISTA if (status == 0) { throw details::create_system_error(GetLastError()); } outStream << dateStr << "T" << timeStr; uint64_t frac_sec = largeInt.QuadPart % _secondTicks; if (frac_sec > 0) { // Append fractional second, which is a 7-digit value with no trailing zeros // This way, '1200' becomes '00012' char buf[9] = { 0 }; sprintf_s(buf, sizeof(buf), ".%07ld", (long int)frac_sec); // trim trailing zeros for (int i = 7; buf[i] == '0'; i--) buf[i] = '\0'; outStream << buf; } outStream << "Z"; } auto result = convert::utf16_to_utf8_internal(outStream.str()); return result; #else //LINUX uint64_t input = m_interval; uint64_t frac_sec = input % _secondTicks; input /= _secondTicks; // convert to seconds time_t time = (time_t)input - (time_t)11644473600LL;// diff between windows and unix epochs (seconds) struct tm datetime; #if defined(PAVO) gmtime_s(&time, &datetime); #else gmtime_r(&time, &datetime); #endif // PAVO const int max_dt_length = 64; char output[max_dt_length + 1] = { 0 }; if (format != RFC_1123 && frac_sec > 0) { // Append fractional second, which is a 7-digit value with no trailing zeros // This way, '1200' becomes '00012' char buf[9] = { 0 }; snprintf(buf, sizeof(buf), ".%07ld", (long int)frac_sec); // trim trailing zeros for (int i = 7; buf[i] == '0'; i--) buf[i] = '\0'; // format the datetime into a separate buffer char datetime_str[max_dt_length + 1] = { 0 }; strftime(datetime_str, sizeof(datetime_str), "%Y-%m-%dT%H:%M:%S", &datetime); // now print this buffer into the output buffer snprintf(output, sizeof(output), "%s%sZ", datetime_str, buf); } else { strftime(output, sizeof(output), format == RFC_1123 ? "%a, %d %b %Y %H:%M:%S GMT" : "%Y-%m-%dT%H:%M:%SZ", &datetime); } return xsapi_internal_string(output); #endif } // Take a string that represents a fractional second and return the number of ticks // This is equivalent to doing atof on the string and multiplying by 10000000, // but does not lose precision template uint64_t timeticks_from_second(StringIterator begin, StringIterator end) { int size = (int)(end - begin); _ASSERTE(begin[0] == U('.')); uint64_t ufrac_second = 0; for (int i = 1; i <= 7; ++i) { ufrac_second *= 10; int add = i < size ? begin[i] - U('0') : 0; ufrac_second += add; } return ufrac_second; } void extract_fractional_second(const xsapi_internal_string& dateString, xsapi_internal_string& resultString, uint64_t& ufrac_second) { resultString = dateString; // First, the string must be strictly longer than 2 characters, and the trailing character must be 'Z' if (resultString.size() > 2 && resultString[resultString.size() - 1] == U('Z')) { // Second, find the last non-digit by scanning the string backwards auto last_non_digit = std::find_if_not(resultString.rbegin() + 1, resultString.rend(), is_digit); if (last_non_digit < resultString.rend() - 1) { // Finally, make sure the last non-digit is a dot: auto last_dot = last_non_digit.base() - 1; if (*last_dot == U('.')) { // Got it! Now extract the fractional second auto last_before_Z = std::end(resultString) - 1; ufrac_second = timeticks_from_second(last_dot, last_before_Z); // And erase it from the string resultString.erase(last_dot, last_before_Z); } } } } #ifdef _WIN32 bool datetime::system_type_to_datetime(void* pvsysTime, uint64_t seconds, datetime* pdt) { SYSTEMTIME* psysTime = (SYSTEMTIME*)pvsysTime; FILETIME fileTime; if (SystemTimeToFileTime(psysTime, &fileTime)) { ULARGE_INTEGER largeInt; largeInt.LowPart = fileTime.dwLowDateTime; largeInt.HighPart = fileTime.dwHighDateTime; // Add hundredths of nanoseconds largeInt.QuadPart += seconds; *pdt = datetime(largeInt.QuadPart); return true; } return false; } #endif datetime datetime::from_string(const xsapi_internal_string& dateString, date_format format) { // avoid floating point math to preserve precision uint64_t ufrac_second = 0; #ifdef _WIN32 datetime result; if (format == RFC_1123) { SYSTEMTIME sysTime = { 0 }; xsapi_internal_string month(3, '\0'); xsapi_internal_string unused(3, '\0'); const char* formatString = "%3c, %2d %3c %4d %2d:%2d:%2d %3c"; auto n = sscanf_s(dateString.c_str(), formatString, unused.data(), unused.size(), &sysTime.wDay, month.data(), month.size(), &sysTime.wYear, &sysTime.wHour, &sysTime.wMinute, &sysTime.wSecond, unused.data(), unused.size()); if (n == 8) { xsapi_internal_string monthnames[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; auto loc = std::find_if(monthnames, monthnames + 12, [&month](const xsapi_internal_string& m) { return m == month; }); if (loc != monthnames + 12) { sysTime.wMonth = (short)((loc - monthnames) + 1); if (system_type_to_datetime(&sysTime, ufrac_second, &result)) { return result; } } } } else if (format == ISO_8601) { // Unlike FILETIME, SYSTEMTIME does not have enough precision to hold seconds in 100 nanosecond // increments. Therefore, start with seconds and milliseconds set to 0, then add them separately // Try to extract the fractional second from the timestamp xsapi_internal_string input; extract_fractional_second(dateString, input, ufrac_second); { SYSTEMTIME sysTime = { 0 }; const char* formatString = "%4d-%2d-%2dT%2d:%2d:%2dZ"; auto n = sscanf_s(input.c_str(), formatString, &sysTime.wYear, &sysTime.wMonth, &sysTime.wDay, &sysTime.wHour, &sysTime.wMinute, &sysTime.wSecond); if (n == 3 || n == 6) { if (system_type_to_datetime(&sysTime, ufrac_second, &result)) { return result; } } } { SYSTEMTIME sysTime = { 0 }; DWORD date = 0; const char* formatString = "%8dT%2d:%2d:%2dZ"; auto n = sscanf_s(input.c_str(), formatString, &date, &sysTime.wHour, &sysTime.wMinute, &sysTime.wSecond); if (n == 1 || n == 4) { sysTime.wDay = date % 100; date /= 100; sysTime.wMonth = date % 100; date /= 100; sysTime.wYear = (WORD)date; if (system_type_to_datetime(&sysTime, ufrac_second, &result)) { return result; } } } { SYSTEMTIME sysTime = { 0 }; GetSystemTime(&sysTime); // Fill date portion with today's information sysTime.wSecond = 0; sysTime.wMilliseconds = 0; const char* formatString = "%2d:%2d:%2dZ"; auto n = sscanf_s(input.c_str(), formatString, &sysTime.wHour, &sysTime.wMinute, &sysTime.wSecond); if (n == 3) { if (system_type_to_datetime(&sysTime, ufrac_second, &result)) { return result; } } } } return datetime(); #else xsapi_internal_string input(dateString); struct tm output = tm(); if (format == RFC_1123) { strptime(input.data(), "%a, %d %b %Y %H:%M:%S GMT", &output); } else { // Try to extract the fractional second from the timestamp xsapi_internal_string input; extract_fractional_second(dateString, input, ufrac_second); auto result = strptime(input.data(), "%Y-%m-%dT%H:%M:%SZ", &output); if (result == nullptr) { result = strptime(input.data(), "%Y%m%dT%H:%M:%SZ", &output); } if (result == nullptr) { // Fill the date portion with the epoch, // strptime will do the rest memset(&output, 0, sizeof(struct tm)); output.tm_year = 70; output.tm_mon = 1; output.tm_mday = 1; result = strptime(input.data(), "%H:%M:%SZ", &output); } if (result == nullptr) { result = strptime(input.data(), "%Y-%m-%d", &output); } if (result == nullptr) { result = strptime(input.data(), "%Y%m%d", &output); } if (result == nullptr) { return datetime(); } } #if (defined(ANDROID) || defined(__ANDROID__)) // HACK: The (nonportable?) POSIX function timegm is not available in // bionic. As a workaround[1][2], we set the C library timezone to // UTC, call mktime, then set the timezone back. However, the C // environment is fundamentally a shared global resource and thread- // unsafe. We can protect our usage here, however any other code might // manipulate the environment at the same time. // // [1] http://linux.die.net/man/3/timegm // [2] http://www.gnu.org/software/libc/manual/html_node/Broken_002ddown-Time.html time_t time; static std::mutex env_var_lock; { std::lock_guard lock(env_var_lock); xsapi_internal_string prev_env; auto prev_env_cstr = getenv("TZ"); if (prev_env_cstr != nullptr) { prev_env = prev_env_cstr; } setenv("TZ", "UTC", 1); time = mktime(&output); if (prev_env_cstr) { setenv("TZ", prev_env.c_str(), 1); } else { unsetenv("TZ"); } tzset(); } #else time_t time = timegm(&output); #endif struct timeval tv = timeval(); tv.tv_sec = time; auto result = timeval_to_datetime(tv); // fractional seconds are already in correct format so just add them. result = result + ufrac_second; return result; #endif } #define LOW_3BITS 0x7 #define LOW_4BITS 0xF #define LOW_5BITS 0x1F #define LOW_6BITS 0x3F #define BIT4 0x8 #define BIT5 0x10 #define BIT6 0x20 #define BIT7 0x40 #define BIT8 0x80 #define L_SURROGATE_START 0xDC00 #define L_SURROGATE_END 0xDFFF #define H_SURROGATE_START 0xD800 #define H_SURROGATE_END 0xDBFF #define SURROGATE_PAIR_START 0x10000 xsapi_internal_wstring convert::utf8_to_utf16(const xsapi_internal_string &s) { #if defined(CPPREST_STDLIB_UNICODE_CONVERSIONS) std::wstring_convert, utf16char> conversion; return conversion.from_bytes(s); #else xsapi_internal_wstring dest; // Save repeated heap allocations, use less than source string size assuming some // of the characters are not just ASCII and collapse. dest.reserve(static_cast(static_cast(s.size()) * .70)); for (auto src = s.begin(); src != s.end(); ++src) { if ((*src & BIT8) == 0) // single byte character, 0x0 to 0x7F { dest.push_back(xsapi_internal_wstring::value_type(*src)); } else { unsigned char numContBytes = 0; uint32_t codePoint; if ((*src & BIT7) == 0) { throw std::range_error("UTF-8 string character can never start with 10xxxxxx"); } else if ((*src & BIT6) == 0) // 2 byte character, 0x80 to 0x7FF { codePoint = *src & LOW_5BITS; numContBytes = 1; } else if ((*src & BIT5) == 0) // 3 byte character, 0x800 to 0xFFFF { codePoint = *src & LOW_4BITS; numContBytes = 2; } else if ((*src & BIT4) == 0) // 4 byte character, 0x10000 to 0x10FFFF { codePoint = *src & LOW_3BITS; numContBytes = 3; } else { throw std::range_error("UTF-8 string has invalid Unicode code point"); } for (unsigned char i = 0; i < numContBytes; ++i) { if (++src == s.end()) { throw std::range_error("UTF-8 string is missing bytes in character"); } if ((*src & BIT8) == 0 || (*src & BIT7) != 0) { throw std::range_error("UTF-8 continuation byte is missing leading byte"); } codePoint <<= 6; codePoint |= *src & LOW_6BITS; } if (codePoint >= SURROGATE_PAIR_START) { // In UTF-16 U+10000 to U+10FFFF are represented as two 16-bit code units, surrogate pairs. // - 0x10000 is subtracted from the code point // - high surrogate is 0xD800 added to the top ten bits // - low surrogate is 0xDC00 added to the low ten bits codePoint -= SURROGATE_PAIR_START; dest.push_back(xsapi_internal_wstring::value_type((codePoint >> 10) | H_SURROGATE_START)); dest.push_back(xsapi_internal_wstring::value_type((codePoint & 0x3FF) | L_SURROGATE_START)); } else { // In UTF-16 U+0000 to U+D7FF and U+E000 to U+FFFF are represented exactly as the Unicode code point value. // U+D800 to U+DFFF are not valid characters, for simplicity we assume they are not present but will encode // them if encountered. dest.push_back(xsapi_internal_wstring::value_type(codePoint)); } } } return dest; #endif } xsapi_internal_string convert::to_utf8string(xsapi_internal_string value) { return value; } xsapi_internal_string convert::to_utf8string(const xsapi_internal_wstring &value) { return utf16_to_utf8_internal(value); } std::string convert::utf16_to_utf8(const std::wstring &w) { #if defined(CPPREST_STDLIB_UNICODE_CONVERSIONS) std::wstring_convert, utf16char> conversion; return conversion.to_bytes(w); #else std::string dest; dest.reserve(w.size()); for (auto src = w.begin(); src != w.end(); ++src) { // Check for high surrogate. if (*src >= H_SURROGATE_START && *src <= H_SURROGATE_END) { const auto highSurrogate = *src++; if (src == w.end()) { throw std::range_error("UTF-16 string is missing low surrogate"); } const auto lowSurrogate = *src; if (lowSurrogate < L_SURROGATE_START || lowSurrogate > L_SURROGATE_END) { throw std::range_error("UTF-16 string has invalid low surrogate"); } // To get from surrogate pair to Unicode code point: // - subract 0xD800 from high surrogate, this forms top ten bits // - subract 0xDC00 from low surrogate, this forms low ten bits // - add 0x10000 // Leaves a code point in U+10000 to U+10FFFF range. uint32_t codePoint = highSurrogate - H_SURROGATE_START; codePoint <<= 10; codePoint |= lowSurrogate - L_SURROGATE_START; codePoint += SURROGATE_PAIR_START; // 4 bytes need using 21 bits dest.push_back(char((codePoint >> 18) | 0xF0)); // leading 3 bits dest.push_back(char(((codePoint >> 12) & LOW_6BITS) | BIT8)); // next 6 bits dest.push_back(char(((codePoint >> 6) & LOW_6BITS) | BIT8)); // next 6 bits dest.push_back(char((codePoint & LOW_6BITS) | BIT8)); // trailing 6 bits } else { if (*src <= 0x7F) // single byte character { dest.push_back(static_cast(*src)); } else if (*src <= 0x7FF) // 2 bytes needed (11 bits used) { dest.push_back(char((*src >> 6) | 0xC0)); // leading 5 bits dest.push_back(char((*src & LOW_6BITS) | BIT8)); // trailing 6 bits } else // 3 bytes needed (16 bits used) { dest.push_back(char((*src >> 12) | 0xE0)); // leading 4 bits dest.push_back(char(((*src >> 6) & LOW_6BITS) | BIT8)); // middle 6 bits dest.push_back(char((*src & LOW_6BITS) | BIT8)); // trailing 6 bits } } } return dest; #endif } xsapi_internal_string convert::utf16_to_utf8_internal(const xsapi_internal_wstring &w) { #if defined(CPPREST_STDLIB_UNICODE_CONVERSIONS) std::wstring_convert, utf16char> conversion; return conversion.to_bytes(w); #else xsapi_internal_string dest; dest.reserve(w.size()); for (auto src = w.begin(); src != w.end(); ++src) { // Check for high surrogate. if (*src >= H_SURROGATE_START && *src <= H_SURROGATE_END) { const auto highSurrogate = *src++; if (src == w.end()) { throw std::range_error("UTF-16 string is missing low surrogate"); } const auto lowSurrogate = *src; if (lowSurrogate < L_SURROGATE_START || lowSurrogate > L_SURROGATE_END) { throw std::range_error("UTF-16 string has invalid low surrogate"); } // To get from surrogate pair to Unicode code point: // - subract 0xD800 from high surrogate, this forms top ten bits // - subract 0xDC00 from low surrogate, this forms low ten bits // - add 0x10000 // Leaves a code point in U+10000 to U+10FFFF range. uint32_t codePoint = highSurrogate - H_SURROGATE_START; codePoint <<= 10; codePoint |= lowSurrogate - L_SURROGATE_START; codePoint += SURROGATE_PAIR_START; // 4 bytes need using 21 bits dest.push_back(char((codePoint >> 18) | 0xF0)); // leading 3 bits dest.push_back(char(((codePoint >> 12) & LOW_6BITS) | BIT8)); // next 6 bits dest.push_back(char(((codePoint >> 6) & LOW_6BITS) | BIT8)); // next 6 bits dest.push_back(char((codePoint & LOW_6BITS) | BIT8)); // trailing 6 bits } else { if (*src <= 0x7F) // single byte character { dest.push_back(static_cast(*src)); } else if (*src <= 0x7FF) // 2 bytes needed (11 bits used) { dest.push_back(char((*src >> 6) | 0xC0)); // leading 5 bits dest.push_back(char((*src & LOW_6BITS) | BIT8)); // trailing 6 bits } else // 3 bytes needed (16 bits used) { dest.push_back(char((*src >> 12) | 0xE0)); // leading 4 bits dest.push_back(char(((*src >> 6) & LOW_6BITS) | BIT8)); // middle 6 bits dest.push_back(char((*src & LOW_6BITS) | BIT8)); // trailing 6 bits } } } return dest; #endif } xsapi_internal_string convert::to_base64(const Vector& input) { if (input.size() == 0) { // return empty string return String(); } return detail::_to_base64(&input[0], input.size()); } std::vector convert::from_base64(const xsapi_internal_string& str) { return detail::_from_base64(str); } namespace details { const std::error_category & platform_category() { #ifdef _WIN32 return windows_category(); #else return linux_category(); #endif } #ifdef _WIN32 // Remove once VS 2013 is no longer supported. #if _MSC_VER < 1900 static details::windows_category_impl instance; #endif const std::error_category & windows_category() { #if _MSC_VER >= 1900 static details::windows_category_impl instance; #endif return instance; } std::string windows_category_impl::message(int errorCode) const CPPREST_NOEXCEPT { #if 0 // this returns a non-mem hooked string which can't be changed so commenting it out since its not really needed const size_t buffer_size = 4096; DWORD dwFlags = FORMAT_MESSAGE_FROM_SYSTEM; LPCVOID lpSource = NULL; #if !defined(__cplusplus_winrt) if (errorCode >= 12000) { dwFlags = FORMAT_MESSAGE_FROM_HMODULE; lpSource = GetModuleHandleA("winhttp.dll"); // this handle DOES NOT need to be freed } #endif std::wstring buffer; buffer.resize(buffer_size); const auto result = ::FormatMessageW( dwFlags, lpSource, errorCode, 0, &buffer[0], buffer_size, NULL); if (result == 0) { std::ostringstream os; os << "Unable to get an error message for error code: " << errorCode << "."; return os.str(); } return convert::to_utf8string(buffer); #else UNREFERENCED_PARAMETER(errorCode); return std::string(); #endif } std::error_condition windows_category_impl::default_error_condition(int errorCode) const CPPREST_NOEXCEPT { // First see if the STL implementation can handle the mapping for common cases. const std::error_condition errCondition = std::system_category().default_error_condition(errorCode); const std::string errConditionMsg = errCondition.message(); if (_stricmp(errConditionMsg.c_str(), "unknown error") != 0) { return errCondition; } switch (errorCode) { #ifndef __cplusplus_winrt case ERROR_WINHTTP_TIMEOUT: return std::errc::timed_out; case ERROR_WINHTTP_CANNOT_CONNECT: return std::errc::host_unreachable; case ERROR_WINHTTP_CONNECTION_ERROR: return std::errc::connection_aborted; #endif case INET_E_RESOURCE_NOT_FOUND: case INET_E_CANNOT_CONNECT: return std::errc::host_unreachable; case INET_E_CONNECTION_TIMEOUT: return std::errc::timed_out; case INET_E_DOWNLOAD_FAILURE: return std::errc::connection_aborted; default: break; } return std::error_condition(errorCode, *this); } #else const std::error_category & linux_category() { // On Linux we are using boost error codes which have the exact same // mapping and are equivalent with std::generic_category error codes. return std::generic_category(); } #endif } } #if HC_PLATFORM_IS_MICROSOFT #pragma warning( push ) #pragma warning( disable : 26444 ) // ignore various unnamed objects #pragma warning( disable : 26812 ) // enum instead of enum class #endif namespace services { namespace details { xsapi_internal_string uri_components::join() { // canonicalize components first // convert scheme to lowercase std::transform(m_scheme.begin(), m_scheme.end(), m_scheme.begin(), [](char c) { return (char)tolower(c); }); // convert host to lowercase std::transform(m_host.begin(), m_host.end(), m_host.begin(), [](char c) { return (char)tolower(c); }); // canonicalize the path to have a leading slash if it's a full uri if (!m_host.empty() && m_path.empty()) { m_path = "/"; } else if (!m_host.empty() && m_path[0] != '/') { m_path.insert(m_path.begin(), 1, '/'); } xsapi_internal_string ret; #if (defined(_MSC_VER) && (_MSC_VER >= 1800)) if (!m_scheme.empty()) { ret.append(m_scheme).append({ ':' }); } if (!m_host.empty()) { ret.append("//"); if (!m_user_info.empty()) { ret.append(m_user_info).append({ '@' }); } ret.append(m_host); if (m_port > 0) { char buf[16] = { 0 }; sprintf_s(buf, sizeof(buf), ":%d", m_port); ret.append(buf); } } if (!m_path.empty()) { // only add the leading slash when the host is present if (!m_host.empty() && m_path.front() != '/') { ret.append({ '/' }); } ret.append(m_path); } if (!m_query.empty()) { ret.append({ '?' }).append(m_query); } if (!m_fragment.empty()) { ret.append({ '#' }).append(m_fragment); } return ret; #else xsapi_internal_ostringstream_t os; os.imbue(std::locale::classic()); if (!m_scheme.empty()) { os << m_scheme << ':'; } if (!m_host.empty()) { os << "//"; if (!m_user_info.empty()) { os << m_user_info << '@'; } os << m_host; if (m_port > 0) { os << ':' << m_port; } } if (!m_path.empty()) { // only add the leading slash when the host is present if (!m_host.empty() && m_path.front() != '/') { os << '/'; } os << m_path; } if (!m_query.empty()) { os << '?' << m_query; } if (!m_fragment.empty()) { os << '#' << m_fragment; } return os.str(); #endif } namespace uri_parser { bool validate(const xsapi_internal_string& encoded_string) { const char* scheme_begin = nullptr; const char* scheme_end = nullptr; const char* uinfo_begin = nullptr; const char* uinfo_end = nullptr; const char* host_begin = nullptr; const char* host_end = nullptr; int port_ptr = 0; const char* path_begin = nullptr; const char* path_end = nullptr; const char* query_begin = nullptr; const char* query_end = nullptr; const char* fragment_begin = nullptr; const char* fragment_end = nullptr; // perform a parse, but don't copy out the data return inner_parse( encoded_string.c_str(), &scheme_begin, &scheme_end, &uinfo_begin, &uinfo_end, &host_begin, &host_end, &port_ptr, &path_begin, &path_end, &query_begin, &query_end, &fragment_begin, &fragment_end); } bool parse(const xsapi_internal_string& encoded_string, uri_components& components) { const char* scheme_begin = nullptr; const char* scheme_end = nullptr; const char* host_begin = nullptr; const char* host_end = nullptr; const char* uinfo_begin = nullptr; const char* uinfo_end = nullptr; int port_ptr = 0; const char* path_begin = nullptr; const char* path_end = nullptr; const char* query_begin = nullptr; const char* query_end = nullptr; const char* fragment_begin = nullptr; const char* fragment_end = nullptr; if (inner_parse( encoded_string.c_str(), &scheme_begin, &scheme_end, &uinfo_begin, &uinfo_end, &host_begin, &host_end, &port_ptr, &path_begin, &path_end, &query_begin, &query_end, &fragment_begin, &fragment_end)) { if (scheme_begin) { components.m_scheme.assign(scheme_begin, scheme_end); // convert scheme to lowercase std::transform(components.m_scheme.begin(), components.m_scheme.end(), components.m_scheme.begin(), [](char c) { return (char)tolower(c); }); } else { components.m_scheme.clear(); } if (uinfo_begin) { components.m_user_info.assign(uinfo_begin, uinfo_end); } if (host_begin) { components.m_host.assign(host_begin, host_end); // convert host to lowercase std::transform(components.m_host.begin(), components.m_host.end(), components.m_host.begin(), [](char c) { return (char)tolower(c); }); } else { components.m_host.clear(); } if (port_ptr) { components.m_port = port_ptr; } else { components.m_port = 0; } if (path_begin) { components.m_path.assign(path_begin, path_end); } else { // default path to begin with a slash for easy comparison components.m_path = "/"; } if (query_begin) { components.m_query.assign(query_begin, query_end); } else { components.m_query.clear(); } if (fragment_begin) { components.m_fragment.assign(fragment_begin, fragment_end); } else { components.m_fragment.clear(); } return true; } else { return false; } } bool inner_parse( const char* encoded, const char** scheme_begin, const char** scheme_end, const char** uinfo_begin, const char** uinfo_end, const char** host_begin, const char** host_end, _Out_ int* port, const char** path_begin, const char** path_end, const char** query_begin, const char** query_end, const char** fragment_begin, const char** fragment_end) { *scheme_begin = nullptr; *scheme_end = nullptr; *uinfo_begin = nullptr; *uinfo_end = nullptr; *host_begin = nullptr; *host_end = nullptr; *port = 0; *path_begin = nullptr; *path_end = nullptr; *query_begin = nullptr; *query_end = nullptr; *fragment_begin = nullptr; *fragment_end = nullptr; const char* p = encoded; // IMPORTANT -- A uri may either be an absolute uri, or an relative-reference // Absolute: 'http://host.com' // Relative-Reference: '//:host.com', '/path1/path2?query', './path1:path2' // A Relative-Reference can be disambiguated by parsing for a ':' before the first slash bool is_relative_reference = true; const char* p2 = p; for (; *p2 != '/' && *p2 != '\0'; p2++) { if (*p2 == ':') { // found a colon, the first portion is a scheme is_relative_reference = false; break; } } if (!is_relative_reference) { // the first character of a scheme must be a letter if (!isalpha(*p)) { return false; } // start parsing the scheme, it's always delimited by a colon (must be present) *scheme_begin = p++; for (; *p != ':'; p++) { if (!is_scheme_character(*p)) { return false; } } *scheme_end = p; // skip over the colon p++; } // if we see two slashes next, then we're going to parse the authority portion // later on we'll break up the authority into the port and host const char* authority_begin = nullptr; const char* authority_end = nullptr; if (*p == '/' && p[1] == '/') { // skip over the slashes p += 2; authority_begin = p; // the authority is delimited by a slash (resource), question-mark (query) or octothorpe (fragment) // or by EOS. The authority could be empty ('file:///C:\file_name.txt') for (; *p != '/' && *p != '?' && *p != '#' && *p != '\0'; p++) { // We're NOT currently supporting IPv6, IPvFuture or username/password in authority if (!is_authority_character(*p)) { return false; } } authority_end = p; // now lets see if we have a port specified -- by working back from the end if (authority_begin != authority_end) { // the port is made up of all digits const char* port_begin = authority_end - 1; for (; isdigit(*port_begin) && port_begin != authority_begin; port_begin--) { } if (*port_begin == ':') { // has a port *host_begin = authority_begin; *host_end = port_begin; //skip the colon port_begin++; *port = convert::scan_string(xsapi_internal_string(port_begin, authority_end), std::locale::classic()); } else { // no port *host_begin = authority_begin; *host_end = authority_end; } // look for a user_info component const char* u_end = *host_begin; for (; is_user_info_character(*u_end) && u_end != *host_end; u_end++) { } if (*u_end == '@') { *host_begin = u_end + 1; *uinfo_begin = authority_begin; *uinfo_end = u_end; } else { uinfo_end = uinfo_begin = nullptr; } } } // if we see a path character or a slash, then the // if we see a slash, or any other legal path character, parse the path next if (*p == '/' || is_path_character(*p)) { *path_begin = p; // the path is delimited by a question-mark (query) or octothorpe (fragment) or by EOS for (; *p != '?' && *p != '#' && *p != '\0'; p++) { if (!is_path_character(*p)) { return false; } } *path_end = p; } // if we see a ?, then the query is next if (*p == '?') { // skip over the question mark p++; *query_begin = p; // the query is delimited by a '#' (fragment) or EOS for (; *p != '#' && *p != '\0'; p++) { if (!is_query_character(*p)) { return false; } } *query_end = p; } // if we see a #, then the fragment is next if (*p == '#') { // skip over the hash mark p++; *fragment_begin = p; // the fragment is delimited by EOS for (; *p != '\0'; p++) { if (!is_fragment_character(*p)) { return false; } } *fragment_end = p; } return true; } } } using namespace details; uri::uri(const details::uri_components& components) : m_components(components) { m_uri = m_components.join(); if (!details::uri_parser::validate(m_uri)) { throw uri_exception("provided uri is invalid: " + convert::to_utf8string(m_uri)); } } uri::uri(const xsapi_internal_string& uri_string) { if (!details::uri_parser::parse(uri_string, m_components)) { throw uri_exception("provided uri is invalid: " + convert::to_utf8string(uri_string)); } m_uri = m_components.join(); } uri::uri(const char* uri_string) : m_uri(uri_string) { if (!details::uri_parser::parse(uri_string, m_components)) { throw uri_exception("provided uri is invalid: " + convert::to_utf8string(uri_string)); } m_uri = m_components.join(); } xsapi_internal_string uri::encode_impl(const xsapi_internal_string& utf8raw, const std::function& should_encode) { const char* const hex = "0123456789ABCDEF"; xsapi_internal_string encoded; for (auto iter = utf8raw.begin(); iter != utf8raw.end(); ++iter) { // for utf8 encoded string, char ASCII can be greater than 127. int ch = static_cast(*iter); // ch should be same under both utf8 and utf16. if (should_encode(ch)) { encoded.push_back('%'); encoded.push_back(hex[(ch >> 4) & 0xF]); encoded.push_back(hex[ch & 0xF]); } else { // ASCII don't need to be encoded, which should be same on both utf8 and utf16. encoded.push_back((char)ch); } } return encoded; } /// /// Encodes a string by converting all characters except for RFC 3986 unreserved characters to their /// hexadecimal representation. /// xsapi_internal_string uri::encode_data_string(const xsapi_internal_string& raw) { return uri::encode_impl(raw, [](int ch) -> bool { return !uri_parser::is_unreserved(ch); }); } xsapi_internal_string uri::encode_uri(const xsapi_internal_string& raw, uri::components::component component) { // Note: we also encode the '+' character because some non-standard implementations // encode the space character as a '+' instead of %20. To better interoperate we encode // '+' to avoid any confusion and be mistaken as a space. switch (component) { case components::user_info: return uri::encode_impl(raw, [](int ch) -> bool { return !uri_parser::is_user_info_character(ch) || ch == '%' || ch == '+'; }); case components::host: return uri::encode_impl(raw, [](int ch) -> bool { // No encoding of ASCII characters in host name (RFC 3986 3.2.2) return ch > 127; }); case components::path: return uri::encode_impl(raw, [](int ch) -> bool { return !uri_parser::is_path_character(ch) || ch == '%' || ch == '+'; }); case components::query: return uri::encode_impl(raw, [](int ch) -> bool { return !uri_parser::is_query_character(ch) || ch == '%' || ch == '+'; }); case components::fragment: return uri::encode_impl(raw, [](int ch) -> bool { return !uri_parser::is_fragment_character(ch) || ch == '%' || ch == '+'; }); case components::full_uri: default: return uri::encode_impl(raw, [](int ch) -> bool { return !uri_parser::is_unreserved(ch) && !uri_parser::is_reserved(ch); }); }; } /// /// Helper function to convert a hex character digit to a decimal character value. /// Throws an exception if not a valid hex digit. /// static int hex_char_digit_to_decimal_char(int hex) { int decimal; if (hex >= '0' && hex <= '9') { decimal = hex - '0'; } else if (hex >= 'A' && hex <= 'F') { decimal = 10 + (hex - 'A'); } else if (hex >= 'a' && hex <= 'f') { decimal = 10 + (hex - 'a'); } else { throw uri_exception("Invalid hexadecimal digit"); } return decimal; } xsapi_internal_string uri::decode(const xsapi_internal_string& encoded) { xsapi_internal_string utf8raw; for (auto iter = encoded.begin(); iter != encoded.end(); ++iter) { if (*iter == '%') { if (++iter == encoded.end()) { throw uri_exception("Invalid URI string, two hexadecimal digits must follow '%'"); } int decimal_value = hex_char_digit_to_decimal_char(static_cast(*iter)) << 4; if (++iter == encoded.end()) { throw uri_exception("Invalid URI string, two hexadecimal digits must follow '%'"); } decimal_value += hex_char_digit_to_decimal_char(static_cast(*iter)); utf8raw.push_back(static_cast(decimal_value)); } else { // encoded string has to be ASCII. utf8raw.push_back(reinterpret_cast(*iter)); } } return utf8raw; } std::vector uri::split_path(const xsapi_internal_string& path) { std::vector results; xsapi_internal_istringstream iss(path); iss.imbue(std::locale::classic()); xsapi_internal_string s; while (std::getline(iss, s, '/')) { if (!s.empty()) { results.push_back(s); } } return results; } std::map uri::split_query(const xsapi_internal_string& query) { std::map results; // Split into key value pairs separated by '&'. size_t prev_amp_index = 0; while (prev_amp_index != xsapi_internal_string::npos) { size_t amp_index = query.find_first_of('&', prev_amp_index); if (amp_index == xsapi_internal_string::npos) amp_index = query.find_first_of(';', prev_amp_index); xsapi_internal_string key_value_pair = query.substr( prev_amp_index, amp_index == xsapi_internal_string::npos ? query.size() - prev_amp_index : amp_index - prev_amp_index); prev_amp_index = amp_index == xsapi_internal_string::npos ? xsapi_internal_string::npos : amp_index + 1; size_t equals_index = key_value_pair.find_first_of('='); if (equals_index == xsapi_internal_string::npos) { continue; } else if (equals_index == 0) { xsapi_internal_string value(key_value_pair.begin() + equals_index + 1, key_value_pair.end()); results[""] = value; } else { xsapi_internal_string key(key_value_pair.begin(), key_value_pair.begin() + equals_index); xsapi_internal_string value(key_value_pair.begin() + equals_index + 1, key_value_pair.end()); results[key] = value; } } return results; } bool uri::validate(const xsapi_internal_string& uri_string) { return uri_parser::validate(uri_string); } uri uri::authority() const { return uri_builder().set_scheme(this->scheme()).set_host(this->host()).set_port(this->port()).set_user_info(this->user_info()).to_uri(); } uri uri::resource() const { return uri_builder().set_path(this->path()).set_query(this->query()).set_fragment(this->fragment()).to_uri(); } bool uri::operator == (const uri& other) const { // Each individual URI component must be decoded before performing comparison. // TFS # 375865 if (this->is_empty() && other.is_empty()) { return true; } else if (this->is_empty() || other.is_empty()) { return false; } else if (this->scheme() != other.scheme()) { // scheme is canonicalized to lowercase return false; } else if (uri::decode(this->user_info()) != uri::decode(other.user_info())) { return false; } else if (uri::decode(this->host()) != uri::decode(other.host())) { // host is canonicalized to lowercase return false; } else if (this->port() != other.port()) { return false; } else if (uri::decode(this->path()) != uri::decode(other.path())) { return false; } else if (uri::decode(this->query()) != uri::decode(other.query())) { return false; } else if (uri::decode(this->fragment()) != uri::decode(other.fragment())) { return false; } return true; } uri_builder& uri_builder::append_path(const xsapi_internal_string& path, bool is_encode) { if (path.empty() || path == "/") { return *this; } auto encoded_path = is_encode ? uri::encode_uri(path, uri::components::path) : path; auto thisPath = this->path(); if (thisPath.empty() || thisPath == "/") { if (encoded_path.front() != '/') { set_path("/" + encoded_path); } else { set_path(encoded_path); } } else if (thisPath.back() == '/' && encoded_path.front() == '/') { thisPath.pop_back(); set_path(thisPath + encoded_path); } else if (thisPath.back() != '/' && encoded_path.front() != '/') { set_path(thisPath + "/" + encoded_path); } else { // Only one slash. set_path(thisPath + encoded_path); } return *this; } uri_builder& uri_builder::append_query(const xsapi_internal_string& query, bool is_encode) { if (query.empty()) { return *this; } auto encoded_query = is_encode ? uri::encode_uri(query, uri::components::query) : query; auto thisQuery = this->query(); if (thisQuery.empty()) { this->set_query(encoded_query); } else if (thisQuery.back() == '&' && encoded_query.front() == '&') { thisQuery.pop_back(); this->set_query(thisQuery + encoded_query); } else if (thisQuery.back() != '&' && encoded_query.front() != '&') { this->set_query(thisQuery + "&" + encoded_query); } else { // Only one ampersand. this->set_query(thisQuery + encoded_query); } return *this; } uri_builder& uri_builder::append(const xbox::services::uri& relative_uri) { append_path(relative_uri.path()); append_query(relative_uri.query()); this->set_fragment(this->fragment() + relative_uri.fragment()); return *this; } xsapi_internal_string uri_builder::to_string() { return to_uri().to_string(); } uri uri_builder::to_uri() { return uri(m_uri); } bool uri_builder::is_valid() { return uri::validate(m_uri.join()); } } } #if HC_PLATFORM_IS_MICROSOFT #pragma warning( pop ) #endif ================================================ FILE: Source/Shared/http_utils.h ================================================ #pragma once #include #include #include #ifndef _WIN32 # define __STDC_LIMIT_MACROS # include #else #include #endif namespace xbox { namespace services { #ifdef _WIN32 #define _UTF16_STRINGS #endif // We should be using a 64-bit size type for most situations that do // not involve specifying the size of a memory allocation or buffer. typedef uint64_t size64_t; #ifdef _UTF16_STRINGS // // On Windows, all strings are wide // typedef wchar_t char_t ; typedef std::wstring string_t; #define _XPLATSTR(x) L ## x typedef std::wostringstream ostringstream_t; typedef std::wofstream ofstream_t; typedef std::wostream ostream_t; typedef std::wistream istream_t; typedef std::wifstream ifstream_t; typedef std::wistringstream istringstream_t; typedef std::wstringstream stringstream_t; #define ucout std::wcout #define ucin std::wcin #define ucerr std::wcerr #else // // On POSIX platforms, all strings are narrow // typedef char char_t; typedef std::string string_t; #define _XPLATSTR(x) x typedef std::ostringstream ostringstream_t; typedef std::ofstream ofstream_t; typedef std::ostream ostream_t; typedef std::istream istream_t; typedef std::ifstream ifstream_t; typedef std::istringstream istringstream_t; typedef std::stringstream stringstream_t; #define ucout std::cout #define ucin std::cin #define ucerr std::cerr #endif // endif _UTF16_STRINGS #ifndef _TURN_OFF_PLATFORM_STRING #define U(x) _XPLATSTR(x) #endif // !_TURN_OFF_PLATFORM_STRING typedef char utf8char; typedef std::string utf8string; typedef xsapi_internal_stringstream utf8stringstream; typedef std::ostringstream utf8ostringstream; typedef std::ostream utf8ostream; typedef std::istream utf8istream; typedef std::istringstream utf8istringstream; #ifdef _UTF16_STRINGS typedef wchar_t utf16char; typedef std::wostringstream utf16ostringstream; typedef std::wostream utf16ostream; typedef std::wistream utf16istream; typedef std::wistringstream utf16istringstream; #else typedef char16_t utf16char; typedef std::basic_ostringstream utf16ostringstream; typedef std::basic_ostream utf16ostream; typedef std::basic_istream utf16istream; typedef std::basic_istringstream utf16istringstream; #endif class datetime { public: typedef uint64_t interval_type; /// /// Defines the supported date and time string formats. /// enum date_format { RFC_1123, ISO_8601 }; /// /// Returns the current UTC time. /// static datetime utc_now(); /// /// An invalid UTC timestamp value. /// enum :interval_type { utc_timestamp_invalid = static_cast(-1) }; /// /// Returns seconds since Unix/POSIX time epoch at 01-01-1970 00:00:00. /// If time is before epoch, utc_timestamp_invalid is returned. /// static interval_type utc_timestamp() { const auto seconds = utc_now().to_interval() / _secondTicks; if (seconds >= 11644473600LL) { return seconds - 11644473600LL; } else { return utc_timestamp_invalid; } } datetime() : m_interval(0) { } /// /// Creates datetime from a string representing time in UTC in RFC 1123 format. /// /// Returns a datetime of zero if not successful. static datetime from_string(const xsapi_internal_string& timestring, date_format format = RFC_1123); /// /// Returns a string representation of the datetime. /// xsapi_internal_string to_string(date_format format = RFC_1123) const; /// /// Returns the integral time value. /// interval_type to_interval() const { return m_interval; } datetime operator- (interval_type value) const { return datetime(m_interval - value); } datetime operator+ (interval_type value) const { return datetime(m_interval + value); } bool operator== (datetime dt) const { return m_interval == dt.m_interval; } bool operator!= (const datetime& dt) const { return !(*this == dt); } static interval_type from_milliseconds(unsigned int milliseconds) { return milliseconds * _msTicks; } static interval_type from_seconds(unsigned int seconds) { return seconds * _secondTicks; } static interval_type from_minutes(unsigned int minutes) { return minutes * _minuteTicks; } static interval_type from_hours(unsigned int hours) { return hours * _hourTicks; } static interval_type from_days(unsigned int days) { return days * _dayTicks; } bool is_initialized() const { return m_interval != 0; } private: friend int operator- (datetime t1, datetime t2); static const interval_type _msTicks = static_cast(10000); static const interval_type _secondTicks = 1000 * _msTicks; static const interval_type _minuteTicks = 60 * _secondTicks; static const interval_type _hourTicks = 60 * 60 * _secondTicks; static const interval_type _dayTicks = 24 * 60 * 60 * _secondTicks; #ifdef _WIN32 // void* to avoid pulling in windows.h static bool system_type_to_datetime(/*SYSTEMTIME*/ void* psysTime, uint64_t seconds, datetime* pdt); #else static datetime timeval_to_datetime(const timeval& time); #endif // Private constructor. Use static methods to create an instance. datetime(interval_type interval) : m_interval(interval) { } // Storing as hundreds of nanoseconds 10e-7, i.e. 1 here equals 100ns. interval_type m_interval; }; inline int operator- (datetime t1, datetime t2) { auto diff = (t1.m_interval - t2.m_interval); // Round it down to seconds diff /= 10 * 1000 * 1000; return static_cast(diff); } /// Functions for Unicode string conversions. namespace convert { /// /// Converts a UTF-16 string to a UTF-8 string. /// /// A two byte character UTF-16 string. /// A single byte character UTF-8 string. xsapi_internal_string utf16_to_utf8_internal(const xsapi_internal_wstring &w); std::string utf16_to_utf8(const std::wstring &w); /// /// Converts a UTF-8 string to a UTF-16 /// /// A single byte character UTF-8 string. /// A two byte character UTF-16 string. xsapi_internal_wstring utf8_to_utf16(const xsapi_internal_string &s); /// /// Converts to a UTF-8 string. /// /// A single byte character UTF-8 string. /// A single byte character UTF-8 string. xsapi_internal_string to_utf8string(xsapi_internal_string value); /// /// Converts to a UTF-8 string. /// /// A two byte character UTF-16 string. /// A single byte character UTF-8 string. xsapi_internal_string to_utf8string(const xsapi_internal_wstring &value); /// /// Converts to a platform dependent Unicode string type. /// /// A single byte character UTF-8 string. /// A platform dependent string type. string_t to_string_t(const xsapi_internal_string_t &s); /// /// Encode the given byte array into a base64 string /// String to_base64(const Vector& data); /// /// Decode the given base64 string to a byte array /// std::vector from_base64(const xsapi_internal_string& str); template xsapi_internal_string print_string(const Source& val, const std::locale& loc) { xsapi_internal_ostringstream oss; (void) oss.imbue(loc); oss << val; if (oss.bad()) { throw std::bad_cast(); } return oss.str(); } template Target scan_string(const xsapi_internal_string& str, const std::locale& loc) { Target t; xsapi_internal_istringstream iss(str); (void) iss.imbue(loc); iss >> t; if (iss.bad()) { throw std::bad_cast(); } return t; } } namespace details { /// /// Gets the one global instance of the current platform's error category. /// const std::error_category & platform_category(); /// /// Creates an instance of std::system_error from a OS error code. /// inline std::system_error create_system_error(unsigned long errorCode) { std::error_code code((int)errorCode, platform_category()); return std::system_error(code, code.message()); } #ifdef _WIN32 /// /// Category error type for Windows OS errors. /// class windows_category_impl : public std::error_category { public: virtual const char *name() const noexcept { return "windows"; } virtual std::string message(int errorCode) const noexcept; virtual std::error_condition default_error_condition(int errorCode) const noexcept; }; /// /// Gets the one global instance of the windows error category. /// /// An error category instance. const std::error_category & windows_category(); #else /// /// Gets the one global instance of the linux error category. /// /// An error category instance. const std::error_category & linux_category(); #endif /// /// Our own implementation of alpha numeric instead of std::isalnum to avoid /// taking global lock for performance reasons. /// inline bool is_alnum(char ch) { return (ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'); } } } namespace services { namespace details { struct uri_components { uri_components() : m_path("/"), m_port(-1) {} uri_components(const uri_components& other) : m_scheme(other.m_scheme), m_host(other.m_host), m_user_info(other.m_user_info), m_path(other.m_path), m_query(other.m_query), m_fragment(other.m_fragment), m_port(other.m_port) {} uri_components& operator=(const uri_components& other) { if (this != &other) { m_scheme = other.m_scheme; m_host = other.m_host; m_user_info = other.m_user_info; m_path = other.m_path; m_query = other.m_query; m_fragment = other.m_fragment; m_port = other.m_port; } return *this; } uri_components(uri_components&& other) noexcept : m_scheme(std::move(other.m_scheme)), m_host(std::move(other.m_host)), m_user_info(std::move(other.m_user_info)), m_path(std::move(other.m_path)), m_query(std::move(other.m_query)), m_fragment(std::move(other.m_fragment)), m_port(other.m_port) {} uri_components& operator=(uri_components&& other) noexcept { if (this != &other) { m_scheme = std::move(other.m_scheme); m_host = std::move(other.m_host); m_user_info = std::move(other.m_user_info); m_path = std::move(other.m_path); m_query = std::move(other.m_query); m_fragment = std::move(other.m_fragment); m_port = other.m_port; } return *this; } xsapi_internal_string join(); xsapi_internal_string m_scheme; xsapi_internal_string m_host; xsapi_internal_string m_user_info; xsapi_internal_string m_path; xsapi_internal_string m_query; xsapi_internal_string m_fragment; int m_port; }; namespace uri_parser { /// /// Parses the uri, attempting to determine its validity. /// /// This function accepts both uris ('http://msn.com') and uri relative-references ('path1/path2?query') /// bool validate(const xsapi_internal_string& encoded_string); /// /// Parses the uri, setting each provided string to the value of that component. Components /// that are not part of the provided text are set to the empty string. Component strings /// DO NOT contain their beginning or ending delimiters. /// /// This function accepts both uris ('http://msn.com') and uri relative-references ('path1/path2?query') /// bool parse(const xsapi_internal_string& encoded_string, uri_components& components); /// /// Unreserved characters are those that are allowed in a URI but do not have a reserved purpose. They include: /// - A-Z /// - a-z /// - 0-9 /// - '-' (hyphen) /// - '.' (period) /// - '_' (underscore) /// - '~' (tilde) /// inline bool is_unreserved(int c) { return ::xbox::services::details::is_alnum((char)c) || c == '-' || c == '.' || c == '_' || c == '~'; } /// /// General delimiters serve as the delimiters between different uri components. /// General delimiters include: /// - All of these :/?#[]@ /// inline bool is_gen_delim(int c) { return c == ':' || c == '/' || c == '?' || c == '#' || c == '[' || c == ']' || c == '@'; } /// /// Subdelimiters are those characters that may have a defined meaning within component /// of a uri for a particular scheme. They do not serve as delimiters in any case between /// uri segments. sub_delimiters include: /// - All of these !$&'()*+,;= /// inline bool is_sub_delim(int c) { switch (c) { case '!': case '$': case '&': case '\'': case '(': case ')': case '*': case '+': case ',': case ';': case '=': return true; default: return false; } } /// /// Reserved characters includes the general delimiters and sub delimiters. Some characters /// are neither reserved nor unreserved, and must be percent-encoded. /// inline bool is_reserved(int c) { return is_gen_delim(c) || is_sub_delim(c); } /// /// Legal characters in the scheme portion include: /// - Any alphanumeric character /// - '+' (plus) /// - '-' (hyphen) /// - '.' (period) /// /// Note that the scheme must BEGIN with an alpha character. /// inline bool is_scheme_character(int c) { return ::xbox::services::details::is_alnum((char)c) || c == '+' || c == '-' || c == '.'; } /// /// Legal characters in the user information portion include: /// - Any unreserved character /// - The percent character ('%'), and thus any percent-endcoded octet /// - The sub-delimiters /// - ':' (colon) /// inline bool is_user_info_character(int c) { return is_unreserved(c) || is_sub_delim(c) || c == '%' || c == ':'; } /// /// Legal characters in the host portion include: /// - Any unreserved character /// - The percent character ('%'), and thus any percent-endcoded octet /// - The sub-delimiters /// - ':' (colon) /// - '[' (open bracket) /// - ']' (close bracket) /// inline bool is_host_character(int c) { return is_unreserved(c) || is_sub_delim(c) || c == '%' || c == ':' || c == '[' || c == ']'; } /// /// Legal characters in the authority portion include: /// - Any unreserved character /// - The percent character ('%'), and thus any percent-endcoded octet /// - The sub-delimiters /// - ':' (colon) /// /// Note that we don't currently support: /// - IPv6 addresses (requires '[]') /// inline bool is_authority_character(int c) { return is_unreserved(c) || is_sub_delim(c) || c == '%' || c == '@' || c == ':'; } /// /// Legal characters in the path portion include: /// - Any unreserved character /// - The percent character ('%'), and thus any percent-endcoded octet /// - The sub-delimiters /// - ':' (colon) /// - '@' (ampersand) /// inline bool is_path_character(int c) { return is_unreserved(c) || is_sub_delim(c) || c == '%' || c == '/' || c == ':' || c == '@'; } /// /// Legal characters in the query portion include: /// - Any path character /// - '?' (question mark) /// inline bool is_query_character(int c) { return is_path_character(c) || c == '?'; } /// /// Legal characters in the fragment portion include: /// - Any path character /// - '?' (question mark) /// inline bool is_fragment_character(int c) { // this is intentional, they have the same set of legal characters return is_query_character(c); } /// /// Parses the uri, setting the given pointers to locations inside the given buffer. /// 'encoded' is expected to point to an encoded zero-terminated string containing a uri /// bool inner_parse( const char* encoded, const char** scheme_begin, const char** scheme_end, const char** uinfo_begin, const char** uinfo_end, const char** host_begin, const char** host_end, _Out_ int* port, const char** path_begin, const char** path_end, const char** query_begin, const char** query_end, const char** fragment_begin, const char** fragment_end); } } /// /// A single exception type to represent errors in parsing, encoding, and decoding URIs. /// class uri_exception : public std::exception { public: uri_exception(xsapi_internal_string msg) : m_msg(std::move(msg)) {} ~uri_exception() noexcept {} const char* what() const noexcept { return m_msg.c_str(); } private: xsapi_internal_string m_msg; }; /// /// A flexible, protocol independent URI implementation. /// /// URI instances are immutable. Querying the various fields on an emtpy URI will return empty strings. Querying /// various diagnostic members on an empty URI will return false. /// /// /// This implementation accepts both URIs ('http://msn.com/path') and URI relative-references /// ('/path?query#frag'). /// /// This implementation does not provide any scheme-specific handling -- an example of this /// would be the following: 'http://path1/path'. This is a valid URI, but it's not a valid /// http-uri -- that is, it's syntactically correct but does not conform to the requirements /// of the http scheme (http requires a host). /// We could provide this by allowing a pluggable 'scheme' policy-class, which would provide /// extra capability for validating and canonicalizing a URI according to scheme, and would /// introduce a layer of type-safety for URIs of differing schemes, and thus differing semantics. /// /// One issue with implementing a scheme-independent URI facility is that of comparing for equality. /// For instance, these URIs are considered equal 'http://msn.com', 'http://msn.com:80'. That is -- /// the 'default' port can be either omitted or explicit. Since we don't have a way to map a scheme /// to it's default port, we don't have a way to know these are equal. This is just one of a class of /// issues with regard to scheme-specific behavior. /// class uri { public: /// /// The various components of a URI. This enum is used to indicate which /// URI component is being encoded to the encode_uri_component. This allows /// specific encoding to be performed. /// /// Scheme and port don't allow '%' so they don't need to be encoded. /// class components { public: enum component { user_info, host, path, query, fragment, full_uri }; }; /// /// Encodes a URI component according to RFC 3986. /// Note if a full URI is specified instead of an individual URI component all /// characters not in the unreserved set are escaped. /// /// The URI as a string. /// The encoded string. static xsapi_internal_string encode_uri(const xsapi_internal_string& raw, uri::components::component = components::full_uri); /// /// Encodes a string by converting all characters except for RFC 3986 unreserved characters to their /// hexadecimal representation. /// /// The UTF-8 string data. /// The encoded string. static xsapi_internal_string encode_data_string(const xsapi_internal_string& utf8data); /// /// Decodes an encoded string. /// /// The URI as a string. /// The decoded string. static xsapi_internal_string decode(const xsapi_internal_string& encoded); /// /// Splits a path into its hierarchical components. /// /// The path as a string /// A std::vector<xsapi_internal_string> containing the segments in the path. static std::vector split_path(const xsapi_internal_string& path); /// /// Splits a query into its key-value components. /// /// The query string /// A std::map<xsapi_internal_string, xsapi_internal_string> containing the key-value components of the query. static std::map split_query(const xsapi_internal_string& query); /// /// Validates a string as a URI. /// /// The URI string to be validated. /// true if the given string represents a valid URI, false otherwise. static bool validate(const xsapi_internal_string& uri_string); /// /// Creates an empty uri /// uri() { m_uri = "/"; }; /// /// Creates a URI from the given URI components. /// /// A URI components object to create the URI instance. uri(const details::uri_components& components); /// /// Creates a URI from the given encoded string. This will throw an exception if the string /// does not contain a valid URI. Use uri::validate if processing user-input. /// /// A pointer to an encoded string to create the URI instance. uri(const char* uri_string); /// /// Creates a URI from the given encoded string. This will throw an exception if the string /// does not contain a valid URI. Use uri::validate if processing user-input. /// /// An encoded URI string to create the URI instance. uri(const xsapi_internal_string& uri_string); /// /// Copy constructor. /// uri(const uri& other) : m_uri(other.m_uri), m_components(other.m_components) {} /// /// Copy assignment operator. /// uri& operator=(const uri& other) { if (this != &other) { m_uri = other.m_uri; m_components = other.m_components; } return *this; } /// /// Move constructor. /// uri(uri&& other) noexcept : m_uri(std::move(other.m_uri)), m_components(std::move(other.m_components)) {} /// /// Move assignment operator /// uri& operator=(uri&& other) noexcept { if (this != &other) { m_uri = std::move(other.m_uri); m_components = std::move(other.m_components); } return *this; } /// /// Get the scheme component of the URI as an encoded string. /// /// The URI scheme as a string. const xsapi_internal_string& scheme() const { return m_components.m_scheme; } /// /// Get the user information component of the URI as an encoded string. /// /// The URI user information as a string. const xsapi_internal_string& user_info() const { return m_components.m_user_info; } /// /// Get the host component of the URI as an encoded string. /// /// The URI host as a string. const xsapi_internal_string& host() const { return m_components.m_host; } /// /// Get the port component of the URI. Returns -1 if no port is specified. /// /// The URI port as an integer. int port() const { return m_components.m_port; } /// /// Get the path component of the URI as an encoded string. /// /// The URI path as a string. const xsapi_internal_string& path() const { return m_components.m_path; } /// /// Get the query component of the URI as an encoded string. /// /// The URI query as a string. const xsapi_internal_string& query() const { return m_components.m_query; } /// /// Get the fragment component of the URI as an encoded string. /// /// The URI fragment as a string. const xsapi_internal_string& fragment() const { return m_components.m_fragment; } /// /// Creates a new uri object with the same authority portion as this one, omitting the resource and query portions. /// /// The new uri object with the same authority. uri authority() const; /// /// Gets the path, query, and fragment portion of this uri, which may be empty. /// /// The new URI object with the path, query and fragment portion of this URI. uri resource() const; /// /// An empty URI specifies no components, and serves as a default value /// bool is_empty() const { return this->m_uri.empty() || this->m_uri == "/"; } /// /// A loopback URI is one which refers to a hostname or ip address with meaning only on the local machine. /// /// /// Examples include "locahost", or ip addresses in the loopback range (127.0.0.0/24). /// /// true if this URI references the local host, false otherwise. bool is_host_loopback() const { return !is_empty() && ((host() == "localhost") || (host().size() > 4 && host().substr(0, 4) == "127.")); } /// /// A wildcard URI is one which refers to all hostnames that resolve to the local machine (using the * or +) /// /// /// http://*:80 /// bool is_host_wildcard() const { return !is_empty() && (this->host() == "*" || this->host() == "+"); } /// /// A portable URI is one with a hostname that can be resolved globally (used from another machine). /// /// true if this URI can be resolved globally (used from another machine), false otherwise. /// /// The hostname "localhost" is a reserved name that is guaranteed to resolve to the local machine, /// and cannot be used for inter-machine communication. Likewise the hostnames "*" and "+" on Windows /// represent wildcards, and do not map to a resolvable address. /// bool is_host_portable() const { return !(is_empty() || is_host_loopback() || is_host_wildcard()); } /// /// A default port is one where the port is unspecified, and will be determined by the operating system. /// The choice of default port may be dictated by the scheme (http -> 80) or not. /// /// true if this URI instance has a default port, false otherwise. bool is_port_default() const { return !is_empty() && this->port() == 0; } /// /// An "authority" URI is one with only a scheme, optional userinfo, hostname, and (optional) port. /// /// true if this is an "authority" URI, false otherwise. bool is_authority() const { return !is_empty() && is_path_empty() && query().empty() && fragment().empty(); } /// /// Returns whether the other URI has the same authority as this one /// /// The URI to compare the authority with. /// true if both the URI's have the same authority, false otherwise. bool has_same_authority(const uri& other) const { return !is_empty() && this->authority() == other.authority(); } /// /// Returns whether the path portion of this URI is empty /// /// true if the path portion of this URI is empty, false otherwise. bool is_path_empty() const { return path().empty() || path() == "/"; } /// /// Returns the full (encoded) URI as a string. /// /// The full encoded URI string. xsapi_internal_string to_string() const { return m_uri; } bool operator == (const uri& other) const; bool operator < (const uri& other) const { return m_uri < other.m_uri; } bool operator != (const uri& other) const { return !(this->operator == (other)); } private: friend class uri_builder; // Encodes all characters not in given set determined by given function. static xsapi_internal_string encode_impl(const xsapi_internal_string& raw, const std::function& should_encode); xsapi_internal_string m_uri; details::uri_components m_components; }; /// /// Builder for constructing URIs incrementally. /// class uri_builder { public: /// /// Creates a builder with an initially empty URI. /// uri_builder() {} /// /// Creates a builder with a existing URI object. /// /// Encoded string containing the URI. uri_builder(const uri& uri_str) : m_uri(uri_str.m_components) {} /// /// Get the scheme component of the URI as an encoded string. /// /// The URI scheme as a string. const xsapi_internal_string& scheme() const { return m_uri.m_scheme; } /// /// Get the user information component of the URI as an encoded string. /// /// The URI user information as a string. const xsapi_internal_string& user_info() const { return m_uri.m_user_info; } /// /// Get the host component of the URI as an encoded string. /// /// The URI host as a string. const xsapi_internal_string& host() const { return m_uri.m_host; } /// /// Get the port component of the URI. Returns -1 if no port is specified. /// /// The URI port as an integer. int port() const { return m_uri.m_port; } /// /// Get the path component of the URI as an encoded string. /// /// The URI path as a string. const xsapi_internal_string& path() const { return m_uri.m_path; } /// /// Get the query component of the URI as an encoded string. /// /// The URI query as a string. const xsapi_internal_string& query() const { return m_uri.m_query; } /// /// Get the fragment component of the URI as an encoded string. /// /// The URI fragment as a string. const xsapi_internal_string& fragment() const { return m_uri.m_fragment; } /// /// Set the scheme of the URI. /// /// Uri scheme. /// A reference to this uri_builder to support chaining. uri_builder& set_scheme(const xsapi_internal_string& scheme) { m_uri.m_scheme = scheme; return *this; } /// /// Set the user info component of the URI. /// /// User info as a decoded string. /// Specify whether to apply URI encoding to the given string. /// A reference to this uri_builder to support chaining. uri_builder& set_user_info(const xsapi_internal_string& user_info, bool do_encoding = false) { m_uri.m_user_info = do_encoding ? uri::encode_uri(user_info, uri::components::user_info) : user_info; return *this; } /// /// Set the host component of the URI. /// /// Host as a decoded string. /// Specify whether to apply URI encoding to the given string. /// A reference to this uri_builder to support chaining. uri_builder& set_host(const xsapi_internal_string& host, bool do_encoding = false) { m_uri.m_host = do_encoding ? uri::encode_uri(host, uri::components::host) : host; return *this; } /// /// Set the port component of the URI. /// /// Port as an integer. /// A reference to this uri_builder to support chaining. uri_builder& set_port(int port) { m_uri.m_port = port; return *this; } /// /// Set the port component of the URI. /// /// Port as a string. /// A reference to this uri_builder to support chaining. /// When string can't be converted to an integer the port is left unchanged. uri_builder& set_port(const xsapi_internal_string& port) { xsapi_internal_istringstream portStream(port); int port_tmp; portStream >> port_tmp; if (portStream.fail() || portStream.bad()) { throw std::invalid_argument("invalid port argument, must be non empty string containing integer value"); } m_uri.m_port = port_tmp; return *this; } /// /// Set the path component of the URI. /// /// Path as a decoded string. /// Specify whether to apply URI encoding to the given string. /// A reference to this uri_builder to support chaining. uri_builder& set_path(const xsapi_internal_string& path, bool do_encoding = false) { m_uri.m_path = do_encoding ? uri::encode_uri(path, uri::components::path) : path; return *this; } /// /// Set the query component of the URI. /// /// Query as a decoded string. /// Specify whether apply URI encoding to the given string. /// A reference to this uri_builder to support chaining. uri_builder& set_query(const xsapi_internal_string& query, bool do_encoding = false) { m_uri.m_query = do_encoding ? uri::encode_uri(query, uri::components::query) : query; return *this; } /// /// Set the fragment component of the URI. /// /// Fragment as a decoded string. /// Specify whether to apply URI encoding to the given string. /// A reference to this uri_builder to support chaining. uri_builder& set_fragment(const xsapi_internal_string& fragment, bool do_encoding = false) { m_uri.m_fragment = do_encoding ? uri::encode_uri(fragment, uri::components::fragment) : fragment; return *this; } /// /// Clears all components of the underlying URI in this uri_builder. /// void clear() { m_uri = details::uri_components(); } /// /// Appends another path to the path of this uri_builder. /// /// Path to append as a already encoded string. /// Specify whether to apply URI encoding to the given string. /// A reference to this uri_builder to support chaining. uri_builder& append_path(const xsapi_internal_string& path, bool do_encoding = false); /// /// Appends another query to the query of this uri_builder. /// /// Query to append as a decoded string. /// Specify whether to apply URI encoding to the given string. /// A reference to this uri_builder to support chaining. uri_builder& append_query(const xsapi_internal_string& query, bool do_encoding = false); /// /// Appends an relative uri (Path, Query and fragment) at the end of the current uri. /// /// The relative uri to append. /// A reference to this uri_builder to support chaining. uri_builder& append(const uri& relative_uri); /// /// Appends another query to the query of this uri_builder, encoding it first. This overload is useful when building a query segment of /// the form "element=10", where the right hand side of the query is stored as a type other than a string, for instance, an integral type. /// /// The name portion of the query string /// The value portion of the query string /// A reference to this uri_builder to support chaining. template uri_builder& append_query(const xsapi_internal_string& name, const T& value, bool do_encoding = true) { auto encodedName = name; auto encodedValue = ::xbox::services::convert::print_string(value, std::locale::classic()); if (do_encoding) { auto encodingCheck = [](int ch) { switch (ch) { // Encode '&', ';', and '=' since they are used // as delimiters in query component. case '&': case ';': case '=': case '%': case '+': return true; default: return !::xbox::services::details::uri_parser::is_query_character(ch); } }; encodedName = uri::encode_impl(encodedName, encodingCheck); encodedValue = uri::encode_impl(encodedValue, encodingCheck); } auto encodedQuery = encodedName; encodedQuery.append("="); encodedQuery.append(encodedValue); // The query key value pair was already encoded by us or the user separately. return append_query(encodedQuery, false); } /// /// Combine and validate the URI components into a encoded string. An exception will be thrown if the URI is invalid. /// /// The created URI as a string. xsapi_internal_string to_string(); /// /// Combine and validate the URI components into a URI class instance. An exception will be thrown if the URI is invalid. /// /// The create URI as a URI class instance. uri to_uri(); /// /// Validate the generated URI from all existing components of this uri_builder. /// /// Whether the URI is valid. bool is_valid(); private: details::uri_components m_uri; }; } // namespace web } ================================================ FILE: Source/Shared/i/guid.mm ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "xbl_guid.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN xsapi_internal_string generate_guid() { NSUUID* guid = [NSUUID UUID]; NSString* guidNSString = [guid UUIDString]; return guidNSString.UTF8String; } NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END ================================================ FILE: Source/Shared/i/utils_locales_ios.mm ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "xsapi_utils.h" using namespace xbox::services; xsapi_internal_vector utils::get_locale_list() { NSString *local = [NSLocale preferredLanguages][0]; string_t localeStr([local UTF8String]); xsapi_internal_vector localeList; localeList.push_back(utils::internal_string_from_string_t(localeStr)); return localeList; } ================================================ FILE: Source/Shared/internal_errors.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include #include #include #include "xsapi_utils.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN template class Result { public: Result() = default; Result(const Result&) = default; Result(Result&& other) = default; ~Result() = default; Result& operator=(const Result& rhs) = default; Result(HRESULT hr, String errorMessage = {}) : m_result{ hr }, m_errorMessage{ std::move(errorMessage) } {} Result(T payload, HRESULT hr = S_OK, String errorMessage = {}) : m_result{ hr }, m_payload{ payload }, m_errorMessage{ std::move(errorMessage) } {} Result(std::error_code errc, String errorMessage = {}) : m_errorMessage{ std::move(errorMessage) } { m_result = utils::convert_xbox_live_error_code_to_hresult(errc); } Result(T payload, std::error_code errc, String errorMessage = {}) : m_payload{ payload }, m_errorMessage{ std::move(errorMessage) } { m_result = utils::convert_xbox_live_error_code_to_hresult(errc); } HRESULT Hresult() const noexcept { return m_result; } const char* ErrorMessage() const noexcept { return m_errorMessage.c_str(); } T& Payload() noexcept { return m_payload; } const T& Payload() const noexcept { return m_payload; } T&& ExtractPayload() { return std::move(m_payload); } private: HRESULT m_result{ S_OK }; T m_payload{}; String m_errorMessage{}; }; template<> class Result { public: Result() = default; Result(const Result&) = default; template Result(const Result& other) : m_result{ other.Hresult() }, m_errorMessage{ other.ErrorMessage() } {} Result(HRESULT hr, String errorMessage = {}) : m_result{ hr }, m_errorMessage{ std::move(errorMessage) } {} Result(std::error_code errc, String errorMessage = {}) : m_errorMessage{ std::move(errorMessage) } { m_result = utils::convert_xbox_live_error_code_to_hresult(errc); } HRESULT Hresult() const noexcept { return m_result; } const char* ErrorMessage() const noexcept { return m_errorMessage.c_str(); } private: HRESULT m_result{ S_OK }; String m_errorMessage{}; }; template bool Succeeded(const Result& result) { return SUCCEEDED(result.Hresult()); } template bool Failed(const Result& result) { return FAILED(result.Hresult()); } NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END ================================================ FILE: Source/Shared/internal_mem.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN XblMemAllocFunction g_pMemAllocHook{ DefaultAlloc }; XblMemFreeFunction g_pMemFreeHook{ DefaultFree }; _Ret_maybenull_ _Post_writable_byte_size_(size) void* DefaultAlloc( size_t size, HCMemoryType memoryType ) noexcept { UNREFERENCED_PARAMETER(memoryType); if (size > 0) { return malloc(size); } return static_cast(nullptr); } void DefaultFree( _In_ _Post_invalid_ void* pointer, HCMemoryType memoryType ) noexcept { UNREFERENCED_PARAMETER(memoryType); if (pointer) { free(pointer); } } _Post_writable_byte_size_(size) void* STDAPIVCALLTYPE Alloc( size_t size, HCMemoryType memoryType ) noexcept { assert(g_pMemAllocHook); try { return g_pMemAllocHook(size, memoryType); } catch (...) { LOGS_ERROR << "Caught exception in MemAlloc hook!"; return nullptr; } } void STDAPIVCALLTYPE Free( _Post_invalid_ void* pointer, HCMemoryType memoryType ) noexcept { assert(g_pMemFreeHook); try { DISABLE_WARNING_PUSH; SUPPRESS_WARNING_UNINITIALIZED_MEMORY; g_pMemFreeHook(pointer, memoryType); DISABLE_WARNING_POP; } catch (...) { LOGS_ERROR << "Caught exception in MemFree hook!"; } } char* Make(const char* str) { auto length = strlen(str) + 1; char* copy = static_cast(Alloc(length)); if (copy != nullptr) { utils::strcpy(copy, length, str); } return copy; } char* Make(const String& str) { char* cstr = static_cast(Alloc(str.length() + 1)); if (cstr != nullptr) { utils::strcpy(cstr, str.length() + 1, str.data()); } return cstr; } char* Make(const string_t& strt) { auto cchCString = utils::utf8_from_char_t(strt.data(), nullptr, 0); char* cstr = static_cast(Alloc(cchCString)); if (cstr != nullptr) { utils::utf8_from_char_t(strt.data(), cstr, cchCString); } return cstr; } NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END ================================================ FILE: Source/Shared/internal_mem.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "xsapi-c/xbox_live_global_c.h" #include #include #include #include #include #include #include NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN // The memhooks are intentionally global and independent of GlobalState. GlobalState depends // on them being configured before it is constructed. extern XblMemAllocFunction g_pMemAllocHook; extern XblMemFreeFunction g_pMemFreeHook; // Raw allocation/deallocation functions, using memhooks if they are provided _Ret_maybenull_ _Post_writable_byte_size_(size) void* DefaultAlloc( size_t size, HCMemoryType memoryType ) noexcept; void DefaultFree( _In_ _Post_invalid_ void* pointer, HCMemoryType memoryType ) noexcept; _Post_writable_byte_size_(size) void* STDAPIVCALLTYPE Alloc( size_t size, HCMemoryType memoryType = 0 ) noexcept; void STDAPIVCALLTYPE Free( _Post_invalid_ void* pointer, HCMemoryType memoryType = 0 ) noexcept; // Memhooked STL types template struct Allocator { public: typedef T value_type; Allocator() = default; template Allocator(Allocator const&) {} T* allocate(size_t n) { T* p = static_cast(Alloc(n * sizeof(T))); if (p == nullptr) { throw std::bad_alloc(); } return p; } void deallocate(_In_opt_ void* p, size_t) { Free(p); } }; template bool operator==(Allocator const&, Allocator const&) { return true; } template bool operator!=(Allocator const&, Allocator const&) { return false; } template struct Deleter { public: Deleter() = default; void operator()(typename std::allocator_traits>::pointer p) const { Allocator alloc{}; std::allocator_traits>::destroy(alloc, std::addressof(*p)); std::allocator_traits>::deallocate(alloc, p, 1); } }; template using UniquePtr = std::unique_ptr>; template using Vector = std::vector>; template> using Map = std::map>>; template> using Set = std::set>; template, class EQUAL = std::equal_to> using UnorderedMap = std::unordered_map>>; template, class EQUAL = std::equal_to> using UnorderedSet = std::unordered_set>; template> using BasicString = std::basic_string>; using String = BasicString; using WString = BasicString; template> using BasicStringsteam = std::basic_stringstream>; using Stringstream = BasicStringsteam; using WStringstream = BasicStringsteam; template> using BasicIStringsteam = std::basic_istringstream>; using IStringstream = BasicIStringsteam; using WIStringstream = BasicIStringsteam; template> using BasicOStringsteam = std::basic_ostringstream>; using OStringstream = BasicOStringsteam; using WOStringstream = BasicOStringsteam; template using Deque = std::deque>; template using Queue = std::queue>; template using List = std::list>; // Memhooked allocation/deallocation helpers template inline std::shared_ptr MakeShared(TArgs&&... args) { #if !HC_PLATFORM_IS_MICROSOFT || _MSC_VER >= 1910 return std::allocate_shared>(Allocator(), std::forward(args)...); #else return std::allocate_shared>(std::allocator(), std::forward(args)...); #endif } template UniquePtr MakeUnique(TArgs&& ... args) { Allocator alloc{}; auto mem = alloc.allocate(1); auto obj = new (mem) T(std::forward(args)...); return UniquePtr{ obj }; } // Make specializations to allocate UTF-8 strings char* Make(const char* str); char* Make(const String& str); char* Make(const string_t& strt); template inline T* Make(TArgs&&... args) { auto mem = Alloc(sizeof(T)); return new (mem) T(std::forward(args)...); } template inline void Delete(T* ptr) { if (ptr != nullptr) { ptr->~T(); Free((void*)ptr); } } #if HC_PLATFORM_IS_MICROSOFT #pragma warning( push ) #pragma warning( disable : 6386 ) // VS2019 code analysis incorrectly warns about 'elementCount*sizeof(T)' bytes #endif template inline T* MakeArray(size_t elementCount) { auto mem = Alloc(elementCount * sizeof(T)); if (mem == nullptr) { return nullptr; } T* arrayPtr = reinterpret_cast(mem); for (size_t i = 0; i < elementCount; ++i) { arrayPtr[i] = T{}; } return arrayPtr; } template inline T* MakeArray(T* elements, size_t elementCount) { auto mem = Alloc(elementCount * sizeof(T)); if (mem == nullptr) { return nullptr; } T* arrayPtr = reinterpret_cast(mem); for (size_t i = 0; i < elementCount; ++i) { arrayPtr[i] = elements[i]; } return arrayPtr; } template inline T* MakeArray(const Vector& vector) { auto mem = Alloc(vector.size() * sizeof(T)); if (mem == nullptr) { return nullptr; } T* arrayPtr = reinterpret_cast(mem); for (size_t i = 0; i < vector.size(); ++i) { arrayPtr[i] = vector[i]; } return arrayPtr; } template inline void DeleteArray(T* arrayPtr, size_t elementCount) { for (size_t i = 0; i < elementCount; ++i) { arrayPtr[i].~T(); } Free(arrayPtr, 0); } inline char** MakeArray(const Vector& vector) { char** arrayPtr = static_cast(Alloc(vector.size() * sizeof(char*))); if (arrayPtr == nullptr) { return nullptr; } for (size_t i = 0; i < vector.size(); ++i) { arrayPtr[i] = Make(vector[i]); } return arrayPtr; } template<> inline void DeleteArray(const char** arrayPtr, size_t elementCount) { for (size_t i = 0; i < elementCount; ++i) { Delete(arrayPtr[i]); } Free(arrayPtr); } #if HC_PLATFORM_IS_MICROSOFT #pragma warning( pop ) #endif NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END // Keep old global namespace typedefs for now as well. These should be removed from xsapi lib and just kept // legacy header eventually. template using xsapi_internal_vector = xbox::services::Vector; template> using xsapi_internal_map = xbox::services::Map; template> using xsapi_internal_set = xbox::services::Set; template, class EQUAL = std::equal_to> using xsapi_internal_unordered_map = xbox::services::UnorderedMap; template, class EQUAL = std::equal_to> using xsapi_internal_unordered_set = xbox::services::UnorderedSet; using xsapi_internal_string = xbox::services::String; using xsapi_internal_wstring = xbox::services::WString; using xsapi_internal_stringstream = xbox::services::Stringstream; using xsapi_internal_wstringstream = xbox::services::WStringstream; using xsapi_internal_istringstream = xbox::services::IStringstream; using xsapi_internal_wistringstream = xbox::services::WIStringstream; using xsapi_internal_ostringstream = xbox::services::OStringstream; using xsapi_internal_wostringstream = xbox::services::WOStringstream; #ifdef _WIN32 using xsapi_internal_string_t = xbox::services::WString; using xsapi_internal_stringstream_t = xbox::services::WStringstream; using xsapi_internal_istringstream_t = xbox::services::WIStringstream; using xsapi_internal_ostringstream_t = xbox::services::WOStringstream; #else using xsapi_internal_string_t = xbox::services::String; using xsapi_internal_stringstream_t = xbox::services::Stringstream; using xsapi_internal_istringstream_t = xbox::services::IStringstream; using xsapi_internal_ostringstream_t = xbox::services::OStringstream; #endif template using xsapi_internal_dequeue = xbox::services::Deque; template using xsapi_internal_queue = xbox::services::Queue; template using xsapi_internal_list = xbox::services::List; ================================================ FILE: Source/Shared/internal_types.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include #include #include #include "xsapi-c/types_c.h" #if HC_PLATFORM_IS_MICROSOFT #define _XSAPIIMP_DEPRECATED __declspec(deprecated) #ifdef _NO_XSAPIIMP #define _XSAPIIMP #else #ifdef _XSAPIIMP_EXPORT #define _XSAPIIMP __declspec(dllexport) #define _XSAPIIMP_DEPRECATED __declspec(dllexport, deprecated) #else #define _XSAPIIMP __declspec(dllimport) #define _XSAPIIMP_DEPRECATED __declspec(dllimport, deprecated) #endif #endif #else #if defined _NO_XSAPIIMP || __GNUC__ < 4 #define _XSAPIIMP #define _XSAPIIMP_DEPRECATED __attribute__ ((deprecated)) #else #define _XSAPIIMP __attribute__ ((visibility ("default"))) #define _XSAPIIMP_DEPRECATED __attribute__ ((visibility ("default"), deprecated)) #endif #endif typedef void* function_context; #if HC_PLATFORM_IS_MICROSOFT typedef wchar_t char_t; typedef std::wstring string_t; typedef std::wstringstream stringstream_t; typedef std::wregex regex_t; typedef std::wsmatch smatch_t; #else typedef char char_t; typedef std::string string_t; typedef std::stringstream stringstream_t; typedef std::regex regex_t; typedef std::smatch smatch_t; #endif #if HC_PLATFORM_IS_MICROSOFT typedef std::chrono::steady_clock chrono_clock_t; #else typedef std::chrono::system_clock chrono_clock_t; #endif // Forward declarations namespace xbox { namespace services { class xbox_live_context_settings; namespace system { class xbox_live_user; } } } #if HC_PLATFORM != HC_PLATFORM_XDK // SSL client certificate context #if HC_PLATFORM_IS_MICROSOFT #include typedef PCCERT_CONTEXT cert_context; #endif #endif #if HC_PLATFORM == HC_PLATFORM_UWP typedef Windows::System::User^ user_creation_context; #else typedef void* user_creation_context; #endif typedef XblUserHandle xbox_live_user_t; #define XBL_ALIGN_SIZE 8 #define NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN namespace xbox { namespace services { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END }} #define NAMESPACE_MICROSOFT_XBOX_SERVICES_BEGIN namespace Microsoft { namespace Xbox { namespace Services { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_END }}} #define NAMESPACE_MICROSOFT_XBOX_SERVICES_SYSTEM_CPP_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN namespace system { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_SYSTEM_CPP_END NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_SYSTEM_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_BEGIN namespace System { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_SYSTEM_END NAMESPACE_MICROSOFT_XBOX_SERVICES_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_SOCIAL_CPP_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN namespace social { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_SOCIAL_CPP_END NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_SOCIAL_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_BEGIN namespace Social { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_SOCIAL_END NAMESPACE_MICROSOFT_XBOX_SERVICES_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_ACHIEVEMENTS_CPP_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN namespace achievements { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_ACHIEVEMENTS_CPP_END NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_ACHIEVEMENTS_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_BEGIN namespace Achievements { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_ACHIEVEMENTS_END NAMESPACE_MICROSOFT_XBOX_SERVICES_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_ACHIEVEMENTS_MANAGER_CPP_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN namespace achievements { namespace manager { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_ACHIEVEMENTS_MANAGER_CPP_END NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END } } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_ACHIEVEMENTS_MANAGER_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_BEGIN namespace Achievements { namespace Manager { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_ACHIEVEMENTS_MANAGER_END NAMESPACE_MICROSOFT_XBOX_SERVICES_END } } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_LEADERBOARD_CPP_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN namespace leaderboard { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_LEADERBOARD_CPP_END NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_LEADERBOARD_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_BEGIN namespace Leaderboard { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_LEADERBOARD_END NAMESPACE_MICROSOFT_XBOX_SERVICES_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_USERSTATISTICS_CPP_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN namespace user_statistics { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_USERSTATISTICS_CPP_END NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_USERSTATISTICS_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_BEGIN namespace UserStatistics { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_USERSTATISTICS_END NAMESPACE_MICROSOFT_XBOX_SERVICES_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_CPP_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN namespace multiplayer { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_CPP_END NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_BEGIN namespace Multiplayer { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_END NAMESPACE_MICROSOFT_XBOX_SERVICES_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_MATCHMAKING_CPP_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN namespace matchmaking { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_MATCHMAKING_CPP_END NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_MATCHMAKING_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_BEGIN namespace Matchmaking { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_MATCHMAKING_END NAMESPACE_MICROSOFT_XBOX_SERVICES_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_MARKETPLACE_CPP_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN namespace marketplace { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_MARKETPLACE_CPP_END NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_MARKETPLACE_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_BEGIN namespace Marketplace { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_MARKETPLACE_END NAMESPACE_MICROSOFT_XBOX_SERVICES_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_PRIVACY_CPP_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN namespace privacy { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_PRIVACY_CPP_END NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_PRIVACY_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_BEGIN namespace Privacy { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_PRIVACY_END NAMESPACE_MICROSOFT_XBOX_SERVICES_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_RTA_CPP_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN namespace real_time_activity { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_RTA_CPP_END NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_RTA_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_BEGIN namespace RealTimeActivity { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_RTA_END NAMESPACE_MICROSOFT_XBOX_SERVICES_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_PRESENCE_CPP_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN namespace presence { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_PRESENCE_CPP_END NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_PRESENCE_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_BEGIN namespace Presence { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_PRESENCE_END NAMESPACE_MICROSOFT_XBOX_SERVICES_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_GAMESERVERPLATFORM_CPP_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN namespace game_server_platform { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_GAMESERVERPLATFORM_CPP_END NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_GAMESERVERPLATFORM_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_BEGIN namespace GameServerPlatform { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_GAMESERVERPLATFORM_END NAMESPACE_MICROSOFT_XBOX_SERVICES_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_TITLE_STORAGE_CPP_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN namespace title_storage { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_TITLE_STORAGE_CPP_END NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_TITLE_STORAGE_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_BEGIN namespace TitleStorage { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_TITLE_STORAGE_END NAMESPACE_MICROSOFT_XBOX_SERVICES_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_EVENTS_CPP_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN namespace events { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_EVENTS_CPP_END NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_EVENTS_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_BEGIN namespace Events { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_EVENTS_END NAMESPACE_MICROSOFT_XBOX_SERVICES_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_CONTEXTUAL_SEARCH_CPP_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN namespace contextual_search { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_CONTEXTUAL_SEARCH_CPP_END NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_CONTEXTUAL_SEARCH_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_BEGIN namespace ContextualSearch { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_CONTEXTUAL_SEARCH_END NAMESPACE_MICROSOFT_XBOX_SERVICES_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_ENTERTAINMENT_PROFILE_CPP_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN namespace entertainment_profile { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_ENTERTAINMENT_PROFILE_CPP_END NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_ENTERTAINMENT_PROFILE_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_BEGIN namespace EntertainmentProfile { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_ENTERTAINMENT_PROFILE_END NAMESPACE_MICROSOFT_XBOX_SERVICES_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_NOTIFICATION_CPP_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN namespace notification { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_NOTIFICATION_CPP_END NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_MANAGER_CPP_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN namespace multiplayer { namespace manager { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_MANAGER_CPP_END NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END } } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_MANAGER_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_BEGIN namespace Multiplayer { namespace Manager { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_MULTIPLAYER_MANAGER_END NAMESPACE_MICROSOFT_XBOX_SERVICES_END } } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_EXPERIMENTAL_CPP_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN namespace experimental { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_EXPERIMENTAL_CPP_END NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_EXPERIMENTAL_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_BEGIN namespace Experimental { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_EXPERIMENTAL_END NAMESPACE_MICROSOFT_XBOX_SERVICES_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_SOCIAL_MANAGER_CPP_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN namespace social { namespace manager { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_SOCIAL_MANAGER_CPP_END NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END } } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_SOCIAL_MANAGER_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_BEGIN namespace Social { namespace Manager { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_SOCIAL_MANAGER_END NAMESPACE_MICROSOFT_XBOX_SERVICES_END } } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_STAT_MANAGER_CPP_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN namespace stats { namespace manager { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_STAT_MANAGER_CPP_END NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END } } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_STATISTIC_MANAGER_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_BEGIN namespace Statistics { namespace Manager { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_STATISTIC_MANAGER_END NAMESPACE_MICROSOFT_XBOX_SERVICES_END } } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_PLAYER_STATE_CPP_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN namespace player_state { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_PLAYER_STATE_CPP_END NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_PLAYER_STATE_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_BEGIN namespace PlayerState { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_PLAYER_STATE_END NAMESPACE_MICROSOFT_XBOX_SERVICES_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_CLUBS_CPP_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN namespace clubs { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_CLUBS_CPP_END NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END } #define NAMESPACE_MICROSOFT_XBOX_SERVICES_CLUBS_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_BEGIN namespace Clubs { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_CLUBS_END NAMESPACE_MICROSOFT_XBOX_SERVICES_END } ================================================ FILE: Source/Shared/mem.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "xsapi-cpp/mem.h" #include "xsapi-cpp/system.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_SYSTEM_CPP_BEGIN NAMESPACE_MICROSOFT_XBOX_SERVICES_SYSTEM_CPP_END ================================================ FILE: Source/Shared/perf_tester.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include #include #include #define ENABLE_PERF_PROFILING 0 #if ENABLE_PERF_PROFILING #define PERF_START() xbox::services::detail::PerfTester::Instance().Start(__FUNCTION__) #define PERF_STOP() xbox::services::detail::PerfTester::Instance().Stop(__FUNCTION__) #define PERF_START_AREA(area) xbox::services::detail::PerfTester::Instance().Start(area) #define PERF_STOP_AREA(area) xbox::services::detail::PerfTester::Instance().Stop(area) namespace xbox{ namespace services{ namespace detail{ struct PerfTester { static PerfTester& Instance() { static PerfTester instance; return instance; } void Start(const char* area) { std::lock_guard lock{ m_mutex }; m_stats[area].StartTime = std::chrono::high_resolution_clock::now(); } void Stop(const char* area) { std::lock_guard lock{ m_mutex }; auto& stats{ m_stats[area] }; ++stats.Count; auto duration{ std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - stats.StartTime).count() }; stats.AverageTime = static_cast(stats.AverageTime * ((double)(stats.Count - 1) / stats.Count) + duration * (1 / (double)stats.Count)); if (duration > stats.MaxTime) { stats.MaxTime = duration; } } std::string FormatStats() const { std::stringstream ss; ss << __FUNCTION__ << std::endl; for (auto& entry : m_stats) { auto& s{ entry.second }; ss << "Area:" << entry.first << " Count:" << s.Count << " MaxTime:" << s.MaxTime << " AverageTime:" << s.AverageTime << std::endl; } return ss.str(); } private: PerfTester() = default; struct Stats { int64_t Count{ 0 }; int64_t MaxTime{ 0 }; int64_t AverageTime{ 0 }; std::chrono::time_point StartTime{}; }; mutable std::mutex m_mutex; std::map m_stats; }; } } } #else #define PERF_START() #define PERF_STOP() #define PERF_START_AREA(area) #define PERF_STOP_AREA(area) #endif ================================================ FILE: Source/Shared/public_utils_legacy.cpp ================================================ #include "pch.h" #define MS_TICKS (10000) #define SECOND_TICKS (1000 * MS_TICKS) #define MINUTE_TICKS (60 * SECOND_TICKS) #define HOUR_TICKS (60 * MINUTE_TICKS) #define DAY_TICKS (24 * HOUR_TICKS) using namespace xbox::services::cppresturi::utility; NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN xbl_error_code ConvertHrToXblErrorCode(HRESULT hr) { switch (hr) { case S_OK: return xbl_error_code::no_error; case E_OUTOFMEMORY: return xbl_error_code::bad_alloc; case E_INVALIDARG: return xbl_error_code::invalid_argument; case E_XBL_RUNTIME_ERROR: return xbl_error_code::runtime_error; case __HRESULT_FROM_WIN32(ERROR_BAD_LENGTH): return xbl_error_code::length_error; case E_BOUNDS: return xbl_error_code::out_of_range; case E_NOINTERFACE: return xbl_error_code::bad_cast; case E_UNEXPECTED: return xbl_error_code::logic_error; case WEB_E_INVALID_JSON_STRING: return xbl_error_code::json_error; case WEB_E_UNEXPECTED_CONTENT: return xbl_error_code::uri_error; case ONL_E_ACTION_REQUIRED: return xbl_error_code::auth_user_interaction_required; case E_XBL_RTA_GENERIC_ERROR: return xbl_error_code::rta_generic_error; case E_XBL_RTA_SUBSCRIPTION_LIMIT_REACHED: return xbl_error_code::rta_subscription_limit_reached; case E_XBL_RTA_ACCESS_DENIED: return xbl_error_code::rta_access_denied; case E_XBL_RTA_NOT_ACTIVATED: return xbl_error_code::rta_not_activated; case E_XBL_AUTH_UNKNOWN_ERROR: return xbl_error_code::auth_unknown_error; case E_XBL_AUTH_RUNTIME_ERROR: return xbl_error_code::auth_runtime_error; case E_XBL_AUTH_NO_TOKEN: return xbl_error_code::auth_no_token_error; case __HRESULT_FROM_WIN32(ERROR_NO_SUCH_USER): return xbl_error_code::auth_user_not_signed_in; case __HRESULT_FROM_WIN32(ERROR_CANCELLED): return xbl_error_code::auth_user_cancel; case __HRESULT_FROM_WIN32(ERROR_BAD_CONFIGURATION): return xbl_error_code::invalid_config; case E_NOTIMPL: return xbl_error_code::unsupported; // HTTP errors case __HRESULT_FROM_WIN32(ERROR_RESOURCE_DATA_NOT_FOUND): return xbl_error_code::http_status_204_resource_data_not_found; case HTTP_E_STATUS_AMBIGUOUS: return xbl_error_code::http_status_300_multiple_choices; case HTTP_E_STATUS_MOVED: return xbl_error_code::http_status_301_moved_permanently; case HTTP_E_STATUS_REDIRECT: return xbl_error_code::http_status_302_found; case HTTP_E_STATUS_REDIRECT_METHOD: return xbl_error_code::http_status_303_see_other; case HTTP_E_STATUS_NOT_MODIFIED: return xbl_error_code::http_status_304_not_modified; case HTTP_E_STATUS_USE_PROXY: return xbl_error_code::http_status_305_use_proxy; case HTTP_E_STATUS_REDIRECT_KEEP_VERB: return xbl_error_code::http_status_307_temporary_redirect; case HTTP_E_STATUS_BAD_REQUEST: return xbl_error_code::http_status_400_bad_request; case HTTP_E_STATUS_DENIED: return xbl_error_code::http_status_401_unauthorized; case HTTP_E_STATUS_PAYMENT_REQ: return xbl_error_code::http_status_402_payment_required; case HTTP_E_STATUS_FORBIDDEN: return xbl_error_code::http_status_403_forbidden; case HTTP_E_STATUS_NOT_FOUND: return xbl_error_code::http_status_404_not_found; case HTTP_E_STATUS_BAD_METHOD: return xbl_error_code::http_status_405_method_not_allowed; case HTTP_E_STATUS_NONE_ACCEPTABLE: return xbl_error_code::http_status_406_not_acceptable; case HTTP_E_STATUS_PROXY_AUTH_REQ: return xbl_error_code::http_status_407_proxy_authentication_required; case HTTP_E_STATUS_REQUEST_TIMEOUT: return xbl_error_code::http_status_408_request_timeout; case HTTP_E_STATUS_CONFLICT: return xbl_error_code::http_status_409_conflict; case HTTP_E_STATUS_GONE: return xbl_error_code::http_status_410_gone; case HTTP_E_STATUS_LENGTH_REQUIRED: return xbl_error_code::http_status_411_length_required; case HTTP_E_STATUS_PRECOND_FAILED: return xbl_error_code::http_status_412_precondition_failed; case HTTP_E_STATUS_REQUEST_TOO_LARGE: return xbl_error_code::http_status_413_request_entity_too_large; case HTTP_E_STATUS_URI_TOO_LONG: return xbl_error_code::http_status_414_request_uri_too_long; case HTTP_E_STATUS_UNSUPPORTED_MEDIA: return xbl_error_code::http_status_415_unsupported_media_type; case HTTP_E_STATUS_RANGE_NOT_SATISFIABLE: return xbl_error_code::http_status_416_requested_range_not_satisfiable; case HTTP_E_STATUS_EXPECTATION_FAILED: return xbl_error_code::http_status_417_expectation_failed; case MAKE_HTTP_HRESULT(421): return xbl_error_code::http_status_421_misdirected_request; case MAKE_HTTP_HRESULT(422): return xbl_error_code::http_status_422_unprocessable_entity; case MAKE_HTTP_HRESULT(423): return xbl_error_code::http_status_423_locked; case MAKE_HTTP_HRESULT(424): return xbl_error_code::http_status_424_failed_dependency; case MAKE_HTTP_HRESULT(426): return xbl_error_code::http_status_426_upgrade_required; case MAKE_HTTP_HRESULT(428): return xbl_error_code::http_status_428_precondition_required; case MAKE_HTTP_HRESULT(429): return xbl_error_code::http_status_429_too_many_requests; case MAKE_HTTP_HRESULT(431): return xbl_error_code::http_status_431_request_header_fields_too_large; case MAKE_HTTP_HRESULT(449): return xbl_error_code::http_status_449_retry_with; case MAKE_HTTP_HRESULT(451): return xbl_error_code::http_status_451_unavailable_for_legal_reasons; case HTTP_E_STATUS_SERVER_ERROR: return xbl_error_code::http_status_500_internal_server_error; case HTTP_E_STATUS_NOT_SUPPORTED: return xbl_error_code::http_status_501_not_implemented; case HTTP_E_STATUS_BAD_GATEWAY: return xbl_error_code::http_status_502_bad_gateway; case HTTP_E_STATUS_SERVICE_UNAVAIL: return xbl_error_code::http_status_503_service_unavailable; case HTTP_E_STATUS_GATEWAY_TIMEOUT: return xbl_error_code::http_status_504_gateway_timeout; case HTTP_E_STATUS_VERSION_NOT_SUP: return xbl_error_code::http_status_505_http_version_not_supported; case MAKE_HTTP_HRESULT(506): return xbl_error_code::http_status_506_variant_also_negotiates; case MAKE_HTTP_HRESULT(507): return xbl_error_code::http_status_507_insufficient_storage; case MAKE_HTTP_HRESULT(508): return xbl_error_code::http_status_508_loop_detected; case MAKE_HTTP_HRESULT(510): return xbl_error_code::http_status_510_not_extended; case MAKE_HTTP_HRESULT(511): return xbl_error_code::http_status_511_network_authentication_required; default: return xbl_error_code::generic_error; } } namespace legacy { string_t StringTFromUtf8(_In_z_ const char* utf8) { if (utf8 == nullptr) { return string_t(); } #if HC_PLATFORM_IS_MICROSOFT auto cchOutString = CharTFromUft8(utf8, nullptr, 0); string_t out(static_cast(cchOutString) - 1, '\0'); CharTFromUft8(utf8, &out[0], cchOutString); return out; #else return string_t(utf8); #endif } std::string StringFromStringT(_In_ const string_t& stringt) { #if HC_PLATFORM_IS_MICROSOFT auto cchOutString = Utf8FromCharT(stringt.data(), nullptr, 0); std::string out(static_cast(cchOutString) - 1, '\0'); Utf8FromCharT(stringt.data(), &out[0], cchOutString); return out; #else return std::string(stringt.data()); #endif } int Utf8FromCharT( _In_z_ const char_t* inArray, _Out_writes_z_(cchOutArray) char* outArray, _In_ int cchOutArray ) { #if HC_PLATFORM_IS_MICROSOFT // query for the buffer size auto queryResult = WideCharToMultiByte( CP_UTF8, WC_ERR_INVALID_CHARS, inArray, -1, nullptr, 0, nullptr, nullptr ); if (queryResult > cchOutArray && cchOutArray == 0) { return queryResult; } else if (queryResult == 0 || queryResult > cchOutArray) { throw std::exception("utf8_from_char_t failed"); } auto conversionResult = WideCharToMultiByte( CP_UTF8, WC_ERR_INVALID_CHARS, inArray, -1, outArray, cchOutArray, nullptr, nullptr ); if (conversionResult == 0) { throw std::exception("utf8_from_char_t failed"); } return conversionResult; #else int len = (int)strlen(inArray); if (len < cchOutArray && outArray != nullptr) { strlcpy(outArray, inArray, len + 1); } else if (cchOutArray > 0) { return 0; } return len + 1; #endif } int CharTFromUft8( _In_z_ const char* inArray, _Out_writes_z_(cchOutArray) char_t* outArray, _In_ int cchOutArray ) { #if HC_PLATFORM_IS_MICROSOFT // query for the buffer size auto queryResult = MultiByteToWideChar( CP_UTF8, MB_ERR_INVALID_CHARS, inArray, -1, nullptr, 0 ); if (queryResult > cchOutArray && cchOutArray == 0) { return queryResult; } else if (queryResult == 0 || queryResult > cchOutArray) { throw std::exception("char_t_from_utf8 failed"); } auto conversionResult = MultiByteToWideChar( CP_UTF8, MB_ERR_INVALID_CHARS, inArray, -1, outArray, cchOutArray ); if (conversionResult == 0) { throw std::exception("char_t_from_utf8 failed"); } return conversionResult; #else int len = (int)strlen(inArray); if (len < cchOutArray && outArray != nullptr) { strlcpy(outArray, inArray, len + 1); } else if (cchOutArray > 0) { return 0; } return len + 1; #endif } size_t CopyUtf8( _In_ char* destinationCharArr, _In_ size_t sizeInWords, _In_ const char* sourceCharArr ) { #if HC_PLATFORM_IS_MICROSOFT return strcpy_s(destinationCharArr, sizeInWords, sourceCharArr); #else return strlcpy(destinationCharArr, sourceCharArr, sizeInWords); #endif } string_t StringTFromUint64(_In_ uint64_t val) { stringstream_t ss; ss << val; return ss.str(); } std::string StringFromUint64(_In_ uint64_t val) { std::stringstream ss; ss << val; return ss.str(); } uint64_t Uint64FromStringT(_In_ const string_t& str) { #if HC_PLATFORM_IS_MICROSOFT return _wtoi64(str.data()); #else return strtoull(str.data(), nullptr, 0); #endif } int Stricmp(const char* left, const char* right) noexcept { #if HC_PLATFORM_IS_MICROSOFT return _stricmp(left, right); #else return strcasecmp(left, right); #endif } int Stricmp(const string_t& left, const string_t& right) { #if HC_PLATFORM_IS_MICROSOFT return _wcsicmp(left.data(), right.data()); #else return strcasecmp(left.data(), right.data()); #endif } std::string SerializeJson(const rapidjson::Value& json) { rapidjson::StringBuffer buffer; rapidjson::Writer writer(buffer); json.Accept(writer); return buffer.GetString(); } const rapidjson::Value& ExtractJsonField( _In_ const rapidjson::Value& json, _In_ const std::string& name, _In_ bool required ) { if (json.IsObject()) { auto it = json.FindMember(name.c_str()); if (it != json.MemberEnd()) { return it->value; } } if (required) { //TODO: Throw exception } return json; } uint64_t ExtractJsonUint64( _In_ const rapidjson::Value& jsonValue, _In_ const std::string& name, _In_ bool required, _In_ uint64_t defaultValue ) { const rapidjson::Value& field(ExtractJsonField(jsonValue, name, required)); if (!field.IsNumber() && !required) { return defaultValue; } return field.GetUint64(); } std::vector XuidStringVectorFromXuidArray(const uint64_t* xuids, size_t xuidsCount) { return Transform(xuids, xuidsCount, StringTFromUint64); } std::vector XuidVectorFromXuidStringVector(const std::vector& xuidStrings) { return Transform(xuidStrings, Uint64FromStringT); } std::vector StringTVectorFromCStringArray(const char** stringArray, size_t arrayCount) { return Transform(stringArray, arrayCount, StringTFromUtf8); } xbox::services::cppresturi::utility::datetime DatetimeFromTimeT(time_t time) { const uint64_t epoch_offset = 11644473600LL; uint64_t result = epoch_offset + time; result *= SECOND_TICKS; // convert to 10e-7 return xbox::services::cppresturi::utility::datetime() + result; } time_t TimeTFromDatetime(const xbox::services::cppresturi::utility::datetime& datetime) { const uint64_t epoch_offset = 11644473600LL; uint64_t seconds = datetime.to_interval() / SECOND_TICKS; if (seconds >= epoch_offset) { return (time_t)(seconds - epoch_offset); } else { // If time is before epoch, 0 is returned. return 0; } } char_t ToLower(char_t c) { return std::tolower(c, std::locale()); } std::error_code ConvertHr(HRESULT hr) { return make_error_code(ConvertHrToXblErrorCode(hr)); } } NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END ================================================ FILE: Source/Shared/public_utils_legacy.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "HookedUri/uri.h" #include "HookedUri/uri_builder.h" #include "HookedUri/asyncrt_utils.h" #ifndef MAKE_HTTP_HRESULT #define MAKE_HTTP_HRESULT(code) MAKE_HRESULT(1, 0x019, code) #endif NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN xbl_error_code ConvertHrToXblErrorCode(HRESULT hr); namespace legacy { string_t StringTFromUtf8( _In_z_ const char* utf8 ); std::string StringFromStringT(_In_ const string_t& stringt); int Utf8FromCharT( _In_z_ const char_t* inArray, _Out_writes_z_(cchOutArray) char* outArray, _In_ int cchOutArray ); int CharTFromUft8( _In_z_ const char* inArray, _Out_writes_z_(cchOutArray) char_t* outArray, _In_ int cchOutArray ); size_t CopyUtf8( _In_ char* destinationCharArr, _In_ size_t sizeInWords, _In_ const char* sourceCharArr ); string_t StringTFromUint64(_In_ uint64_t val); std::string StringFromUint64(_In_ uint64_t val); uint64_t Uint64FromStringT(_In_ const string_t& str); int Stricmp(const char* left, const char* right) noexcept; int Stricmp(const string_t& left, const string_t& right); std::string SerializeJson(const rapidjson::Value& json); const rapidjson::Value& ExtractJsonField( _In_ const rapidjson::Value& json, _In_ const std::string& name, _In_ bool required ); uint64_t ExtractJsonUint64( _In_ const rapidjson::Value& jsonValue, _In_ const std::string& name, _In_ bool required = false, _In_ uint64_t defaultValue = 0 ); template std::vector Transform(InputIt first, InputIt last, Transformer op) { std::vector out; std::transform(first, last, std::back_inserter(out), op); return out; } template std::vector Transform(const std::vector& in, Transformer op) { return Transform(in.begin(), in.end(), op); } template std::vector Transform(TIn* inArray, size_t inArrayCount, Transformer op) { return Transform(inArray, inArray + inArrayCount, op); } template std::vector Transform(TIn* inArray, size_t inArrayCount) { return Transform(inArray, inArrayCount, [](const TIn& in) { return TOut(in); }); } std::vector XuidStringVectorFromXuidArray(const uint64_t* xuids, size_t xuidsCount); std::vector XuidVectorFromXuidStringVector(const std::vector& xuidStrings); std::vector StringTVectorFromCStringArray(const char** stringArray, size_t arrayCount); xbox::services::cppresturi::utility::datetime DatetimeFromTimeT(time_t time); time_t TimeTFromDatetime(const xbox::services::cppresturi::utility::datetime& datetime); char_t ToLower(char_t c); std::error_code ConvertHr(HRESULT hr); #if !XSAPI_NO_PPL template struct AsyncWrapper { typedef std::function ResultExtractor; AsyncWrapper(ResultExtractor resultExtractor) : m_resultExtractor(std::move(resultExtractor)) { async.queue = XblGetAsyncQueue(); async.context = this; async.callback = [](XAsyncBlock* async) { auto thisPtr = static_cast*>(async->context); T result; auto hr = thisPtr->m_resultExtractor(async, result); if (SUCCEEDED(hr)) { thisPtr->m_taskCompletionEvent.set(xbl_result(result)); } else { thisPtr->m_taskCompletionEvent.set(xbl_result(ConvertHr(hr))); } delete thisPtr; }; } XAsyncBlock async{}; // If the Async API fails, the callback will never be invoked. Return a failure task and self destruct. pplx::task> Task(HRESULT asyncApiResult) { if (SUCCEEDED(asyncApiResult)) { return pplx::task>(m_taskCompletionEvent); } else { delete this; return pplx::task_from_result(xbl_result(ConvertHr(asyncApiResult))); } } private: AsyncWrapper(const AsyncWrapper&) = delete; AsyncWrapper& operator=(AsyncWrapper) = delete; ResultExtractor m_resultExtractor; pplx::task_completion_event> m_taskCompletionEvent; }; template<> struct AsyncWrapper { typedef std::function ResultExtractor; AsyncWrapper() : AsyncWrapper{ [](XAsyncBlock* async) { return XAsyncGetStatus(async, false); } } { } AsyncWrapper(ResultExtractor resultExtractor) : m_resultExtractor{ std::move(resultExtractor) } { async.queue = XblGetAsyncQueue(); async.context = this; async.callback = [](XAsyncBlock* async) { auto thisPtr = static_cast*>(async->context); auto hr = thisPtr->m_resultExtractor(async); thisPtr->m_taskCompletionEvent.set(xbl_result(ConvertHr(hr))); delete thisPtr; }; } XAsyncBlock async{}; pplx::task> Task(HRESULT asyncApiResult) { if (SUCCEEDED(asyncApiResult)) { return pplx::task>(m_taskCompletionEvent); } else { // If the Async API fails, the callback will never be invoked. Return a failure task and self destruct. delete this; return pplx::task_from_result(xbl_result(ConvertHr(asyncApiResult))); } } private: AsyncWrapper(const AsyncWrapper&) = delete; AsyncWrapper& operator=(AsyncWrapper) = delete; ResultExtractor m_resultExtractor; pplx::task_completion_event> m_taskCompletionEvent; }; #endif } NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END ================================================ FILE: Source/Shared/ref_counter.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "ref_counter.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN void RefCounter::AddRef() { if (m_refCount++ == 0) { m_extraRefHolder = GetSharedThis(); } } void RefCounter::DecRef() { if (--m_refCount == 0) { m_extraRefHolder.reset(); } } NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END ================================================ FILE: Source/Shared/ref_counter.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN // Class for managing client ref count to Xbox Live objects. All types that need client references // counted should inherit from this class struct RefCounter { RefCounter() = default; virtual ~RefCounter() = default; void AddRef(); void DecRef(); protected: virtual std::shared_ptr GetSharedThis() = 0; private: std::atomic m_refCount{ 0 }; std::shared_ptr m_extraRefHolder; }; NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END ================================================ FILE: Source/Shared/service_call_routed_handler.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "service_call_routed_handler.h" #include "http_call_request_message_internal.h" #include "xsapi_utils.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN ServiceCallRoutedHandler::ServiceCallRoutedHandler( _In_ XblCallRoutedHandler handler, _In_opt_ void* context ) noexcept : m_clientHandler{ handler }, m_clientContext{ context } { m_hcToken = HCAddCallRoutedHandler(HCCallRoutedHandler, this); } ServiceCallRoutedHandler::~ServiceCallRoutedHandler() noexcept { HCRemoveCallRoutedHandler(m_hcToken); } void ServiceCallRoutedHandler::HCCallRoutedHandler( _In_ HCCallHandle call, _In_ void* context ) { auto pThis{ static_cast(context) }; String formattedResponse{ pThis->GetFormattedResponse(call) }; XblServiceCallRoutedArgs args { call, s_nextResponseNumber++, formattedResponse.data() }; pThis->m_clientHandler(args, pThis->m_clientContext); } String ServiceCallRoutedHandler::GetFormattedResponse( HCCallHandle call ) const noexcept { Stringstream response; response << "== [XBOX SERVICE CALL] #"; response << s_nextResponseNumber; response << "\r\n"; const char* uri{ nullptr }; HCHttpCallGetRequestUrl(call, &uri); response << "\r\n[URI]: "; response << uri; const char* token{ nullptr }; HCHttpCallResponseGetHeader(call, AUTH_HEADER, &token); if (token) { response << "\r\n[Authorization Header]: "; response << token; } const char* signature{ nullptr }; HCHttpCallResponseGetHeader(call, SIG_HEADER, &signature); if (signature) { response << "\r\n[Signature Header]: "; response << signature; } uint32_t httpStatus{ 0 }; HCHttpCallResponseGetStatusCode(call, &httpStatus); response << "\r\n[HTTP Status]: "; response << httpStatus; #if HC_PLATFORM_IS_MICROSOFT HRESULT hr = utils::convert_http_status_to_hresult(httpStatus); response << " ["; response << utils::convert_hresult_to_error_name(hr); response << "] "; #endif uint32_t numHeaders{ 0 }; HCHttpCallResponseGetNumHeaders(call, &numHeaders); if (numHeaders > 0) { response << "\r\n[Response Headers]: "; const char* headerName{ nullptr }; const char* headerValue{ nullptr }; for (uint32_t i = 0; i < numHeaders; ++i) { HCHttpCallResponseGetHeaderAtIndex(call, i, &headerName, &headerValue); response << headerName << " : " << headerValue << "; "; } } const char* responseBody{ nullptr }; HCHttpCallResponseGetResponseString(call, &responseBody); if (responseBody) { response << "\r\n[Response Body]: "; response << responseBody; } response << "\r\n"; return response.str(); } std::atomic ServiceCallRoutedHandler::s_nextResponseNumber{ 0 }; NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END ================================================ FILE: Source/Shared/service_call_routed_handler.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "http_call_request_message_internal.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN class ServiceCallRoutedHandler { public: ServiceCallRoutedHandler( _In_ XblCallRoutedHandler handler, _In_opt_ void* context ) noexcept; ~ServiceCallRoutedHandler() noexcept; private: static void HCCallRoutedHandler( _In_ HCCallHandle call, _In_ void* context ); String GetFormattedResponse( HCCallHandle call ) const noexcept; XblCallRoutedHandler m_clientHandler{ nullptr }; void* m_clientContext{ nullptr }; int32_t m_hcToken{ 0 }; static std::atomic s_nextResponseNumber; }; NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END ================================================ FILE: Source/Shared/shared_macros.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "build_version.h" #ifndef XSAPI_UNIT_TESTS #define XSAPI_ASSERT(x) assert(x); #else #define XSAPI_ASSERT(x) if(!(x)) throw std::invalid_argument(""); #endif #define VERIFY_XBL_INITIALIZED() { if (xbox::services::GlobalState::Get() == nullptr) return E_XBL_NOT_INITIALIZED; } #if defined(XSAPI_UNIT_TESTS) #if !XSAPI_NO_PPL #define RETURN_TASK_CPP_INVALIDARGUMENT_IF(x, type, message) { if ( x ) { return pplx::task_from_result(xbox::services::xbox_live_result(xbox_live_error_code::invalid_argument, message)); } } #endif // !XSAPI_NO_PPL #define RETURN_CPP_INVALIDARGUMENT_IF(x, type, message) { if ( x ) { return xbox::services::xbox_live_result(xbox_live_error_code::invalid_argument, message); } } #define RETURN_HR_INVALIDARGUMENT_IF(x) { if ( x ) { return E_INVALIDARG; } } #define RETURN_HR_INVALIDARGUMENT_IF_NULL(x) { if ( ( x ) == nullptr ) { return E_INVALIDARG; } } #define RETURN_HR_INVALIDARGUMENT_IF_EMPTY_STRING(x) { if ( x[0] == 0) { return E_INVALIDARG; } } #else #if !XSAPI_NO_PPL #define RETURN_TASK_CPP_INVALIDARGUMENT_IF(x, type, message) { assert(!(x)); if ( x ) { return pplx::task_from_result(xbox::services::xbox_live_result(xbox_live_error_code::invalid_argument, message)); } } #endif // !XSAPI_NO_PPL #define RETURN_CPP_INVALIDARGUMENT_IF(x, type, message) { assert(!(x)); if ( x ) { return xbox::services::xbox_live_result(xbox_live_error_code::invalid_argument, message); } } #define RETURN_HR_INVALIDARGUMENT_IF(x) { assert(!(x)); if ( x ) { return E_INVALIDARG; } } #define RETURN_HR_INVALIDARGUMENT_IF_NULL(x) { assert(!(( x ) == nullptr)); if ( ( x ) == nullptr ) { return E_INVALIDARG; } } #define RETURN_HR_INVALIDARGUMENT_IF_EMPTY_STRING(x) { assert(!(x[0] == 0)); if ( x[0] == 0) { return E_INVALIDARG; } } #endif #define INIT_OUT_PTR_PARAM(x) { if ( x ) { *x = nullptr; } } #define THROW_CPP_INVALIDARGUMENT_IF(x) if ( x ) { throw std::invalid_argument(""); } #define THROW_CPP_INVALIDARGUMENT_IF_NULL(x) if ( ( x ) == nullptr ) { throw std::invalid_argument(""); } #if !XSAPI_NO_PPL #if defined(XSAPI_UNIT_TESTS) #define RETURN_TASK_CPP_INVALIDARGUMENT_IF(x, type, message) { if ( x ) { return pplx::task_from_result(xbox::services::xbox_live_result(xbox_live_error_code::invalid_argument, message)); } } #define RETURN_TASK_CPP_INVALIDARGUMENT_IF_STRING_EMPTY(x, type, message) { if ( x.empty() ) { return pplx::task_from_result(xbox::services::xbox_live_result(xbox_live_error_code::invalid_argument, message)); } } #else #define RETURN_TASK_CPP_INVALIDARGUMENT_IF_STRING_EMPTY(x, type, message) { assert(!x.empty()); if ( x.empty() ) { return pplx::task_from_result(xbox::services::xbox_live_result(xbox_live_error_code::invalid_argument, message)); } } #define RETURN_TASK_CPP_INVALIDARGUMENT_IF(x, type, message) { assert(!(x)); if ( x ) { return pplx::task_from_result(xbox::services::xbox_live_result(xbox_live_error_code::invalid_argument, message)); } } #endif #define RETURN_TASK_CPP_IF_ERR(x, type) if ( x.err() ) { return pplx::task_from_result(xbox::services::xbox_live_result(x.err(), x.err_message())); } #define RETURN_TASK_CPP_IF(x, type, message) { if ( x ) { return pplx::task_from_result(xbox::services::xbox_live_result(xbox_live_error_code::logic_error, message)); } } #endif // !XSAPI_NO_PPL #define THROW_CPP_INVALIDARGUMENT_IF_STRING_EMPTY(x) { auto y = x; if ( y.empty() ) { throw std::invalid_argument(""); } } #define RETURN_CPP_IF_ERR(x, type) if ( x.err() ) { return xbox::services::xbox_live_result(x.err(), x.err_message()); } #define RETURN_CPP_IF(x, type, errc, message) { if ( x ) { return xbox::services::xbox_live_result(errc, message); } } #define RETURN_HR_IF(x, hr) { if (x) { return hr; } } #define RETURN_HR_IF_FAILED(expr) { HRESULT exprResult{ expr }; if (FAILED(exprResult)) { return exprResult; } } #define RETURN_PENDING_OR_HR(expr) { HRESULT exprResult{ expr }; return SUCCEEDED(exprResult) ? E_PENDING : exprResult; } #define RETURN_HR_IF_LOG_DEBUG(x, hr, message) { if (x) { LOG_DEBUG(message); return hr; } } #define ASSERT_CPP_INVALIDARGUMENT_IF_NULL(x) XSAPI_ASSERT(x != nullptr); #define ASSERT_CPP_INVALIDARGUMENT_IF(x) XSAPI_ASSERT(x) #define ASSERT_CPP_INVALIDARGUMENT_IF_STRING_EMPTY(x) XSAPI_ASSERT(!x.empty()); #define THROW_CPP_RUNTIME_IF(x,y) if ( x ) { throw std::runtime_error(y); } #define COMPLETE_ASYNC_AND_RETURN(async, result, resultSize, returnValue) { XAsyncComplete(async, result, resultSize); return returnValue; } #define COMPLETE_ASYNC_AND_RETURN_VOID(async, result, resultSize) { XAsyncComplete(async, result, resultSize); return; } #define NO_COPY_AND_ASSIGN(T) \ T(const T&); \ T& operator=(const T&); #define SECONDS_PER_DAY 86400 #define STRING_T_FROM_PLATFORM_STRING(x) \ (x->IsEmpty() ? string_t() : string_t(x->Data())) #define PLATFORM_STRING_FROM_STRING_T(x) \ (x.empty() ? nullptr : ref new Platform::String(x.c_str())) #define PLATFORM_STRING_FROM_INTERNAL_STRING(x) \ (x.empty() ? nullptr : ref new Platform::String(xbox::services::utils::string_t_from_internal_string(x).c_str())) #define INTERNAL_STRING_FROM_PLATFORM_STRING(x) \ (x->IsEmpty() ? xsapi_internal_string() : xbox::services::utils::internal_string_from_utf16(x->Data())) #define AUTH_HEADER ("Authorization") #define SIG_HEADER ("Signature") #define ETAG_HEADER ("ETag") #define DATE_HEADER ("Date") #define RETRY_AFTER_HEADER ("Retry-After") #define DEFAULT_USER_AGENT "XboxServicesAPI/" XBOX_SERVICES_API_VERSION_STRING #define RETURN_EXCEPTION_FREE_XBOX_LIVE_RESULT(func, type) \ { \ try { return func; } \ catch (const std::exception& e) \ { \ xbox_live_error_code err = xbox::services::utils::convert_exception_to_xbox_live_error_code(); \ return xbox_live_result(err, e.what()); \ } \ } #define RETURN_EXCEPTION_FREE_XBOX_LIVE_RESULT_FROM_HR(func, type) \ { \ try \ { \ auto hr = func; \ return xbox_live_result(xbox_live_error_code(hr)); \ } \ catch (const std::exception& e) \ { \ xbox_live_error_code err = xbox::services::utils::convert_exception_to_xbox_live_error_code(); \ return xbox_live_result(err, e.what()); \ } \ } #define RETURN_EXCEPTION_FREE_HRESULT(func) \ { \ try { return func; } \ catch (const std::exception& e) \ { \ e; \ return utils::convert_exception_to_hresult(); \ } \ } #define CREATE_EXTERNAL_XBOX_LIVE_RESULT(type, internalReturnObj) \ xbox_live_result(type(internalReturnObj.payload()), internalReturnObj.err(), internalReturnObj.err_message()) #define CATCH_RETURN() \ catch (...) { return xbox::services::utils::convert_exception_to_hresult(); } #define CATCH_RETURN_WITH(errCode) \ catch (...) \ { \ HRESULT hr = xbox::services::utils::convert_exception_to_hresult(); \ xsapi_internal_stringstream ss; \ ss << "Exception reached api boundry: HR" << hr; \ LOG_ERROR(ss.str().data()); \ return errCode; \ } #define DEFINE_GET_STRING(className, methodName) \ string_t className::methodName() const \ { \ return utils::string_t_from_internal_string(m_internalObj->methodName()); \ } #define DEFINE_GET_STD_STRING(className, methodName) \ std::string className::methodName() const \ { \ return std::string(m_internalObj->methodName().data()); \ } #define DEFINE_GET_BOOL(className, methodName) \ bool className::methodName() const \ { \ return m_internalObj->methodName(); \ } #define DEFINE_GET_UINT32(className, methodName) \ uint32_t className::methodName() const \ { \ return m_internalObj->methodName(); \ } #define DEFINE_GET_ENUM_TYPE(className, enumType, methodName) \ enumType className::methodName() const \ { \ return m_internalObj->methodName(); \ } #define DEFINE_GET_URI(className, methodName) \ const xbox::services::uri& className::methodName() const \ { \ return m_internalObj->methodName(); \ } #define DEFINE_GET_VECTOR_INTERNAL_TYPE(className, externalType, methodName) \ std::vector className::methodName() const \ { \ return utils::std_vector_external_from_internal_vector>(m_internalObj->methodName()); \ } #define DEFINE_GET_VECTOR(className, typeName, methodName) \ std::vector className::methodName() const \ { \ return utils::std_vector_from_internal_vector(m_internalObj->methodName()); \ } #define DEFINE_GET_OBJECT(className, objectType, methodName) \ objectType className::methodName() const \ { \ return m_internalObj->methodName(); \ } #define DEFINE_GET_OBJECT_REF(className, objectType, methodName) \ const objectType& className::methodName() const \ { \ return m_internalObj->methodName(); \ } // Disable Warning macros // if msvc #if defined (_MSC_VER) #define DISABLE_WARNING_PUSH __pragma(warning(push)) #define DISABLE_WARNING_POP __pragma(warning(pop)) #define DISABLE_WARNING(warningCode) __pragma(warning(disable:warningCode)) // expects numeric code for msvc #define SUPPRESS_ANALYSIS_WARNING(warningCode) __pragma(warning(suppress:warningCode)) #define SUPPRESS_WARNING_NULL_PTR_DEREFERENCE SUPPRESS_ANALYSIS_WARNING(6011) #define SUPPRESS_WARNING_UNINITIALIZED_MEMORY SUPPRESS_ANALYSIS_WARNING(6001) #define SUPPRESS_WARNING_EXPRESSION_NOT_TRUE SUPPRESS_ANALYSIS_WARNING(28020) #define SUPPRESS_WARNING_UNINITIALIZED_MEMBER SUPPRESS_ANALYSIS_WARNING(26495) #define SUPPRESS_WARNING_UNNAMED_CUSTOM_OBJ SUPPRESS_ANALYSIS_WARNING(26444) #elif defined(__GNUC__) #define DO_PRAGMA(X) _Pragma(#X) #define DISABLE_WARNING_PUSH DO_PRAGMA(GCC diagnostic push) #define DISABLE_WARNING_POP DO_PRAGMA(GCC diagnostic pop) #define DISABLE_WARNING(warningCode) DO_PRAGMA(GCC diagnostic ignored #warningCode) // expects arg name for clang and gnu compilers #define SUPPRESS_ANALYSIS_WARNING(warningCode) // gnu doesn't support per-instance static analyzer warning suppression #define SUPPRESS_WARNING_NULL_PTR_DEREFERENCE #define SUPPRESS_WARNING_UNINITIALIZED_MEMORY #define SUPPRESS_WARNING_EXPRESSION_NOT_TRUE #define SUPPRESS_WARNING_UNINITIALIZED_MEMBER #define SUPPRESS_WARNING_UNNAMED_CUSTOM_OBJ #elif defined(__clang__) #define DO_PRAGMA(X) _Pragma(#X) #define DISABLE_WARNING_PUSH DO_PRAGMA(GCC diagnostic push) #define DISABLE_WARNING_POP DO_PRAGMA(GCC diagnostic pop) #define DISABLE_WARNING(warningCode) DO_PRAGMA(GCC diagnostic ignored #warningCode) // expects arg name for clang and gnu compilers #define SUPPRESS_ANALYSIS_WARNING(warningCode) // clang doesn't support per-instance static analyzer warning suppression #define SUPPRESS_WARNING_NULL_PTR_DEREFERENCE #define SUPPRESS_WARNING_UNINITIALIZED_MEMORY #define SUPPRESS_WARNING_EXPRESSION_NOT_TRUE #define SUPPRESS_WARNING_UNINITIALIZED_MEMBER #define SUPPRESS_WARNING_UNNAMED_CUSTOM_OBJ // default for non-defined platforms #else #define DISABLE_WARNING_PUSH #define DISABLE_WARNING_POP #define DISABLE_WARNING(warningCode) #define SUPPRESS_WARNING_NULL_PTR_DEREFERENCE #define SUPPRESS_WARNING_UNINITIALIZED_MEMORY #define SUPPRESS_WARNING_EXPRESSION_NOT_TRUE #define SUPPRESS_WARNING_UNINITIALIZED_MEMBER #define SUPPRESS_WARNING_UNNAMED_CUSTOM_OBJ #endif ================================================ FILE: Source/Shared/string_array.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN // RAII class wrapping an array C-Strings class UTF8StringArray { public: UTF8StringArray(const xsapi_internal_vector& vector) { std::transform(vector.begin(), vector.end(), std::back_inserter(m_strings), [](const xsapi_internal_string& in) { return Make(in); }); } UTF8StringArray(const UTF8StringArray& other) { std::transform(other.m_strings.begin(), other.m_strings.end(), std::back_inserter(m_strings), [](const char* in) { return Make(in); }); } UTF8StringArray(UTF8StringArray&& other) noexcept : m_strings{ std::move(other.m_strings) } { } UTF8StringArray& operator=(UTF8StringArray other) { std::swap(other.m_strings, m_strings); return *this; } ~UTF8StringArray() noexcept { for (auto string : m_strings) { Delete(string); } } const char** Data() noexcept { return m_strings.data(); } size_t Size() const noexcept { return m_strings.size(); } private: xsapi_internal_vector m_strings; }; NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END ================================================ FILE: Source/Shared/u/xbl_guid.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "pch.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN xsapi_internal_string generate_guid(); NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END ================================================ FILE: Source/Shared/uri_impl.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "HookedUri/details/asyncrt_utils.hpp" #include "HookedUri/details/uri.hpp" #include "HookedUri/details/uri_builder.hpp" #include "HookedUri/details/uri_parser.hpp" ================================================ FILE: Source/Shared/user.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "user.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN User::User(XblUserHandle userHandle) noexcept : m_handle(userHandle) {} User::User(User&& other) noexcept : m_handle{ other.m_handle }, m_xuid{ other.m_xuid }, m_localId { std::move(other.m_localId) } { Map::iterator it = other.m_gamertags.begin(); while(it != other.m_gamertags.end()) { m_gamertags[it->first] = std::move(it->second); it++; } other.m_gamertags.clear(); other.m_handle = nullptr; } User& User::operator=(User&& other) noexcept { std::swap(other.m_handle, m_handle); m_localId = std::move(other.m_localId); m_xuid = other.m_xuid; Map::iterator it = other.m_gamertags.begin(); while (it != other.m_gamertags.end()) { m_gamertags[it->first] = std::move(it->second); it++; } other.m_gamertags.clear(); return *this; } User::~User() noexcept { if (m_handle) { XalUserCloseHandle(m_handle); } } /*static*/ Result User::WrapHandle(XblUserHandle userHandle) noexcept { if (XblShouldFaultInject(INJECTION_FEATURE_USER)) { LOGS_ERROR << "FAULT INJECTION: User::WrapHandle ID:" << XblGetFaultCounter(); return Result{ User(nullptr), E_FAIL }; } if (userHandle == nullptr) { return Result{ User(nullptr), E_INVALIDARG }; } XalUserHandle copiedHandle; auto hr = XalUserDuplicateHandle(userHandle, &copiedHandle); if (FAILED(hr)) { LOGS_ERROR << "Copying user failed: User failed to duplicate."; return Result{ User(nullptr), hr }; } else { User user{ copiedHandle }; hr = user.InitializeUser(); if (FAILED(hr)) { LOGS_ERROR << "Copying user failed: User failed to duplicate."; return Result{ User(nullptr), hr }; } return Result{ std::move(user) , S_OK }; } } HRESULT User::InitializeUser() noexcept { auto hr = XalUserGetId(m_handle, &m_xuid); if (FAILED(hr)) { LOGS_ERROR << "Failed to get User ID with HRESULT: " << hr; return hr; } hr = XalUserGetLocalId(m_handle, &m_localId); if (FAILED(hr)) { LOGS_ERROR << "Failed to get User LocalID with HRESULT: " << hr; return hr; } auto gamertagComponentResult = GetGamertagComponent(XalGamertagComponent_Classic); if (FAILED(gamertagComponentResult.Hresult())) { LOGS_ERROR << "Failed to get Gamertag Component" << XalGamertagComponent_Classic << " with HRESULT: " << hr; return hr; } m_gamertags[XalGamertagComponent_Classic] = gamertagComponentResult.ExtractPayload(); gamertagComponentResult = GetGamertagComponent(XalGamertagComponent_Modern); if (FAILED(gamertagComponentResult.Hresult())) { LOGS_ERROR << "Failed to get Gamertag Component" << XalGamertagComponent_Modern << " with HRESULT: " << hr; return hr; } m_gamertags[XalGamertagComponent_Modern] = gamertagComponentResult.ExtractPayload(); gamertagComponentResult = GetGamertagComponent(XalGamertagComponent_ModernSuffix); if (FAILED(gamertagComponentResult.Hresult())) { LOGS_ERROR << "Failed to get Gamertag Component" << XalGamertagComponent_ModernSuffix << " with HRESULT: " << hr; return hr; } m_gamertags[XalGamertagComponent_ModernSuffix] = gamertagComponentResult.ExtractPayload(); gamertagComponentResult = GetGamertagComponent(XalGamertagComponent_UniqueModern); if (FAILED(gamertagComponentResult.Hresult())) { LOGS_ERROR << "Failed to get Gamertag Component" << XalGamertagComponent_UniqueModern << " with HRESULT: " << hr; return hr; } m_gamertags[XalGamertagComponent_UniqueModern] = gamertagComponentResult.ExtractPayload(); return S_OK; } Result User::Copy() const noexcept { if (XblShouldFaultInject(INJECTION_FEATURE_USER)) { LOGS_ERROR << "FAULT INJECTION: User::Copy ID:" << XblGetFaultCounter(); return Result{ User(nullptr), E_FAIL }; } XalUserHandle copiedHandle; auto hr = XalUserDuplicateHandle(this->m_handle, &copiedHandle); if (FAILED(hr)) { LOGS_ERROR << "Copying user failed: User failed to duplicate."; return Result{ User(nullptr), hr}; } else { User copiedUser{ copiedHandle }; hr = copiedUser.InitializeUser(); return Result{std::move(copiedUser), hr}; } } uint64_t User::Xuid() const noexcept { return m_xuid; } uint64_t User::LocalId() const noexcept { XalUserLocalId localId{ 0 }; if (m_handle != nullptr && !XblShouldFaultInject(INJECTION_FEATURE_USER)) { auto hr = XalUserGetLocalId(m_handle, &localId); if (SUCCEEDED(hr)) { m_localId = localId; } } return m_localId.value; } xsapi_internal_string User::Gamertag() const noexcept { auto result = GetGamertagComponent(XalGamertagComponent_Classic); if (Failed(result)) { return m_gamertags[XalGamertagComponent_Classic]; } else { return result.ExtractPayload(); } } xsapi_internal_string User::ModernGamertag() const noexcept { auto result = GetGamertagComponent(XalGamertagComponent_Modern); if (Failed(result)) { return m_gamertags[XalGamertagComponent_Modern]; } else { return result.ExtractPayload(); } } xsapi_internal_string User::ModernGamertagSuffix() const noexcept { auto result = GetGamertagComponent(XalGamertagComponent_ModernSuffix); if (Failed(result)) { return m_gamertags[XalGamertagComponent_ModernSuffix]; } else { return result.ExtractPayload(); } } xsapi_internal_string User::UniqueModernGamertag() const noexcept { auto result = GetGamertagComponent(XalGamertagComponent_UniqueModern); if (Failed(result)) { return m_gamertags[XalGamertagComponent_UniqueModern]; } else { return result.ExtractPayload(); } } HRESULT User::GetTokenAndSignature( const String& httpMethod, const String& url, const HttpHeaders& headers, const uint8_t* requestBody, size_t requestBodySize, bool allUsers, AsyncContext>&& async ) noexcept { if (XblShouldFaultInject(INJECTION_FEATURE_USER)) { LOGS_ERROR << "FAULT INJECTION: User::GetTokenAndSignature ID:" << XblGetFaultCounter(); return E_FAIL; } bool forceRefresh{ false }; auto state{ GlobalState::Get() }; if (state) { if (state->EraseUserExpiredToken(Xuid()) > 0) { forceRefresh = true; } } XalUserGetTokenAndSignatureArgs tokenAndSigArgs{ httpMethod.data(), url.data(), static_cast(headers.size()), nullptr, requestBodySize, (requestBodySize == 0) ? nullptr : requestBody, // XUser requires nullptr body if body is 0 size forceRefresh, allUsers }; Vector xalHttpHeaders{}; if (headers.size() > 0) { xalHttpHeaders.reserve(tokenAndSigArgs.headerCount); for (const auto& header : headers) { xalHttpHeaders.push_back(XalHttpHeader{ header.first.data(), header.second.data() }); } tokenAndSigArgs.headers = xalHttpHeaders.data(); } auto asyncBlock{ Make() }; asyncBlock->queue = async.Queue().GetHandle(); asyncBlock->context = Make>>(std::move(async)); asyncBlock->callback = [](XAsyncBlock* asyncBlock) { auto async{ static_cast>*>(asyncBlock->context) }; size_t bufferSize{ 0 }; HRESULT hr = XalUserGetTokenAndSignatureSilentlyResultSize(asyncBlock, &bufferSize); TokenAndSignature payload; if (SUCCEEDED(hr)) { auto buffer{ MakeArray(bufferSize) }; XalUserGetTokenAndSignatureData* xalTokenSignatureData{ nullptr }; hr = XalUserGetTokenAndSignatureSilentlyResult(asyncBlock, bufferSize, buffer, &xalTokenSignatureData, nullptr); if (SUCCEEDED(hr)) { payload.token = String{ xalTokenSignatureData->token, xalTokenSignatureData->tokenSize }; payload.signature = String{ xalTokenSignatureData->signature, xalTokenSignatureData->signatureSize }; } DeleteArray(buffer, bufferSize); } else if (hr == E_XAL_NOTOKENREQUIRED) { // Consider this a success hr = S_OK; } async->Complete(Result{ payload, hr }); Delete(async); Delete(asyncBlock); }; HRESULT hr = XalUserGetTokenAndSignatureSilentlyAsync( m_handle, &tokenAndSigArgs, asyncBlock); if (FAILED(hr)) { auto asyncPtr{ static_cast>*>(asyncBlock->context) }; Delete(asyncPtr); Delete(asyncBlock); } return hr; } XalUserHandle User::Handle() const noexcept { return m_handle; } void User::SetTokenExpired(uint64_t xuid) noexcept { auto state{ GlobalState::Get() }; if (state) { state->InsertUserExpiredToken(xuid); } } Result User::RegisterChangeEventHandler( UserChangeEventHandler handler ) noexcept { XalRegistrationToken token{}; auto context{ MakeShared(std::move(handler)) }; auto hr = E_FAIL; if (!XblShouldFaultInject(INJECTION_FEATURE_USER)) { hr = XalUserRegisterChangeEventHandler( TaskQueue().GetHandle(), context.get(), [](void* context, UserLocalId userId, UserChangeType change) { auto handler{ static_cast(context) }; (*handler)(std::move(userId), std::move(static_cast(change))); }, &token ); } else { LOGS_ERROR << "FAULT INJECTION: User::RegisterChangeEventHandler ID:" << XblGetFaultCounter(); } if (SUCCEEDED(hr)) { auto state{ GlobalState::Get() }; if (state) { state->SetUserChangeHandler(token.token, context); } } return Result{token.token, hr }; } void User::UnregisterChangeEventHandle( uint64_t token ) noexcept { XalUserUnregisterChangeEventHandler(XalRegistrationToken{ token }); auto state{ GlobalState::Get() }; if (state) { state->EraseUserChangeHandler(token); } } Result User::GetGamertagComponent( XalGamertagComponent component ) const noexcept { if (m_handle != nullptr) { size_t size = XalUserGetGamertagSize(m_handle, component); Vector gamertagComponent(size, char{}); auto hr = XalUserGetGamertag(m_handle, component, size, &gamertagComponent[0], nullptr); if (SUCCEEDED(hr)) { m_gamertags[component] = gamertagComponent.data(); } else { LOGS_ERROR << "Getting Gamertag failed with HR: "<< hr ; } return Result{m_gamertags[component], hr }; } return E_UNEXPECTED; } NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END ================================================ FILE: Source/Shared/user.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once struct XblHttpCall; NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN using HttpHeaders = Map; struct TokenAndSignature { String token; String signature; }; #if HC_PLATFORM == HC_PLATFORM_GDK typedef XUserLocalId UserLocalId; typedef XUserChangeEvent UserChangeType; #else typedef XalUserLocalId UserLocalId; typedef XalUserChangeType UserChangeType; #endif using UserChangeEventHandler = Function; class User; template<> class Result; // RAII wrapper around XalUser class User { public: User(const User& other) = delete; User(User&& other) noexcept; User& operator=(User&& other) noexcept; ~User() noexcept; static Result WrapHandle(XblUserHandle userHandle) noexcept; Result Copy() const noexcept; uint64_t Xuid() const noexcept; uint64_t LocalId() const noexcept; String Gamertag() const noexcept; String ModernGamertag() const noexcept; String ModernGamertagSuffix() const noexcept; String UniqueModernGamertag() const noexcept; HRESULT GetTokenAndSignature( const String& httpMethod, const String& url, const HttpHeaders& headers, const uint8_t* requestBody, size_t requestBodySize, bool allUsers, AsyncContext>&& async ) noexcept; XalUserHandle Handle() const noexcept; static void SetTokenExpired(uint64_t xuid) noexcept; static Result RegisterChangeEventHandler( UserChangeEventHandler handler ) noexcept; static void UnregisterChangeEventHandle( uint64_t token ) noexcept; private: User() noexcept = default; User(XblUserHandle userHandle) noexcept; HRESULT InitializeUser() noexcept; Result GetGamertagComponent(XalGamertagComponent component) const noexcept; XblUserHandle m_handle{ nullptr }; mutable uint64_t m_xuid; mutable XalUserLocalId m_localId; mutable Map m_gamertags; friend class Result; }; template<> class Result { public: Result(User&& user) : m_payload{ std::move(user) } {} Result(User&& user, HRESULT error) : m_result{ error }, m_payload{ std::move(user) } {} Result(Result&& other) = default; Result(const Result& other) = delete; HRESULT Hresult() const noexcept { return m_result; } const User& Payload() const noexcept { return m_payload; } User&& ExtractPayload() noexcept { return std::move(m_payload); } private: HRESULT m_result; User m_payload; }; NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END ================================================ FILE: Source/Shared/utils_locales.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include #include "xsapi_utils.h" #if HC_PLATFORM == HC_PLATFORM_ANDROID #include "a/java_interop.h" #include "a/jni_utils.h" #endif NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN #if HC_PLATFORM == HC_PLATFORM_ANDROID std::map serviceLocales = { { "es_AR", "es-AR" }, { "AR", "es-AR" }, { "en_AU", "en-AU" }, { "AU", "en-AU" }, { "de_AT", "de-AT" }, { "AT", "de-AT" }, { "fr_BE", "fr-BE" }, { "nl_BE", "nl-BE" }, { "BE", "fr-BE" }, { "pt_BR", "pt-BR" }, { "BR", "pt-BR" }, { "en_CA", "en-CA" }, { "fr_CA", "fr-CA" }, { "CA", "en-CA" }, { "en_CZ", "en-CZ" }, { "CZ", "en-CZ" }, { "da_DK", "da-DK" }, { "DK", "da-DK" }, { "fi_FI", "fi-FI" }, { "FI", "fi-FI" }, { "fr_FR", "fr-FR" }, { "FR", "fr-FR" }, { "de_DE", "de-DE" }, { "DE", "de-DE" }, { "en_GR", "en-GR" }, { "GR", "en-GR" }, { "en_HK", "en-HK" }, { "zh_HK", "zh-HK" }, { "HK", "en-HK" }, { "en_HU", "en-HU" }, { "HU", "en-HU" }, { "en_IN", "en-IN" }, { "IN", "en-IN" }, { "en_GB", "en-GB" }, { "GB", "en-GB" }, { "en_IL", "en-IL" }, { "IL", "en-IL" }, { "it_IT", "it-IT" }, { "IT", "it-IT" }, { "ja_JP", "ja-JP" }, { "JP", "ja-JP" }, { "zh_CN", "zh-CN" }, { "CN", "zh-CN" }, { "es_MX", "es-MX" }, { "MX", "es-MX" }, { "es_CL", "es-CL" }, { "CL", "es-CL" }, { "es_CO", "es-CO" }, { "CO", "es-CO" }, { "nl_NL", "nl-NL" }, { "NL", "nl-NL" }, { "en_NZ", "en-NZ" }, { "NZ", "en-NZ" }, { "nb_NO", "nb-NO" }, { "NO", "nb-NO" }, { "pl_PL", "pl-PL" }, { "PL", "pl-PL" }, { "pt_PT", "pt-PT" }, { "PT", "pt-PT" }, { "ru_RU", "ru-RU" }, { "RU", "ru-RU" }, { "en_SA", "en-SA" }, { "SA", "en-SA" }, { "en_SG", "en-SG" }, { "zh_SG", "zh-SG" }, { "SG", "en-SG" }, { "en_SK", "en-SK" }, { "SK", "en-SK" }, { "en_ZA", "en-ZA" }, { "ZA", "en-ZA" }, { "ko_KR", "ko-KR" }, { "KR", "ko-KR" }, { "es_ES", "es-ES" }, { "es", "es-ES" }, { "de_CH", "de-CH" }, { "fr_CH", "fr-CH" }, { "CH", "fr-CH" }, { "zh_TW", "zh-TW" }, { "TW", "zh-TW" }, { "en_AE", "en-AE" }, { "AE", "en-AE" }, { "en_US", "en-US" }, { "US", "en-US" }, { "sv_SE", "sv-SE" }, { "SE", "sv-SE" }, { "tr_Tr", "tr-TR" }, { "Tr", "tr-TR" }, { "en_IE", "en-IE" }, { "IE", "en-IE" }, { "es_US", "es-US" } }; #endif #if HC_PLATFORM == HC_PLATFORM_WIN32 || HC_PLATFORM == HC_PLATFORM_XDK || HC_PLATFORM == HC_PLATFORM_GDK // Locale api for desktop and xbox xsapi_internal_vector utils::get_locale_list() { xsapi_internal_vector localeList; char_t localeName[LOCALE_NAME_MAX_LENGTH] = { 0 }; auto localeLen = GetUserDefaultLocaleName(localeName, ARRAYSIZE(localeName)); if (localeLen > 0) { localeList.push_back(utils::internal_string_from_char_t(localeName)); } else { localeList.push_back("en-US"); } return localeList; } #elif HC_PLATFORM == HC_PLATFORM_UWP xsapi_internal_vector utils::get_locale_list() { xsapi_internal_vector localeList; try { auto resourceContext = Windows::ApplicationModel::Resources::Core::ResourceContext::GetForCurrentView(); auto languages = resourceContext->Languages; for (auto language : languages) { localeList.push_back(utils::internal_string_from_utf16(language->Data())); } } catch (...) { LOG_ERROR("Failed to get system locale, fall back to en-US"); localeList.push_back("en-US"); } return localeList; } #elif HC_PLATFORM == HC_PLATFORM_ANDROID xsapi_internal_vector utils::get_locale_list() { auto javaInterop = java_interop::get_java_interop_singleton(); xsapi_internal_vector localeList; rwlock_guard guard(javaInterop->java_interop_singletonLock, false); auto javaVM = javaInterop->get_java_vm(); if (javaVM == nullptr) { LOG_ERROR("java interop not initialized properly"); return localeList; } auto marketActivityClass = javaInterop->get_market_activity_class(); if (javaVM != nullptr && marketActivityClass != nullptr) { JNIEnv* jniEnv; JNI_ATTACH_THREAD(javaVM, jniEnv); jmethodID getLocaleMethod = jniEnv->GetStaticMethodID(marketActivityClass, "getLocale", "()Ljava/lang/String;"); if (getLocaleMethod != nullptr) { jstring localeJString = (jstring)jniEnv->CallStaticObjectMethod(marketActivityClass, getLocaleMethod); xsapi_internal_string localeString = jniEnv->GetStringUTFChars(localeJString, nullptr); auto findResult = serviceLocales.find(localeString); if (findResult != serviceLocales.end()) { localeList.push_back(findResult->second); } else { localeList.push_back(_T("en-US")); } return localeList; } } localeList.push_back(_T("en-US")); return localeList; } #endif String utils::generate_locales(_In_z_ const xsapi_internal_string& overrideLocale) { xsapi_internal_vector localeList; // If an overrideLocale is provided, it should be added to the front of the localeList auto osLocaleList = get_locale_list(); if (!overrideLocale.empty()) { localeList.push_back(overrideLocale); localeList.insert(localeList.end(), osLocaleList.begin(), osLocaleList.end()); } else { localeList = osLocaleList; } xsapi_internal_vector localeFallbackList; for (auto& locale : localeList) { // Build up fallback list, for instance, if the lang is "sd-Arab-PK" // We add "sd-Arab" and "sd" as well // So that if an user's language preference is "fr-ml", "zh-hans", "en-us" // fallback chain is going to be: // fr-ml -> fr -> zh-hans -> zh -> en-us -> en localeFallbackList.push_back(locale); size_t nPos = locale.rfind("-"); while (nPos != xsapi_internal_string::npos) { localeFallbackList.push_back(locale.substr(0, nPos)); nPos = locale.rfind("-", nPos - 1); } } String locales{}; for (auto& locale : localeFallbackList) { locales += locale; locales += ','; } // erase the last ',' locales.pop_back(); return locales; } String utils::get_locales() { auto state = GlobalState::Get(); if (state) { return state->Locales().data(); } return String{ "en-US" }; } NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END ================================================ FILE: Source/Shared/web_socket.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "web_socket.h" #include "xsapi_utils.h" #ifdef XSAPI_UNIT_TESTS #include "mock_web_socket.h" #endif NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN using namespace xbox::services::system; // Context passed to LHC XAsync APIs. Ensures WebSocket is kept alive until those calls complete struct XAsyncContext { XAsyncContext(std::shared_ptr _websocket) : websocket{ std::move(_websocket) } { } std::shared_ptr websocket; }; Websocket::Websocket( _In_ User&& user, _In_ TaskQueue queue ) noexcept : m_user{ std::move(user) }, m_queue{ std::move(queue) } { HCWebSocketCreate(&m_hcWebsocket, ReceiveHandler, BinaryReceiveHandler, CloseHandler, this); } Websocket::~Websocket() { if (m_hcWebsocket) { HCWebSocketCloseHandle(m_hcWebsocket); } } HRESULT Websocket::Connect( _In_ const String& uri, _In_ const String& subProtocol ) noexcept { auto state = GlobalState::Get(); if (!state) { return E_XBL_NOT_INITIALIZED; } m_user.GetTokenAndSignature("GET", uri, HttpHeaders{}, nullptr, 0, false, AsyncContext>{ m_queue.GetHandle(), [ uri = String{ uri }, subProtocol = String{ subProtocol }, locales = state->Locales(), weakThis{ std::weak_ptr{ shared_from_this() } } ] (Result authResult) { auto sharedThis{ weakThis.lock() }; if (!sharedThis) { LOGS_DEBUG << "Websocket object destroyed before auth call completed"; return; } else if (Failed(authResult)) { sharedThis->m_connectCompleteHandler(WebsocketResult{ authResult.Hresult(), 0 }); return; } else { const auto& authPayload = authResult.Payload(); HCWebSocketSetHeader(sharedThis->m_hcWebsocket, "Authorization", authPayload.token.data()); HCWebSocketSetHeader(sharedThis->m_hcWebsocket, "Signature", authPayload.signature.data()); HCWebSocketSetHeader(sharedThis->m_hcWebsocket, "Accept-Language", locales.data()); xsapi_internal_string userAgent = DEFAULT_USER_AGENT; HCWebSocketSetHeader(sharedThis->m_hcWebsocket, "User-Agent", userAgent.data()); auto asyncContext = MakeUnique(sharedThis); auto asyncBlock = MakeUnique(); asyncBlock->queue = sharedThis->m_queue.GetHandle(); asyncBlock->context = asyncContext.get(); asyncBlock->callback = [](_In_ XAsyncBlock* asyncBlock) { UniquePtr asyncUnique{ asyncBlock }; UniquePtr asyncContext{ static_cast(asyncBlock->context) }; WebSocketCompletionResult hcResult{}; HRESULT hr = HCGetWebSocketConnectResult(asyncBlock, &hcResult); WebsocketResult result{ hr }; if (SUCCEEDED(hr)) { result.hr = hcResult.errorCode; result.platformErrorCode = hcResult.platformErrorCode; } asyncContext->websocket->m_connectCompleteHandler(result); }; auto hr = HCWebSocketConnectAsync(uri.data(), subProtocol.data(), sharedThis->m_hcWebsocket, asyncBlock.get()); if (SUCCEEDED(hr)) { asyncBlock.release(); asyncContext.release(); } else { sharedThis->m_connectCompleteHandler(WebsocketResult{ hr, 0 }); } } } }); return S_OK; } HRESULT Websocket::Send(_In_ const char* message) noexcept { auto asyncContext = MakeUnique(shared_from_this()); auto asyncBlock = MakeUnique(); asyncBlock->queue = m_queue.GetHandle(); asyncBlock->context = asyncContext.get(); asyncBlock->callback = [](_In_ XAsyncBlock* asyncBlock) { UniquePtr asyncUnique{ asyncBlock }; UniquePtr asyncContext{ static_cast(asyncBlock->context) }; WebSocketCompletionResult hcResult{}; HRESULT hr = HCGetWebSocketSendMessageResult(asyncBlock, &hcResult); WebsocketResult result{ hr }; if (SUCCEEDED(hr)) { result.hr = hcResult.errorCode; result.platformErrorCode = hcResult.platformErrorCode; } asyncContext->websocket->m_sendCompleteHandler(result); }; HRESULT hr = HCWebSocketSendMessageAsync(m_hcWebsocket, message, asyncBlock.get()); if (SUCCEEDED(hr)) { asyncBlock.release(); asyncContext.release(); } return hr; } HRESULT Websocket::Disconnect() noexcept { return HCWebSocketDisconnect(m_hcWebsocket); } void Websocket::OnMessageReceived(String&& m) const noexcept { // LHC doesn't guarantee that it will invoke message handlers on a specific thread. Because we want to ensure our // callbacks are made on the proper thread, submit the message handler to the provided TaskQueue. m_queue.RunWork([clientHandler = m_receiveHandler, message = std::move(m) ]() mutable { clientHandler(std::move(message)); }); } void Websocket::ReceiveHandler( _In_ HCWebsocketHandle /*websocket*/, _In_z_ const char* incomingBodyString, _In_ void* functionContext ) { auto thisPtr{ static_cast(functionContext) }; thisPtr->OnMessageReceived(incomingBodyString); } void Websocket::BinaryReceiveHandler( _In_ HCWebsocketHandle /*websocket*/, _In_reads_bytes_(payloadSize) const uint8_t* payloadBytes, _In_ uint32_t payloadSize, _In_ void* functionContext ) { auto thisPtr{ static_cast(functionContext) }; thisPtr->OnMessageReceived(xsapi_internal_string{ reinterpret_cast(payloadBytes), payloadSize }); } void Websocket::CloseHandler( _In_ HCWebsocketHandle /*websocket*/, _In_ HCWebSocketCloseStatus closeStatus, _In_ void* functionContext ) { auto thisPtr{ static_cast(functionContext) }; thisPtr->m_disconnectHandler(closeStatus); } std::shared_ptr IWebsocket::Make( User&& user, TaskQueue queue ) noexcept { #ifdef XSAPI_UNIT_TESTS auto webSocket = MakeShared(std::move(user), std::move(queue)); #else auto webSocket = MakeShared(std::move(user), std::move(queue)); #endif return webSocket; } void IWebsocket::SetConnectCompleteHandler(_In_ Callback connectCompleteHandler) noexcept { std::lock_guard lock{ m_mutex }; m_connectCompleteHandler = std::move(connectCompleteHandler); } void IWebsocket::SetDisconnectHandler(_In_ Callback disconnectHandler) noexcept { std::lock_guard lock{ m_mutex }; m_disconnectHandler = std::move(disconnectHandler); } void IWebsocket::SetSendCompleteHandler(_In_ Callback sendCompleteHandler) noexcept { std::lock_guard lock{ m_mutex }; m_sendCompleteHandler = std::move(sendCompleteHandler); } void IWebsocket::SetReceiveHandler(_In_ Callback receiveHandler) noexcept { std::lock_guard lock{ m_mutex }; m_receiveHandler = std::move(receiveHandler); } NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END ================================================ FILE: Source/Shared/web_socket.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN typedef HCWebSocketCloseStatus WebSocketCloseStatus; struct WebsocketResult { HRESULT hr; uint32_t platformErrorCode; }; // Base class for Websocket and MockWebsocket class IWebsocket { public: static std::shared_ptr Make( User&& user, TaskQueue queue ) noexcept; virtual ~IWebsocket() = default; virtual void SetConnectCompleteHandler(_In_ Callback connectCompleteHandler) noexcept; virtual void SetDisconnectHandler(_In_ Callback disconnectHandler) noexcept; virtual void SetSendCompleteHandler(_In_ Callback sendCompleteHandler) noexcept; virtual void SetReceiveHandler(_In_ Callback receiveHandler) noexcept; virtual HRESULT Connect( _In_ const String& uri, _In_ const String& subProtocol ) noexcept = 0; virtual HRESULT Send(_In_ const char* message) noexcept = 0; virtual HRESULT Disconnect() noexcept = 0; protected: std::recursive_mutex m_mutex; Callback m_connectCompleteHandler; Callback m_disconnectHandler; Callback m_receiveHandler; Callback m_sendCompleteHandler; }; class Websocket : public IWebsocket, public std::enable_shared_from_this { public: Websocket( _In_ User&& user, _In_ TaskQueue queue ) noexcept; ~Websocket(); HRESULT Connect( _In_ const String& uri, _In_ const String& subProtocol ) noexcept override; HRESULT Send(_In_ const char* message) noexcept override; HRESULT Disconnect() noexcept override; private: void OnMessageReceived(String&& message) const noexcept; static void CALLBACK ReceiveHandler( _In_ HCWebsocketHandle websocket, _In_z_ const char* incomingBodyString, _In_ void* functionContext ); static void CALLBACK BinaryReceiveHandler( _In_ HCWebsocketHandle websocket, _In_reads_bytes_(payloadSize) const uint8_t* payloadBytes, _In_ uint32_t payloadSize, _In_ void* functionContext ); static void CALLBACK CloseHandler( _In_ HCWebsocketHandle websocket, _In_ HCWebSocketCloseStatus closeStatus, _In_ void* functionContext ); HCWebsocketHandle m_hcWebsocket{ nullptr }; User m_user; TaskQueue m_queue; }; NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END ================================================ FILE: Source/Shared/xbox_live_app_config.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "shared_macros.h" #include "xbox_live_app_config_internal.h" using namespace xbox::services; NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN std::shared_ptr AppConfig::Instance() { auto state = GlobalState::Get(); if (state) { return state->AppConfig(); } return nullptr; } #if HC_PLATFORM == HC_PLATFORM_XDK HRESULT AppConfig::Initialize() { m_sandbox = utils::internal_string_from_utf16(Windows::Xbox::Services::XboxLiveConfiguration::SandboxId->Data()); m_titleId = std::stoi(Windows::Xbox::Services::XboxLiveConfiguration::TitleId->Data()); m_scid = utils::internal_string_from_utf16(Windows::Xbox::Services::XboxLiveConfiguration::PrimaryServiceConfigId->Data()); return S_OK; } #else HRESULT AppConfig::Initialize( xsapi_internal_string scid ) { HRESULT hr = XalGetTitleId(&m_titleId); size_t sandboxSize = XalGetSandboxSize(); char* sandbox = MakeArray(sandboxSize); hr = XalGetSandbox(sandboxSize, sandbox, nullptr); if (SUCCEEDED(hr)) { m_sandbox = sandbox; } DeleteArray(sandbox, sandboxSize); m_scid = std::move(scid); return hr; } #endif uint32_t AppConfig::TitleId() { return m_titleId; } uint32_t AppConfig::OverrideTitleId() const { if (m_overrideTitleId == 0) { return m_titleId; } return m_overrideTitleId; } void AppConfig::SetOverrideTitleId(uint32_t overrideTitleId) { m_overrideTitleId = overrideTitleId; } const xsapi_internal_string& AppConfig::Scid() const { return m_scid; } const xsapi_internal_string& AppConfig::OverrideScid() const { if (m_overrideScid.empty()) { return m_scid; } return m_overrideScid; } void AppConfig::SetOverrideScid(const xsapi_internal_string& overrideScid) { m_overrideScid = overrideScid; } const xsapi_internal_string& AppConfig::Sandbox() const { return m_sandbox; } #if HC_PLATFORM == HC_PLATFORM_UWP void AppConfig::SetSandbox(const xsapi_internal_string& sandbox) { m_sandbox = sandbox; } #endif const xsapi_internal_string& AppConfig::EndpointId() const { return m_endpointId; } void AppConfig::SetEndpointId(const xsapi_internal_string& endpointId) { m_endpointId = endpointId; } void AppConfig::DisableAssertsForXboxLiveThrottlingInDevSandboxes() { m_disableAssertsForXboxLiveThrottlingInDevSandboxes = true; } bool AppConfig::IsDisableAssertsForXboxLiveThrottlingInDevSandboxes() const { return m_disableAssertsForXboxLiveThrottlingInDevSandboxes; } #if HC_PLATFORM == HC_PLATFORM_IOS const xsapi_internal_string& AppConfig::APNSEnvironment() const { return m_apnsEnvironment; } void AppConfig::SetAPNSEnvironment(const xsapi_internal_string& apnsEnvironment) { m_apnsEnvironment = apnsEnvironment; } #endif #if HC_PLATFORM_IS_EXTERNAL xsapi_internal_string const& AppConfig::AppId() const { return m_telemetryAppId; } xsapi_internal_string const& AppConfig::AppVer() const { return m_telemetryAppVer; } xsapi_internal_string const& AppConfig::OsName() const { return m_telemetryOsName; } xsapi_internal_string const& AppConfig::OsLocale() const { return m_telemetryOsLocale; } xsapi_internal_string const& AppConfig::OsVersion() const { return m_telemetryOsVersion; } xsapi_internal_string const& AppConfig::DeviceClass() const { return m_telemetryDeviceClass; } xsapi_internal_string const& AppConfig::DeviceId() const { return m_telemetryDeviceId; } void AppConfig::SetAppId(xsapi_internal_string&& v) { m_telemetryAppId = std::move(v); } void AppConfig::SetAppVer(xsapi_internal_string&& v) { m_telemetryAppVer = std::move(v); } void AppConfig::SetOsName(xsapi_internal_string&& v) { m_telemetryOsName = std::move(v); } void AppConfig::SetOsLocale(xsapi_internal_string&& v) { m_telemetryOsLocale = std::move(v); } void AppConfig::SetOsVersion(xsapi_internal_string&& v) { m_telemetryOsVersion = std::move(v); } void AppConfig::SetDeviceClass(xsapi_internal_string&& v) { m_telemetryDeviceClass = std::move(v); } void AppConfig::SetDeviceId(xsapi_internal_string&& v) { m_telemetryDeviceId = std::move(v); } #endif NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END ================================================ FILE: Source/Shared/xbox_live_app_config_internal.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include #ifdef __OBJC__ #import #endif NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN class AppConfig : public std::enable_shared_from_this { public: AppConfig() = default; // TODO Remove. Unsafe method - returns null if GlobalState doesn't exist and is unchecked by callers static std::shared_ptr Instance(); #if HC_PLATFORM == HC_PLATFORM_UWP || HC_PLATFORM == HC_PLATFORM_XDK HRESULT Initialize(); #else HRESULT Initialize(xsapi_internal_string scid); #endif uint32_t TitleId(); const xsapi_internal_string& Scid() const; const xsapi_internal_string& Sandbox() const; const xsapi_internal_string& EndpointId() const; void SetEndpointId(const xsapi_internal_string& endpointId); uint32_t OverrideTitleId() const; void SetOverrideTitleId(uint32_t overrideTitleId); const xsapi_internal_string& OverrideScid() const; void SetOverrideScid(const xsapi_internal_string& overrideScid); void DisableAssertsForXboxLiveThrottlingInDevSandboxes(); bool IsDisableAssertsForXboxLiveThrottlingInDevSandboxes() const; #if HC_PLATFORM == HC_PLATFORM_IOS const xsapi_internal_string& APNSEnvironment() const; void SetAPNSEnvironment(const xsapi_internal_string& apnsEnvironment); #endif #if !HC_PLATFORM_IS_MICROSOFT && defined(__OBJC__) /// Set the view controller that your app wants to launch any xbox live services ui like sign in, profile /// card, etc from. This will set a weak pointer to this view controller. If at any point this view controller /// is no longer the desired presenting view controller, then set this to nil. If the weak pointer that backs /// this view controller ever is nil, then the fallback view controller for the app is the application's /// key window's root view controller. /// /// @param viewController The view controller to present any xbox live services ui from. Can be nil. /// /// Usage for using a view controller as the presenting view controller only once: /// /// xbox_live_app_config::set_launch_view_controller(viewController) /// user->signin().then... /// // Then when done with sign in or any ui /// xbox_live_app_config::set_launch_view_controller(nil) /// /// Usage for setting the view controller that will always be used: /// /// xbox_live_app_config::set_launch_view_controller(viewController) /// // Some time later in your application /// title_callable_ui::show_profile_card_ui(xuid) /// _XSAPIIMP static void set_launch_view_controller(_In_ UIViewController *viewController); #endif #if HC_PLATFORM_IS_EXTERNAL xsapi_internal_string const& AppId() const; xsapi_internal_string const& AppVer() const; xsapi_internal_string const& OsName() const; xsapi_internal_string const& OsLocale() const; xsapi_internal_string const& OsVersion() const; xsapi_internal_string const& DeviceClass() const; xsapi_internal_string const& DeviceId() const; void SetAppId(xsapi_internal_string&& v); void SetAppVer(xsapi_internal_string&& v); void SetOsName(xsapi_internal_string&& v); void SetOsLocale(xsapi_internal_string&& v); void SetOsVersion(xsapi_internal_string&& v); void SetDeviceClass(xsapi_internal_string&& v); void SetDeviceId(xsapi_internal_string&& v); #endif private: uint32_t m_titleId{ 0 }; uint32_t m_overrideTitleId{ 0 }; xsapi_internal_string m_sandbox; xsapi_internal_string m_scid; xsapi_internal_string m_overrideScid; xsapi_internal_string m_endpointId; bool m_disableAssertsForXboxLiveThrottlingInDevSandboxes{ false }; #if HC_PLATFORM == HC_PLATFORM_IOS xsapi_internal_string m_apnsEnvironment{ "apnsProduction" }; xsapi_internal_string m_registrationToken; #endif #if HC_PLATFORM_IS_EXTERNAL xsapi_internal_string m_telemetryAppId; xsapi_internal_string m_telemetryAppVer; xsapi_internal_string m_telemetryOsName; xsapi_internal_string m_telemetryOsLocale; xsapi_internal_string m_telemetryOsVersion; xsapi_internal_string m_telemetryDeviceClass; xsapi_internal_string m_telemetryDeviceId; #endif }; NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END ================================================ FILE: Source/Shared/xsapi_json_utils.cpp ================================================ #include "pch.h" #include "xsapi_json_utils.h" #include "uri_impl.h" using namespace xbox::services::legacy; NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN HRESULT JsonUtils::ExtractJsonFieldAsString( _In_ const JsonValue& json, _In_ const xsapi_internal_string& name, _Inout_ xsapi_internal_string& outString, _In_ bool required ) { if (json.IsObject()) { if (json.HasMember(name.c_str())) { const JsonValue& jsonField = json[name.c_str()]; outString = SerializeJson(jsonField); return S_OK; } else if (!required) { return S_OK; } } return WEB_E_INVALID_JSON_STRING; } HRESULT JsonUtils::ExtractJsonStringVector( _In_ const JsonValue& json, _In_ const xsapi_internal_string& name, _Inout_ xsapi_internal_vector& outVector, _In_ bool required ) { if (json.IsObject()) { if (json.HasMember(name.c_str())) { return ExtractJsonStringVector( json[name.c_str()], outVector ); } else if (!required) { outVector = xsapi_internal_vector(); return S_OK; } } outVector = xsapi_internal_vector(); return WEB_E_INVALID_JSON_STRING; } HRESULT JsonUtils::ExtractJsonStringVector( _In_ const JsonValue& json, _Inout_ xsapi_internal_vector& outVector ) { outVector = xsapi_internal_vector(); if (!json.IsArray()) { return WEB_E_INVALID_JSON_STRING; } for (const auto& string : json.GetArray()) { if (!string.IsString()) { return WEB_E_INVALID_JSON_STRING; } outVector.push_back(string.GetString()); } return S_OK; } Result JsonUtils::JsonStringExtractor(_In_ const JsonValue& json) { if (!json.IsString()) { return Result(WEB_E_INVALID_JSON_STRING); } return Result(json.GetString()); } void JsonUtils::JsonStringSerializer(_In_ const xsapi_internal_string& value, _Out_ JsonValue& json, JsonDocument::AllocatorType& allocator) { json.SetString(value.c_str(), allocator); } void JsonUtils::JsonXuidSerializer(_In_ uint64_t xuid, _Out_ JsonValue& json, _In_ JsonDocument::AllocatorType& allocator) { json.SetString(utils::uint64_to_internal_string(xuid).c_str(), allocator); } Result JsonUtils::JsonIntExtractor(_In_ const JsonValue& json) { if (!json.IsInt()) { return Result(0, WEB_E_INVALID_JSON_STRING); } return Result(json.GetInt(), S_OK); } Result JsonUtils::JsonXuidExtractor(_In_ const JsonValue& json) { if (!json.IsString()) { return Result(WEB_E_INVALID_JSON_STRING); } return Result(utils::internal_string_to_uint64(json.GetString())); } void JsonUtils::JsonUtf8Serializer(_In_ const char* value, _Out_ JsonValue& json, _In_ JsonDocument::AllocatorType& allocator) { //TODO: Verify UTF8 format is maintained json.SetString(value, allocator); } Result JsonUtils::JsonUtf8Extractor(_In_ const JsonValue& json) { if (!json.IsString()) { return Result(WEB_E_INVALID_JSON_STRING); } return Result(Make(json.GetString())); } void JsonUtils::JsonIntSerializer(_In_ int32_t value, _Out_ JsonValue& json, _In_ JsonDocument::AllocatorType&) { json.SetInt(value); } HRESULT JsonUtils::ExtractJsonXuid( _In_ const JsonValue& jsonValue, _In_ const xsapi_internal_string& name, _Out_ uint64_t& xuid, _In_ bool required /*= false*/ ) { xsapi_internal_string xuidString; RETURN_HR_IF_FAILED(ExtractJsonString(jsonValue, name, xuidString, required)); xuid = utils::internal_string_to_uint64(xuidString); return S_OK; } HRESULT JsonUtils::ExtractJsonString( _In_ const JsonValue& jsonValue, _In_ const xsapi_internal_string& stringName, _Inout_ xsapi_internal_string& outString, _In_ bool required ) { if (jsonValue.IsObject()) { if (jsonValue.HasMember(stringName.c_str())) { const JsonValue& field = jsonValue[stringName.c_str()]; if (field.IsString()) { outString = field.GetString(); return S_OK; } else if (field.IsNull()) { return S_OK; } } else if (!required) { return S_OK; } } return WEB_E_INVALID_JSON_STRING; } HRESULT JsonUtils::ExtractJsonStringToCharArray( _In_ const JsonValue& jsonValue, _In_ const xsapi_internal_string& stringName, _Inout_updates_bytes_(size) char* charArray, _In_ size_t size ) { xsapi_internal_string jsonStr; RETURN_HR_IF_FAILED(ExtractJsonString(jsonValue, stringName, jsonStr)); if (jsonStr.size() < size) { utils::strcpy(charArray, size, jsonStr.data()); } else { return E_INVALIDARG; } return S_OK; } JsonValue::ConstArray JsonUtils::ExtractJsonArray( _In_ const JsonValue& jsonValue, _In_ const xsapi_internal_string& arrayName, _In_ bool required ) { if (jsonValue.IsObject() && jsonValue.HasMember(arrayName.c_str())) { const JsonValue& field = jsonValue[arrayName.c_str()]; if ((!field.IsArray() && !required) || field.IsNull()) { const JsonValue emptyArrayJson(rapidjson::kArrayType); return emptyArrayJson.GetArray(); } return field.GetArray(); } const JsonValue emptyArrayJson(rapidjson::kArrayType); return emptyArrayJson.GetArray(); } HRESULT JsonUtils::ExtractJsonAsString( _In_ const JsonValue& jsonValue, _Inout_ xsapi_internal_string& outString ) { if (jsonValue.IsString()) { outString = jsonValue.GetString(); return S_OK; } return WEB_E_INVALID_JSON_STRING; } HRESULT JsonUtils::ExtractJsonBool( _In_ const JsonValue& jsonValue, _In_ const xsapi_internal_string& stringName, _Inout_ bool& outBool, _In_ bool required ) { if (jsonValue.IsObject()) { if (jsonValue.HasMember(stringName.c_str())) { const JsonValue& field = jsonValue[stringName.c_str()]; if (field.IsBool()) { outBool = field.GetBool(); return S_OK; } } else if (!required) { return S_OK; } } return WEB_E_INVALID_JSON_STRING; } HRESULT JsonUtils::ExtractJsonInt( _In_ const JsonValue& jsonValue, _In_ const xsapi_internal_string& name, _Inout_ int32_t& outInt, _In_ bool required ) { if (jsonValue.IsObject()) { if (jsonValue.HasMember(name.c_str())) { const JsonValue& field = jsonValue[name.c_str()]; if (field.IsInt()) { outInt = field.GetInt(); return S_OK; } } else if (!required) { return S_OK; } } return WEB_E_INVALID_JSON_STRING; } HRESULT JsonUtils::ExtractJsonInt( _In_ const JsonValue& jsonValue, _In_ const xsapi_internal_string& name, _Inout_ uint32_t& outInt, _In_ bool required ) { if (jsonValue.IsObject()) { if (jsonValue.HasMember(name.c_str())) { const JsonValue& field = jsonValue[name.c_str()]; if (field.IsUint()) { outInt = field.GetUint(); return S_OK; } } else if (!required) { return S_OK; } } return WEB_E_INVALID_JSON_STRING; } HRESULT JsonUtils::ExtractJsonInt( _In_ const JsonValue& jsonValue, _In_ const xsapi_internal_string& name, _Inout_ int64_t& outInt, _In_ bool required ) { if (jsonValue.IsObject()) { if (jsonValue.HasMember(name.c_str())) { const JsonValue& field = jsonValue[name.c_str()]; if (field.IsInt64()) { outInt = field.GetInt64(); return S_OK; } } else if (!required) { return S_OK; } } return WEB_E_INVALID_JSON_STRING; } HRESULT JsonUtils::ExtractJsonInt( _In_ const JsonValue& jsonValue, _In_ const xsapi_internal_string& name, _Inout_ uint64_t& outInt, _In_ bool required ) { if (jsonValue.IsObject()) { if (jsonValue.HasMember(name.c_str())) { const JsonValue& field = jsonValue[name.c_str()]; if (field.IsUint64()) { outInt = field.GetUint64(); return S_OK; } } else if (!required) { return S_OK; } } return WEB_E_INVALID_JSON_STRING; } HRESULT JsonUtils::ExtractJsonSizeT( _In_ const JsonValue& jsonValue, _In_ const String& name, _Inout_ size_t& size, _In_ bool required ) { uint64_t temp{}; RETURN_HR_IF_FAILED(ExtractJsonInt(jsonValue, name, temp, required)); size = static_cast(temp); return S_OK; } HRESULT JsonUtils::ExtractJsonStringToUInt64( _In_ const JsonValue& jsonValue, _In_ const xsapi_internal_string& name, _Inout_ uint64_t& outUInt64, _In_ bool required ) { if (jsonValue.IsObject()) { if (jsonValue.HasMember(name.c_str())) { const JsonValue& field = jsonValue[name.c_str()]; if (field.IsString()) { outUInt64 = utils::internal_string_to_uint64(field.GetString()); return S_OK; } } else if (!required) { return S_OK; } } return WEB_E_INVALID_JSON_STRING; } HRESULT JsonUtils::ExtractJsonUInt64( _In_ const JsonValue& jsonValue, _In_ const xsapi_internal_string& name, _Inout_ uint64_t& outUInt64, _In_ bool required ) { if (jsonValue.IsObject()) { if (jsonValue.HasMember(name.c_str())) { const JsonValue& field = jsonValue[name.c_str()]; if (field.IsNumber()) { outUInt64 = field.GetUint64(); return S_OK; } } else if (!required) { return S_OK; } } return WEB_E_INVALID_JSON_STRING; } HRESULT JsonUtils::ExtractJsonTime( _In_ const JsonValue& jsonValue, _In_ const xsapi_internal_string& name, _Inout_ xbox::services::datetime& outTime, _In_ bool required ) { if (jsonValue.IsObject()) { if (jsonValue.HasMember(name.c_str())) { const JsonValue& field = jsonValue[name.c_str()]; if (field.IsString()) { //convert to wstring for use with xbox::services::datetime //xbox::services::datetime is still part of cpprestsdk outTime = xbox::services::datetime::from_string(field.GetString(), xbox::services::datetime::date_format::ISO_8601); return S_OK; } } else if (!required) { return S_OK; } } return WEB_E_INVALID_JSON_STRING; } HRESULT JsonUtils::ExtractJsonTimeT( _In_ const JsonValue& jsonValue, _In_ const xsapi_internal_string& name, _Inout_ time_t& outTime, _In_ bool required ) { xbox::services::datetime time; RETURN_HR_IF_FAILED(ExtractJsonTime(jsonValue, name, time, required)); outTime = utils::time_t_from_datetime(time); return S_OK; } HRESULT JsonUtils::ExtractJsonStringTimespanInSeconds( _In_ const JsonValue& jsonValue, _In_ const xsapi_internal_string& name, _Inout_ std::chrono::seconds& outTime, _In_ bool required) { if (jsonValue.IsObject()) { if (jsonValue.HasMember(name.c_str())) { const JsonValue& field = jsonValue[name.c_str()]; if (field.IsString()) { char delimiter; int hour = 0, min = 0, sec = 0; xsapi_internal_stringstream ss(field.GetString()); ss >> hour >> delimiter >> min >> delimiter >> sec; outTime = std::chrono::hours(hour) + std::chrono::minutes(min) + std::chrono::seconds(sec); return S_OK; } } else if (!required) { return S_OK; } } return WEB_E_INVALID_JSON_STRING; } HRESULT JsonUtils::ExtractJsonDouble( _In_ const JsonValue& jsonValue, _In_ const xsapi_internal_string& name, _Inout_ double& outDouble, _In_ bool required /* = false */ ) { if (jsonValue.IsObject()) { if (jsonValue.HasMember(name.c_str())) { const JsonValue& field = jsonValue[name.c_str()]; if (field.IsDouble()) { outDouble = field.GetDouble(); return S_OK; } } else if(!required) { return S_OK; } } return WEB_E_INVALID_JSON_STRING; } void JsonUtils::SerializeUInt52ToJson( _In_ uint64_t integer, _Inout_ JsonValue& json ) { if ((integer & 0xFFF0000000000000) != 0) { //TODO: Throw exception here return; } json.SetUint64(integer); } JsonValue JsonUtils::SerializeTime( _In_ time_t time, _In_ JsonDocument::AllocatorType& a ) noexcept { auto timestampString = DatetimeFromTimeT(time).to_string_internal(xbox::services::cppresturi::utility::datetime::ISO_8601); return JsonValue{ timestampString.data(), a }; } HRESULT JsonUtils::ValidateJson( _In_ const char* jsonString ) { if (jsonString != nullptr) { JsonDocument d; d.Parse(jsonString); if (d.HasParseError()) { return WEB_E_INVALID_JSON_STRING; } } return S_OK; } HRESULT JsonUtils::ValidateJson( _In_ const char* jsonString, _Out_ JsonDocument& jsonDocument ) { if (jsonString == nullptr) { return WEB_E_INVALID_JSON_STRING; } jsonDocument.Parse(jsonString); if (jsonDocument.HasParseError()) { return WEB_E_INVALID_JSON_STRING; } return S_OK; } void JsonUtils::CopyFrom(JsonDocument& dest, const JsonValue& src) { if (static_cast(&dest) != static_cast(&src)) { dest.CopyFrom(src, dest.GetAllocator()); } } HRESULT JsonUtils::SetMember( _In_ JsonDocument& document, _In_ const String& key, _In_ const JsonValue& value ) noexcept { return SetMember(document, document.GetAllocator(), key, value); } HRESULT JsonUtils::SetMember( _In_ JsonValue& object, _In_ JsonDocument::AllocatorType& a, _In_ const String& key, _In_ const JsonValue& value ) noexcept { if (!object.IsObject()) { return E_UNEXPECTED; } auto existingMember = object.FindMember(key.data()); if (existingMember == object.MemberEnd()) { object.AddMember(JsonValue{ key.data(), a }.Move(), JsonValue{}.CopyFrom(value, a).Move(), a); } else { existingMember->value.CopyFrom(value, a); } return S_OK; } xsapi_internal_string JsonUtils::SerializeJson(_In_ const JsonValue& json) { rapidjson::StringBuffer buffer; rapidjson::Writer writer(buffer); json.Accept(writer); return buffer.GetString(); } void* JsonAllocator::Malloc(size_t size) { if (size) { return xbox::services::Alloc(size); } return nullptr; } void* JsonAllocator::Realloc(void* originalPtr, size_t originalSize, size_t newSize) { void* newPtr = nullptr; if (newSize > 0) { newPtr = Alloc(newSize); memcpy(newPtr, originalPtr, (originalSize < newSize ? originalSize : newSize)); } xbox::services::Free(originalPtr); return newPtr; } void JsonAllocator::Free(void* ptr) { xbox::services::Free(ptr); } NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END ================================================ FILE: Source/Shared/xsapi_json_utils.h ================================================ #pragma once #include "internal_errors.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN class JsonAllocator { public: static const bool kNeedFree = true; void* Malloc(size_t size); void* Realloc(void* originalPtr, size_t originalSize, size_t newSize); static void Free(void* ptr); }; typedef rapidjson::GenericDocument, JsonAllocator> JsonDocument; typedef rapidjson::GenericValue, JsonAllocator> JsonValue; class JsonUtils { public: static HRESULT ExtractJsonXuid( _In_ const JsonValue& jsonValue, _In_ const xsapi_internal_string& name, _Out_ uint64_t& xuid, _In_ bool required = false ); static HRESULT ExtractJsonString( _In_ const JsonValue& jsonValue, _In_ const xsapi_internal_string& stringName, _Inout_ xsapi_internal_string& outString, _In_ bool required = false ); static HRESULT ExtractJsonStringToCharArray( _In_ const JsonValue& jsonValue, _In_ const xsapi_internal_string& stringName, _Inout_updates_bytes_(size) char* charArr, _In_ size_t size ); static HRESULT ExtractJsonAsString( _In_ const JsonValue& jsonValue, _Inout_ xsapi_internal_string& outString ); static JsonValue::ConstArray ExtractJsonArray( _In_ const JsonValue& jsonValue, _In_ const xsapi_internal_string& arrayName, _In_ bool required ); static HRESULT ExtractJsonBool( _In_ const JsonValue& jsonValue, _In_ const xsapi_internal_string& stringName, _Inout_ bool& outBool, _In_ bool required = false ); static HRESULT ExtractJsonInt( _In_ const JsonValue& jsonValue, _In_ const xsapi_internal_string& name, _Inout_ int32_t& outInt, _In_ bool required = false ); static HRESULT ExtractJsonInt( _In_ const JsonValue& jsonValue, _In_ const xsapi_internal_string& name, _Inout_ uint32_t& outInt, _In_ bool required = false ); static HRESULT ExtractJsonInt( _In_ const JsonValue& jsonValue, _In_ const xsapi_internal_string& name, _Inout_ int64_t& outInt, _In_ bool required = false ); static HRESULT ExtractJsonInt( _In_ const JsonValue& jsonValue, _In_ const xsapi_internal_string& name, _Inout_ uint64_t& outInt, _In_ bool required = false ); static HRESULT ExtractJsonSizeT( _In_ const JsonValue& jsonValue, _In_ const String& name, _Inout_ size_t& size, _In_ bool required = false ); static HRESULT ExtractJsonStringToUInt64( _In_ const JsonValue& jsonValue, _In_ const xsapi_internal_string& name, _Inout_ uint64_t& outUInt64, _In_ bool required = false ); static HRESULT ExtractJsonUInt64( _In_ const JsonValue& jsonValue, _In_ const xsapi_internal_string& name, _Inout_ uint64_t& outUInt64, _In_ bool required = false ); static HRESULT ExtractJsonTime( _In_ const JsonValue& jsonValue, _In_ const xsapi_internal_string& name, _Inout_ xbox::services::datetime& outTime, _In_ bool required = false ); static HRESULT ExtractJsonTimeT( _In_ const JsonValue& jsonValue, _In_ const xsapi_internal_string& name, _Inout_ time_t& outTime, _In_ bool required = false ); static HRESULT ExtractJsonStringTimespanInSeconds( _In_ const JsonValue& jsonValue, _In_ const xsapi_internal_string& stringName, _Inout_ std::chrono::seconds& outTime, _In_ bool required = false ); static HRESULT ExtractJsonDouble( _In_ const JsonValue& jsonValue, _In_ const xsapi_internal_string& name, _Inout_ double& outDouble, _In_ bool required = false ); static HRESULT ExtractJsonFieldAsString( _In_ const JsonValue& json, _In_ const xsapi_internal_string& name, _Inout_ xsapi_internal_string& outString, _In_ bool required ); static HRESULT ExtractJsonStringVector( _In_ const JsonValue& json, _In_ const xsapi_internal_string& name, _Inout_ xsapi_internal_vector& outVector, _In_ bool required ); static HRESULT ExtractJsonStringVector( _In_ const JsonValue& json, _Inout_ xsapi_internal_vector& outVector ); template static HRESULT ExtractJsonVector( _In_ F deserialize, _In_ const JsonValue& json, _In_ const xsapi_internal_string& name, _Inout_ xsapi_internal_vector& outVector, _In_ bool required ) { outVector = xsapi_internal_vector(); if (json.IsObject()) { if (json.HasMember(name.c_str())) { const JsonValue& field = json[name.c_str()]; if (field.IsArray()) { for (auto it = field.Begin(); it != field.End(); ++it) { auto obj = deserialize(*it); if (Failed(obj)) { return obj.Hresult(); break; } outVector.push_back(obj.Payload()); } return S_OK; } } else if (!required) { return S_OK; } } return WEB_E_INVALID_JSON_STRING; } template static HRESULT ExtractJsonVector( _In_ F deserialize, _In_ const JsonValue& json, _Inout_ xsapi_internal_vector& outVector ) { outVector = xsapi_internal_vector(); if (!json.IsArray()) { return WEB_E_INVALID_JSON_STRING; } for (auto it = json.Begin(); it != json.End(); ++it) { auto obj = deserialize(*it); if (Failed(obj)) { return obj.Hresult(); break; } outVector.push_back(obj.Payload()); } return S_OK; } static Result JsonStringExtractor(_In_ const JsonValue& json); static void JsonStringSerializer(_In_ const xsapi_internal_string& value, _Out_ JsonValue& json, _In_ JsonDocument::AllocatorType& allocator); static void JsonXuidSerializer(_In_ uint64_t xuid, _Out_ JsonValue& json, _In_ JsonDocument::AllocatorType& allocator); static Result JsonIntExtractor(_In_ const JsonValue& json); static Result JsonXuidExtractor(_In_ const JsonValue& json); static void JsonUtf8Serializer(_In_ const char* value, _Out_ JsonValue& json, _In_ JsonDocument::AllocatorType& allocator); // Note that this function allocates memory that must be freed by the caller. static Result JsonUtf8Extractor(_In_ const JsonValue& json); static void JsonIntSerializer(_In_ int32_t value, _Out_ JsonValue& json, _In_ JsonDocument::AllocatorType& UNUSED); template static void SerializeVector( _In_ F serializer, _In_ xsapi_internal_vector inputVector, _Out_ JsonValue& jsonArray, _In_ JsonDocument::AllocatorType& allocator ) { jsonArray.SetArray(); for (auto& s : inputVector) { JsonValue val; serializer(s, val, allocator); jsonArray.PushBack(val, allocator); } } static void SerializeUInt52ToJson(_In_ uint64_t integer, _Inout_ JsonValue& json); static JsonValue SerializeTime( _In_ time_t time, _In_ JsonDocument::AllocatorType& allocator ) noexcept; static HRESULT ValidateJson(_In_ const char* jsonString); static HRESULT ValidateJson(_In_ const char* jsonString, _Out_ JsonDocument& jsonDocument); static void CopyFrom(JsonDocument& dest, const JsonValue& src); // Set a member of an json object to a new value. The added value to be added will be deep copied. // Note that the semantics of JsonValue::AddMember are to add a second member with // the same key if one already exists; this method instead updates an existing member. static HRESULT SetMember( _In_ JsonDocument& document, _In_ const String& key, _In_ const JsonValue& value ) noexcept; static HRESULT SetMember( _In_ JsonValue& object, _In_ JsonDocument::AllocatorType& allocator, _In_ const String& key, _In_ const JsonValue& value ) noexcept; static xsapi_internal_string SerializeJson(_In_ const JsonValue& json); }; NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END ================================================ FILE: Source/Shared/xsapi_utils.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "xsapi_utils.h" #include "xbox_live_app_config_internal.h" #include #include #include #include #if !HC_PLATFORM_IS_MICROSOFT #include "xbl_guid.h" #elif defined(_WIN32) #include #endif #if HC_PLATFORM == HC_PLATFORM_ANDROID #include "a/utils_a.h" #include "a/java_interop.h" #endif #include "presence_internal.h" #include "httpClient/httpClient.h" #include "Logger/log_hc_output.h" #include "global_state.h" #if !_LINK_WITH_CPPRESTSDK && HC_PLATFORM != HC_PLATFORM_GDK #include "cpprestsdk_impl.h" #endif #ifndef _XTIME_TICKS_PER_TIME_T #define _XTIME_TICKS_PER_TIME_T 10000000LL #endif NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN #define MAKE_HTTP_HRESULT(code) MAKE_HRESULT(1, 0x019, code) static char const * _sdaPrefix = "AAAAAAAA"; static const uint64_t _msTicks = static_cast(10000); static const uint64_t _secondTicks = 1000*_msTicks; xsapi_internal_string utils::encode_uri( _In_ const xsapi_internal_string& data, _In_ xbox::services::uri::components::component component ) { return xbox::services::uri::encode_uri(data, component); } xsapi_internal_string utils::headers_to_string( _In_ const xsapi_internal_http_headers& headers ) { xsapi_internal_stringstream ss; for (const auto& header : headers) { ss << header.first << ": " << header.second << "\r\n"; } return ss.str(); } xsapi_internal_string utils::get_query_from_params( _In_ const xsapi_internal_vector& params ) { xsapi_internal_stringstream strQueryString; size_t cItems = params.size(); if (cItems > 0) { xsapi_internal_string strDelimiter = "&"; strQueryString << "?"; strQueryString << params[0]; size_t i = 0; while (++i < cItems) { strQueryString << strDelimiter; strQueryString << params[i]; } } return strQueryString.str(); } void utils::append_paging_info( _In_ xbox::services::uri_builder& uriBuilder, _In_ unsigned int skipItems, _In_ unsigned int maxItems, _In_opt_ xsapi_internal_string continuationToken ) { // add maxItem parameter if (maxItems > 0) { uriBuilder.append_query("maxItems", maxItems); } if (continuationToken.empty()) { // use skip items value if continuation token is empty if (skipItems > 0) { uriBuilder.append_query("skipItems", skipItems); } } else { uriBuilder.append_query("continuationToken", continuationToken); } } #if defined(_WIN32) uint32_t utils::convert_timespan_to_days( _In_ uint64_t timespan ) { int64_t days = (timespan / _XTIME_TICKS_PER_TIME_T) / SECONDS_PER_DAY; THROW_CPP_INVALIDARGUMENT_IF(days < 0 || days > UINT32_MAX); return static_cast(days); } void utils::convert_unix_time_to_filetime( _In_ std::time_t t, _In_ FILETIME* ft ) { if (!ft) { return; } LONGLONG ll; #ifdef _USE_32BIT_TIME_T ll = Int32x32To64(t * 10000000) + 116444736000000000; #else ll = (t * 10000000) + 116444736000000000; #endif ft->dwLowDateTime = (DWORD)ll; ft->dwHighDateTime = ll >> 32; } void utils::convert_timepoint_to_filetime( _In_ const chrono_clock_t::time_point& time_point, _Inout_ uint64_t& largeInt ) { // time_point to system time std::time_t t = convert_timepoint_to_time(time_point); // system time to FILETIME FILETIME ft = { 0 }; convert_unix_time_to_filetime(t, &ft); if (largeInt) { ULARGE_INTEGER large; large.LowPart = ft.dwLowDateTime; large.HighPart = ft.dwHighDateTime; largeInt = large.QuadPart; } } #endif std::time_t utils::convert_timepoint_to_time( _In_ const chrono_clock_t::time_point& time_point ) { #if _MSC_VER <= 1800 return chrono_clock_t::to_time_t(time_point); #else uint64_t timeDiff = std::chrono::duration_cast(time_point.time_since_epoch() - std::chrono::steady_clock::now().time_since_epoch()).count(); uint64_t timeNow = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); return timeNow + timeDiff; #endif } xsapi_internal_string utils::convert_timepoint_to_string( _In_ const chrono_clock_t::time_point& time_point ) { xsapi_internal_string result; xsapi_internal_string::value_type buff[FILENAME_MAX]; std::chrono::milliseconds ms = std::chrono::duration_cast(time_point.time_since_epoch()); time_t t = utils::convert_timepoint_to_time(time_point); std::tm time; #if defined(_WIN32) errno_t errorCode = localtime_s(&time, &t); if (errorCode != 0) { return result; } sprintf_s(buff, "%04d-%02d-%02dT%02d:%02d:%02d.%03dZ", time.tm_year + 1900, time.tm_mon + 1, time.tm_mday, time.tm_hour, time.tm_min, time.tm_sec, static_cast(ms.count() % 1000)); #else #if defined(PAVO) std::tm* error = localtime_s(&t, &time); #else std::tm* error = localtime_r(&t, &time); #endif // PAVO if (error == nullptr) { return result; } snprintf(buff, sizeof(buff), _T("%04d-%02d-%02dT%02d:%02d:%02d.%03dZ"), time.tm_year + 1900, time.tm_mon + 1, time.tm_mday, time.tm_hour, time.tm_min, time.tm_sec, static_cast(ms.count() % 1000)); #endif result = buff; return result; } xsapi_internal_string utils::escape_special_characters(const xsapi_internal_string& str) { xsapi_internal_string result = str; for (auto iter = result.begin(); iter != result.end(); ++iter) { if (*iter == '\r' || *iter == '\n') { iter = result.insert(iter, ' '); iter = result.erase(iter + 1); --iter; } else if (*iter == '\"') { iter = result.insert(iter, '\"'); ++iter; } } return result; } uint32_t utils::char_t_copy( _In_reads_bytes_(sizeInWords) char_t* destinationCharArr, _In_ size_t sizeInWords, _In_ const char_t* sourceCharArr ) { #if HC_PLATFORM_IS_MICROSOFT return wcscpy_s(destinationCharArr, sizeInWords, sourceCharArr); #else return (uint32_t)strlcpy(destinationCharArr, sourceCharArr, (uint32_t)sizeInWords); #endif } size_t utils::strcpy( _In_ char* destinationCharArr, _In_ size_t sizeInWords, _In_ const char* sourceCharArr ) { #if HC_PLATFORM_IS_MICROSOFT return strcpy_s(destinationCharArr, sizeInWords, sourceCharArr); #else return strlcpy(destinationCharArr, sourceCharArr, sizeInWords); #endif } HRESULT utils::convert_exception_to_hresult() { // Default value, if there is no exception appears, return S_OK HRESULT hr = S_OK; try { throw; } // std exceptions catch (const std::bad_alloc&) // is an exception { hr = E_OUTOFMEMORY; } catch (const std::bad_cast&) // is an exception { hr = E_NOINTERFACE; } catch (const std::invalid_argument&) // is a logic_error { hr = E_INVALIDARG; } catch (const std::out_of_range&) // is a logic_error { hr = E_BOUNDS; } catch (const std::length_error&) // is a logic_error { hr = __HRESULT_FROM_WIN32(ERROR_BAD_LENGTH); } catch (const std::overflow_error&) // is a runtime_error { hr = __HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW); } catch (const std::underflow_error&) // is a runtime_error { hr = __HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW); } catch (const std::range_error&) // is a runtime_error { hr = E_BOUNDS; } catch (const std::system_error& ex) // is a runtime_error { if (ex.code().category() == std::system_category()) { hr = __HRESULT_FROM_WIN32(ex.code().value()); } else { hr = ex.code().value(); } } catch (const std::logic_error&) // is an exception { hr = E_UNEXPECTED; } catch (const std::runtime_error&) // is an exception { hr = E_FAIL; } #if !XSAPI_NO_PPL catch (const web::http::http_exception&) // is an exception { hr = HTTP_E_STATUS_UNEXPECTED; } #endif // !XSAPI_NO_PPL catch (const xbox::services::uri_exception&) // is an exception { hr = WEB_E_UNEXPECTED_CONTENT; } catch (const std::exception&) // base class for standard C++ exceptions { hr = E_FAIL; } catch (HRESULT exceptionHR) { hr = exceptionHR; } catch (...) // everything else { hr = E_FAIL; } return hr; } HRESULT utils::convert_xbox_live_error_code_to_hresult( _In_ const std::error_code& errCode ) { int err = static_cast(errCode.value()); xbl_error_code xblErr = static_cast(err); if (err == 204) { return __HRESULT_FROM_WIN32(ERROR_RESOURCE_DATA_NOT_FOUND); } else if (err >= 300 && err <= 505) { return (HRESULT)convert_http_status_to_hresult(err); } else if (err >= 1000 && err <= 9999) { switch (xblErr) { case xbl_error_code::bad_alloc: return E_OUTOFMEMORY; case xbl_error_code::invalid_argument: return E_INVALIDARG; case xbl_error_code::runtime_error: return E_XBL_RUNTIME_ERROR; case xbl_error_code::length_error: return __HRESULT_FROM_WIN32(ERROR_BAD_LENGTH); case xbl_error_code::out_of_range: return E_BOUNDS; case xbl_error_code::range_error: return E_BOUNDS; case xbl_error_code::bad_cast: return E_NOINTERFACE; case xbl_error_code::logic_error: return E_UNEXPECTED; case xbl_error_code::json_error: return WEB_E_INVALID_JSON_STRING; case xbl_error_code::uri_error: return WEB_E_UNEXPECTED_CONTENT; case xbl_error_code::websocket_error: return WEB_E_UNEXPECTED_CONTENT; case xbl_error_code::auth_user_interaction_required: return ONL_E_ACTION_REQUIRED; case xbl_error_code::rta_generic_error: return E_XBL_RTA_GENERIC_ERROR; case xbl_error_code::rta_subscription_limit_reached: return E_XBL_RTA_SUBSCRIPTION_LIMIT_REACHED; case xbl_error_code::rta_access_denied: return E_XBL_RTA_ACCESS_DENIED; case xbl_error_code::auth_unknown_error: return E_XBL_AUTH_UNKNOWN_ERROR; case xbl_error_code::auth_runtime_error: return E_XBL_AUTH_RUNTIME_ERROR; case xbl_error_code::auth_no_token_error: return E_XBL_AUTH_NO_TOKEN; case xbl_error_code::auth_user_not_signed_in: return __HRESULT_FROM_WIN32(ERROR_NO_SUCH_USER); case xbl_error_code::auth_user_cancel: return __HRESULT_FROM_WIN32(ERROR_CANCELLED); case xbl_error_code::auth_user_switched: return __HRESULT_FROM_WIN32(ERROR_NO_SUCH_USER); case xbl_error_code::invalid_config: return __HRESULT_FROM_WIN32(ERROR_BAD_CONFIGURATION); case xbl_error_code::unsupported: return E_NOTIMPL; default: return E_FAIL; } } else if ((err & 0x87DD0000) == 0x87D8000) { return HTTP_E_STATUS_UNEXPECTED_SERVER_ERROR; } else return err; //return the original error code if can't be translated. } xbl_error_code utils::convert_http_status_to_xbox_live_error_code( _In_ uint32_t statusCode ) { if (statusCode < 300 || statusCode >= 600) { // Treat as success so // if (!result.err()) // works properly which requires all non-errors to be 0. return xbl_error_code::no_error; } else { return static_cast(statusCode); } } HRESULT utils::convert_http_status_to_hresult(_In_ uint32_t httpStatusCode) { xbl_error_code errCode = static_cast(httpStatusCode); HRESULT hr = HTTP_E_STATUS_UNEXPECTED; // 2xx are http success codes if ((httpStatusCode >= 200) && (httpStatusCode < 300)) { hr = S_OK; } // MSXML XHR bug: get_status() returns HTTP/1223 for HTTP/204: // http://blogs.msdn.com/b/ieinternals/archive/2009/07/23/the-ie8-native-xmlhttprequest-object.aspx // treat it as success code as well else if (httpStatusCode == 1223) { hr = S_OK; } else { switch (errCode) { case xbl_error_code::http_status_300_multiple_choices: hr = HTTP_E_STATUS_AMBIGUOUS; break; case xbl_error_code::http_status_301_moved_permanently: hr = HTTP_E_STATUS_MOVED; break; case xbl_error_code::http_status_302_found: hr = HTTP_E_STATUS_REDIRECT; break; case xbl_error_code::http_status_303_see_other: hr = HTTP_E_STATUS_REDIRECT_METHOD; break; case xbl_error_code::http_status_304_not_modified: hr = HTTP_E_STATUS_NOT_MODIFIED; break; case xbl_error_code::http_status_305_use_proxy: hr = HTTP_E_STATUS_USE_PROXY; break; case xbl_error_code::http_status_307_temporary_redirect: hr = HTTP_E_STATUS_REDIRECT_KEEP_VERB; break; case xbl_error_code::http_status_400_bad_request: hr = HTTP_E_STATUS_BAD_REQUEST; break; case xbl_error_code::http_status_401_unauthorized: hr = HTTP_E_STATUS_DENIED; break; case xbl_error_code::http_status_402_payment_required: hr = HTTP_E_STATUS_PAYMENT_REQ; break; case xbl_error_code::http_status_403_forbidden: hr = HTTP_E_STATUS_FORBIDDEN; break; case xbl_error_code::http_status_404_not_found: hr = HTTP_E_STATUS_NOT_FOUND; break; case xbl_error_code::http_status_405_method_not_allowed: hr = HTTP_E_STATUS_BAD_METHOD; break; case xbl_error_code::http_status_406_not_acceptable: hr = HTTP_E_STATUS_NONE_ACCEPTABLE; break; case xbl_error_code::http_status_407_proxy_authentication_required: hr = HTTP_E_STATUS_PROXY_AUTH_REQ; break; case xbl_error_code::http_status_408_request_timeout: hr = HTTP_E_STATUS_REQUEST_TIMEOUT; break; case xbl_error_code::http_status_409_conflict: hr = HTTP_E_STATUS_CONFLICT; break; case xbl_error_code::http_status_410_gone: hr = HTTP_E_STATUS_GONE; break; case xbl_error_code::http_status_411_length_required: hr = HTTP_E_STATUS_LENGTH_REQUIRED; break; case xbl_error_code::http_status_412_precondition_failed: hr = HTTP_E_STATUS_PRECOND_FAILED; break; case xbl_error_code::http_status_413_request_entity_too_large: hr = HTTP_E_STATUS_REQUEST_TOO_LARGE; break; case xbl_error_code::http_status_414_request_uri_too_long: hr = HTTP_E_STATUS_URI_TOO_LONG; break; case xbl_error_code::http_status_415_unsupported_media_type: hr = HTTP_E_STATUS_UNSUPPORTED_MEDIA; break; case xbl_error_code::http_status_416_requested_range_not_satisfiable: hr = HTTP_E_STATUS_RANGE_NOT_SATISFIABLE; break; case xbl_error_code::http_status_417_expectation_failed: hr = HTTP_E_STATUS_EXPECTATION_FAILED; break; case xbl_error_code::http_status_421_misdirected_request: hr = MAKE_HTTP_HRESULT(421); break; case xbl_error_code::http_status_422_unprocessable_entity: hr = MAKE_HTTP_HRESULT(422); break; case xbl_error_code::http_status_423_locked: hr = MAKE_HTTP_HRESULT(423); break; case xbl_error_code::http_status_424_failed_dependency: hr = MAKE_HTTP_HRESULT(424); break; case xbl_error_code::http_status_426_upgrade_required: hr = MAKE_HTTP_HRESULT(426); break; case xbl_error_code::http_status_428_precondition_required: hr = MAKE_HTTP_HRESULT(428); break; case xbl_error_code::http_status_429_too_many_requests: hr = MAKE_HTTP_HRESULT(429); break; case xbl_error_code::http_status_431_request_header_fields_too_large: hr = MAKE_HTTP_HRESULT(431); break; case xbl_error_code::http_status_449_retry_with:hr = MAKE_HTTP_HRESULT(449); break; case xbl_error_code::http_status_451_unavailable_for_legal_reasons: hr = MAKE_HTTP_HRESULT(451); break; case xbl_error_code::http_status_500_internal_server_error: hr = HTTP_E_STATUS_SERVER_ERROR; break; case xbl_error_code::http_status_501_not_implemented: hr = HTTP_E_STATUS_NOT_SUPPORTED; break; case xbl_error_code::http_status_502_bad_gateway: hr = HTTP_E_STATUS_BAD_GATEWAY; break; case xbl_error_code::http_status_503_service_unavailable: hr = HTTP_E_STATUS_SERVICE_UNAVAIL; break; case xbl_error_code::http_status_504_gateway_timeout: hr = HTTP_E_STATUS_GATEWAY_TIMEOUT; break; case xbl_error_code::http_status_505_http_version_not_supported: hr = HTTP_E_STATUS_VERSION_NOT_SUP; break; case xbl_error_code::http_status_506_variant_also_negotiates: hr = MAKE_HTTP_HRESULT(506); break; case xbl_error_code::http_status_507_insufficient_storage: hr = MAKE_HTTP_HRESULT(507); break; case xbl_error_code::http_status_508_loop_detected: hr = MAKE_HTTP_HRESULT(508); break; case xbl_error_code::http_status_510_not_extended: hr = MAKE_HTTP_HRESULT(510); break; case xbl_error_code::http_status_511_network_authentication_required: hr = MAKE_HTTP_HRESULT(511); break; default: hr = HTTP_E_STATUS_UNEXPECTED; break; } } return hr; } #if HC_PLATFORM_IS_MICROSOFT // TODO: remove xsapi_internal_string utils::convert_hresult_to_error_name(_In_ long hr) { switch (hr) { // Generic errors case S_OK: return "S_OK"; case S_FALSE: return "S_FALSE"; case E_OUTOFMEMORY: return "E_OUTOFMEMORY"; case E_ACCESSDENIED: return "E_ACCESSDENIED"; case E_INVALIDARG: return "E_INVALIDARG"; case E_UNEXPECTED: return "E_UNEXPECTED"; case E_ABORT: return "E_ABORT"; case E_FAIL: return "E_FAIL"; case E_NOTIMPL: return "E_NOTIMPL"; case E_ILLEGAL_METHOD_CALL: return "E_ILLEGAL_METHOD_CALL"; // Authentication specific errors case 0x87DD0003: return "AM_E_XASD_UNEXPECTED"; case 0x87DD0004: return "AM_E_XASU_UNEXPECTED"; case 0x87DD0005: return "AM_E_XAST_UNEXPECTED"; case 0x87DD0006: return "AM_E_XSTS_UNEXPECTED"; case 0x87DD0007: return "AM_E_XDEVICE_UNEXPECTED"; case 0x87DD0008: return "AM_E_DEVMODE_NOT_AUTHORIZED"; case 0x87DD0009: return "AM_E_NOT_AUTHORIZED"; case 0x87DD000A: return "AM_E_FORBIDDEN"; case 0x87DD000B: return "AM_E_UNKNOWN_TARGET"; case 0x87DD000C: return "AM_E_INVALID_NSAL_DATA"; case 0x87DD000D: return "AM_E_TITLE_NOT_AUTHENTICATED"; case 0x87DD000E: return "AM_E_TITLE_NOT_AUTHORIZED"; case 0x87DD000F: return "AM_E_DEVICE_NOT_AUTHENTICATED"; case 0x87DD0010: return "AM_E_INVALID_USER_INDEX"; case 0x8015DC00: return "XO_E_DEVMODE_NOT_AUTHORIZED"; case 0x8015DC01: return "XO_E_SYSTEM_UPDATE_REQUIRED"; case 0x8015DC02: return "XO_E_CONTENT_UPDATE_REQUIRED"; case 0x8015DC03: return "XO_E_ENFORCEMENT_BAN"; case 0x8015DC04: return "XO_E_THIRD_PARTY_BAN"; case 0x8015DC05: return "XO_E_ACCOUNT_PARENTALLY_RESTRICTED"; case 0x8015DC06: return "XO_E_DEVICE_SUBSCRIPTION_NOT_ACTIVATED"; case 0x8015DC08: return "XO_E_ACCOUNT_BILLING_MAINTENANCE_REQUIRED"; case 0x8015DC09: return "XO_E_ACCOUNT_CREATION_REQUIRED"; case 0x8015DC0A: return "XO_E_ACCOUNT_TERMS_OF_USE_NOT_ACCEPTED"; case 0x8015DC0B: return "XO_E_ACCOUNT_COUNTRY_NOT_AUTHORIZED"; case 0x8015DC0C: return "XO_E_ACCOUNT_AGE_VERIFICATION_REQUIRED"; case 0x8015DC0D: return "XO_E_ACCOUNT_CURFEW"; case 0x8015DC0E: return "XO_E_ACCOUNT_CHILD_NOT_IN_FAMILY"; case 0x8015DC0F: return "XO_E_ACCOUNT_CSV_TRANSITION_REQUIRED"; case 0x8015DC10: return "XO_E_ACCOUNT_MAINTENANCE_REQUIRED"; case 0x8015DC11: return "XO_E_ACCOUNT_TYPE_NOT_ALLOWED"; // dev account on retail box case 0x8015DC12: return "XO_E_CONTENT_ISOLATION (Verify SCID / Sandbox)"; case 0x8015DC13: return "XO_E_ACCOUNT_NAME_CHANGE_REQUIRED"; case 0x8015DC14: return "XO_E_DEVICE_CHALLENGE_REQUIRED"; // case 0x8015DC15: synthetic device type not allowed - does not apply to consoles case 0x8015DC16: return "XO_E_SIGNIN_COUNT_BY_DEVICE_TYPE_EXCEEDED"; case 0x8015DC17: return "XO_E_PIN_CHALLENGE_REQUIRED"; case 0x8015DC18: return "XO_E_RETAIL_ACCOUNT_NOT_ALLOWED"; // RETAIL account on devkit case 0x8015DC19: return "XO_E_SANDBOX_NOT_ALLOWED"; case 0x8015DC1A: return "XO_E_ACCOUNT_SERVICE_UNAVAILABLE_UNKNOWN_USER"; case 0x8015DC1B: return "XO_E_GREEN_SIGNED_CONTENT_NOT_AUTHORIZED"; case 0x8015DC1C: return "XO_E_CONTENT_NOT_AUTHORIZED"; case 0x8015DC20: return "XO_E_EXPIRED_DEVICE_TOKEN"; case 0x8015DC21: return "XO_E_EXPIRED_TITLE_TOKEN"; case 0x8015DC22: return "XO_E_EXPIRED_USER_TOKEN"; case 0x8015DC23: return "XO_E_INVALID_DEVICE_TOKEN"; case 0x8015DC24: return "XO_E_INVALID_TITLE_TOKEN"; case 0x8015DC25: return "XO_E_INVALID_USER_TOKEN"; // HTTP specific errors case WEB_E_UNSUPPORTED_FORMAT: return "WEB_E_UNSUPPORTED_FORMAT"; case WEB_E_INVALID_XML: return "WEB_E_INVALID_XML"; case WEB_E_MISSING_REQUIRED_ELEMENT: return "WEB_E_MISSING_REQUIRED_ELEMENT"; case WEB_E_MISSING_REQUIRED_ATTRIBUTE: return "WEB_E_MISSING_REQUIRED_ATTRIBUTE"; case WEB_E_UNEXPECTED_CONTENT: return "WEB_E_UNEXPECTED_CONTENT"; case WEB_E_RESOURCE_TOO_LARGE: return "WEB_E_RESOURCE_TOO_LARGE"; case WEB_E_INVALID_JSON_STRING: return "WEB_E_INVALID_JSON_STRING"; case WEB_E_INVALID_JSON_NUMBER: return "WEB_E_INVALID_JSON_NUMBER"; case WEB_E_JSON_VALUE_NOT_FOUND: return "WEB_E_JSON_VALUE_NOT_FOUND"; case ERROR_RESOURCE_DATA_NOT_FOUND: return "ERROR_RESOURCE_DATA_NOT_FOUND"; case HTTP_E_STATUS_UNEXPECTED: return "HTTP_E_STATUS_UNEXPECTED"; case HTTP_E_STATUS_UNEXPECTED_REDIRECTION: return "HTTP_E_STATUS_UNEXPECTED_REDIRECTION"; case HTTP_E_STATUS_UNEXPECTED_CLIENT_ERROR: return "HTTP_E_STATUS_UNEXPECTED_CLIENT_ERROR"; case HTTP_E_STATUS_UNEXPECTED_SERVER_ERROR: return "HTTP_E_STATUS_UNEXPECTED_SERVER_ERROR"; case HTTP_E_STATUS_AMBIGUOUS: return "HTTP_E_STATUS_AMBIGUOUS"; case HTTP_E_STATUS_MOVED: return "HTTP_E_STATUS_MOVED"; case HTTP_E_STATUS_REDIRECT: return "HTTP_E_STATUS_REDIRECT"; case HTTP_E_STATUS_REDIRECT_METHOD: return "HTTP_E_STATUS_REDIRECT_METHOD"; case HTTP_E_STATUS_NOT_MODIFIED: return "HTTP_E_STATUS_NOT_MODIFIED"; case HTTP_E_STATUS_USE_PROXY: return "HTTP_E_STATUS_USE_PROXY"; case HTTP_E_STATUS_REDIRECT_KEEP_VERB: return "HTTP_E_STATUS_REDIRECT_KEEP_VERB"; case HTTP_E_STATUS_BAD_REQUEST: return "HTTP_E_STATUS_BAD_REQUEST"; case HTTP_E_STATUS_DENIED: return "HTTP_E_STATUS_DENIED"; case HTTP_E_STATUS_PAYMENT_REQ: return "HTTP_E_STATUS_PAYMENT_REQ"; case HTTP_E_STATUS_FORBIDDEN: return "HTTP_E_STATUS_FORBIDDEN"; case HTTP_E_STATUS_NOT_FOUND: return "HTTP_E_STATUS_NOT_FOUND"; case HTTP_E_STATUS_BAD_METHOD: return "HTTP_E_STATUS_BAD_METHOD"; case HTTP_E_STATUS_NONE_ACCEPTABLE: return "HTTP_E_STATUS_NONE_ACCEPTABLE"; case HTTP_E_STATUS_PROXY_AUTH_REQ: return "HTTP_E_STATUS_PROXY_AUTH_REQ"; case HTTP_E_STATUS_REQUEST_TIMEOUT: return "HTTP_E_STATUS_REQUEST_TIMEOUT"; case HTTP_E_STATUS_CONFLICT: return "HTTP_E_STATUS_CONFLICT"; case HTTP_E_STATUS_GONE: return "HTTP_E_STATUS_GONE"; case HTTP_E_STATUS_LENGTH_REQUIRED: return "HTTP_E_STATUS_LENGTH_REQUIRED"; case HTTP_E_STATUS_PRECOND_FAILED: return "HTTP_E_STATUS_PRECOND_FAILED"; case HTTP_E_STATUS_REQUEST_TOO_LARGE: return "HTTP_E_STATUS_REQUEST_TOO_LARGE"; case HTTP_E_STATUS_URI_TOO_LONG: return "HTTP_E_STATUS_URI_TOO_LONG"; case HTTP_E_STATUS_UNSUPPORTED_MEDIA: return "HTTP_E_STATUS_UNSUPPORTED_MEDIA"; case HTTP_E_STATUS_RANGE_NOT_SATISFIABLE: return "HTTP_E_STATUS_RANGE_NOT_SATISFIABLE"; case HTTP_E_STATUS_EXPECTATION_FAILED: return "HTTP_E_STATUS_EXPECTATION_FAILED"; case MAKE_HTTP_HRESULT(421): return "HTTP_E_STATUS_421_MISDIRECTED_REQUEST"; case MAKE_HTTP_HRESULT(422): return "HTTP_E_STATUS_422_UNPROCESSABLE_ENTITY"; case MAKE_HTTP_HRESULT(423): return "HTTP_E_STATUS_423_LOCKED"; case MAKE_HTTP_HRESULT(424): return "HTTP_E_STATUS_424_FAILED_DEPENDENCY"; case MAKE_HTTP_HRESULT(426): return "HTTP_E_STATUS_426_UPGRADE_REQUIRED"; case MAKE_HTTP_HRESULT(428): return "HTTP_E_STATUS_428_PRECONDITION_REQUIRED"; case MAKE_HTTP_HRESULT(429): return "HTTP_E_STATUS_429_TOO_MANY_REQUESTS"; case MAKE_HTTP_HRESULT(431): return "HTTP_E_STATUS_431_REQUEST_HEADER_FIELDS_TOO_LARGE"; case MAKE_HTTP_HRESULT(449): return "HTTP_E_STATUS_449_RETRY_WITH"; case MAKE_HTTP_HRESULT(451): return "HTTP_E_STATUS_451_UNAVAILABLE_FOR_LEGAL_REASONS"; case HTTP_E_STATUS_SERVER_ERROR: return "HTTP_E_STATUS_SERVER_ERROR"; case HTTP_E_STATUS_NOT_SUPPORTED: return "HTTP_E_STATUS_NOT_SUPPORTED"; case HTTP_E_STATUS_BAD_GATEWAY: return "HTTP_E_STATUS_BAD_GATEWAY"; case HTTP_E_STATUS_SERVICE_UNAVAIL: return "HTTP_E_STATUS_SERVICE_UNAVAIL"; case HTTP_E_STATUS_GATEWAY_TIMEOUT: return "HTTP_E_STATUS_GATEWAY_TIMEOUT"; case HTTP_E_STATUS_VERSION_NOT_SUP: return "HTTP_E_STATUS_VERSION_NOT_SUP"; case MAKE_HTTP_HRESULT(506): return "HTTP_E_STATUS_506_VARIANT_ALSO_NEGOTIATES"; case MAKE_HTTP_HRESULT(507): return "HTTP_E_STATUS_507_INSUFFICIENT_STORAGE"; case MAKE_HTTP_HRESULT(508): return "HTTP_E_STATUS_508_LOOP_DETECTED"; case MAKE_HTTP_HRESULT(510): return "HTTP_E_STATUS_510_NOT_EXTENDED"; case MAKE_HTTP_HRESULT(511): return "HTTP_E_STATUS_511_NETWORK_AUTHENTICATION_REQUIRED"; // WinINet specific errors case INET_E_INVALID_URL: return "INET_E_INVALID_URL"; case INET_E_NO_SESSION: return "INET_E_NO_SESSION"; case INET_E_CANNOT_CONNECT: return "INET_E_CANNOT_CONNECT"; case INET_E_RESOURCE_NOT_FOUND: return "INET_E_RESOURCE_NOT_FOUND"; case INET_E_OBJECT_NOT_FOUND: return "INET_E_OBJECT_NOT_FOUND"; case INET_E_DATA_NOT_AVAILABLE: return "INET_E_DATA_NOT_AVAILABLE"; case INET_E_DOWNLOAD_FAILURE: return "INET_E_DOWNLOAD_FAILURE"; case INET_E_AUTHENTICATION_REQUIRED: return "INET_E_AUTHENTICATION_REQUIRED"; case INET_E_NO_VALID_MEDIA: return "INET_E_NO_VALID_MEDIA"; case INET_E_CONNECTION_TIMEOUT: return "INET_E_CONNECTION_TIMEOUT"; case INET_E_INVALID_REQUEST: return "INET_E_INVALID_REQUEST"; case INET_E_UNKNOWN_PROTOCOL: return "INET_E_UNKNOWN_PROTOCOL"; case INET_E_SECURITY_PROBLEM: return "INET_E_SECURITY_PROBLEM"; case INET_E_CANNOT_LOAD_DATA: return "INET_E_CANNOT_LOAD_DATA"; case INET_E_CANNOT_INSTANTIATE_OBJECT: return "INET_E_CANNOT_INSTANTIATE_OBJECT"; case INET_E_INVALID_CERTIFICATE: return "INET_E_INVALID_CERTIFICATE"; case INET_E_REDIRECT_FAILED: return "INET_E_REDIRECT_FAILED"; case INET_E_REDIRECT_TO_DIR: return "INET_E_REDIRECT_TO_DIR"; } return "Unknown error"; } #endif xbox::services::xbl_error_code utils::convert_exception_to_xbox_live_error_code() { // Default value, if there is no exception appears, return no_error xbox::services::xbl_error_code errCode = xbl_error_code::no_error; try { throw; } // std exceptions catch (const std::bad_alloc&) // is an exception { errCode = xbl_error_code::bad_alloc; } catch (const std::bad_cast&) // is an exception { errCode = xbl_error_code::bad_cast; } catch (const std::invalid_argument&) // is a logic_error { errCode = xbl_error_code::invalid_argument; } catch (const std::out_of_range&) // is a logic_error { errCode = xbl_error_code::out_of_range; } catch (const std::length_error&) // is a logic_error { errCode = xbl_error_code::length_error; } catch (const std::range_error&) // is a runtime_error { errCode = xbl_error_code::range_error; } catch (const std::system_error& ex) // is a runtime_error { errCode = static_cast(ex.code().value()); } catch (const std::logic_error&) // is an exception { errCode = xbl_error_code::logic_error; } catch (const std::runtime_error&) // is an exception { errCode = xbl_error_code::runtime_error; } #if !XSAPI_NO_PPL catch (const web::http::http_exception& ex) // is an exception { errCode = static_cast(ex.error_code().value()); } #endif // !XSAPI_NO_PPL catch (const xbox::services::uri_exception&) // is an exception { errCode = xbl_error_code::uri_error; } catch (const std::exception&) // base class for standard C++ exceptions { errCode = xbl_error_code::generic_error; } #if HC_PLATFORM_IS_MICROSOFT catch (HRESULT exceptionHR) { errCode = static_cast(exceptionHR); } #endif catch (...) // everything else { errCode = xbl_error_code::generic_error; } return errCode; } #if HC_PLATFORM_IS_MICROSOFT std::error_code utils::guid_from_string( _In_ const string_t& str, _In_ GUID* guid, _In_ bool withBraces ) { uint32_t data[3] = {0}; uint32_t charData[8] = {0}; auto n = swscanf_s( str.c_str(), withBraces ? L"{%x-%x-%x-%2x%2x-%2x%2x%2x%2x%2x%2x}" : L"%x-%x-%x-%2x%2x-%2x%2x%2x%2x%2x%2x", &data[0], &data[1], &data[2], &charData[0], &charData[1], &charData[2], &charData[3], &charData[4], &charData[5], &charData[6], &charData[7] ); guid->Data1 = data[0]; guid->Data2 = static_cast(data[1]); guid->Data3 = static_cast(data[2]); for(uint32_t i=0; i<8; i++ ) { guid->Data4[i] = static_cast(charData[i]); } return ( n == 11 ) ? xbl_error_code::no_error : xbl_error_code::logic_error; } #endif xsapi_internal_string utils::create_guid(_In_ bool removeBraces) { #if HC_PLATFORM_IS_MICROSOFT GUID guid = {0}; THROW_CPP_RUNTIME_IF(FAILED(CoCreateGuid(&guid)), ""); WCHAR wszGuid[50]; THROW_CPP_RUNTIME_IF(FAILED(::StringFromGUID2( guid, wszGuid, ARRAYSIZE(wszGuid) )), ""); xsapi_internal_string strGuid = utils::internal_string_from_utf16(wszGuid); #elif !HC_PLATFORM_IS_MICROSOFT xsapi_internal_string strGuid = generate_guid(); #else uuid_t uuid; uuid_generate_random(uuid); char s[37] = { 0 }; uuid_unparse(uuid, s); string_t strGuid = s; #endif if (removeBraces) { if (strGuid.length() > 3 && strGuid[0] == L'{') { // Remove the { } strGuid.erase(0, 1); strGuid.erase(strGuid.end() - 1, strGuid.end()); } } return strGuid; } String utils::format_secure_device_address(String deviceAddress) { if (deviceAddress.empty()) { return ""; } // A secure device address(SDA) is a legacy concept for UWP/Xbox One. // SDAs encapsulate the deviceToken which is used by MPSD to identify // a session host and the connection address which is used by the title // to connect to the title. String formattedDeviceAddress = deviceAddress; #if !(HC_PLATFORM == HC_PLATFORM_XDK || HC_PLATFORM == HC_PLATFORM_UWP) // SDAs that start with a 1 byte (\001) will be parsed differently in MPSD. // Since this platform does not have a valid SDA, we are adding a prefix // to ensure that the SDA is handled as a non-valid SDA. // // MPSD will base64 decode a non-valid SDA and the hashed value will be used // as the device token. The SDA can then be parsed by the title while // deserializing the MPSD session to retrieve the connectionAddress. formattedDeviceAddress = _sdaPrefix + deviceAddress; #endif Vector input(formattedDeviceAddress.c_str(), formattedDeviceAddress.c_str() + formattedDeviceAddress.size()); String sda = xbox::services::convert::to_base64(input); return sda; } String utils::parse_secure_device_address(String secureDeviceAddress) { if (secureDeviceAddress.empty()) { return ""; } // A secure device address(SDA) is a legacy concept for UWP/Xbox One. // SDAs encapsulate the deviceToken which is used by MPSD to identify // a session host and the connection address which is used by the title // to connect to the title. std::vector base64ConnectionAddress(xbox::services::convert::from_base64(secureDeviceAddress.c_str())); auto formattedDeviceAddress = String(base64ConnectionAddress.begin(), base64ConnectionAddress.end()); String deviceAddress = formattedDeviceAddress; #if !(HC_PLATFORM == HC_PLATFORM_XDK || HC_PLATFORM == HC_PLATFORM_UWP) if (deviceAddress.find(_sdaPrefix) == 0) { deviceAddress = deviceAddress.substr(strlen(_sdaPrefix)); } #endif return deviceAddress; } std::vector utils::string_split( _In_ const string_t& string, _In_ string_t::value_type seperator ) { std::vector vSubStrings; if (!string.empty()) { size_t posStart = 0, posFound = 0; while (posFound != string_t::npos && posStart < string.length()) { posFound = string.find(seperator, posStart); if (posFound != string_t::npos) { if (posFound != posStart) { // this substring is not empty vSubStrings.push_back(string.substr(posStart, posFound - posStart)); } posStart = posFound + 1; } else { vSubStrings.push_back(string.substr(posStart)); } } } return vSubStrings; } xsapi_internal_vector utils::string_split_internal( _In_ const xsapi_internal_string& string, _In_ xsapi_internal_string::value_type seperator ) { xsapi_internal_vector vSubStrings; if (!string.empty()) { size_t posStart = 0, posFound = 0; while (posFound != xsapi_internal_string::npos && posStart < string.length()) { posFound = string.find(seperator, posStart); if (posFound != string_t::npos) { if (posFound != posStart) { // this substring is not empty vSubStrings.push_back(string.substr(posStart, posFound - posStart)); } posStart = posFound + 1; } else { vSubStrings.push_back(string.substr(posStart)); } } } return vSubStrings; } string_t utils::vector_join( _In_ const std::vector& vector, _In_ string_t::value_type seperator ) { stringstream_t ss; if (!vector.empty()) { string_t::value_type delimiter[2] = { seperator, 0 }; std::copy(vector.begin(), vector.end() - 1, std::ostream_iterator(ss, delimiter)); ss << vector.back(); } return ss.str(); } xsapi_internal_string utils::vector_join_internal( _In_ const std::vector& vector, _In_ xsapi_internal_string::value_type seperator ) { xsapi_internal_stringstream ss; if (!vector.empty()) { xsapi_internal_string::value_type delimiter[2] = { seperator, 0 }; std::copy(vector.begin(), vector.end() - 1, std::ostream_iterator(ss, delimiter)); ss << vector.back(); } return ss.str(); } string_t utils::replace_sub_string( _In_ const string_t& source, _In_ const string_t& pattern, _In_ const string_t& replacement ) { string_t result = source; // Search the string backward for the given pattern first size_t nPos = source.rfind(pattern); while (nPos != source.npos) { result.replace(nPos, pattern.length(), replacement); if (nPos == 0) { // There is nothing left to look at, break break; } // Find the next match starting from the last replaced position nPos = source.rfind(pattern, nPos - 1); } return result; } xsapi_internal_string_t utils::read_file_to_string( _In_ const xsapi_internal_string_t& filePath ) { std::ifstream in(filePath.c_str(), std::ios::in | std::ios::binary); if (in) { std::vector fileData; in.seekg(0, std::ios::end); uint32_t fileSizeInBytes = static_cast(in.tellg()); if (fileSizeInBytes > 3) { fileData.resize(fileSizeInBytes); in.seekg(0, std::ios::beg); if (fileData.size() > 0) { in.read(&fileData[0], fileData.size()); } in.close(); bool isUtf16LE = (static_cast(fileData[0]) == 0xFF && static_cast(fileData[1]) == 0xFE); // check for UTF-16 LE BOM bool isUtf8 = (static_cast(fileData[0]) == 0xEF && static_cast(fileData[1]) == 0xBB && static_cast(fileData[2]) == 0xBF); // check for UTF-8 BOM xsapi_internal_string_t fileDataString; #ifdef WIN32 // Convert file data to UTF16 string if (isUtf16LE) { uint32_t byteOrderMarkSizeInBytes = 2; uint32_t strLength = (fileSizeInBytes - byteOrderMarkSizeInBytes) / sizeof(WCHAR); fileDataString = xsapi_internal_string_t(reinterpret_cast(fileData.data() + byteOrderMarkSizeInBytes), strLength); } else { int byteOrderMarkSizeInBytes = (isUtf8) ? 3 : 0; uint32_t strLength = fileSizeInBytes - byteOrderMarkSizeInBytes; xsapi_internal_string utf8FileData = xsapi_internal_string(fileData.data() + byteOrderMarkSizeInBytes, strLength); fileDataString = xbox::services::convert::utf8_to_utf16(utf8FileData); } #else // Convert file data to UTF8 string if (isUtf16LE) { int byteOrderMarkSizeInBytes = 2; uint32_t strLength = (fileSizeInBytes - byteOrderMarkSizeInBytes) / sizeof(wchar_t); xsapi_internal_string_t utf16FileData = xsapi_internal_string_t(fileData.data(), strLength); fileDataString = utf16FileData; } else { int byteOrderMarkSizeInBytes = (isUtf8) ? 3 : 0; uint32_t strLength = fileSizeInBytes - byteOrderMarkSizeInBytes; fileDataString = xsapi_internal_string(fileData.data(), strLength); } #endif return fileDataString; } } return xsapi_internal_string_t(); } int utils::interlocked_increment(volatile long& incrementNum) { #if HC_PLATFORM_IS_MICROSOFT return InterlockedIncrement(&incrementNum); #else return static_cast(__sync_fetch_and_add(&incrementNum, 1)); #endif } int utils::interlocked_decrement(volatile long& decrementNum) { #if HC_PLATFORM_IS_MICROSOFT return InterlockedDecrement(&decrementNum); #else return static_cast(__sync_fetch_and_sub(&decrementNum, 1)); #endif } std::vector utils::string_array_to_string_vector( const char* *stringArray, size_t stringArrayCount ) { std::vector stringVector; for (size_t i = 0; i < stringArrayCount; ++i) { stringVector.push_back(string_t_from_utf8(stringArray[i])); } return stringVector; } xsapi_internal_vector utils::string_array_to_internal_string_vector( const char* *stringArray, size_t stringArrayCount ) { xsapi_internal_vector stringVector; stringVector.reserve(stringArrayCount); for (size_t i = 0; i < stringArrayCount; ++i) { stringVector.push_back(stringArray[i]); } return stringVector; } xsapi_internal_vector utils::xuid_array_to_internal_string_vector( uint64_t* xuidArray, size_t xuidArrayCount ) { xsapi_internal_vector stringVector; stringVector.reserve(xuidArrayCount); for (size_t i = 0; i < xuidArrayCount; ++i) { stringVector.push_back(utils::uint64_to_internal_string(xuidArray[i])); } return stringVector; } xsapi_internal_vector utils::uint32_array_to_internal_vector( uint32_t* intArray, size_t intArrayCount ) { xsapi_internal_vector vector; vector.reserve(intArrayCount); for (size_t i = 0; i < intArrayCount; ++i) { vector.push_back(intArray[i]); } return vector; } bool utils::EnsureLessThanMaxLength(const char* str, size_t maxLength) { size_t i = 0; while (true) { if (i >= maxLength) { return false; } if (str[i] == '\0') { return true; } i++; } return false; } String utils::ToLower(String str) noexcept { std::transform(str.begin(), str.end(), str.begin(), [](unsigned char c) { return static_cast(tolower(c)); }); return str; } XAsyncBlock* utils::MakeAsyncBlock(XTaskQueueHandle queue, void* context, XAsyncCompletionRoutine* callback) { auto async = Make(); async->queue = queue; async->context = context; async->callback = callback; return async; } static void CALLBACK s_defaultAsyncBlockCallback(XAsyncBlock* async) { Delete(async); } XAsyncBlock* utils::MakeDefaultAsyncBlock(XTaskQueueHandle queue) { return MakeAsyncBlock(queue, nullptr, s_defaultAsyncBlockCallback); } time_t utils::time_t_from_datetime(const xbox::services::datetime& datetime) { uint64_t seconds = datetime.to_interval() / _secondTicks; if (seconds >= 11644473600LL) { return (time_t)(seconds - 11644473600LL); } else { // If time is before epoch, 0 is returned. return 0; } } #if HC_PLATFORM_IS_MICROSOFT xsapi_internal_string utils::internal_string_from_utf16(_In_z_ const wchar_t* utf16) { return internal_string_from_char_t(utf16); } #endif #ifdef XSAPI_WRL_EVENTS_SERVICE Microsoft::WRL::Wrappers::HString utils::HStringFromUtf8(_In_z_ const char* utf8) { auto cchOutString = char_t_from_utf8(utf8, nullptr, 0); xsapi_internal_wstring wstring(cchOutString - 1, '\0'); char_t_from_utf8(utf8, &wstring[0], cchOutString); Microsoft::WRL::Wrappers::HString hstring; hstring.Set(wstring.data()); return hstring; } #endif #if __cplusplus_winrt Platform::String^ utils::PlatformStringFromUtf8(_In_z_ const char* utf8) { auto cchOutString = char_t_from_utf8(utf8, nullptr, 0); xsapi_internal_wstring wstr(cchOutString - 1, '\0'); char_t_from_utf8(utf8, &wstr[0], cchOutString); return ref new Platform::String(wstr.data()); } #endif std::string utils::std_string_from_string_t(_In_ const string_t& stringt) { #if HC_PLATFORM_IS_MICROSOFT auto cchOutString = utf8_from_char_t(stringt.data(), nullptr, 0); std::string out(static_cast(cchOutString) - 1, '\0'); utf8_from_char_t(stringt.data(), &out[0], cchOutString); return out; #else return std::string(stringt.data()); #endif } xsapi_internal_string utils::internal_string_from_char_t(_In_ const char_t* char_t) { #if HC_PLATFORM_IS_MICROSOFT auto cchOutString = utf8_from_char_t(char_t, nullptr, 0); xsapi_internal_string out(static_cast(cchOutString) - 1, '\0'); utf8_from_char_t(char_t, &out[0], cchOutString); return out; #else return xsapi_internal_string(char_t); #endif } string_t utils::string_t_from_internal_string(_In_ const xsapi_internal_string& internalString) { #if HC_PLATFORM_IS_MICROSOFT return string_t_from_utf8(internalString.data()); #else return string_t(internalString.c_str()); #endif } string_t utils::string_t_from_utf8(_In_z_ const char* utf8) { #if HC_PLATFORM_IS_MICROSOFT auto cchOutString = char_t_from_utf8(utf8, nullptr, 0); string_t out(static_cast(cchOutString) - 1, '\0'); char_t_from_utf8(utf8, &out[0], cchOutString); return out; #else return string_t(utf8); #endif } xsapi_internal_string utils::internal_string_from_string_t(_In_ const string_t& externalString) { #if HC_PLATFORM_IS_MICROSOFT return internal_string_from_utf16(externalString.c_str()); #else return xsapi_internal_string(externalString.c_str()); #endif } int utils::utf8_from_char_t( _In_z_ const char_t* inArray, _Out_writes_z_(cchOutArray) char* outArray, _In_ int cchOutArray ) { #if HC_PLATFORM_IS_MICROSOFT // query for the buffer size auto queryResult = WideCharToMultiByte( CP_UTF8, WC_ERR_INVALID_CHARS, inArray, -1, nullptr, 0, nullptr, nullptr ); if (queryResult > cchOutArray && cchOutArray == 0) { return queryResult; } else if (queryResult == 0 || queryResult > cchOutArray) { throw std::exception("utf8_from_char_t failed"); } auto conversionResult = WideCharToMultiByte( CP_UTF8, WC_ERR_INVALID_CHARS, inArray, -1, outArray, cchOutArray, nullptr, nullptr ); if (conversionResult == 0) { throw std::exception("utf8_from_char_t failed"); } return conversionResult; #else int len = (int)strlen(inArray); if (len < cchOutArray && outArray != nullptr) { strcpy(outArray, len + 1, inArray); } else if (cchOutArray > 0) { return 0; } return len + 1; #endif } int utils::char_t_from_utf8( _In_z_ const char* inArray, _Out_writes_z_(cchOutArray) char_t* outArray, _In_ int cchOutArray ) { #if HC_PLATFORM_IS_MICROSOFT // query for the buffer size auto queryResult = MultiByteToWideChar( CP_UTF8, MB_ERR_INVALID_CHARS, inArray, -1, nullptr, 0 ); if (queryResult > cchOutArray && cchOutArray == 0) { return queryResult; } else if (queryResult == 0 || queryResult > cchOutArray) { throw std::exception("char_t_from_utf8 failed"); } auto conversionResult = MultiByteToWideChar( CP_UTF8, MB_ERR_INVALID_CHARS, inArray, -1, outArray, cchOutArray ); if (conversionResult == 0) { throw std::exception("char_t_from_utf8 failed"); } return conversionResult; #else int len = (int)strlen(inArray); if (len < cchOutArray && outArray != nullptr) { strcpy(outArray, len + 1, inArray); } else if (cchOutArray > 0) { return 0; } return len + 1; #endif } NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END ================================================ FILE: Source/Shared/xsapi_utils.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #if HC_PLATFORM_IS_MICROSOFT #include #include #endif #ifdef XSAPI_WRL_EVENTS_SERVICE #include #endif #include "errors_legacy.h" #include "xsapi-c/pal.h" HC_DECLARE_TRACE_AREA(XSAPI); NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN #ifndef __min #define __min(a,b) (((a) < (b)) ? (a) : (b)) #endif #ifndef __max #define __max(a,b) (((a) < (b)) ? (b) : (a)) #endif inline bool operator<(const xbox::services::datetime& lhs, const xbox::services::datetime& rhs) { return lhs.to_interval() < rhs.to_interval(); } #ifndef _In_reads_bytes_ #define _In_reads_bytes_(s) #endif class utils { public: static int interlocked_increment(volatile long& incrementNum); static int interlocked_decrement(volatile long& decrementNum); static xsapi_internal_string encode_uri(_In_ const xsapi_internal_string& data, _In_ xbox::services::uri::components::component component = xbox::services::uri::components::full_uri); static xsapi_internal_string headers_to_string(_In_ const xsapi_internal_http_headers& headers); static xsapi_internal_string get_query_from_params(_In_ const xsapi_internal_vector& params); static xsapi_internal_string create_guid(_In_ bool removeBraces); #if HC_PLATFORM_IS_MICROSOFT static std::error_code guid_from_string(_In_ const string_t& str, _In_ GUID* guid, _In_ bool withBraces); #endif static String format_secure_device_address(String deviceAddress); static String parse_secure_device_address(String secureDeviceAddress); static void append_paging_info( _In_ xbox::services::uri_builder& uriBuilder, _In_ unsigned int skipItems, _In_ unsigned int maxItems, _In_opt_ xsapi_internal_string continuationToken ); static uint32_t convert_timespan_to_days( _In_ uint64_t timespan ); static std::time_t convert_timepoint_to_time( _In_ const chrono_clock_t::time_point& time_point ); static xsapi_internal_string convert_timepoint_to_string( _In_ const chrono_clock_t::time_point& time_point ); static inline xbox::services::datetime DatetimeFromTimeT(time_t time) { uint64_t result = EPOCH_OFFSET + time; result *= TICKS_PER_SEC; // convert to 10e-7 return xbox::services::datetime() + result; } static inline time_t TimeTFromDatetime(const xbox::services::datetime& datetime) { uint64_t seconds = datetime.to_interval() / TICKS_PER_SEC; if (seconds >= EPOCH_OFFSET) { return (time_t)(seconds - EPOCH_OFFSET); } else { // If time is before epoch, 0 is returned. return 0; } } static xsapi_internal_string escape_special_characters(const xsapi_internal_string& str); static inline int str_icmp(const string_t &left, const string_t &right) { return char_t_cmp(left.c_str(), right.c_str()); } static inline int str_icmp_internal(const xsapi_internal_string& left, const xsapi_internal_string& right) { return str_icmp(left.data(), right.data()); } static inline int str_icmp(const char* left, const char* right) { #if HC_PLATFORM_IS_MICROSOFT return _stricmp(left, right); #else return strcasecmp(left, right); #endif } static inline int char_t_cmp(const char_t* left, const char_t* right) { #if HC_PLATFORM_IS_MICROSOFT return _wcsicmp(left, right); #else return strcasecmp(left, right); #endif } static std::vector string_split( _In_ const string_t& string, _In_ string_t::value_type seperator ); static xsapi_internal_vector string_split_internal( _In_ const xsapi_internal_string& string, _In_ xsapi_internal_string::value_type seperator ); static xbl_error_code convert_exception_to_xbox_live_error_code(); static string_t vector_join( _In_ const std::vector& vector, _In_ string_t::value_type seperator ); static xsapi_internal_string vector_join_internal( _In_ const std::vector& vector, _In_ xsapi_internal_string::value_type seperator ); #if HC_PLATFORM_IS_MICROSOFT static void convert_unix_time_to_filetime( _In_ std::time_t t, _In_ FILETIME* ft); static void convert_timepoint_to_filetime( _In_ const chrono_clock_t::time_point& time_point, _Inout_ uint64_t& largeInt); #endif static HRESULT convert_exception_to_hresult(); static HRESULT convert_xbox_live_error_code_to_hresult(_In_ const std::error_code& errCode); static xsapi_internal_string convert_hresult_to_error_name(_In_ long hr); static HRESULT convert_http_status_to_hresult(_In_ uint32_t httpStatusCode); static xbl_error_code convert_http_status_to_xbox_live_error_code(_In_ uint32_t statusCode); #if HC_PLATFORM_IS_MICROSOFT static xsapi_internal_string internal_string_from_utf16(_In_z_ const wchar_t* utf16); #endif #ifdef XSAPI_WRL_EVENTS_SERVICE static Microsoft::WRL::Wrappers::HString HStringFromUtf8(_In_z_ const char* utf8); #endif #if __cplusplus_winrt static Platform::String^ PlatformStringFromUtf8(_In_z_ const char* utf8); #endif static std::string std_string_from_string_t(_In_ const string_t& stringt); static xsapi_internal_string internal_string_from_string_t(_In_ const string_t& stringt); static xsapi_internal_string internal_string_from_char_t(_In_ const char_t* char_t); static string_t string_t_from_internal_string(_In_ const xsapi_internal_string& internalString); static string_t string_t_from_utf8(_In_z_ const char* utf8); static int utf8_from_char_t(_In_z_ const char_t* inArray, _Out_writes_z_(cchOutArray) char* outArray, _In_ int cchOutArray); static int char_t_from_utf8(_In_z_ const char* inArray, _Out_writes_z_(cchOutArray) char_t* outArray, _In_ int cchOutArray); static String generate_locales(_In_z_ const xsapi_internal_string& locale = ""); // Helper function to get locales from GlobalState. Fallback to "en-us" if GlobalState is not initialized static String get_locales(); static string_t replace_sub_string( _In_ const string_t& source, _In_ const string_t& pattern, _In_ const string_t& replacement ); static xsapi_internal_string_t read_file_to_string( _In_ const xsapi_internal_string_t& filePath ); inline static uint32_t string_t_to_uint32( _In_ const string_t& str ) { #if HC_PLATFORM_IS_MICROSOFT return std::stoul(str); #else return (uint32_t)std::strtoul(str.c_str(), nullptr, 0); #endif } inline static uint32_t internal_string_to_uint32( _In_ const xsapi_internal_string& str ) { return (uint32_t)std::strtoul(str.c_str(), nullptr, 0); } inline static string_t uint32_to_string_t( _In_ uint32_t val ) { stringstream_t stream; stream << val; return stream.str(); } inline static xsapi_internal_string uint32_to_internal_string( _In_ uint32_t val ) { xsapi_internal_stringstream stream; stream << val; return stream.str(); } inline static xsapi_internal_string uint64_to_internal_string( _In_ uint64_t val ) { xsapi_internal_stringstream stream; stream << val; return stream.str(); } inline static uint64_t string_t_to_uint64( _In_ const string_t& str ) { return uint64_from_char_t(str.data()); } inline static uint64_t uint64_from_char_t( _In_ const char_t* str ) { #if HC_PLATFORM_IS_MICROSOFT return _wtoi64(str); #else return strtoull(str, nullptr, 0); #endif } inline static uint64_t internal_string_to_uint64( _In_ const xsapi_internal_string& str ) { return strtoull(str.c_str(), nullptr, 0); } inline static int32_t string_t_to_int32( _In_ const string_t& str ) { #if HC_PLATFORM_IS_MICROSOFT return _wtoi(str.c_str()); #else return std::atoi(str.c_str()); #endif } #define initialize_char_arr(charArr) \ { \ auto charArrSize = sizeof(charArr) / sizeof(*charArr); \ std::fill(charArr, charArr + charArrSize, _T('\0')); \ } #define initialize_arr(arr) \ { \ auto arrSize = sizeof(arr) / sizeof(*arr); \ std::fill(arr, arr + arrSize, 0); \ } inline static string_t uint64_to_string_t( _In_ uint64_t num ) { stringstream_t stream; stream << num; auto numStr = stream.str(); return numStr; } static uint32_t char_t_copy( _In_reads_bytes_(sizeInWords) char_t* destinationCharArr, _In_ size_t sizeInWords, _In_ const char_t* sourceCharArr ); static size_t strcpy( _In_ char* destinationCharArr, _In_ size_t sizeInWords, _In_ const char* sourceCharArr ); static std::vector string_array_to_string_vector( const char* *stringArray, size_t stringArrayCount ); static xsapi_internal_vector string_array_to_internal_string_vector( const char* *stringArray, size_t stringArrayCount ); static xsapi_internal_vector xuid_array_to_internal_string_vector( uint64_t* xuidArray, size_t xuidArrayCount ); static xsapi_internal_vector uint32_array_to_internal_vector( uint32_t* intArray, size_t intArrayCount ); static bool EnsureLessThanMaxLength(const char* str, size_t maxLength); // date and time constants static constexpr uint64_t TICKS_PER_MS = 10000; static constexpr uint64_t TICKS_PER_SEC = (1000 * TICKS_PER_MS); static constexpr uint64_t EPOCH_OFFSET = 11644473600LL; static String ToLower(String str) noexcept; private: template struct SmartPointerContainer { virtual ~SmartPointerContainer() {} virtual std::shared_ptr GetShared() const = 0; }; template struct SharedPointerContainer : public SmartPointerContainer { SharedPointerContainer(std::shared_ptr sharedPtr) : m_sharedPtr(std::move(sharedPtr)) {} std::shared_ptr GetShared() const override { return m_sharedPtr; } private: std::shared_ptr m_sharedPtr; }; template struct WeakPointerContainer : public SmartPointerContainer { WeakPointerContainer(std::weak_ptr weakPtr) : m_weakPtr(std::move(weakPtr)) {} std::shared_ptr GetShared() const override { return m_weakPtr.lock(); } private: std::weak_ptr m_weakPtr; }; public: // Returns a handle that can be used to later retrieve the stored shared_ptr. // Used when an asynchronous flat-C API requires a shared_ptr as its context. template static void* store_shared_ptr(std::shared_ptr contextSharedPtr) { return Make>(contextSharedPtr); } // Returns a handle that can be used to later retrieve the stored weak_ptr (as a shared_ptr, so // it will be null if the object has since been cleaned up). template static void* store_weak_ptr(std::weak_ptr contextWeakPtr) { return Make>(contextWeakPtr); } // Retrieves a shared previouly stored with store_shared_ptr or store_weak_ptr. If releaseContext is true // the copy of the smart pointer stored internally is deleted. template static std::shared_ptr get_shared_ptr(void* context, bool releaseContext = true) { auto smartPtrContainer = reinterpret_cast*>(context); auto sharedPtr = smartPtrContainer->GetShared(); if (releaseContext) { Delete(smartPtrContainer); } return sharedPtr; } static time_t time_t_from_datetime(const xbox::services::datetime& datetime); // Creates an asyncBlock that clean itself up in the completion callback static XAsyncBlock* MakeDefaultAsyncBlock(XTaskQueueHandle queue); static XAsyncBlock* MakeAsyncBlock(XTaskQueueHandle queue, void* context, XAsyncCompletionRoutine* callback); private: static xsapi_internal_vector get_locale_list(); utils(); utils(const utils&); utils& operator=(const utils&); }; NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END ================================================ FILE: Source/System/Android/local_storage_android.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "local_storage.h" #include "a/java_interop.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_SYSTEM_CPP_BEGIN String LocalStorage::GetDefaultStoragePath() { std::shared_ptr interop{ java_interop::get_java_interop_singleton() }; JNIEnv* jniEnv{ interop->GetJniEnv() }; jclass localStorageClass = interop->GetLocalStorageClass(); if (localStorageClass == nullptr) { LOGS_ERROR << "Couldn't find Storage class in Jni Environment."; assert(false); } jmethodID getStoragePathMethodId = jniEnv->GetStaticMethodID(localStorageClass, "getPath", "(Landroid/content/Context;)Ljava/lang/String;"); jstring jStr = static_cast(jniEnv->CallStaticObjectMethod(localStorageClass, getStoragePathMethodId, interop->get_activity())); if (jStr == nullptr) { LOGS_ERROR << "getStoragePath returned a null path"; assert(false); } return java_interop::StringFromJString(jniEnv, jStr) + "/"; } NAMESPACE_MICROSOFT_XBOX_SERVICES_SYSTEM_CPP_END ================================================ FILE: Source/System/Win32/local_storage_win32.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "local_storage.h" #include NAMESPACE_MICROSOFT_XBOX_SERVICES_SYSTEM_CPP_BEGIN HRESULT LocalStorage::SetStoragePath( _In_opt_z_ const char* path ) { // On Win32, if the default storage handlers are being used, the client must // specify a base storage path. If custom storage handlers have been specified, // the path will just be ignored. if (m_writeHandler == DefaultWrite) { if (path) { m_path = path; return S_OK; } else { return E_INVALIDARG; } } return S_OK; } NAMESPACE_MICROSOFT_XBOX_SERVICES_SYSTEM_CPP_END ================================================ FILE: Source/System/a/java_interop.cpp ================================================ #include "pch.h" #include #include "a/http_call_static_glue.h" #include "a/xbox_live_app_config_static_glue.h" #include "TCUI/Android/title_callable_static_glue.h" #include "a/jni_utils.h" #include "a/java_interop.h" #include "TCUI/Android/title_callable_ui_jni.h" using namespace xbox::services::system; NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN std::shared_ptr java_interop::s_javaInterop; std::shared_ptr java_interop::java_interop::get_java_interop_singleton() { if (s_javaInterop != nullptr) { return s_javaInterop; } s_javaInterop = std::make_shared(); return s_javaInterop; } java_interop::java_interop(): m_javaVM(nullptr), m_activity(nullptr), m_marketActivityClass(nullptr), m_contextObject(nullptr), m_tcuiInteropClass(nullptr), m_initialized(false) { } xbl_result java_interop::initialize(JavaVM* jvm, jobject activity) { rwlock_guard guard(java_interop_singletonLock, true); m_javaVM = jvm; m_activity = activity; cpprest_init(m_javaVM); JNIEnv* jniEnv; JNI_ATTACH_THREAD(m_javaVM, jniEnv) jclass acl = jniEnv->GetObjectClass(m_activity); if (acl != NULL) { jmethodID getClassLoader = jniEnv->GetMethodID(acl, "getClassLoader", "()Ljava/lang/ClassLoader;"); if (getClassLoader != NULL) { jobject cls = jniEnv->CallObjectMethod(m_activity, getClassLoader); if (cls != NULL) { jclass classLoader = jniEnv->FindClass("java/lang/ClassLoader"); if (classLoader != NULL) { jmethodID findClass = jniEnv->GetMethodID(classLoader, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;"); if (findClass != NULL) { // Get a reference to the core activity for purposes of having a context: https://groups.google.com/forum/#!topic/android-ndk/SHCXgUCE7t4, http://stackoverflow.com/questions/25167806/sending-an-intent-from-c-via-jni // And be able to create an intent which will launch the activity jclass classNativeActivity = jniEnv->FindClass("android/app/NativeActivity"); if (classNativeActivity != NULL) { jclass contextClass = jniEnv->FindClass("android/content/Context"); if (contextClass != NULL) { jmethodID startActivityMethodId = jniEnv->GetMethodID(contextClass, "startActivity", "(Landroid/content/Intent;)V"); if (startActivityMethodId != NULL) { jmethodID applicationContextMethodId = jniEnv->GetMethodID(classNativeActivity, "getApplicationContext", "()Landroid/content/Context;"); if (applicationContextMethodId != NULL) { jobject contextObj = jniEnv->CallObjectMethod(m_activity, applicationContextMethodId); if (contextObj != NULL) { m_contextObject = jniEnv->NewGlobalRef(contextObj); if (m_contextObject != NULL) { jstring strClassNameActivity = jniEnv->NewStringUTF("com/microsoft/xbox/idp/interop/Interop"); if (strClassNameActivity != NULL) { jclass marketActivityClass = (jclass)jniEnv->CallObjectMethod(cls, findClass, strClassNameActivity); if (marketActivityClass != NULL) { m_marketActivityClass = (jclass)jniEnv->NewGlobalRef(marketActivityClass); jstring strTCUIClassNameActivity = jniEnv->NewStringUTF("com/microsoft/xboxtcui/Interop"); if (strTCUIClassNameActivity != NULL) { jclass tcuiClass = (jclass)jniEnv->CallObjectMethod(cls, findClass, strTCUIClassNameActivity); if (tcuiClass != NULL) { m_tcuiInteropClass = (jclass)jniEnv->NewGlobalRef(tcuiClass); if (m_marketActivityClass != NULL && m_tcuiInteropClass != NULL) { jstring localStorageClassName = jniEnv->NewStringUTF("com/microsoft/xboxlive/LocalStorage"); auto tempClass = (jclass)jniEnv->CallObjectMethod(cls, findClass, localStorageClassName); assert(tempClass); m_localStorageClass = (jclass)jniEnv->NewGlobalRef(tempClass); return finish_initialization(jniEnv, cls, findClass, true); } } } } } } } } } } } } } } } } JNI_ERROR_CHECK(jniEnv) return xbl_result(xbl_error_code::runtime_error, "Initialize failed"); } xbl_result java_interop::initialize(JNIEnv* env, jclass clsInterop, jobject context) { rwlock_guard guard(java_interop_singletonLock, true); JavaVM* jvm; if (env->GetJavaVM(&jvm) == 0) { m_javaVM = jvm; cpprest_init(m_javaVM); m_contextObject = env->NewGlobalRef(context); m_marketActivityClass = (jclass)env->NewGlobalRef(clsInterop); jclass clsContext = env->GetObjectClass(m_contextObject); if (clsContext != NULL) { jmethodID getClassLoader = env->GetMethodID(clsContext, "getClassLoader", "()Ljava/lang/ClassLoader;"); if (getClassLoader != NULL) { jobject classLoader = env->CallObjectMethod(m_contextObject, getClassLoader); if (classLoader != NULL) { jmethodID loadClass = env->GetMethodID(env->GetObjectClass(classLoader), "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;"); if (loadClass != NULL) { return finish_initialization(env, classLoader, loadClass, false); } } } } } JNI_ERROR_CHECK(env) return xbl_result(xbl_error_code::runtime_error, "Initialize failed"); } jobject java_interop::app_callback_intent() { return m_pendingIntent; } void java_interop::set_app_callback_intent( jobject pendingIntent ) { m_pendingIntent = pendingIntent; } xbl_result java_interop::finish_initialization(JNIEnv* env, jobject clsLoader, jmethodID loadClass, bool useTcui) { if (http_call_register_natives(env, clsLoader, loadClass) && xbox_live_app_config_register_natives(env, clsLoader, loadClass) && (!useTcui || title_callable_ui_register_natives(env, clsLoader, loadClass)) ) { m_initialized = true; LOG_INFO("java_interop initialized"); } else { LOG_ERROR("error registering native methods"); return xbl_result(xbl_error_code::runtime_error, "Registration error"); } return xbl_result(xbl_error_code::no_error); } void java_interop::deinitialize() { rwlock_guard guard(java_interop_singletonLock, true); if (m_javaVM == nullptr) { return; } JNIEnv* jniEnv; JNI_ATTACH_THREAD(m_javaVM, jniEnv) if (m_marketActivityClass != nullptr) { jniEnv->DeleteGlobalRef(m_marketActivityClass); } if (m_contextObject != nullptr) { jniEnv->DeleteGlobalRef(m_contextObject); } JNI_ERROR_CHECK(jniEnv) m_initialized = false; m_javaVM = nullptr; m_activity = nullptr; m_marketActivityClass = nullptr; m_contextObject = nullptr; } xbl_result java_interop::log_cll(const string_t& xuid, const string_t& eventName, const string_t& eventData) { rwlock_guard guard(java_interop_singletonLock, false); if (!m_initialized) { LOG_ERROR("java_interop not initialized"); assert(false); return xbl_result(xbl_error_code::runtime_error, "java_interop not initialized"); } JNIEnv* jniEnv; JNI_ATTACH_THREAD(m_javaVM, jniEnv) auto localCapacityResult = jniEnv->EnsureLocalCapacity(24); // the size of the three pointers we allocate below (assuming they are 8 bytes each) if (localCapacityResult == 0) { jmethodID initMethodId = jniEnv->GetStaticMethodID(m_marketActivityClass, "LogCLL", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); if (initMethodId != NULL) { JNI_ERROR_CHECK(jniEnv) jstring jxuid = jniEnv->NewStringUTF(xuid.c_str()); jstring jeventName = jniEnv->NewStringUTF(eventName.c_str()); jstring jeventData = jniEnv->NewStringUTF(eventData.c_str()); if (jniEnv->ExceptionCheck()) { JNI_ERROR_CHECK(jniEnv) jniEnv->DeleteLocalRef(jxuid); jniEnv->DeleteLocalRef(jeventName); jniEnv->DeleteLocalRef(jeventData); LOG_ERROR("failure to allocated"); return xbl_result(xbl_error_code::runtime_error, "failed to allocate"); } jniEnv->CallStaticVoidMethod(m_marketActivityClass, initMethodId, jxuid, jeventName, jeventData); jniEnv->DeleteLocalRef(jxuid); jniEnv->DeleteLocalRef(jeventName); jniEnv->DeleteLocalRef(jeventData); if (!jniEnv->ExceptionCheck()) { return xbl_result(xbl_error_code::no_error); } } } JNI_ERROR_CHECK(jniEnv) return xbl_result(xbl_error_code::runtime_error, "cll logging failed"); } xbl_result java_interop::log_telemetry_signin(bool silentAPI, const string_t& state) { rwlock_guard guard(java_interop_singletonLock, false); if (!m_initialized) { LOG_ERROR("java_interop not initialized"); assert(false); return xbl_result(xbl_error_code::runtime_error, "java_interop not initialized"); } JNIEnv* jniEnv; JNI_ATTACH_THREAD(m_javaVM, jniEnv) auto localCapacityResult = jniEnv->EnsureLocalCapacity(16); // the size of the two pointers we allocate below (assuming they are 8 bytes each) if (localCapacityResult == 0) { jmethodID initMethodId = jniEnv->GetStaticMethodID(m_marketActivityClass, "LogTelemetrySignIn", "(Ljava/lang/String;Ljava/lang/String;)V"); if (initMethodId != NULL) { JNI_ERROR_CHECK(jniEnv) jstring japi; if (silentAPI) { japi = jniEnv->NewStringUTF("API - signin_silently - "); } else { japi = jniEnv->NewStringUTF("API - signin - "); } jstring jstate = jniEnv->NewStringUTF(state.c_str()); if (jniEnv->ExceptionCheck()) { JNI_ERROR_CHECK(jniEnv) jniEnv->DeleteLocalRef(japi); jniEnv->DeleteLocalRef(jstate); LOG_ERROR("failure to allocated"); return xbl_result(xbl_error_code::runtime_error, "failed to allocate"); } jniEnv->CallStaticVoidMethod(m_marketActivityClass, initMethodId, japi, jstate); jniEnv->DeleteLocalRef(japi); jniEnv->DeleteLocalRef(jstate); if (!jniEnv->ExceptionCheck()) { return xbl_result(xbl_error_code::no_error); } } } JNI_ERROR_CHECK(jniEnv) return xbl_result(xbl_error_code::runtime_error, "cll logging failed"); } string_t java_interop::read_config_file() { rwlock_guard guard(java_interop_singletonLock, false); if (!m_initialized) { LOG_ERROR("java_interop not initialized"); assert(false); return {}; } string_t configText; JNIEnv* jniEnv; JNI_ATTACH_THREAD(m_javaVM, jniEnv) jmethodID initMethodId = jniEnv->GetStaticMethodID(m_marketActivityClass, "ReadConfigFile", "(Landroid/content/Context;)Ljava/lang/String;"); if (initMethodId != NULL) { jobject result = jniEnv->CallStaticObjectMethod(m_marketActivityClass, initMethodId, m_contextObject); if (jniEnv->ExceptionCheck()) { return configText; } const char* str = jniEnv->GetStringUTFChars((jstring)result, NULL); configText = str; jniEnv->ReleaseStringUTFChars((jstring)result, str); } JNI_ERROR_CHECK(jniEnv) return configText; } string_t java_interop::get_local_storage_path() { rwlock_guard singletonGuard(java_interop_singletonLock, false); if (!m_initialized) { LOG_ERROR("java_interop not initialized"); assert(false); return {}; } std::lock_guard guard(m_localStoragePathLock); if (m_localStoragePath.empty()) { JNIEnv* jniEnv; JNI_ATTACH_THREAD(m_javaVM, jniEnv) jmethodID initMethodId = jniEnv->GetStaticMethodID(m_marketActivityClass, "GetLocalStoragePath", "(Landroid/content/Context;)Ljava/lang/String;"); if (initMethodId != NULL) { jobject result = jniEnv->CallStaticObjectMethod(m_marketActivityClass, initMethodId, m_contextObject); if (jniEnv->ExceptionCheck()) { return m_localStoragePath; } const char* str = jniEnv->GetStringUTFChars((jstring)result, NULL); m_localStoragePath = str; jniEnv->ReleaseStringUTFChars((jstring)result, str); } JNI_ERROR_CHECK(jniEnv) } return m_localStoragePath; } JavaVM* java_interop::get_java_vm() { return m_javaVM; } jobject java_interop::get_activity() { return m_activity; } jclass java_interop::get_market_activity_class() { return m_marketActivityClass; } jclass java_interop::get_tcui_interop_class() { return m_tcuiInteropClass; } jobject java_interop::get_context_object() { return m_contextObject; } void java_interop::register_natives( JNINativeMethod nativeMethods[] ) { JNIEnv* jniEnv; JNI_ATTACH_THREAD(m_javaVM, jniEnv) jniEnv->RegisterNatives(m_marketActivityClass, nativeMethods, 1); JNI_ERROR_CHECK(jniEnv) } JNIEnv* java_interop::GetJniEnv() const { JNIEnv* env{ nullptr }; auto result = m_javaVM->GetEnv((void**)& env, JNI_VERSION_1_6); if (result != JNI_OK) { LOGS_ERROR << "Failed to retrieve the JNIEnv from the JavaVM."; assert(false); } return env; } String java_interop::StringFromJString(JNIEnv* env, jstring jStr) { assert(env); assert(jStr); const char* charPtr = env->GetStringUTFChars(jStr, nullptr); String string{ charPtr }; env->ReleaseStringUTFChars(jStr, charPtr); return string; } void java_interop::StoreUser(User&& user) { std::lock_guard lock{ m_storedUserMutex }; m_storedUser = std::make_shared(std::move(user)); } std::shared_ptr java_interop::GetStoredUser() { std::lock_guard lock{ m_storedUserMutex }; return m_storedUser; } std::shared_ptr java_interop::ExtractStoredUser() { std::lock_guard lock{ m_storedUserMutex }; return std::move(m_storedUser); } NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END ================================================ FILE: Source/System/a/java_interop.h ================================================ //********************************************************* // // Copyright (c) Microsoft. All rights reserved. // THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF // ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY // IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR // PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. // //********************************************************* #pragma once #include #include "a/rwlock_guard.h" namespace xbox { namespace services { class java_interop : public std::enable_shared_from_this { public: static std::shared_ptr get_java_interop_singleton(); java_interop(); xbl_result initialize(JavaVM* jvm, jobject activity); xbl_result initialize(JNIEnv* env, jclass clsInterop, jobject context); void deinitialize(); xbl_result log_cll(const string_t& xuid, const string_t& eventName, const string_t& eventData); xbl_result log_telemetry_signin(bool silentAPI, const string_t& state); string_t read_config_file(); string_t get_local_storage_path(); JavaVM* get_java_vm(); jobject get_activity(); jclass get_market_activity_class(); jclass get_tcui_interop_class(); jobject get_context_object(); void register_natives(JNINativeMethod nativeMethods[]); jobject app_callback_intent(); void set_app_callback_intent(jobject pendingIntent); pthread_rwlock_t java_interop_singletonLock = PTHREAD_RWLOCK_INITIALIZER; JNIEnv* GetJniEnv() const; static String StringFromJString(JNIEnv* env, jstring jStr); jclass GetLocalStorageClass() const { return m_localStorageClass; } // TODO This is a temporary workaround for TCUI. XSAPI C++ TCUI API's accept a xal_user_handle, from which we extract // XUID and privileges and call into Java code. Java code then calls back into XSAPI to make an HTTP call, but it does not pass and user // context or xal_user_handle but it most likely should. Previously we just remembered the last signed in user, so we can // emulate that behavior again for now. void StoreUser(User&& user); std::shared_ptr GetStoredUser(); std::shared_ptr ExtractStoredUser(); private: JavaVM* m_javaVM; jobject m_activity; jclass m_marketActivityClass; jclass m_tcuiInteropClass; jobject m_contextObject; jobject m_pendingIntent; static std::shared_ptr s_javaInterop; bool m_initialized; jclass m_localStorageClass{ nullptr }; string_t m_localStoragePath; std::mutex m_localStoragePathLock; xbl_result finish_initialization(JNIEnv* env, jobject clsLoader, jmethodID loadClass, bool useTcui); std::shared_ptr m_storedUser{ nullptr }; std::mutex m_storedUserMutex; }; }} ================================================ FILE: Source/System/client_operation.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "xsapi-c/platform_c.h" struct XblClientOperation { public: static HRESULT HresultFromResult(XblClientOperationResult result) noexcept { switch (result) { case XblClientOperationResult::Success: { return S_OK; } case XblClientOperationResult::Failure: { return E_FAIL; } default: { return S_OK; } } } virtual HRESULT Begin() noexcept = 0; virtual HRESULT Fail(HRESULT result) noexcept = 0; }; NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN template class ClientOperation : public XblClientOperation, public RefCounter, public std::enable_shared_from_this> { public: using OperationLauncher = Function; ClientOperation( OperationLauncher launcher, AsyncContext async ) noexcept; ClientOperation() = delete; ClientOperation(const ClientOperation&) = delete; ClientOperation(ClientOperation&&) = delete; ClientOperation& operator=(ClientOperation) = delete; virtual HRESULT Begin() noexcept override; virtual HRESULT Fail(HRESULT result) noexcept override; virtual HRESULT Complete(ResultT args) noexcept; private: // RefCounter std::shared_ptr GetSharedThis() override; OperationLauncher m_launcher; AsyncContext m_asyncContext; }; template ClientOperation::ClientOperation( OperationLauncher launcher, AsyncContext async ) noexcept : m_launcher{ std::move(launcher) }, m_asyncContext{ std::move(async) } { } template HRESULT ClientOperation::Begin() noexcept { // Leak a ref until client calls Complete AddRef(); // Marshall the launcher to the correct task queue return m_asyncContext.Queue().RunWork([this] { m_launcher(this); }); } template HRESULT ClientOperation::Fail( HRESULT result ) noexcept { return Complete(ResultT{ result }); } template HRESULT ClientOperation::Complete( ResultT result ) noexcept { // Marshall the completion to the completion port return m_asyncContext.Queue().RunCompletion( [ this, result{ std::move(result) } ] () mutable { m_asyncContext.Complete(std::move(result)); // Reclaim the ref leaked in Run DecRef(); }); } template std::shared_ptr ClientOperation::GetSharedThis() { return this->shared_from_this(); } NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END ================================================ FILE: Source/System/iOS/XBLDictionaryToJSON.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #import #import "xsapi-cpp/services.h" @interface XBLDictionaryToJSON : NSObject - (web::json::value)jsonForObject:(id)object; @end ================================================ FILE: Source/System/iOS/XBLDictionaryToJSON.mm ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #import "XBLDictionaryToJSON.h" #import "xsapi_utils.h" using namespace web::http; using namespace web::http::client; using namespace xbox::services; using namespace xbox::services::system; @implementation XBLDictionaryToJSON - (web::json::value)jsonForObject:(id)object { if ([object isKindOfClass:[NSString class]]) { return [self jsonForString:object]; } else if ([object isKindOfClass:[NSNumber class]]) { return [self jsonForNumber:object]; } else if ([object isKindOfClass:[NSArray class]]) { return [self jsonForArray:object]; } else if ([object isKindOfClass:[NSDictionary class]]) { return [self jsonForDictionary:object]; } else { return web::json::value::null(); } } - (web::json::value)jsonForString:(NSString *)string { NSString *value = (NSString *)string; string_t value_t = string_t([value UTF8String]); return web::json::value(value_t); } - (web::json::value)jsonForNumber:(NSNumber *)number { if ([self numberIsBool:number]) { BOOL value = [number boolValue]; if (value) { return web::json::value("true"); } else { return web::json::value("false"); } } else if ([self numberIsInteger:number]) { uint64_t value = (uint64_t)[number unsignedLongLongValue]; return web::json::value(value); } else { float value = [number floatValue]; return web::json::value(value); } } - (web::json::value)jsonForDictionary:(NSDictionary *)json { web::json::value node; for (NSString *key in json) { id value = json[key]; string_t key_t = string_t([key UTF8String]); auto value_t = [self jsonForObject:value]; node[key_t] = value_t; } return node; } - (web::json::value)jsonForArray:(NSArray *)json { std::vector array; for (id value in json) { web::json::value jsonValue = [self jsonForObject:value]; array.push_back(jsonValue); } return web::json::value::array(array); } - (BOOL)numberIsBool:(NSNumber *)number { NSNumber *trueNumber = [NSNumber numberWithBool:YES]; NSString *trueString = [NSString stringWithUTF8String:[trueNumber objCType]]; NSNumber *falseNumber = [NSNumber numberWithBool:NO]; NSString *falseString = [NSString stringWithUTF8String:[falseNumber objCType]]; NSString *objCType = [NSString stringWithUTF8String:[number objCType]]; if (([trueNumber compare:number] == NSOrderedSame && [objCType isEqualToString:trueString]) || ([falseNumber compare:number] == NSOrderedSame && [objCType isEqualToString:falseString])) { return YES; } return NO; } - (BOOL)numberIsInteger:(NSNumber *)number { if(CFNumberIsFloatType((CFNumberRef)number)) { return NO; } else { return YES; } } @end ================================================ FILE: Source/System/iOS/XBLKeychainStorage.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. /** The default access group Xbox Live Services uses for shared data. */ extern NSString *const _Nonnull XBLKeychainStorageSharedAccessGroup; /** Interface for accessing the keychain on iOS. */ @interface XBLKeychainStorage : NSObject /** Gets the access group this storage object is pointing to. */ @property (readonly, nonatomic, nonnull) NSString *accessGroup; /** Gets and sets the label to use to filter read items and to use to mark written items with. Use nil to indicate read all items and to write no label. */ @property (copy, nonatomic, nullable) NSString *itemLabel; /** * Do not use. Please use one of the other initializers. */ - (instancetype _Nonnull)init NS_UNAVAILABLE; /** Initializes the keychain storage object to put items into the default access group of the current running app. */ - (instancetype _Nonnull)initWithDefaultAccessGroup; /** Initializes the keychain storage object with a given access group. The identifier prefix is prepended automatically. @param accessGroup The access group to use for this keychain storage object or nil to not specify one and allow the keychain service to use its default behavior. */ - (instancetype _Nonnull)initWithAccessGroup:(NSString * _Nullable)accessGroup; /** Initializes the keychain storage with a specific label. @param label Value to be set for kSecAttrLabel to make it easy to query for items in keychain. */ - (instancetype _Nonnull)initWithDefaultAccessGroupAndLabel:(NSString * _Nullable)label; /** Initializes the keychain storage with a given access group and label. @param accessGroup The access group to use for this keychain storage object or nil to not specify one and allow the keychain service to use its default behavior. @param label Value to be set for kSecAttrLabel to make it easy to query for items in keychain. */ - (instancetype _Nonnull)initWithAccessGroup:(NSString * _Nullable)accessGroup label:(NSString * _Nullable)label; /** Adds or updates an item to the keychain under the specified key. If an item does not already exist at the key one will be added. If an item does already exist at the key, it will be updated. Returns YES if the operation succeeds and NO with a corresponding error if it does not. @param key The key to store the item under. @param item Archivable item to save. @param error optional: The error pointer to assign upon error or nil if not wanted. @return YES if the operation succeeds and NO with an accompanying error if it does not. */ - (BOOL)addOrUpdateItemForKey:(NSString * _Nonnull)key item:(id _Nonnull)item error:(out NSError * _Nullable __autoreleasing * _Nullable)error; /** Retrieves the attributes for the item with the given key. If the item is missing, the out parameter is set to nil and YES is returned. @param key The key to query the attributes for. @param attributes The out param to which a new dictionary will be assigned. @param error optional: The error pointer to assign upon error or nil if not wanted. @return YES if the operation succeeds and NO with an accompanying error if it does not. */ - (BOOL)getAttributesForKey:(NSString * _Nonnull)key attributes:(out NSDictionary * _Nullable __autoreleasing * _Nonnull)attributes error:(out NSError * _Nullable __autoreleasing * _Nullable)error; /** Reads and unarchives the data at the specified key. Returns YES if the operation succeeds and NO with a corresponding error if it does not. If the item is missing, the out parameter is set to nil and YES is returned. @param key The key to read the item under. @param item The id to assign upon unarchiving. @param error optional: The error pointer to assign upon error or nil if not wanted. @return YES if the operation succeeds and NO with an accompanying error if it does not. */ - (BOOL)readItemForKey:(NSString * _Nonnull)key item:(out id _Nullable __autoreleasing * _Nonnull)item error:(out NSError * _Nullable __autoreleasing * _Nullable)error; /** Deletes the item at the specified index if it exists. @param key The key of the item to delete. @param error optional: The error pointer to assign upon error or nil if not wanted. @return YES if the operation succeeds and NO with an accompanying error if it does not. */ - (BOOL)deleteItemForKey:(NSString * _Nonnull)key error:(out NSError * _Nullable __autoreleasing * _Nullable)error; /** Reads and unarchives the data for the current access group and label combination. Returns YES if the operation succeeds and NO with a corresponding error if it does not. If there are no items in the access group, the out parameter NSArray is set to empty array and YES is returned. @param items An array of items in the access group. @param error optional: The error pointer to assign upon error or nil if not wanted. @return YES if the operation succeeds and NO with an accompanying error if it does not. */ - (BOOL)readAllItemsForAccessGroup:(out NSArray * _Nullable __autoreleasing * _Nonnull)items error:(out NSError * _Nullable __autoreleasing * _Nullable)error; /** Reads all the keys in the current access group and label combination. Returns YES on success and NO with an error object if the operation fails. If no keys are found, an empty array is returned. @param keys An array of existing keys. @param error optional: The error pointer to assign upon error or nil if not wanted. @return YES if the operation succeeds and NO with an accompanying error if it does not. */ - (BOOL)getAllKeys:(out NSArray * _Nullable __autoreleasing * _Nonnull)keys error:(out NSError * _Nullable __autoreleasing * _Nullable)error; @end ================================================ FILE: Source/System/iOS/XBLKeychainStorage.mm ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #import "XBLKeychainStorage.h" #import "XBLServiceManager.h" NSString *const _Nonnull XBLKeychainStorageSharedAccessGroup = @"com.microsoft.xboxliveservices"; /** The service name to use for items stored in the keychain by Xbox Live Services. */ static NSString *const XBLKeychainStorageServiceName = @"com.microsoft.xboxliveservices"; /** The application identifier prefix for the current running app. Note: this should only be accessed via getApplicationPrefix. */ static NSString *XBLKeychainStorageApplicationIdentifierPrefix = nil; @implementation XBLKeychainStorage - (instancetype _Nonnull)initWithDefaultAccessGroup { return [self initWithAccessGroup:nil label:nil]; } - (instancetype _Nonnull)initWithAccessGroup:(NSString * _Nullable)accessGroup; { return [self initWithAccessGroup:accessGroup label:nil]; } - (instancetype _Nonnull)initWithDefaultAccessGroupAndLabel:(NSString * _Nullable)label { return [self initWithAccessGroup:nil label:label]; } - (instancetype _Nonnull)initWithAccessGroup:(NSString * _Nullable)accessGroup label:(NSString * _Nullable)label { self = [super init]; if (self != nil) { if (accessGroup != nil) { _accessGroup = [NSString stringWithFormat:@"%@.%@", [XBLKeychainStorage getApplicationPrefix], accessGroup]; } _itemLabel = [label copy]; } return self; } /** Retrieves the application identifier prefix for use with accessing keychain groups. */ + (NSString * _Nonnull)getApplicationPrefix { if (XBLKeychainStorageApplicationIdentifierPrefix == nil) { // We have to call this method to get the prefix initialized as well. [XBLKeychainStorage getApplicationAccessGroup]; LOG_DEBUG(([[NSString stringWithFormat:@"Application bundle prefix detected: %@", XBLKeychainStorageApplicationIdentifierPrefix] UTF8String])); } return XBLKeychainStorageApplicationIdentifierPrefix; } /** Retrieves the application's default access group. In the case of a simulator runtime, this might return an empty string. @return The application's default access group specified in the application's entitlements file. */ + (NSString * _Nonnull)getApplicationAccessGroup { static NSString *keychainStorageApplicationAccessGroup = nil; // Perform the detection inside of a dispatch_once block. We don't expect any failures but there's no reason to kill // the app if one step fails. static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ // Add a unique item to the keychain. The item will be added with the app's default access group. XBLKeychainStorage *keychain = [[XBLKeychainStorage alloc] initWithAccessGroup:nil]; NSString *uniqueKey = [NSString stringWithFormat:@"AccessGroupDetection-%@", [[NSUUID UUID] UUIDString]]; NSError *error = nil; if (![keychain addOrUpdateItemForKey:uniqueKey item:uniqueKey error:&error]) { NSAssert(NO, @"Error when adding new unique item %@ to the keychain: %@", uniqueKey, error); return; } NSString *applicationAccessGroup = nil; // Retrieve the unique item's access group. if (![keychain retrieveItemAccessGroupForKey:uniqueKey accessGroup:&applicationAccessGroup error:&error]) { NSAssert(NO, @"Error when reading the default access group: %@", error); return; } // For signed apps this will come back as the first entry in the entitlements file or in an // unsigned build it should come back simply as @"test". NSAssert([applicationAccessGroup length] > 0, @"Default access group came back as empty: %@", applicationAccessGroup); NSArray *components = [applicationAccessGroup componentsSeparatedByString:@"."]; XBLKeychainStorageApplicationIdentifierPrefix = [[components objectEnumerator] nextObject]; keychainStorageApplicationAccessGroup = [applicationAccessGroup substringFromIndex: XBLKeychainStorageApplicationIdentifierPrefix.length]; if ([keychainStorageApplicationAccessGroup hasPrefix:@"."]) { // Remove the first '.' from the default group name keychainStorageApplicationAccessGroup = [keychainStorageApplicationAccessGroup substringFromIndex:1]; } // Delete the unique item. if (![keychain deleteItemForKey:uniqueKey error:&error]) { NSAssert(NO, @"Error when deleting unique item %@ from the keychain: %@", uniqueKey, error); return; } LOG_DEBUG(([[NSString stringWithFormat:@"Default app access group detected: %@", keychainStorageApplicationAccessGroup] UTF8String])); }); return keychainStorageApplicationAccessGroup; } /** Reads the access group for the specified key. YES with a nil access group is returned if the item cannot be found. @param key The key of the item to examine. @param accessGroup The out parameter the access group is returned through. @param error optional: The error object describing the failure that occurred if one occurred. @return YES if the call succeeds. Otherwise NO is returned along with a populated error object. */ - (BOOL)retrieveItemAccessGroupForKey:(NSString * _Nonnull)key accessGroup:(out NSString * _Nullable __autoreleasing * _Nonnull)accessGroup error:(out NSError * _Nullable __autoreleasing * _Nullable)error { NSAssert(accessGroup != nil, @"Out parameter must be non-nil."); // Build the query to search for the requested item. NSMutableDictionary *query = [self dictionaryForKeychainQuery:key]; query[(__bridge id)kSecReturnAttributes] = (__bridge id)kCFBooleanTrue; // Search for it. CFDictionaryRef cfAttributes = NULL; OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, (CFTypeRef *)&cfAttributes); if (status == errSecSuccess) { NSDictionary *attributes = (__bridge_transfer NSDictionary *)cfAttributes; *accessGroup = attributes[(__bridge id)kSecAttrAccessGroup]; NSAssert([*accessGroup length] > 0, @"The access group should be non-empty: %@", *accessGroup); if ([*accessGroup length] == 0) { // For compiled versions of the library we ship, make sure that if this is running in an environment where // access groups are empty, we should still behave nicely *accessGroup = nil; } } else if (status == errSecItemNotFound) { // Item not found. Set return to nil and still call this a success. *accessGroup = nil; } else { NSAssert(NO, @"Error loading keychain item: %d", (int)status); if (error != nil) { *error = [NSError errorWithDomain:kXboxLiveServicesDomain code:status userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Error occurred while loading keychain item \"%@\" for retrieving access group.", key]}]; } return NO; } return YES; } - (BOOL)addOrUpdateItemForKey:(NSString * _Nonnull)key item:(id _Nonnull)item error:(out NSError * _Nullable __autoreleasing * _Nullable)error { NSAssert(item != nil, @"Trying to write a nil item to keychain. This probably isn't intended. key: %@", key); // First find out if the item already exists NSMutableDictionary *query = [self dictionaryForKeychainQuery:key]; // For the initial query we don't want to use a label because the caller is asking to update the item. This should // be performed even if an existing item with a different label exists. If the item does exist, the label will be // added as part of the update dictionary. If the item does not exist, the query dictionary will be replaced // entirely. [query removeObjectForKey:(__bridge id)kSecAttrLabel]; OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, NULL); if (status == errSecSuccess) { LOG_DEBUG(([[NSString stringWithFormat:@"Updating existing item in keychain: %@", key] UTF8String])); // The item exists, perform an update NSMutableDictionary *update = [self dictionaryForKeychainAddOrUpdate:key]; // Set the data on the update update[(__bridge id)kSecValueData] = [NSKeyedArchiver archivedDataWithRootObject:item]; // The class key must be removed from the update because... dragons? [update removeObjectForKey:(__bridge id)kSecClass]; status = SecItemUpdate((__bridge CFDictionaryRef)query, (__bridge CFDictionaryRef)update); if (status == errSecItemNotFound) { // If we get errSecItemNotFound, assume the item was deleted by another thread and allow the deleter to win // the last-write-wins game. LOG_WARN("Item we were trying to update no longer exists."); } else if (status != errSecSuccess) { if (error != nil) { *error = [NSError errorWithDomain:kXboxLiveServicesDomain code:status userInfo:@{NSLocalizedDescriptionKey: @"Error occurred while updating keychain item."}]; } return NO; } } else if (status == errSecItemNotFound) { LOG_DEBUG(([[NSString stringWithFormat:@"Adding new item in keychain: %@", key] UTF8String])); // The item does not exist, perform an add query = [self dictionaryForKeychainAddOrUpdate:key]; query[(__bridge id)kSecValueData] = [NSKeyedArchiver archivedDataWithRootObject:item]; status = SecItemAdd((__bridge CFDictionaryRef)query, NULL); if (status == errSecDuplicateItem) { // If we get errSecDuplicateItem assume the item was added by another thread and allow them to win the // last-write-wins game. LOG_WARN("Item we were trying to add is now a duplicate."); } else if (status != errSecSuccess) { if (error != nil) { *error = [NSError errorWithDomain:kXboxLiveServicesDomain code:status userInfo:@{NSLocalizedDescriptionKey: @"Error occurred while trying to add keychain item."}]; } return NO; } } else { // Unexpected error if (error != nil) { *error = [NSError errorWithDomain:kXboxLiveServicesDomain code:status userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Error occurred while checking the status of a keychain item \"%@\".", key]}]; } return NO; } return YES; } - (BOOL)getAttributesForKey:(NSString * _Nonnull)key attributes:(out NSDictionary * _Nullable __autoreleasing * _Nonnull)attributes error:(out NSError * _Nullable __autoreleasing * _Nullable)error { NSAssert(key.length > 0, @"key must be non-empty."); NSAssert(attributes != nil, @"Out param must be non-nil."); BOOL success = YES; CFArrayRef itemAttributes = NULL; NSMutableDictionary *query = [self dictionaryForKeychainQuery:key]; query[(__bridge id)kSecReturnAttributes] = (__bridge id)kCFBooleanTrue; query[(__bridge id)kSecMatchLimit] = (__bridge id)kSecMatchLimitOne; OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, (CFTypeRef *)&itemAttributes); if (status == errSecSuccess) { NSAssert(itemAttributes != NULL, @"Attributes expected to be non-nil on success."); *attributes = (__bridge_transfer NSDictionary*)itemAttributes; } else if (status == errSecItemNotFound) { *attributes = nil; } else { if (error != nil) { *error = [NSError errorWithDomain:kXboxLiveServicesDomain code:status userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Error occurred while reading attributes for item \"%@\" from keychain.", key]}]; } success = NO; } return success; } - (BOOL)readItemForKey:(NSString * _Nonnull)key item:(out id _Nullable __autoreleasing * _Nonnull)item error:(out NSError * _Nullable __autoreleasing * _Nullable)error { NSAssert(item != nil, @"Out param must be non-nil."); BOOL success = YES; CFDataRef itemData = NULL; NSMutableDictionary *query = [self dictionaryForKeychainQuery:key]; query[(__bridge id)kSecReturnData] = (__bridge id)kCFBooleanTrue; query[(__bridge id)kSecMatchLimit] = (__bridge id)kSecMatchLimitOne; OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, (CFTypeRef *)&itemData); if (status == errSecSuccess) { NSAssert(itemData != NULL, @"Data expected to be non-nil on success."); *item = [NSKeyedUnarchiver unarchiveObjectWithData:(__bridge NSData *)itemData]; } else if (status == errSecItemNotFound) { *item = nil; } else { if (error != nil) { *error = [NSError errorWithDomain:kXboxLiveServicesDomain code:status userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Error occurred while reading item \"%@\" from keychain.", key]}]; } // Don't return right away so that we make sure to clean up itemData. // In theory there should be nothing to clean up if an error occurred but this is safer. success = NO; } if (itemData != NULL) { CFRelease(itemData); } return success; } - (BOOL)deleteItemForKey:(NSString * _Nonnull)key error:(out NSError * _Nullable __autoreleasing * _Nullable)error { NSMutableDictionary *query = [self dictionaryForKeychainQuery:key]; OSStatus status = SecItemDelete((__bridge CFDictionaryRef)query); if (status != errSecSuccess && status != errSecItemNotFound) { if (error != nil) { *error = [NSError errorWithDomain:kXboxLiveServicesDomain code:status userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Error occurred while deleting item \"%@\" from keychain.", key]}]; } return NO; } return YES; } - (BOOL)readAllItemsForAccessGroup:(out NSArray * _Nullable __autoreleasing * _Nonnull)items error:(out NSError * _Nullable __autoreleasing * _Nullable)error { NSAssert(items != nil, @"Out param must be non-nil."); BOOL success = YES; CFArrayRef itemData = NULL; NSMutableDictionary *query = [self dictionaryForKeychainQuery:nil]; query[(__bridge id)kSecReturnData] = (__bridge id)kCFBooleanTrue; query[(__bridge id)kSecMatchLimit] = (__bridge id)kSecMatchLimitAll; OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, (CFTypeRef *)&itemData); if (status == errSecSuccess) { NSAssert(itemData != NULL, @"Data expected to be non-nil on success."); NSArray *itemArray = (__bridge_transfer NSArray *)itemData; NSMutableArray *valueData = [[NSMutableArray alloc] initWithCapacity:itemArray.count]; for (NSData *item in itemArray) { id decodedItem = [NSKeyedUnarchiver unarchiveObjectWithData:item]; // For unknown reasons, it's possible for the item to come out as nil if (decodedItem != nil) { [valueData addObject:decodedItem]; } else { LOG_ERROR(([[NSString stringWithFormat:@"Decoded value from keychain resulted in nil: %@", (item == nil) ? @"nil" : @"non-nil"] UTF8String])); NSAssert(NO, @"A keychain item is either nil or could not be unarchived."); } } *items = [valueData copy]; } else if (status == errSecItemNotFound) { *items = [NSArray array]; } else { if (error != nil) { *error = [NSError errorWithDomain:kXboxLiveServicesDomain code:status userInfo:@{NSLocalizedDescriptionKey: @"Error occurred while reading items from keychain."}]; } success = NO; } return success; } - (BOOL)getAllKeys:(out NSArray * _Nullable __autoreleasing * _Nonnull)keys error:(out NSError * __autoreleasing * _Nullable)error { NSAssert(keys != nil, @"Out param must be non-nil."); BOOL success = YES; CFArrayRef itemAttributes = NULL; NSMutableDictionary *query = [self dictionaryForKeychainQuery:nil]; query[(__bridge id)kSecReturnAttributes] = (__bridge id)kCFBooleanTrue; query[(__bridge id)kSecMatchLimit] = (__bridge id)kSecMatchLimitAll; OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, (CFTypeRef *)&itemAttributes); if (status == errSecSuccess) { NSAssert(itemAttributes != NULL, @"Attributes expected to be non-nil on success."); NSArray *attributesArray = (__bridge_transfer NSArray *)itemAttributes; NSMutableArray *mutableKeyList = [[NSMutableArray alloc] initWithCapacity:attributesArray.count]; for (NSDictionary *attributes in attributesArray) { NSString *key = attributes[(__bridge id)kSecAttrAccount]; if (key.length > 0) { [mutableKeyList addObject:[key copy]]; } } *keys = [mutableKeyList copy]; } else if (status == errSecItemNotFound) { *keys = [NSArray array]; } else { if (error != nil) { *error = [NSError errorWithDomain:kXboxLiveServicesDomain code:status userInfo:@{NSLocalizedDescriptionKey: @"Error occurred while reading attributes from keychain."}]; } success = NO; } return success; } /** Creates a keychain query for writing or updating the given key. @param key The key to taget the query to. @return A keychain query dictionary. */ - (NSMutableDictionary * _Nonnull)dictionaryForKeychainAddOrUpdate:(NSString * _Nonnull)key { NSMutableDictionary *query = [self dictionaryForKeychainQuery:key]; query[(__bridge id)kSecAttrAccessible] = (__bridge id)kSecAttrAccessibleAfterFirstUnlock; query[(__bridge id)kSecAttrIsInvisible] = (__bridge id)kCFBooleanTrue; return query; } /** Creates a keychain query for reading or deleting the given key. @param key The key to target the query to. @return A keychain query dictionary. */ - (NSMutableDictionary * _Nonnull)dictionaryForKeychainQuery:(NSString * _Nonnull)key { NSMutableDictionary *query = [NSMutableDictionary dictionary]; query[(__bridge id)kSecClass] = (__bridge id)kSecClassGenericPassword; query[(__bridge id)kSecAttrService] = XBLKeychainStorageServiceName; query[(__bridge id)kSecAttrAccount] = key; if (_itemLabel != nil) { query[(__bridge id)kSecAttrLabel] = _itemLabel; } if (_accessGroup != nil) { query[(__bridge id)kSecAttrAccessGroup] = _accessGroup; } return query; } @end ================================================ FILE: Source/System/iOS/XBLiOSGlobalState.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #import #import @interface XBLiOSGlobalState : NSObject /** Set the launch view controller to be used by XSAPI. This is held by a weak pointer and is not thread safe to set this. Typically you only need to set this once. If this is nil when XSAPI UI is launched, the application root view controller will be used @param viewController The view controller or nil to launch XSAPI from. Default is application root view controller */ + (void)setLaunchViewController:(UIViewController * _Nullable)viewController; /** Returns the launch view controller used by XSAPI to launch UI from. This is either the view controller set previously in the +setLaunchViewController method or the root view controller of the application @return The launch view controller used by XSAPI */ + (UIViewController * _Nullable)launchViewController; @end ================================================ FILE: Source/System/iOS/XBLiOSGlobalState.mm ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #import "XBLiOSGlobalState.h" __weak static UIViewController *gLaunchViewController; @implementation XBLiOSGlobalState + (void)setLaunchViewController:(UIViewController *)viewController { gLaunchViewController = viewController; } + (UIViewController *)launchViewController { return nullptr; // TODO 1808 return gLaunchViewController ?: [XBLIDPScenario getTopViewController]; } @end ================================================ FILE: Source/System/iOS/local_storage_ios.mm ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "local_storage.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_SYSTEM_CPP_BEGIN String LocalStorage::GetDefaultStoragePath() { NSArray* paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES); NSString* applicationSupportDirectory = [paths firstObject]; if (applicationSupportDirectory.length == 0) { LOGS_DEBUG << "Failed to find application support directory."; assert(false); } NSString* storagePath = [NSString stringWithFormat:@"%@/", applicationSupportDirectory]; NSError* createDirectoryError = nil; [[NSFileManager defaultManager] createDirectoryAtPath:storagePath withIntermediateDirectories:YES attributes:nil error:&createDirectoryError]; if (createDirectoryError != nil) { LOGS_DEBUG << "Failed to create application support directory: " << createDirectoryError.localizedDescription.UTF8String; assert(false); } return storagePath.UTF8String; } NAMESPACE_MICROSOFT_XBOX_SERVICES_SYSTEM_CPP_END ================================================ FILE: Source/System/iOS/xbox_live_app_config_ios.mm ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "xsapi-cpp/system.h" #include "xbox_live_app_config_internal.h" #import "XBLiOSGlobalState.h" #include #include "uri_impl.h" using namespace xbox::services; NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN const xsapi_internal_string& AppConfig::APNSEnvironment() const { return m_apnsEnvironment; } NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END ================================================ FILE: Source/System/local_storage.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "local_storage.h" #include NAMESPACE_MICROSOFT_XBOX_SERVICES_SYSTEM_CPP_BEGIN XblLocalStorageWriteHandler g_localStorageWriteHandler{ nullptr }; XblLocalStorageReadHandler g_localStorageReadHandler{ nullptr }; XblLocalStorageClearHandler g_localStorageClearHandler{ nullptr }; XTaskQueueHandle g_localStorageTaskQueue{ nullptr }; void* g_localStorageClientContext{ nullptr }; LocalStorage::LocalStorage( const TaskQueue& queue ) : m_writeHandler{ g_localStorageWriteHandler }, m_readHandler{ g_localStorageReadHandler }, m_clearHandler{ g_localStorageClearHandler }, m_context{ g_localStorageClientContext } { // If the storage hooks were not configured, fall back to defaults if (!m_writeHandler || !m_readHandler || !m_clearHandler) { m_writeHandler = DefaultWrite; m_readHandler = DefaultRead; m_clearHandler = DefaultClear; m_context = this; #if HC_PLATFORM == HC_PLATFORM_IOS || HC_PLATFORM == HC_PLATFORM_ANDROID m_path = GetDefaultStoragePath(); #endif } // If title configured a queue for local storage, use that. Otherwise use provided queue if (g_localStorageTaskQueue) { m_queue = TaskQueue::DeriveWorkerQueue(g_localStorageTaskQueue); } else { m_queue = queue.DeriveWorkerQueue(); } } HRESULT LocalStorage::WriteAsync( const User& user, XblLocalStorageWriteMode mode, String key, Vector data, Callback> callback ) noexcept { auto copyUserResult{ user.Copy() }; RETURN_HR_IF_FAILED(copyUserResult.Hresult()); WriteOperation::OperationLauncher launcher{ [ sharedThis{ shared_from_this() }, user = MakeShared(copyUserResult.ExtractPayload()), mode, key{ std::move(key) }, data{ std::move(data) } ] (XblClientOperationHandle op) { sharedThis->m_writeHandler( sharedThis->m_context, op, user->Handle(), mode, key.data(), data.size(), data.data() ); }}; auto op = MakeShared( std::move(launcher), AsyncContext>{ m_queue, [ sharedThis{ shared_from_this() }, callback{ std::move(callback) } ] (Result result) { sharedThis->OperationComplete(); callback(std::move(result)); } }); QueueOperation(op); return S_OK; } HRESULT LocalStorage::ReadAsync( const User& user, String key, Callback>> callback ) noexcept { auto copyUserResult{ user.Copy() }; RETURN_HR_IF_FAILED(copyUserResult.Hresult()); ReadOperation::OperationLauncher launcher{ [ sharedThis{ shared_from_this() }, user = MakeShared(copyUserResult.ExtractPayload()), key{ std::move(key) } ] (XblClientOperationHandle op) { sharedThis->m_readHandler(sharedThis->m_context, op, user->Handle(), key.data()); }}; auto op = MakeShared( std::move(launcher), AsyncContext>>{ m_queue, [ sharedThis{ shared_from_this() }, callback{ std::move(callback) } ] (Result> result) { sharedThis->OperationComplete(); callback(std::move(result)); } }); QueueOperation(op); return S_OK; } HRESULT LocalStorage::ClearAsync( const User& user, String key, Callback callback ) noexcept { auto copyUserResult{ user.Copy() }; RETURN_HR_IF_FAILED(copyUserResult.Hresult()); ClearOperation::OperationLauncher launcher { [ sharedThis{ shared_from_this() }, user = MakeShared(copyUserResult.ExtractPayload()), key{ std::move(key) } ] (XblClientOperationHandle op) { sharedThis->m_clearHandler(sharedThis->m_context, op, user->Handle(), key.data()); }}; auto op = MakeShared( std::move(launcher), AsyncContext{ m_queue, [ sharedThis{ shared_from_this() }, callback{ std::move(callback) } ] (HRESULT result) { sharedThis->OperationComplete(); callback(result); } }); QueueOperation(op); return S_OK; } void LocalStorage::QueueOperation( std::shared_ptr op ) noexcept { std::lock_guard lock{ m_mutex }; m_operationQueue.push(op); RunNextOperation(); } void LocalStorage::RunNextOperation() noexcept { // m_mutex must be held when calling this function if (!m_currentOperation && !m_operationQueue.empty()) { m_currentOperation = m_operationQueue.front(); m_operationQueue.pop(); // Because operations aren't even kicked off synchronously, always raise errors through the // AsyncContext. If begin succeeds, client will complete operation later and the operation will // manage its own lifetime. HRESULT hr = m_currentOperation->Begin(); if (FAILED(hr)) { m_currentOperation->Fail(hr); } } } void LocalStorage::OperationComplete() noexcept { std::lock_guard lock{ m_mutex }; m_currentOperation = nullptr; RunNextOperation(); } void LocalStorage::DefaultWrite( _In_opt_ void* context, _In_ XblClientOperationHandle op, _In_ XblUserHandle user, _In_ XblLocalStorageWriteMode mode, _In_z_ char const* key, _In_ size_t dataSize, _In_reads_bytes_(dataSize) void const* data ) { assert(context); UNREFERENCED_PARAMETER(user); auto pThis{ static_cast(context) }; auto writeOp{ static_cast(op) }; String fullPath = pThis->m_path + key; std::ios_base::openmode openMode{}; switch (mode) { case XblLocalStorageWriteMode::Append: { openMode = std::ios_base::app; break; } case XblLocalStorageWriteMode::Truncate: { openMode = std::ios_base::trunc; break; } } std::ofstream file{ fullPath.data(), std::ios::binary | openMode }; if (!file.is_open()) { LOGS_DEBUG << "Failed to open file during LocalStorageService::WriteAsync, errno = " << errno; writeOp->Complete(E_FAIL); return; } file.write(reinterpret_cast(data), dataSize); if (!file.good()) { LOGS_DEBUG << "Write failed during LocalStorageService::WriteAsync, errno = " << errno; writeOp->Complete(E_FAIL); return; } uint64_t filesize = file.tellp(); writeOp->Complete(static_cast(filesize)); } void LocalStorage::DefaultRead( _In_opt_ void* context, _In_ XblClientOperationHandle op, _In_ XblUserHandle user, _In_z_ const char* key ) { assert(context); UNREFERENCED_PARAMETER(user); auto pThis{ static_cast(context) }; auto readOp{ static_cast(op) }; String fullPath = pThis->m_path + key; std::ifstream file{ fullPath.data(), std::ios::binary | std::ios::ate }; if (!file.is_open()) { LOGS_DEBUG << "Failed to open file during LocalStorageService::ReadAsync, errno = " << errno; readOp->Complete(E_FAIL); return; } int64_t size = file.tellg(); if (size < 0) { LOGS_DEBUG << "Failed to read file length during LocalStorageService::ReadAsync, errno = " << errno; readOp->Complete(E_FAIL); return; } file.seekg(0); Vector data(static_cast(size)); file.read(reinterpret_cast(data.data()), data.size()); if (!file.good()) { LOGS_DEBUG << "Failed to read file during LocalStorageService::ReadAsync, errno = " << errno; readOp->Complete(E_FAIL); return; } assert(size == file.gcount()); readOp->Complete(std::move(data)); } void LocalStorage::DefaultClear( _In_opt_ void* context, _In_ XblClientOperationHandle op, _In_ XblUserHandle user, _In_z_ const char* key ) { assert(context); UNREFERENCED_PARAMETER(user); auto pThis{ static_cast(context) }; auto clearOp{ static_cast(op) }; String fullPath = pThis->m_path + key; int res = std::remove(fullPath.data()); if (res != 0) { LOGS_DEBUG << "Failed to delete file in LocalStorageService::DeleteAsync, errno = " << errno; clearOp->Complete(E_FAIL); } else { clearOp->Complete(S_OK); } } NAMESPACE_MICROSOFT_XBOX_SERVICES_SYSTEM_CPP_END ================================================ FILE: Source/System/local_storage.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "xsapi-c/platform_c.h" #include "client_operation.h" NAMESPACE_MICROSOFT_XBOX_SERVICES_SYSTEM_CPP_BEGIN extern XblLocalStorageWriteHandler g_localStorageWriteHandler; extern XblLocalStorageReadHandler g_localStorageReadHandler; extern XblLocalStorageClearHandler g_localStorageClearHandler; extern XTaskQueueHandle g_localStorageTaskQueue; extern void* g_localStorageClientContext; class LocalStorage : public std::enable_shared_from_this { public: LocalStorage(const TaskQueue& queue); #if HC_PLATFORM == HC_PLATFORM_WIN32 HRESULT SetStoragePath(_In_opt_z_ const char* path); #endif using WriteOperation = ClientOperation>; using ReadOperation = ClientOperation>>; using ClearOperation = ClientOperation; HRESULT WriteAsync( const User& user, XblLocalStorageWriteMode mode, String key, Vector data, Callback> callback ) noexcept; HRESULT ReadAsync( const User& user, String key, Callback>> callback ) noexcept; HRESULT ClearAsync( const User& user, String key, Callback callback ) noexcept; private: void QueueOperation(std::shared_ptr op) noexcept; void RunNextOperation() noexcept; void OperationComplete() noexcept; static void DefaultWrite( _In_opt_ void* context, _In_ XblClientOperationHandle operation, _In_ XblUserHandle user, _In_ XblLocalStorageWriteMode mode, _In_z_ char const* key, _In_ size_t dataSize, _In_reads_bytes_(dataSize) void const* data ); static void DefaultRead( _In_opt_ void* context, _In_ XblClientOperationHandle operation, _In_ XblUserHandle user, _In_z_ const char* key ); static void DefaultClear( _In_opt_ void* context, _In_ XblClientOperationHandle operation, _In_ XblUserHandle user, _In_z_ const char* key ); std::mutex m_mutex; TaskQueue m_queue; Queue> m_operationQueue; std::shared_ptr m_currentOperation; XblLocalStorageWriteHandler m_writeHandler; XblLocalStorageReadHandler m_readHandler; XblLocalStorageClearHandler m_clearHandler; void* m_context; // State for default handlers String m_path; #if HC_PLATFORM == HC_PLATFORM_IOS || HC_PLATFORM == HC_PLATFORM_ANDROID static String GetDefaultStoragePath(); #endif friend class StorageOperationLauncher; }; NAMESPACE_MICROSOFT_XBOX_SERVICES_SYSTEM_CPP_END ================================================ FILE: Source/System/platform_api.cpp ================================================ #include "pch.h" #include "client_operation.h" #include "local_storage.h" using namespace xbox::services; using namespace xbox::services::system; STDAPI XblLocalStorageWriteComplete( _In_ XblClientOperationHandle operation, _In_ XblClientOperationResult result, _In_ size_t dataSize ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(operation); auto writeOp{ static_cast(operation) }; return writeOp->Complete({ dataSize, XblClientOperation::HresultFromResult(result) }); } CATCH_RETURN() STDAPI XblLocalStorageReadComplete( _In_ XblClientOperationHandle operation, _In_ XblClientOperationResult result, _In_ size_t dataSize, _In_reads_bytes_opt_(dataSize) void const* data ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(operation); RETURN_HR_INVALIDARGUMENT_IF(dataSize && !data); auto readOp{ static_cast(operation) }; Vector dataVector(dataSize); memcpy(dataVector.data(), data, dataSize); return readOp->Complete(Result>{ std::move(dataVector), XblClientOperation::HresultFromResult(result) }); } CATCH_RETURN() STDAPI XblLocalStorageClearComplete( _In_ XblClientOperationHandle operation, _In_ XblClientOperationResult result ) XBL_NOEXCEPT try { RETURN_HR_INVALIDARGUMENT_IF_NULL(operation); auto clearOp{ static_cast(operation) }; return clearOp->Complete(XblClientOperation::HresultFromResult(result)); } CATCH_RETURN() STDAPI XblLocalStorageSetHandlers( _In_opt_ XTaskQueueHandle queue, _In_ XblLocalStorageWriteHandler writeHandler, _In_ XblLocalStorageReadHandler readHandler, _In_ XblLocalStorageClearHandler clearHandler, _In_opt_ void* context ) XBL_NOEXCEPT try { if (GlobalState::Get()) { return E_XBL_ALREADY_INITIALIZED; } if ((writeHandler && readHandler && clearHandler) || (!writeHandler && !readHandler && !clearHandler)) { g_localStorageWriteHandler = writeHandler; g_localStorageReadHandler = readHandler; g_localStorageClearHandler = clearHandler; g_localStorageClientContext = context; g_localStorageTaskQueue = queue; // Store a non-owning pointer until XblInitialize is called } else { // Require that the hooks be set together return E_INVALIDARG; } return S_OK; } CATCH_RETURN() ================================================ FILE: Tests/ApiExplorer/APIExplorer.Shared.vcxitems ================================================  $(MSBuildAllProjects);$(MSBuildThisFileFullPath) true {CBC0BEC4-131D-4868-9345-71813557FB39} NotUsing true NotUsing true NotUsing true NotUsing true NotUsing true NotUsing true NotUsing true NotUsing true NotUsing true NotUsing true NotUsing true NotUsing true NotUsing true NotUsing true NotUsing true NotUsing true NotUsing true NotUsing true NotUsing true NotUsing true NotUsing true NotUsing true NotUsing true NotUsing true NotUsing true NotUsing true NotUsing true NotUsing true NotUsing true NotUsing true NotUsing true NotUsing true NotUsing true NotUsing NotUsing NotUsing true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true ================================================ FILE: Tests/ApiExplorer/APIExplorer.Shared.vcxitems.filters ================================================  Shared Shared APIs APIs APIs Shared Shared LUA LUA LUA LUA LUA LUA LUA LUA LUA LUA LUA LUA LUA LUA LUA LUA LUA LUA LUA LUA LUA LUA LUA LUA LUA LUA LUA LUA LUA LUA LUA LUA LUA Shared Shared APIs APIs APIs APIs APIs APIs APIs APIs APIs APIs APIs APIs APIs APIs APIs APIs Shared APIs APIs Shared APIs APIs APIs Shared APIs APIs APIs APIs APIs APIs APIs APIs APIs APIs APIs APIs APIs APIs APIs APIs APIs APIs Shared {1b3bad2e-684e-4774-b5c3-c898ff1fe82a} {a78b1f06-3ccf-4126-ab54-3f6d64ffee17} {461223d9-00e1-4edd-8e00-4ce6d2a1e08f} {d029161e-3101-45c2-a911-1e7ea5272070} {794ce350-68de-4521-bc2c-72a7e4a8ae52} {4b0ee3e5-31ef-4a67-a583-8c526eb8600d} {293c02c1-bd8d-43f2-a66a-5d8f14ace95e} {8c17651b-4171-4478-b119-8a38277dcc92} {735bd0d7-b2eb-40b2-9b6a-8c7716b7f3ba} {c7d6f82d-c973-49e4-9182-bfaf5f3d9f7c} {ed76de34-56a5-4397-b48d-c025f3d0f77a} {71e58020-fc08-4499-94ac-0a63526ee83a} {4f077db1-539e-494b-8a09-5297b318d974} {47403098-99a3-4b9e-9e9a-bf77f814ade6} {c7061bd9-a6c7-4e14-9781-8faf6deebab0} {61a364d7-b4ca-4d79-88d9-556e78b36357} {568ef0b4-4d4e-49a6-a248-d895d558e529} {bf87e97f-bc1b-4d47-981a-0715c54ee9f8} {bea3c22d-0bb2-4cde-bdb6-4ea78f13e2df} {4a5c870b-1540-4160-8ae8-46c8bb18bfe4} {c9ea0c89-d604-46ab-8b52-972078a2f857} {b3715910-a3cd-4f6f-83d5-077f0a6a7325} {a67c0f51-6ded-4bda-b61b-7e032a7ae5a4} {4b9ea591-072a-4fda-8fcb-dd9e20778fc0} {cc49c082-b80b-4362-9212-e5afcc83c21e} {e54673b6-05cd-4d00-83df-a74ba081a5d1} {16160a81-596f-4afb-ab5c-e7b0946714ac} {a12769dd-fa4e-4f7e-ab94-7e339900e11f} {f62f4642-5d6a-4c0d-ba9a-9f8bce53994d} {4cddcf8f-24ed-4308-9d6a-d5367a8920a9} Shared Shared Include Include LUA LUA LUA LUA LUA LUA LUA LUA LUA LUA LUA LUA LUA LUA LUA LUA LUA LUA LUA LUA LUA LUA LUA LUA LUA LUA APIs Include Shared Shared Tests\achievements Tests\misc Tests\misc Tests\misc Tests\social Tests\social Shared Tests\profile Tests\profile Tests\profile Tests\profile Tests\profile Tests\profile Tests\profile Tests\profile Tests\profile Tests\libHttp Tests\libHttp Tests\privacy Tests\events Tests\stats Tests\leaderboard Tests\mp Tests\presence Tests Tests\xal Tests\_luasetup_\u-test Tests\_luasetup_\xal Tests\_luasetup_\xal Tests\achievements Tests\rta Tests\social Tests\social Tests\xblHttp Tests\xblHttp Tests\xal Tests\stats2017 Tests\events Tests\events Tests\social Tests\libHttp Tests\libHttp Tests\titleStorage Tests\mp Tests\mp Tests\mp Tests\mp Tests\stats Tests\leaderboard Tests\libHttp Tests\misc Tests\misc Tests\notification Tests\social Tests\social Tests\stringVerify Tests\mp Tests\multiplayerManager Tests\multiplayerManager Tests\multiplayerManager Tests\multiplayerManager Tests\multiplayerManager Tests\mp Tests\multiplayerActivity Tests\multiplayerActivity Tests\multiplayerActivity Tests\multiplayerActivity Tests\achievements Tests\achievements Tests\leaderboard Tests\profile Tests\profile Tests\profile Tests\privacy Tests\stringVerify Tests\titleStorage Tests\notification Tests\gdk-gameinvite Tests\gdk-gameinvite Tests\rta Tests\stats Tests\presence Tests\social Tests\rta Tests\rta Tests\stats2017 Tests\mp Tests\multiplayerActivity Tests\multiplayerActivity Tests\stats2017 Tests\social Tests\social Tests\social Tests\social Tests\social Tests\social Tests\social Tests\presence Tests\stats Tests\rta Tests\mp Tests\mp Tests\mp Tests\mp Tests\leaderboard Tests\achievements Tests\achievements Tests\achievements Tests\achievements Tests\achievements Tests\multiplayerManager Tests\multiplayerManager Tests\titleStorage Tests\social Tests\social Tests\rta Tests\rta Tests\gdk-gameinvite Tests\libHttp Tests\libHttp Tests\libHttp Tests\notification Tests\mp Tests\libHttp ================================================ FILE: Tests/ApiExplorer/APIs/api_xblc_multiplayer_activity.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" // Random Xuids from XDKS.1 sandbox static uint64_t xuids[] = { 2814639011617876, 2814641789541994, 2814644008675844 }; int XblMultiplayerActivityUpdateRecentPlayers_Lua(lua_State *L) { XblContextHandle xblContext{ Data()->xboxLiveContext }; uint32_t xuidIndex{ GetUint32FromLua(L, 1, 0) }; uint64_t xuid{ xuids[xuidIndex] }; // CODE SNIPPET START: XblMultiplayerActivityUpdateRecentPlayers_C XblMultiplayerActivityRecentPlayerUpdate update{ xuid }; HRESULT hr = XblMultiplayerActivityUpdateRecentPlayers(xblContext, &update, 1); // CODE SNIPPET END return LuaReturnHR(L, hr); } int XblMultiplayerActivityFlushRecentPlayersAsync_Lua(lua_State* L) { XblContextHandle xblContext{ Data()->xboxLiveContext }; // CODE SNIPPET START: XblMultiplayerActivityFlushRecentPlayersAsync_C auto async = std::make_unique(); async->queue = Data()->queue; async->callback = [](XAsyncBlock* async) { std::unique_ptr asyncBlockPtr{ async }; // take ownership of XAsyncBlock HRESULT hr = XAsyncGetStatus(async, false); CallLuaFunctionWithHr(hr, "OnXblMultiplayerActivityFlushRecentPlayersAsync"); // CODE SNIP SKIP }; HRESULT hr = XblMultiplayerActivityFlushRecentPlayersAsync(xblContext, async.get()); if (SUCCEEDED(hr)) { async.release(); } // CODE SNIPPET END return LuaReturnHR(L, hr); } int XblMultiplayerActivitySetActivityAsync_Lua(lua_State* L) { XblContextHandle xblContext{ Data()->xboxLiveContext }; // CODE SNIPPET START: XblMultiplayerActivitySetActivityAsync_C auto async = std::make_unique(); async->queue = Data()->queue; async->callback = [](XAsyncBlock* async) { std::unique_ptr asyncBlockPtr{ async }; // take ownership of XAsyncBlock HRESULT hr = XAsyncGetStatus(async, false); CallLuaFunctionWithHr(hr, "OnXblMultiplayerActivitySetActivityAsync"); // CODE SNIP SKIP }; XblMultiplayerActivityInfo info{}; info.connectionString = "dummyConnectionString"; info.joinRestriction = XblMultiplayerActivityJoinRestriction::Followed; info.maxPlayers = 10; info.currentPlayers = 1; info.groupId = "dummyGroupId"; HRESULT hr = XblMultiplayerActivitySetActivityAsync( xblContext, &info, true, async.get() ); if (SUCCEEDED(hr)) { async.release(); } // CODE SNIPPET END return LuaReturnHR(L, hr); } std::string SerializeActivityInfo( const XblMultiplayerActivityInfo* activityInfo, size_t activityCount ) noexcept { std::stringstream ss; for (size_t i = 0; i < activityCount; ++i, ++activityInfo) { ss << "{\n"; ss << "\txuid: " << activityInfo->xuid << "\n"; ss << "\tconnectionString: "; if (activityInfo->connectionString) { ss << activityInfo->connectionString << "\n"; } ss << "\n"; ss << "\tjoinRestriction: " << static_cast(activityInfo->joinRestriction) << "\n"; ss << "\tmaxPlayers: " << activityInfo->maxPlayers << "\n"; ss << "\tcurrentPlayers: " << activityInfo->currentPlayers << "\n"; ss << "\tgroupId: "; if (activityInfo->groupId) { ss << activityInfo->groupId << "\n"; } ss << "\n"; ss << "\tplatform: " << static_cast(activityInfo->platform) << "\n"; ss << "},\n"; } return ss.str(); } int XblMultiplayerActivityGetActivityAsync_Lua(lua_State* L) { XblContextHandle xblContext{ Data()->xboxLiveContext }; // Get our own activity uint64_t xuid{ Data()->xboxUserId }; // CODE SNIPPET START: XblMultiplayerActivityGetActivityAsync_C auto async = std::make_unique(); async->queue = Data()->queue; async->callback = [](XAsyncBlock* async) { std::unique_ptr asyncBlockPtr{ async }; // take ownership of XAsyncBlock size_t resultSize{}; HRESULT hr = XblMultiplayerActivityGetActivityResultSize(async, &resultSize); if (SUCCEEDED(hr)) { std::vector buffer(resultSize); XblMultiplayerActivityInfo* activityInfo{}; size_t resultCount{}; hr = XblMultiplayerActivityGetActivityResult(async, buffer.size(), buffer.data(), &activityInfo, &resultCount, nullptr); if (SUCCEEDED(hr)) { // ... // CODE SKIP START auto serializedInfo{ SerializeActivityInfo(activityInfo, resultCount) }; LogToFile("XblMultiplayerActivityGetActivityAsync complete with %u results:\n%s", resultCount, serializedInfo.data()); // CODE SKIP END } } CallLuaFunctionWithHr(hr, "OnXblMultiplayerActivityGetActivityAsync"); // CODE SNIP SKIP }; HRESULT hr = XblMultiplayerActivityGetActivityAsync( xblContext, &xuid, 1, async.get() ); if (SUCCEEDED(hr)) { async.release(); } // CODE SNIPPET END return LuaReturnHR(L, hr); } int XblMultiplayerActivityDeleteActivityAsync_Lua(lua_State* L) { XblContextHandle xblContext{ Data()->xboxLiveContext }; // CODE SNIPPET START: XblMultiplayerActivityDeleteActivityAsync_C auto async = std::make_unique(); async->queue = Data()->queue; async->callback = [](XAsyncBlock* async) { std::unique_ptr asyncBlockPtr{ async }; // take ownership of XAsyncBlock HRESULT hr = XAsyncGetStatus(async, false); CallLuaFunctionWithHr(hr, "OnXblMultiplayerActivityDeleteActivityAsync"); // CODE SNIP SKIP }; HRESULT hr = XblMultiplayerActivityDeleteActivityAsync(xblContext, async.get()); if (SUCCEEDED(hr)) { async.release(); } // CODE SNIPPET END return LuaReturnHR(L, hr); } int XblMultiplayerActivitySendInvitesAsync_Lua(lua_State* L) { XblContextHandle xblContext{ Data()->xboxLiveContext }; //uint64_t xuid{ GetUint64FromLua(L, 1, xuids[0]) }; //uint64_t xuid{ 2814636782672891 }; uint64_t xuid{ 2533274873775631 }; // CODE SNIPPET START: XblMultiplayerActivitySendInvitesAsync_C auto async = std::make_unique(); async->queue = Data()->queue; async->callback = [](XAsyncBlock* async) { std::unique_ptr asyncBlockPtr{ async }; // take ownership of XAsyncBlock HRESULT hr = XAsyncGetStatus(async, false); CallLuaFunctionWithHr(hr, "OnXblMultiplayerActivitySendInvitesAsync"); // CODE SNIP SKIP }; HRESULT hr = XblMultiplayerActivitySendInvitesAsync( xblContext, &xuid, 1, true, nullptr, async.get() ); if (SUCCEEDED(hr)) { async.release(); } // CODE SNIPPET END return LuaReturnHR(L, hr); } #if HC_PLATFORM == HC_PLATFORM_WIN32 || HC_PLATFORM_IS_EXTERNAL static struct MultiplayerActivityState { MultiplayerActivityState() = default; ~MultiplayerActivityState() { assert(!gameInviteHandlerToken); } XblFunctionContext gameInviteHandlerToken{ 0 }; } state; int XblMultiplayerActivityAddInviteHandler_Lua(lua_State* L) { XblContextHandle xblContext{ Data()->xboxLiveContext }; // CODE SNIPPET START: XblMultiplayerActivityAddInviteHandler_C state.gameInviteHandlerToken = XblMultiplayerActivityAddInviteHandler( xblContext, [](_In_ const XblMultiplayerActivityInviteData* data, _In_opt_ void*) { // DOTS // CODE SKIP START std::stringstream ss; ss << "{\n"; ss << "\tinvitedXuid: " << data->invitedXuid << "\n"; ss << "\tsenderXuid: " << data->senderXuid << "\n"; ss << "\tsenderGamertag: " << data->senderGamertag << "\n"; ss << "\tsenderUniqueModernGamertag: " << data->senderUniqueModernGamertag << "\n"; ss << "\ttitleName: " << data->titleName << "\n"; ss << "\texpirationTime: " << data->expirationTime << "\n"; ss << "}\n"; LogToScreen("MultiplayerActivity invite received: \n%s", ss.str().data()); CallLuaFunctionWithHr(S_OK, "OnMultiplayerActivityGameInvite"); // CODE SKIP END }, nullptr ); // CODE SNIPPET END LogToFile("XblMultiplayerActivityAddInviteHandler"); return LuaReturnHR(L, S_OK); } int XblMultiplayerActivityRemoveInviteHandler_Lua(lua_State* L) { XblContextHandle xblContext{ Data()->xboxLiveContext }; // CODE SNIPPET START: XblMultiplayerActivityRemoveInviteHandler_C HRESULT hr = XblMultiplayerActivityRemoveInviteHandler( xblContext, state.gameInviteHandlerToken ); state.gameInviteHandlerToken = 0; // CODE SNIPPET END LogToFile("XblMultiplayerActivityRemoveInviteHandler: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } #endif void SetupAPIs_XblMultiplayerActivity() { lua_register(Data()->L, "XblMultiplayerActivityUpdateRecentPlayers", XblMultiplayerActivityUpdateRecentPlayers_Lua); lua_register(Data()->L, "XblMultiplayerActivityFlushRecentPlayersAsync", XblMultiplayerActivityFlushRecentPlayersAsync_Lua); lua_register(Data()->L, "XblMultiplayerActivitySetActivityAsync", XblMultiplayerActivitySetActivityAsync_Lua); lua_register(Data()->L, "XblMultiplayerActivityGetActivityAsync", XblMultiplayerActivityGetActivityAsync_Lua); lua_register(Data()->L, "XblMultiplayerActivityDeleteActivityAsync", XblMultiplayerActivityDeleteActivityAsync_Lua); lua_register(Data()->L, "XblMultiplayerActivitySendInvitesAsync", XblMultiplayerActivitySendInvitesAsync_Lua); #if HC_PLATFORM == HC_PLATFORM_WIN32 || HC_PLATFORM_IS_EXTERNAL lua_register(Data()->L, "XblMultiplayerActivityAddInviteHandler", XblMultiplayerActivityAddInviteHandler_Lua); lua_register(Data()->L, "XblMultiplayerActivityRemoveInviteHandler", XblMultiplayerActivityRemoveInviteHandler_Lua); #endif } ================================================ FILE: Tests/ApiExplorer/APIs/apis.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" int SetCheckHR_Lua(lua_State *L) { Data()->m_checkHR = GetBoolFromLua(L, 1, true); return 0; } int GetCheckHR_Lua(lua_State *L) { lua_pushboolean(L, Data()->m_checkHR); return 1; } int Sleep_Lua(lua_State *L) { DWORD time = (DWORD)GetUint32FromLua(L, 1, 0); LogToScreen("Sleep(%d)", time); pal::Sleep(time); return LuaReturnHR(L, S_OK); } int StopTestFile_Lua(lua_State *L) { Data()->m_stopTest = true; return LuaReturnHR(L, S_OK); } int LogHelper(bool logToFile, lua_State *L) { int arg = 1; int nargs = lua_gettop(L); for (; nargs--; arg++) { if (lua_type(L, arg) == LUA_TNUMBER) { if (lua_isinteger(L, arg)) { LogCat(logToFile, "%d", lua_tointeger(L, arg)); } else { LogCat(logToFile, "%f", lua_tonumber(L, arg)); } } else if (lua_type(L, arg) == LUA_TBOOLEAN) { LogCat(logToFile, "%d", lua_toboolean(L, arg)); } else if (lua_type(L, arg) == LUA_TSTRING) { size_t l; const char *s = luaL_checklstring(L, arg, &l); LogCat(logToFile, s); } } LogCat(logToFile, "\n"); return 0; } int LogToFile_Lua(lua_State *L) { return LogHelper(true, L); } int LogToScreen_Lua(lua_State *L) { return LogHelper(false, L); } int IsRunningTests_Lua(lua_State *L) { bool isTestDone = !Data()->m_runningTests; lua_pushboolean(L, isTestDone); return 1; } int GetLastError_Lua(lua_State *L) { HRESULT hr = Data()->m_lastError; lua_pushinteger(L, hr); return 1; } int SetCallUpdate_Lua(lua_State *L) { Data()->m_callUpdate = GetBoolFromLua(L, 1, true); return 0; } int SetTestWasSkipped_Lua(lua_State *L) { UNREFERENCED_PARAMETER(L); Data()->m_wasTestSkipped = true; return 0; } int SetOnXalTryAddFirstUserSilentlyAsync_Lua(lua_State *L) { Data()->m_onXalTryAddFirstUserSilentlyAsync = GetStringFromLua(L, 1, ""); return 0; } int SetOnTaskQueueTerminateWithAsyncWait_Lua(lua_State *L) { Data()->m_onTaskQueueTerminateWithAsyncWait = GetStringFromLua(L, 1, ""); return 0; } int MultiDeviceGetRemoteState_Lua(lua_State *L) { std::string key = GetStringFromLua(L, 1, ""); std::string value = Data()->m_multiDeviceManager->GetStateValueFromKey(key); lua_pushstring(L, value.c_str()); return 1; } void MultiDeviceWaitTillRemoteStateHelper(const std::string key, const std::string& valueToWaitFor) { LogToScreen("MultiDevice: Waiting for %s key to be %s in cloud DB", key.c_str(), valueToWaitFor.c_str()); while (true) { std::string curValue = Data()->m_multiDeviceManager->GetStateValueFromKey(key); if (curValue == valueToWaitFor) { LogToScreen("MultiDevice: Done waiting. Got %s from cloud DB", valueToWaitFor.c_str()); break; } pal::Sleep(100); } } int MultiDeviceWaitTillRemoteState_Lua(lua_State *L) { std::string key = GetStringFromLua(L, 1, ""); std::string valueToWaitFor = GetStringFromLua(L, 2, ""); MultiDeviceWaitTillRemoteStateHelper(key, valueToWaitFor); return 0; } int MultiDeviceSyncAndWait_Lua(lua_State *L) { std::string key = GetStringFromLua(L, 1, ""); if (Data()->m_multiDeviceManager->IsHost()) { // Set to READY, and wait for peer to ACK, then delete ACK Data()->m_multiDeviceManager->SetSessionState(key, "READY", [](HRESULT) {}); MultiDeviceWaitTillRemoteStateHelper(key, "ACK"); Data()->m_multiDeviceManager->SetSessionState(key, "", [](HRESULT) {}); } else { // Wait for state to be READY, then ACK MultiDeviceWaitTillRemoteStateHelper(key, "READY"); Data()->m_multiDeviceManager->SetSessionState(key, "ACK", [](HRESULT) {}); } return 0; } int MultiDeviceSetLocalState_Lua(lua_State *L) { std::string key = GetStringFromLua(L, 1, ""); std::string value = GetStringFromLua(L, 2, ""); Data()->m_multiDeviceManager->SetSessionState(key, value, [](HRESULT) {}); return 0; } int MultiDeviceIsHost_Lua(lua_State *L) { int value = Data()->m_multiDeviceManager->IsHost() ? 1 : 0; lua_pushnumber(L, value); return 1; } int MultiDeviceGetRemoteXuid_Lua(lua_State *L) { if (Data()->m_multiDeviceManager->IsHost()) { std::string value = Data()->m_multiDeviceManager->GetSessionState().client2xuid; lua_pushstring(L, value.c_str()); } else { std::string value = Data()->m_multiDeviceManager->GetSessionState().client1xuid; lua_pushstring(L, value.c_str()); } return 1; } int APIRunner_AssertOnAllocOfId_Lua(lua_State *L) { auto id = GetUint64FromLua(L, 1, 0); auto memHook = GetApiRunnerMemHook(); memHook->AssertOnAllocOfId(id); return LuaReturnHR(L, S_OK); } int APIRunner_MemStartTracking_Lua(lua_State *L) { auto memHook = GetApiRunnerMemHook(); memHook->StartMemTracking(); return LuaReturnHR(L, S_OK); } int APIRunner_LogStats_Lua(lua_State *L) { auto memHook = GetApiRunnerMemHook(); memHook->LogStats("MemCheck"); return LuaReturnHR(L, S_OK); } int APIRunner_MemLogUnhookedStats_Lua(lua_State *L) { auto memHook = GetApiRunnerMemHook(); memHook->LogUnhookedStats(); return LuaReturnHR(L, S_OK); } int IsGDKPlatform_Lua(lua_State *L) { #if HC_PLATFORM == HC_PLATFORM_GDK lua_pushboolean(L, true); #else lua_pushboolean(L, false); #endif return 1; } void RegisterLuaAPIs() { SetupAPIs_Xal(); SetupAPIs_Xbl(); SetupAPIs_Async(); SetupAPIs_XblAchievements(); SetupAPIs_XblAchievementsManager(); SetupAPIs_XblAchievementsProgressNotifications(); SetupAPIs_XblSocial(); SetupAPIs_XblSocialManager(); SetupAPIs_XblProfile(); SetupAPIS_Platform(); SetupAPIs_LibHttp(); SetupAPIs_XblMultiplayer(); SetupAPIs_XblMultiplayerManager(); SetupAPIs_XblPrivacy(); SetupAPIs_XblEvents(); SetupAPIs_XblStatistics(); SetupAPIs_XblLeaderboard(); SetupAPIs_GrtsGameInvite(); SetupAPIs_XblRta(); SetupAPIs_XblPresence(); SetupAPIs_XblHttp(); SetupAPIs_XblTitleStorage(); SetupAPIs_XblTitleManagedStats(); SetupAPIs_XblStringVerify(); SetupAPIs_XblMultiplayerActivity(); SetupAPIs_CppAchievements(); SetupAPIs_CppLeaderboard(); SetupAPIs_CppProfile(); SetupAPIs_CppPrivacy(); SetupAPIs_CppStringVerify(); SetupAPIs_CppTitleStorage(); SetupAPIs_CppEvents(); SetupAPIs_CppSocial(); SetupAPIs_CppSocialManager(); SetupAPIs_CppPresence(); SetupAPIs_CppStatistics(); SetupAPIs_CppRta(); SetupAPIs_CppMultiplayer(); #if HC_PLATFORM == HC_PLATFORM_WIN32 SetupupAPIs_XblGameInviteNotifications(); SetupAPIs_XblAchievementUnlockNotification(); #endif SetupAPIs_GRTS(); lua_register(Data()->L, "SetCheckHR", SetCheckHR_Lua); lua_register(Data()->L, "GetCheckHR", GetCheckHR_Lua); lua_register(Data()->L, "StopTestFile", StopTestFile_Lua); lua_register(Data()->L, "Sleep", Sleep_Lua); lua_register(Data()->L, "LogToScreen", LogToScreen_Lua); lua_register(Data()->L, "LogToFile", LogToFile_Lua); lua_register(Data()->L, "IsRunningTests", IsRunningTests_Lua); lua_register(Data()->L, "GetLastError", GetLastError_Lua); lua_register(Data()->L, "SetCallUpdate", SetCallUpdate_Lua); lua_register(Data()->L, "SetTestWasSkipped", SetTestWasSkipped_Lua); lua_register(Data()->L, "SetOnXalTryAddFirstUserSilentlyAsync", SetOnXalTryAddFirstUserSilentlyAsync_Lua); lua_register(Data()->L, "SetOnTaskQueueTerminateWithAsyncWait", SetOnTaskQueueTerminateWithAsyncWait_Lua); lua_register(Data()->L, "MultiDeviceGetRemoteState", MultiDeviceGetRemoteState_Lua); lua_register(Data()->L, "MultiDeviceSetLocalState", MultiDeviceSetLocalState_Lua); lua_register(Data()->L, "MultiDeviceSyncAndWait", MultiDeviceSyncAndWait_Lua); lua_register(Data()->L, "MultiDeviceIsHost", MultiDeviceIsHost_Lua); lua_register(Data()->L, "MultiDeviceGetRemoteXuid", MultiDeviceGetRemoteXuid_Lua); lua_register(Data()->L, "MultiDeviceWaitTillRemoteState", MultiDeviceWaitTillRemoteState_Lua); lua_register(Data()->L, "APIRunner_MemStartTracking", APIRunner_MemStartTracking_Lua); lua_register(Data()->L, "APIRunner_LogStats", APIRunner_LogStats_Lua); lua_register(Data()->L, "APIRunner_AssertOnAllocOfId", APIRunner_AssertOnAllocOfId_Lua); lua_register(Data()->L, "APIRunner_MemLogUnhookedStats", APIRunner_MemLogUnhookedStats_Lua); lua_register(Data()->L, "IsGDKPlatform", IsGDKPlatform_Lua); } void SetupAPIS_Platform() { } ================================================ FILE: Tests/ApiExplorer/APIs/apis.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once void SetupAPIs_Xal(); void SetupAPIs_Xbl(); void SetupAPIs_Async(); void SetupAPIs_XblAchievements(); void SetupAPIs_XblAchievementsManager(); void SetupAPIs_XblAchievementsProgressNotifications(); void SetupAPIs_XblSocial(); void SetupAPIs_XblSocialManager(); void SetupAPIs_XblProfile(); void SetupAPIS_Platform(); void SetupAPIs_LibHttp(); void SetupAPIs_XblMultiplayer(); void SetupAPIs_XblMultiplayerManager(); void SetupAPIs_XblPrivacy(); void SetupAPIs_XblEvents(); void SetupAPIs_XblStatistics(); void SetupAPIs_XblLeaderboard(); void SetupAPIs_XblRta(); void SetupAPIs_GrtsGameInvite(); void SetupAPIs_XblPresence(); void SetupAPIs_XblHttp(); void SetupAPIs_XblTitleManagedStats(); void SetupAPIs_XblTitleStorage(); void SetupAPIs_XblStringVerify(); void SetupAPIs_GRTS(); void SetupAPIs_XblMultiplayerActivity(); void SetupAPIs_CppAchievements(); void SetupAPIs_CppLeaderboard(); void SetupAPIs_CppProfile(); void SetupAPIs_CppPrivacy(); void SetupAPIs_CppStringVerify(); void SetupAPIs_CppTitleStorage(); void SetupAPIs_CppEvents(); void SetupAPIs_CppSocial(); void SetupAPIs_CppSocialManager(); void SetupAPIs_CppPresence(); void SetupAPIs_CppStatistics(); void SetupAPIs_CppRta(); void SetupAPIs_CppMultiplayer(); #if HC_PLATFORM == HC_PLATFORM_WIN32 void SetupupAPIs_XblGameInviteNotifications(); void SetupAPIs_XblAchievementUnlockNotification(); #endif int LuaReturnHR(lua_State *L, HRESULT hr, int extraParams = 0); void LuaStopTestIfFailed(HRESULT hr); HRESULT CallLuaString(std::string str); HRESULT CallLuaStringWithDefault(std::string customFn, std::string defaultFn); HRESULT CallLuaFunction(std::string fnName); HRESULT CallLuaFunctionWithHr(HRESULT hr, std::string fnName); int StopTestFile_Lua(lua_State *L); uint64_t GetUint64FromLua(lua_State *L, int paramNum, uint64_t defaultArg); uint32_t GetUint32FromLua(lua_State *L, int paramNum, uint32_t defaultArg); bool GetBoolFromLua(lua_State *L, int paramNum, bool defaultArg); std::string GetStringFromLua(lua_State *L, int paramNum, std::string defaultArg); int XalPlatformWebSetEventHandler_Lua(lua_State *L); int XalPlatformStorageSetEventHandlers_Lua(lua_State *L); ================================================ FILE: Tests/ApiExplorer/APIs/apis_async.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" int XTaskQueueCreate_Lua(lua_State *L) { // CODE SNIPPET START: XTaskQueueCreate XTaskQueueHandle queue = nullptr; HRESULT hr = XTaskQueueCreate( XTaskQueueDispatchMode::Manual, XTaskQueueDispatchMode::Manual, &queue); // CODE SNIPPET END Data()->queue = queue; LogToFile("XTaskQueueCreate: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XTaskQueueDuplicateHandle_Lua(lua_State *L) { XTaskQueueHandle queue = Data()->queue; // CODE SNIPPET START: XTaskQueueDuplicateHandle XTaskQueueHandle newQueue = nullptr; HRESULT hr = XTaskQueueDuplicateHandle( queue, &newQueue); // CODE SNIPPET END XTaskQueueCloseHandle(newQueue); return LuaReturnHR(L, hr); } int XTaskQueueDispatch_Lua(lua_State *L) { XTaskQueueHandle queue = Data()->queue; // CODE SNIPPET START: XTaskQueueDispatch HRESULT hr = XTaskQueueDispatch(queue, XTaskQueuePort::Completion, 0); // CODE SNIPPET END LogToFile("XTaskQueueDispatch: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XTaskQueueCloseHandle_Lua(lua_State *L) { XTaskQueueHandle queue = Data()->queue; // CODE SNIPPET START: XTaskQueueCloseHandle if (queue != nullptr) { XTaskQueueCloseHandle(queue); } // CODE SNIPPET END Data()->queue = nullptr; return LuaReturnHR(L, S_OK); } int XTaskQueueTerminate_Lua(lua_State *L) { XTaskQueueHandle queue = Data()->queue; // CODE SNIPPET START: XTaskQueueTerminate HRESULT hr = XTaskQueueTerminate(queue, true, nullptr, nullptr); // CODE SNIPPET END LogToFile("XTaskQueueTerminate: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, S_OK); } int XTaskQueueTerminateWithAsyncWait_Lua(lua_State *L) { XTaskQueueHandle queue = Data()->queue; HRESULT hr = S_OK; if (queue == nullptr) { Data()->m_stopTest = true; LogToFile("XTaskQueueTerminate with async wait: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } else { hr = XTaskQueueTerminate(queue, false, nullptr, [](void*) { CallLuaStringWithDefault(Data()->m_onTaskQueueTerminateWithAsyncWait, "common = require 'common'; common.cleanup()"); LogToFile("StopTest\n"); Data()->m_stopTest = true; }); } LogToFile("XTaskQueueTerminate with async wait: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XTaskQueueSetCurrentProcessTaskQueue_Lua(lua_State *L) { auto luaHandle = GetUint64FromLua(L, 1, 0); // CODE SNIPPET START: XTaskQueueSetCurrentProcessTaskQueue XTaskQueueHandle queue = nullptr; // CODE SKIP START if (luaHandle != 0) { queue = reinterpret_cast(luaHandle); } // CODE SKIP END XTaskQueueSetCurrentProcessTaskQueue(queue); // CODE SNIPPET END LogToFile("XTaskQueueSetCurrentProcessTaskQueue"); return LuaReturnHR(L, S_OK); } int XTaskQueueGetCurrentProcessTaskQueue_Lua(lua_State *L) { // CODE SNIPPET START: XTaskQueueGetCurrentProcessTaskQueue XTaskQueueHandle queue = nullptr; XTaskQueueGetCurrentProcessTaskQueue(&queue); // CODE SNIPPET END LogToFile("XTaskQueueGetCurrentProcessTaskQueue 0x%0.8x", queue); lua_pushinteger(L, reinterpret_cast(queue)); return LuaReturnHR(L, S_OK, 1); } std::thread g_dispatchThread{}; bool g_dispatch = false; int StartManualDispatchThread_Lua(lua_State* L) { g_dispatch = true; g_dispatchThread = std::thread{ []() { while (g_dispatch) { auto queue = Data()->queue; bool workAvailable = true; while (workAvailable) { workAvailable = XTaskQueueDispatch(queue, XTaskQueuePort::Work, 0); } workAvailable = true; while (workAvailable) { workAvailable = XTaskQueueDispatch(queue, XTaskQueuePort::Completion, 0); } pal::Sleep(10); } } }; g_dispatchThread.detach(); return LuaReturnHR(L, S_OK); } int StopManualDispatchThread_Lua(lua_State* L) { g_dispatch = false; return LuaReturnHR(L, S_OK); } void SetupAPIs_Async() { lua_register(Data()->L, "XTaskQueueCreate", XTaskQueueCreate_Lua); lua_register(Data()->L, "XTaskQueueDuplicateHandle", XTaskQueueDuplicateHandle_Lua); lua_register(Data()->L, "XTaskQueueDispatch", XTaskQueueDispatch_Lua); lua_register(Data()->L, "XTaskQueueCloseHandle", XTaskQueueCloseHandle_Lua); lua_register(Data()->L, "XTaskQueueTerminate", XTaskQueueTerminate_Lua); lua_register(Data()->L, "XTaskQueueTerminateWithAsyncWait", XTaskQueueTerminateWithAsyncWait_Lua); lua_register(Data()->L, "XTaskQueueSetCurrentProcessTaskQueue", XTaskQueueSetCurrentProcessTaskQueue_Lua); lua_register(Data()->L, "XTaskQueueGetCurrentProcessTaskQueue", XTaskQueueGetCurrentProcessTaskQueue_Lua); //lua_register(Data()->L, "XTaskQueueGetPort", XTaskQueueGetPort_Lua); //lua_register(Data()->L, "XTaskQueueCreateComposite", XTaskQueueCreateComposite_Lua); //lua_register(Data()->L, "XTaskQueueSubmitCallback", XTaskQueueSubmitCallback_Lua); //lua_register(Data()->L, "XTaskQueueSubmitDelayedCallback", XTaskQueueSubmitDelayedCallback_Lua); //lua_register(Data()->L, "XTaskQueueRegisterWaiter", XTaskQueueRegisterWaiter_Lua); //lua_register(Data()->L, "XTaskQueueUnregisterWaiter", XTaskQueueUnregisterWaiter_Lua); //lua_register(Data()->L, "XTaskQueueRegisterMonitor", XTaskQueueRegisterMonitor_Lua); //lua_register(Data()->L, "XTaskQueueUnregisterMonitor", XTaskQueueUnregisterMonitor_Lua); // Helper methods lua_register(Data()->L, "StartManualDispatchThread", StartManualDispatchThread_Lua); lua_register(Data()->L, "StopManualDispatchThread", StopManualDispatchThread_Lua); } ================================================ FILE: Tests/ApiExplorer/APIs/apis_cpp_achievements.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #if CPP_TESTS_ENABLED xbox::services::achievements::achievement_type ConvertStringToCppAchievementType(const char* str) { xbox::services::achievements::achievement_type type = xbox::services::achievements::achievement_type::unknown; if (pal::stricmp(str, "XblAchievementType::Unknown") == 0) type = xbox::services::achievements::achievement_type::unknown; else if (pal::stricmp(str, "XblAchievementType::All") == 0) type = xbox::services::achievements::achievement_type::all; else if (pal::stricmp(str, "XblAchievementType::Persistent") == 0) type = xbox::services::achievements::achievement_type::persistent; else if (pal::stricmp(str, "XblAchievementType::Challenge") == 0) type = xbox::services::achievements::achievement_type::challenge; return type; } xbox::services::achievements::achievement_order_by ConvertStringToCppAchievementOrderBy(const char* str) { xbox::services::achievements::achievement_order_by orderBy = xbox::services::achievements::achievement_order_by::default_order; if (pal::stricmp(str, "XblAchievementOrderBy::DefaultOrder") == 0) orderBy = xbox::services::achievements::achievement_order_by::default_order; else if (pal::stricmp(str, "XblAchievementOrderBy::TitleId") == 0) orderBy = xbox::services::achievements::achievement_order_by::title_id; else if (pal::stricmp(str, "XblAchievementOrderBy::UnlockTime") == 0) orderBy = xbox::services::achievements::achievement_order_by::unlock_time; return orderBy; } #endif int AchievementsResultHasNextCpp_Lua(lua_State* L) { bool hasNext = false; #if CPP_TESTS_ENABLED hasNext = Data()->achievementsResultCpp.has_next(); LogToFile("AchievementsResultHasNextCpp: hasNext=%s", hasNext ? "true" : "false"); #else LogToFile("AchievementsResultHasNextCpp disabled for this platform."); #endif lua_pushnumber(L, (int)hasNext); return LuaReturnHR(L, S_OK, 1); } int AchievementsResultGetNextCpp_Lua(lua_State* L) { #if CPP_TESTS_ENABLED uint32_t maxItems = GetUint32FromLua(L, 1, 100); LogToFile("XblAchievementsResultGetNextAsync: MaxItems: %d", maxItems); Data()->achievementsResultCpp.get_next(maxItems).then( [](xbox::services::xbox_live_result result) { HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); if (SUCCEEDED(hr)) { xbox::services::achievements::achievements_result achievementsResult = result.payload(); Data()->achievementsResultCpp = xbox::services::achievements::achievements_result(achievementsResult); auto achievements = achievementsResult.items(); size_t achievementsCount = achievements.size(); LogToFile("AchievementsServiceGetAchievementsForTitleId: Got achievementsCount: %d", achievementsCount); for (size_t i = 0; i < achievementsCount; i++) { LogToScreen("Achievement %s: %s = %s", xbox::services::Utils::StringFromStringT(achievements[i].id()).c_str(), xbox::services::Utils::StringFromStringT(achievements[i].name()).c_str(), (achievements[i].progress_state() == xbox::services::achievements::achievement_progress_state::achieved) ? "Achieved" : "Not achieved"); } } CallLuaFunctionWithHr(hr, "OnAchievementsResultGetNextCpp"); } ); #else CallLuaFunctionWithHr(S_OK, "OnAchievementsResultGetNextCpp"); LogToFile("AchievementsResultGetNextCpp disabled for this platform."); #endif return LuaReturnHR(L, S_OK); } int AchievementsServiceGetAchievementsForTitleId_Lua(lua_State* L) { #if CPP_TESTS_ENABLED xbox::services::achievements::achievement_type achievementType = ConvertStringToCppAchievementType(GetStringFromLua(L, 1, "XblAchievementType::All").c_str()); bool unlockedOnly = GetBoolFromLua(L, 2, false); xbox::services::achievements::achievement_order_by orderBy = ConvertStringToCppAchievementOrderBy(GetStringFromLua(L, 3, "XblAchievementOrderBy::DefaultOrder").c_str()); uint32_t skipItems = GetUint32FromLua(L, 4, 0); uint32_t maxItems = GetUint32FromLua(L, 5, 100); string_t xboxUserId = xbox::services::Utils::StringTFromUint64(Data()->xboxUserId); LogToFile("AchievementsServiceGetAchievementsForTitleId: AchievementType: %d", achievementType); LogToFile("AchievementsServiceGetAchievementsForTitleId: unlockedOnly: %s", unlockedOnly ? "true" : "false"); LogToFile("AchievementsServiceGetAchievementsForTitleId: OrderBy: %d", orderBy); LogToFile("AchievementsServiceGetAchievementsForTitleId: SkipItems: %d", skipItems); LogToFile("AchievementsServiceGetAchievementsForTitleId: MaxItems: %d", maxItems); std::shared_ptr xblc = std::make_shared(Data()->xalUser); xblc->achievement_service().get_achievements_for_title_id( xboxUserId, Data()->titleId, achievementType, unlockedOnly, orderBy, skipItems, maxItems ).then([](xbox::services::xbox_live_result < xbox::services::achievements::achievements_result> result) { HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); LogToFile("AchievementsServiceGetAchievementsForTitleId: hr=%s", ConvertHR(hr).c_str()); if (SUCCEEDED(hr)) { xbox::services::achievements::achievements_result achievementsResult = result.payload(); Data()->achievementsResultCpp = xbox::services::achievements::achievements_result(achievementsResult); auto achievements = achievementsResult.items(); size_t achievementsCount = achievements.size(); LogToFile("AchievementsServiceGetAchievementsForTitleId: Got achievementsCount: %d", achievementsCount); for (size_t i = 0; i < achievementsCount; i++) { LogToScreen("Achievement %s: %s = %s", xbox::services::Utils::StringFromStringT(achievements[i].id()).c_str(), xbox::services::Utils::StringFromStringT(achievements[i].name()).c_str(), (achievements[i].progress_state() == xbox::services::achievements::achievement_progress_state::achieved) ? "Achieved" : "Not achieved"); } } CallLuaFunctionWithHr(hr, "OnAchievementsServiceGetAchievementsForTitleId"); }); #else CallLuaFunctionWithHr(S_OK, "OnAchievementsServiceGetAchievementsForTitleId"); LogToFile("AchievementsServiceGetAchievementsForTitleId disabled for this platform."); #endif return LuaReturnHR(L, S_OK); } int AchievementsServiceGetAchievement_Lua(lua_State* L) { #if CPP_TESTS_ENABLED auto achievementId = GetStringFromLua(L, 1, "1"); LogToFile("XblAchievementsGetAchievementAsync: AchievementId: %s", achievementId.c_str()); string_t xboxUserId = xbox::services::Utils::StringTFromUint64(Data()->xboxUserId); string_t serviceConfigId = xbox::services::Utils::StringTFromUtf8(Data()->scid); string_t achievementIdString = xbox::services::Utils::StringTFromUtf8(achievementId.c_str()); std::shared_ptr xblc = std::make_shared(Data()->xalUser); xblc->achievement_service().get_achievement( xboxUserId, serviceConfigId, achievementIdString ).then( [](xbox::services::xbox_live_result result) { HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); LogToFile("AchievementsServiceGetAchievement: hr=%s", ConvertHR(hr).c_str()); if (SUCCEEDED(hr)) { xbox::services::achievements::achievement achievement = result.payload(); LogToScreen("Achievement %s: %s = %s", xbox::services::Utils::StringFromStringT(achievement.id()).c_str(), xbox::services::Utils::StringFromStringT(achievement.name()).c_str(), (achievement.progress_state() == xbox::services::achievements::achievement_progress_state::achieved) ? "Achieved" : "Not achieved"); } CallLuaFunctionWithHr(hr, "OnAchievementsServiceGetAchievement"); }); #else CallLuaFunctionWithHr(S_OK, "OnAchievemementsServiceGetAchievement"); LogToFile("AchievemementsServiceGetAchievement disabled for this platform."); #endif return LuaReturnHR(L, S_OK); } int AchievementsServiceUpdateAchievement_Lua(lua_State *L) { #if CPP_TESTS_ENABLED auto achievementId = GetStringFromLua(L, 1, "1"); uint32_t percentComplete = GetUint32FromLua(L, 2, 100); LogToFile("XblAchievementsUpdateAchievementAsync: AchievementId: %s", achievementId.c_str()); LogToFile("XblAchievementsUpdateAchievementAsync: PercentComplete: %d", percentComplete); string_t xboxUserId = xbox::services::Utils::StringTFromUint64(Data()->xboxUserId); string_t achievementIdString = xbox::services::Utils::StringTFromUtf8(achievementId.c_str()); std::shared_ptr xblc = std::make_shared(Data()->xalUser); xblc->achievement_service().update_achievement( xboxUserId, achievementIdString, percentComplete ).then( [](xbox::services::xbox_live_result result) { HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); LogToFile("AchievementsServiceUpdateAchievement: hr=%s", ConvertHR(hr).c_str()); if (SUCCEEDED(hr)) { //Achievement Updated } else if (hr == HTTP_E_STATUS_NOT_MODIFIED) { //Achievement Not Modified } else { //Achievement Failed to Update } CallLuaFunctionWithHr(hr, "OnAchievementsServiceUpdateAchievement"); }); #else CallLuaFunctionWithHr(S_OK, "OnAchievementsServiceUpdateAchievement"); LogToFile("AchievementsServiceUpdateAchievement disabled for this platform."); #endif return LuaReturnHR(L, S_OK); } int AchievementsServiceUpdateAchievementForTitleId_Lua(lua_State* L) { #if CPP_TESTS_ENABLED auto achievementId = GetStringFromLua(L, 1, "1"); uint32_t percentComplete = GetUint32FromLua(L, 2, 100); LogToFile("XblAchievementsUpdateAchievementAsync: AchievementId: %s", achievementId.c_str()); LogToFile("XblAchievementsUpdateAchievementAsync: PercentComplete: %d", percentComplete); string_t xboxUserId = xbox::services::Utils::StringTFromUint64(Data()->xboxUserId); string_t serviceConfigId = xbox::services::Utils::StringTFromUtf8(Data()->scid); string_t achievementIdString = xbox::services::Utils::StringTFromUtf8(achievementId.c_str()); std::shared_ptr xblc = std::make_shared(Data()->xalUser); xblc->achievement_service().update_achievement( xboxUserId, Data()->titleId, serviceConfigId, achievementIdString, percentComplete ).then( [](xbox::services::xbox_live_result result) { HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); LogToFile("AchievementsServiceUpdateAchievementForTitleId: hr=%s", ConvertHR(hr).c_str()); if (SUCCEEDED(hr)) { //Achievement Updated } else if (hr == HTTP_E_STATUS_NOT_MODIFIED) { //Achievement Not Modified } else { //Achievement Failed to Update } CallLuaFunctionWithHr(hr, "OnAchievementServiceUpdateAchievementForTitleId"); }); #else CallLuaFunctionWithHr(S_OK, "OnAchievementServiceUpdateAchievementForTitleId"); LogToFile("AchievementServiceUpdateAchievementForTitleId disabled for this platform."); #endif return LuaReturnHR(L, S_OK); } void SetupAPIs_CppAchievements() { lua_register(Data()->L, "AchievementsResultGetNextCpp", AchievementsResultGetNextCpp_Lua); lua_register(Data()->L, "AchievementsResultHasNextCpp", AchievementsResultHasNextCpp_Lua); lua_register(Data()->L, "AchievementsServiceGetAchievement", AchievementsServiceGetAchievement_Lua); lua_register(Data()->L, "AchievementsServiceGetAchievementsForTitleId", AchievementsServiceGetAchievementsForTitleId_Lua); lua_register(Data()->L, "AchievementsServiceUpdateAchievement", AchievementsServiceUpdateAchievement_Lua); lua_register(Data()->L, "AchievementsServiceUpdateAchievementForTitleId", AchievementsServiceUpdateAchievementForTitleId_Lua); } ================================================ FILE: Tests/ApiExplorer/APIs/apis_cpp_events.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" int EventsServiceWriteInGameEvent_Lua(lua_State *L) { #if CPP_TESTS_ENABLED && HC_PLATFORM != HC_PLATFORM_XDK string_t eventName = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 1, "PuzzleSolved").c_str()); string_t dimensionsJson = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 2, "{\"DifficultyLevelId\":100,\"EnemyRoleId\":3,\"GameplayModeId\":\"gameplay mode id\",\"KillTypeId\":4,\"MultiplayerCorrelationId\":\"multiplayer correlation id\",\"PlayerRoleId\":1,\"PlayerWeaponId\":2,\"RoundId\":1}").c_str()); string_t measurementsJson = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 3, "{\"LocationX\":1,\"LocationY\":2.12121,\"LocationZ\":-90909093}").c_str()); std::shared_ptr xblc = std::make_shared(Data()->xalUser); xbox::services::xbox_live_result result = xblc->events_service().write_in_game_event( eventName, web::json::value::parse(dimensionsJson), web::json::value::parse(measurementsJson) ); HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); LogToFile("EventsServiceWriteInGameEvent: hr=%s", ConvertHR(hr).c_str()); #else HRESULT hr = S_OK; LogToFile("EventsServiceWriteInGameEvent is disabled for this platform."); #endif return LuaReturnHR(L, hr); } void SetupAPIs_CppEvents() { lua_register(Data()->L, "EventsServiceWriteInGameEvent", EventsServiceWriteInGameEvent_Lua); } ================================================ FILE: Tests/ApiExplorer/APIs/apis_cpp_leaderboard.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" int LeaderboardServiceGetLeaderboard_Lua(lua_State* L) { #if CPP_TESTS_ENABLED string_t scid = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 1, "00000000-0000-0000-0000-000076029b4d").c_str()); string_t leaderboardName = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 2, "TotalPuzzlesSolvedLB").c_str()); string_t xboxUserId = xbox::services::Utils::StringTFromUint64(GetUint64FromLua(L, 3, Data()->xboxUserId)); string_t socialGroup = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 8, "XblSocialGroupType::None").c_str()); uint32_t maxItems = GetUint32FromLua(L, 5, 0); std::shared_ptr xblc = std::make_shared(Data()->xalUser); xblc->leaderboard_service().get_leaderboard( scid, leaderboardName, xboxUserId, socialGroup, maxItems ).then( [xblc](xbox::services::xbox_live_result result) { HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); LogToFile("LeaderboardServiceGetLeaderboard: hr=%s", ConvertHR(hr).c_str()); if (SUCCEEDED(hr)) { xbox::services::leaderboard::leaderboard_result leaderboardResult{ result.payload() }; Data()->leaderboardResultCpp = leaderboardResult; LogToScreen("Got %d rows in leaderboard", leaderboardResult.rows().size()); for (size_t row = 0; row < leaderboardResult.rows().size(); ++row) { std::stringstream rowText; rowText << xbox::services::Utils::Uint64FromStringT(leaderboardResult.rows()[row].xbox_user_id()) << "\t"; for (size_t column = 0; column < leaderboardResult.rows()[row].column_values().size(); ++column) { rowText << xbox::services::Utils::StringFromStringT(leaderboardResult.rows()[row].column_values()[column]) << "\t"; } LogToFile(rowText.str().data()); } } CallLuaFunctionWithHr(hr, "OnLeaderboardServiceGetLeaderboard"); }); #else CallLuaFunctionWithHr(S_OK, "OnLeaderboardServiceGetLeaderboard"); LogToFile("LeaderboardServiceGetLeaderboard disabled for this platform."); #endif return LuaReturnHR(L, S_OK); } int LeaderboardResultHasNextCpp_Lua(lua_State* L) { bool hasNext = false; #if CPP_TESTS_ENABLED hasNext = Data()->leaderboardResultCpp.has_next(); LogToFile("LeaderboardResultHasNextCpp: hasNext=%s", hasNext ? "true" : "false"); #else LogToFile("LeaderboardResultHasNextCpp disabled for this platform."); #endif lua_pushnumber(L, (int)hasNext); return LuaReturnHR(L, S_OK, 1); } int LeaderboardResultGetNextCpp_Lua(lua_State* L) { #if CPP_TESTS_ENABLED uint32_t maxItems = GetUint32FromLua(L, 1, 0); LogToFile("LeaderboardResultGetNextCpp: maxItems: %d", maxItems); Data()->leaderboardResultCpp.get_next(maxItems).then( [](xbox::services::xbox_live_result result) { HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); LogToFile("LeaderboardResultGetNextCpp: hr=%s", ConvertHR(hr).c_str()); if (SUCCEEDED(hr)) { xbox::services::leaderboard::leaderboard_result leaderboardResult{ result.payload() }; Data()->leaderboardResultCpp = leaderboardResult; LogToScreen("Got %d rows in leaderboard", leaderboardResult.rows().size()); for (size_t row = 0; row < leaderboardResult.rows().size(); ++row) { std::stringstream rowText; rowText << xbox::services::Utils::Uint64FromStringT(leaderboardResult.rows()[row].xbox_user_id()) << "\t"; for (size_t column = 0; column < leaderboardResult.rows()[row].column_values().size(); ++column) { rowText << xbox::services::Utils::StringFromStringT(leaderboardResult.rows()[row].column_values()[column]) << "\t"; } LogToFile(rowText.str().data()); } } CallLuaFunctionWithHr(hr, "OnLeaderboardResultGetNextCpp"); } ); #else CallLuaFunctionWithHr(S_OK, "OnLeaderboardResultGetNextCpp"); LogToFile("LeaderboardResultGetNextCpp disabled for this platform."); #endif return LuaReturnHR(L, S_OK); } void SetupAPIs_CppLeaderboard() { lua_register(Data()->L, "LeaderboardServiceGetLeaderboard", LeaderboardServiceGetLeaderboard_Lua); lua_register(Data()->L, "LeaderboardResultGetNextCpp", LeaderboardResultGetNextCpp_Lua); lua_register(Data()->L, "LeaderboardResultHasNextCpp", LeaderboardResultHasNextCpp_Lua); } ================================================ FILE: Tests/ApiExplorer/APIs/apis_cpp_multiplayer.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" using namespace utility; #if CPP_TESTS_ENABLED struct MultiplayerStateCpp { std::vector> sessionHandles; xbox::services::multiplayer::multiplayer_session_reference sessionRef; std::shared_ptr searchHandleDetails; std::string inviteHandle{}; function_context sessionChangedContext{ 0 }; function_context subscriptionLostContext{ 0 }; function_context connectionIdChangedContext{ 0 }; std::string activityHandle{}; static std::string GetSessionName(uint64_t sessionId = 0) noexcept { // ID to make session names unique per API runner run #if HC_PLATFORM == HC_PLATFORM_GDK ULARGE_INTEGER largeInt; FILETIME fileTime; GetSystemTimeAsFileTime(&fileTime); largeInt.LowPart = fileTime.dwLowDateTime; largeInt.HighPart = fileTime.dwHighDateTime; static uint64_t runId{ largeInt.QuadPart }; #else static uint64_t runId{ datetime::utc_now().to_interval() }; #endif std::stringstream ss; ss << "GameSession-" << runId << "-ID" << sessionId; return ss.str(); } }; std::unique_ptr g_multiplayerStateCpp; MultiplayerStateCpp* MPStateCpp() { if (!g_multiplayerStateCpp) { g_multiplayerStateCpp = std::make_unique(); g_multiplayerStateCpp->sessionHandles.resize(10); } return g_multiplayerStateCpp.get(); } xbox::services::multiplayer::multiplayer_session_write_mode ConvertStringToCppMultiplayerSessionWriteMode(const char* str) { xbox::services::multiplayer::multiplayer_session_write_mode writeMode = xbox::services::multiplayer::multiplayer_session_write_mode::update_or_create_new; if (pal::stricmp(str, "multiplayer_session_write_mode::synchronized_update") == 0) writeMode = xbox::services::multiplayer::multiplayer_session_write_mode::synchronized_update; else if (pal::stricmp(str, "multiplayer_session_write_mode::create_new") == 0) writeMode = xbox::services::multiplayer::multiplayer_session_write_mode::create_new; else if (pal::stricmp(str, "multiplayer_session_write_mode::update_existing") == 0) writeMode = xbox::services::multiplayer::multiplayer_session_write_mode::update_existing; return writeMode; } //multiplayer_session_reference void LogSessionRef(const xbox::services::multiplayer::multiplayer_session_reference* sessionRef) { LogToFile("Scid:%s", xbox::services::Utils::StringFromStringT(sessionRef->service_configuration_id()).c_str()); LogToFile("SessionName:%s", xbox::services::Utils::StringFromStringT(sessionRef->session_name()).c_str()); LogToFile("SessionTemplateName:%s", xbox::services::Utils::StringFromStringT(sessionRef->session_template_name()).c_str()); } int MultiplayerSessionReferenceIsValidCpp_Lua(lua_State *L) { bool isValid = !MPStateCpp()->sessionRef.is_null(); LogToFile("MultiplayerSessionReferenceIsValidCpp isValid:%d", isValid); return LuaReturnHR(L, S_OK); } int MultiplayerSessionReferenceCreateCpp_Lua(lua_State* L) { string_t scid = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 1, Data()->scid).c_str()); string_t sessionTemplateName = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 2, "MinGameSession").c_str()); string_t sessionName = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 3, MultiplayerStateCpp::GetSessionName()).c_str()); MPStateCpp()->sessionRef = xbox::services::multiplayer::multiplayer_session_reference(scid, sessionTemplateName, sessionName); LogToFile("MultiplayerSessionReferenceCreateCpp"); LogSessionRef(&MPStateCpp()->sessionRef); return LuaReturnHR(L, S_OK); } int MultiplayerSessionReferenceParseFromUriPathCpp_Lua(lua_State* L) { string_t path = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 1, "").c_str()); if (path.empty()) { stringstream_t ss; ss << L"/serviceconfigs/00000000-0000-0000-0000-000076029b4d/sessionTemplates/MinGameSession/sessions/"; ss << xbox::services::Utils::StringTFromUtf8(MultiplayerStateCpp::GetSessionName().c_str()); path = ss.str(); } MPStateCpp()->sessionRef.parse_from_uri_path(path); LogToFile("MultiplayerSessionReferenceParseFromUriPathCpp"); LogToFile("Scid:%s", xbox::services::Utils::StringFromStringT(MPStateCpp()->sessionRef.service_configuration_id()).c_str()); LogToFile("SessionName:%s", xbox::services::Utils::StringFromStringT(MPStateCpp()->sessionRef.session_name()).c_str()); LogToFile("SessionTemplateName:%s", xbox::services::Utils::StringFromStringT(MPStateCpp()->sessionRef.session_template_name()).c_str()); return LuaReturnHR(L, S_OK); } //multitplayer_session std::shared_ptr GetSessionHandleFromArgCpp(lua_State *L, int paramNum, uint64_t* sessionIndexOut = nullptr) { uint64_t sessionIndex { GetUint64FromLua(L, paramNum, 0) }; assert(MPStateCpp()->sessionHandles.size() > sessionIndex); if (sessionIndexOut != nullptr) { *sessionIndexOut = sessionIndex; } return MPStateCpp()->sessionHandles[static_cast(sessionIndex)]; } int MultiplayerSessionCreateCpp_Lua(lua_State *L) { string_t scid = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 1, Data()->scid).c_str()); string_t sessionTemplateName = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 2, "MinGameSession").c_str()); uint64_t sessionIndex{ GetUint64FromLua(L, 4, 0) }; string_t sessionName = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 3, "").c_str()); if (sessionName.empty()) { sessionName = xbox::services::Utils::StringTFromUtf8(MultiplayerStateCpp::GetSessionName(sessionIndex).c_str()); } xbox::services::multiplayer::multiplayer_session_reference sessionRef(scid, sessionTemplateName, sessionName); string_t xuid = xbox::services::Utils::StringTFromUint64(Data()->xboxUserId); MPStateCpp()->sessionHandles[static_cast(sessionIndex)] = std::make_shared(xuid, sessionRef); lua_pushinteger(L, static_cast(sessionIndex)); LogToFile("MultiplayerSessionCreateCpp"); return LuaReturnHR(L, S_OK, 1); } int MultiplayerSessionJoinCpp_Lua(lua_State *L) { auto sessionHandle = GetSessionHandleFromArgCpp(L, 1); string_t memberCustomConstantsJsonString = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 2, "{}").c_str()); bool initializeRequested = GetBoolFromLua(L, 3, true); bool joinWithActiveStatus = GetBoolFromLua(L, 4, true); bool addInitializePropertyToRequest = GetBoolFromLua(L, 5, true); web::json::value memberCustomConstantsJson = web::json::value::parse(memberCustomConstantsJsonString); sessionHandle->join(memberCustomConstantsJson, initializeRequested, joinWithActiveStatus, addInitializePropertyToRequest); LogToFile("MultiplayerSessionJoinCpp"); return LuaReturnHR(L, S_OK); } int MultiplayerSessionAddMemberReservationCpp_Lua(lua_State *L) { auto sessionHandle = GetSessionHandleFromArgCpp(L, 1); string_t xuid = xbox::services::Utils::StringTFromUint64(GetUint64FromLua(L, 2, 2814636782672891)); string_t memberCustomConstantsJsonString = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 3, "{}").c_str()); web::json::value memberCustomConstantsJson = web::json::value::parse(memberCustomConstantsJsonString); bool initializeRequested = GetBoolFromLua(L, 4, true); sessionHandle->add_member_reservation(xuid, memberCustomConstantsJson, initializeRequested); LogToFile("MultiplayerSessionAddMemberReservationCpp"); return LuaReturnHR(L, S_OK); } int MultiplayerSessionTimeOfSessionCpp_Lua(lua_State *L) { auto sessionHandle = GetSessionHandleFromArgCpp(L, 1); datetime timeOfSession = sessionHandle->date_of_session(); LogToFile("MultiplayerSessionStartTime timeOfSession:%s", xbox::services::Utils::StringFromStringT(timeOfSession.to_string()).c_str()); return LuaReturnHR(L, S_OK); } int MultiplayerSessionGetInitializationInfoCpp_Lua(lua_State *L) { auto sessionHandle = GetSessionHandleFromArgCpp(L, 1); xbox::services::multiplayer::multiplayer_initialization_stage stage = sessionHandle->initialization_stage(); datetime stageStartTime = sessionHandle->initializing_stage_start_time(); uint32_t episode = sessionHandle->intializing_episode(); LogToFile("MultiplayerSessionGetInitializationInfoCpp"); LogToFile("Stage: %d", stage); LogToFile("StageStartTime: %ul", stageStartTime); LogToFile("Episode: %d", episode); return LuaReturnHR(L, S_OK); } int MultiplayerSessionSubscribedChangeTypesCpp_Lua(lua_State *L) { auto sessionHandle = GetSessionHandleFromArgCpp(L, 1); xbox::services::multiplayer::multiplayer_session_change_types changeTypes = sessionHandle->subscribed_change_types(); LogToFile("MultiplayerSessionSubscribedChangeTypesCpp"); LogToFile("changeTypes: 0x%0.4x", changeTypes); return LuaReturnHR(L, S_OK); } int MultiplayerSessionHostCandidatesCpp_Lua(lua_State *L) { auto sessionHandle = GetSessionHandleFromArgCpp(L, 1); std::vector hostCandidates = sessionHandle->host_candidates(); LogToFile("MultiplayerSessionHostCandidatesCpp:"); for (string_t hostCandidate : hostCandidates) { LogToFile(xbox::services::Utils::StringFromStringT(hostCandidate).c_str()); } return LuaReturnHR(L, S_OK); } int MultiplayerSessionSessionReferenceCpp_Lua(lua_State *L) { auto sessionHandle = GetSessionHandleFromArgCpp(L, 1); xbox::services::multiplayer::multiplayer_session_reference sessionRef = sessionHandle->session_reference(); LogToFile("MultiplayerSessionSessionReferenceCpp"); LogToFile("Scid:%s", xbox::services::Utils::StringFromStringT(sessionRef.service_configuration_id()).c_str()); LogToFile("SessionName:%s", xbox::services::Utils::StringFromStringT(sessionRef.session_name()).c_str()); LogToFile("SessionTemplateName:%s", xbox::services::Utils::StringFromStringT(sessionRef.session_template_name()).c_str()); MPStateCpp()->sessionRef = sessionRef; return LuaReturnHR(L, S_OK); } int MultiplayerSessionSessionConstantsCpp_Lua(lua_State *L) { auto sessionHandle = GetSessionHandleFromArgCpp(L, 1); auto sessionConstants = sessionHandle->session_constants(); LogToFile("MultiplayerSessionSessionConstantsCpp"); LogToFile("MaxMembersInSession: %d", sessionConstants->max_members_in_session()); LogToFile("Visibility: %d", sessionConstants->visibility()); LogToFile("InitiatorXuidsSize: %ul", sessionConstants->initiator_xbox_user_ids().size()); LogToFile("CustomJson: %s", xbox::services::Utils::StringFromStringT(sessionConstants->session_custom_constants_json().serialize()).c_str()); LogToFile("SessionCloudComputePackageConstantsJson: %s", xbox::services::Utils::StringFromStringT(sessionConstants->session_cloud_compute_package_constants_json().serialize()).c_str()); LogToFile("MemberReservedTimeout: %ul", sessionConstants->member_reserved_time_out()); LogToFile("MemberInactiveTimeout: %ul", sessionConstants->member_inactive_timeout()); LogToFile("MemberReadyTimeout: %ul", sessionConstants->member_ready_timeout()); LogToFile("SessionEmptyTimeout: %ul", sessionConstants->session_empty_timeout()); LogToFile("EnableMetricsLatency: %d", sessionConstants->enable_metrics_latency()); LogToFile("EnableMetricsBandwidthDown: %d", sessionConstants->enable_metrics_bandwidth_down()); LogToFile("EnableMetricsBandwidthUp: %d", sessionConstants->enable_metrics_bandwidth_up()); LogToFile("EnableMetricsCustom: %d", sessionConstants->enable_metrics_custom()); LogToFile("PeerToPeerRequirements->LatencyMaximum: %ul", sessionConstants->peer_to_peer_requirements().latency_maximum()); LogToFile("PeerToPeerRequirements->BandwidthMinimumInKbps: %ul", sessionConstants->peer_to_peer_requirements().bandwidth_minimum_in_kilobits_per_second()); LogToFile("PeerToHostRequirements->LatencyMaximum: %ul", sessionConstants->peer_to_host_requirements().latency_maximum()); LogToFile("PeerToHostRequirements->BandwidthMinimumInKbps: %ul", sessionConstants->peer_to_host_requirements().bandwidth_down_minimum_in_kilobits_per_second()); LogToFile("PeerToHostRequirements->BandwidthUpMinimumInKbps: %ul", sessionConstants->peer_to_host_requirements().bandwidth_up_minimum_in_kilobits_per_second()); LogToFile("PeerToHostRequirements->HostSelectionMetric: %ul", sessionConstants->peer_to_host_requirements().host_selection_metric()); LogToFile("MeasurementServerAddressesJson: %s", xbox::services::Utils::StringFromStringT(sessionConstants->measurement_server_addresses_json().serialize()).c_str()); LogToFile("ClientMatchmakingCapable: %d", sessionConstants->client_matchmaking_capable()); LogToFile("EnableMetricsCustom: %d", sessionConstants->enable_metrics_custom()); LogToFile("CapabilitiesConnectivity: %d", sessionConstants->capabilities_connectivity()); LogToFile("CapabilitiesSuppressPresenceActivityCheck: %d", sessionConstants->capabilities_suppress_presence_activity_check()); LogToFile("CapabilitiesGameplay: %d", sessionConstants->capabilities_gameplay()); LogToFile("CapabilitiesLarge: %d", sessionConstants->capabilities_large()); LogToFile("CapabilitiesConnectionRequiredForActiveMembers: %d", sessionConstants->capabilities_connection_required_for_active_member()); LogToFile("CapabilitiesUserAuthorizationStyle: %d", sessionConstants->capabilities_user_authorization_style()); LogToFile("CapabilitiesCrossplay: %d", sessionConstants->capabilities_crossplay()); LogToFile("CapabilitiesSearchable: %d", sessionConstants->capabilities_searchable()); return LuaReturnHR(L, S_OK); } int MultiplayerSessionSetVisibilityCpp_Lua(lua_State *L) { xbox::services::multiplayer::multiplayer_session_visibility visibility = static_cast(GetUint32FromLua(L, 1, 3)); auto sessionHandle = GetSessionHandleFromArgCpp(L, 2); sessionHandle->set_visibility(visibility); LogToFile("MultiplayerSessionSetVisibilityCpp"); return LuaReturnHR(L, S_OK); } int MultiplayerSessionSetMaxMembersInSessionCpp_Lua(lua_State *L) { uint32_t maxMembersInSession = GetUint32FromLua(L, 1, 10); auto sessionHandle = GetSessionHandleFromArgCpp(L, 2); sessionHandle->set_max_members_in_session(maxMembersInSession); LogToFile("MultiplayerSessionSetMaxMembersInSessionCpp"); return LuaReturnHR(L, S_OK); } int MultiplayerSessionSetTimeoutsCpp_Lua(lua_State *L) { std::chrono::milliseconds memberReservedTimeout{ GetUint64FromLua(L, 1, 100) }; std::chrono::milliseconds memberInactiveTimeout{ GetUint64FromLua(L, 2, 100) }; std::chrono::milliseconds memberReadyTimeout{ GetUint64FromLua(L, 3, 100) }; std::chrono::milliseconds sessionEmptyTimeout{ GetUint64FromLua(L, 4, 100) }; auto sessionHandle = GetSessionHandleFromArgCpp(L, 5); sessionHandle->set_timeouts(memberReservedTimeout, memberInactiveTimeout, memberReadyTimeout, sessionEmptyTimeout); LogToFile("MultiplayerSessionSetTimeoutsCpp"); return LuaReturnHR(L, S_OK); } int MultiplayerSessionSetQosConnectivityMetricsCpp_Lua(lua_State *L) { bool enableLatencyMetric = GetBoolFromLua(L, 1, false); bool enableBandwidthDownMetric = GetBoolFromLua(L, 2, false); bool enableBandwidthUpMetric = GetBoolFromLua(L, 3, false); bool enableCustomMetric = GetBoolFromLua(L, 4, false); auto sessionHandle = GetSessionHandleFromArgCpp(L, 5); sessionHandle->set_quality_of_service_connectivity_metrics(enableLatencyMetric, enableBandwidthDownMetric, enableBandwidthUpMetric, enableCustomMetric); LogToFile("MultiplayerSessionSetQosConnectivityMetricsCpp"); return LuaReturnHR(L, S_OK); } int MultiplayerSessionSetMemberInitializationCpp_Lua(lua_State *L) { std::chrono::milliseconds joinTimeout{ GetUint64FromLua(L, 1, 100) }; std::chrono::milliseconds measurementTimeout{ GetUint64FromLua(L, 2, 100) }; std::chrono::milliseconds evaluationTimeout{ GetUint64FromLua(L, 3, 100) }; bool externalEvaluation = GetBoolFromLua(L, 4, false); uint32_t membersNeededToStart = GetUint32FromLua(L, 5, 2); auto sessionHandle = GetSessionHandleFromArgCpp(L, 6); sessionHandle->set_member_initialization(joinTimeout, measurementTimeout, evaluationTimeout, externalEvaluation, membersNeededToStart); LogToFile("MultiplayerSessionSetMemberInitializationCpp"); return LuaReturnHR(L, S_OK); } int MultiplayerSessionSetPeerToPeerRequirementsCpp_Lua(lua_State *L) { std::chrono::milliseconds latencyMaximum{ GetUint64FromLua(L, 1, 100) }; uint32_t bandwidthMinimumInKbps = GetUint32FromLua(L, 2, 100); auto sessionHandle = GetSessionHandleFromArgCpp(L, 3); sessionHandle->set_peer_to_peer_requirements(latencyMaximum, bandwidthMinimumInKbps); LogToFile("MultiplayerSessionSetPeerToPeerRequirementsCpp"); return LuaReturnHR(L, S_OK); } int MultiplayerSessionSetPeerToHostRequirementsCpp_Lua(lua_State *L) { std::chrono::milliseconds latencyMaximum{ GetUint64FromLua(L, 1, 100) }; uint32_t bandwidthDownMinimumInKbps = GetUint32FromLua(L, 2, 10); uint32_t bandwidthUpMinimumInKbps = GetUint32FromLua(L, 3, 10); xbox::services::multiplayer::multiplay_metrics hostSelectionMetric = static_cast(GetUint64FromLua(L, 4, 1)); auto sessionHandle = GetSessionHandleFromArgCpp(L, 5); sessionHandle->set_peer_to_host_requirements(latencyMaximum, bandwidthDownMinimumInKbps, bandwidthUpMinimumInKbps, hostSelectionMetric); LogToFile("MultiplayerSessionSetPeerToHostRequirementsCpp"); return LuaReturnHR(L, S_OK); } int MultiplayerSessionSetSessionCapabilitiesCpp_Lua(lua_State *L) { xbox::services::multiplayer::multiplayer_session_capabilities caps = {}; caps.set_connectivity(GetBoolFromLua(L, 1, false)); caps.set_suppress_presence_activity_check(GetBoolFromLua(L, 4, false)); caps.set_gameplay(GetBoolFromLua(L, 5, false)); caps.set_large(GetBoolFromLua(L, 6, true)); caps.set_connection_required_for_active_members(GetBoolFromLua(L, 7, false)); caps.set_user_authorization_style(GetBoolFromLua(L, 8, false)); caps.set_crossplay(GetBoolFromLua(L, 9, false)); caps.set_searchable(GetBoolFromLua(L, 10, false)); caps.set_has_owners(GetBoolFromLua(L, 11, false)); auto sessionHandle = GetSessionHandleFromArgCpp(L, 12); sessionHandle->set_session_capabilities(caps); LogToFile("MultiplayerSessionSetSessionCapabilitiesCpp"); return LuaReturnHR(L, S_OK); } int MultiplayerSessionSetCloudComputePackageJsonCpp_Lua(lua_State *L) { string_t sessionCloudComputePackageConstantsJsonString = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 1, "{}").c_str()); web::json::value sessionCloudComputePackageConstantsJson = web::json::value::parse(sessionCloudComputePackageConstantsJsonString); auto sessionHandle = GetSessionHandleFromArgCpp(L, 2); sessionHandle->set_cloud_compute_package_json(sessionCloudComputePackageConstantsJson); LogToFile("MultiplayerSessionSetCloudComputePackageJsonCpp"); return LuaReturnHR(L, S_OK); } int MultiplayerSessionSessionPropertiesCpp_Lua(lua_State *L) { auto sessionHandle = GetSessionHandleFromArgCpp(L, 1); std::shared_ptr props = sessionHandle->session_properties(); LogToFile("MultiplayerSessionSessionPropertiesCpp"); LogToFile("KeywordCount: %ul", props->keywords().size()); for (size_t i = 0; i < props->keywords().size(); i++) { LogToFile("Keywords[%ul]: %s", i, xbox::services::Utils::StringFromStringT(props->keywords()[i]).c_str()); } LogToFile("JoinRestriction: %d", props->join_restriction()); LogToFile("ReadRestriction: %d", props->read_restriction()); LogToFile("TurnCollectionCount: %ul", props->turn_collection().size()); for (size_t i = 0; i < props->turn_collection().size(); i++) { LogToFile("TurnCollection[%d]: %x", i, props->turn_collection()[i].get()); } LogToFile("MatchmakingTargetSessionConstantsJson: %s", xbox::services::Utils::StringFromStringT(props->matchmaking_target_session_constants_json().serialize()).c_str()); LogToFile("SessionCustomPropertiesJson: %s", xbox::services::Utils::StringFromStringT(props->session_custom_properties_json().serialize()).c_str()); LogToFile("MatchmakingServerConnectionString: %s", xbox::services::Utils::StringFromStringT(props->matchmaking_server_connection_string()).c_str()); LogToFile("ServerConnectionStringCandidatesCount: %ul", props->server_connection_string_candidates().size()); for (size_t i = 0; i < props->server_connection_string_candidates().size(); i++) { LogToFile("ServerConnectionStringCandidates[%ul]: %s", i, xbox::services::Utils::StringFromStringT(props->server_connection_string_candidates()[i]).c_str()); } LogToFile("HostDeviceToken: %s", xbox::services::Utils::StringFromStringT(props->host_device_token()).c_str()); LogToFile("Closed: %d", props->closed()); LogToFile("Locked: %d", props->locked()); LogToFile("AllocateCloudCompute: %d", props->allocate_cloud_compute()); return LuaReturnHR(L, S_OK); } int MultiplayerSessionPropertiesSetKeywordsCpp_Lua(lua_State *L) { string_t key1 = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 1, "Keyword1").c_str()); string_t key2 = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 2, "Keyword2").c_str()); std::vector keywords{ key1, key2 }; auto sessionHandle = GetSessionHandleFromArgCpp(L, 3); sessionHandle->session_properties()->set_keywords(keywords); LogToFile("MultiplayerSessionPropertiesSetKeywordsCpp"); return LuaReturnHR(L, S_OK); } int MultiplayerSessionPropertiesSetJoinRestrictionCpp_Lua(lua_State *L) { auto joinRestriction = static_cast(GetUint64FromLua(L, 1, static_cast(xbox::services::multiplayer::multiplayer_session_restriction::followed))); auto sessionHandle = GetSessionHandleFromArgCpp(L, 2); std::error_code err = sessionHandle->session_properties()->set_join_restriction(joinRestriction); HRESULT hr = ConvertXboxLiveErrorCodeToHresult(err); LogToFile("MultiplayerSessionPropertiesSetJoinRestrictionCpp: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int MultiplayerSessionPropertiesSetReadRestrictionCpp_Lua(lua_State *L) { auto readRestriction = static_cast(GetUint64FromLua(L, 1, static_cast(xbox::services::multiplayer::multiplayer_session_restriction::followed))); auto sessionHandle = GetSessionHandleFromArgCpp(L, 2); sessionHandle->session_properties()->set_read_restriction(readRestriction); LogToFile("MultiplayerSessionPropertiesSetReadRestrictionCpp"); return LuaReturnHR(L, S_OK); } int MultiplayerSessionRoleTypesCpp_Lua(lua_State *L) { auto sessionHandle = GetSessionHandleFromArgCpp(L, 1); std::shared_ptr roleTypes = sessionHandle->session_role_types(); LogToFile("MultiplayerSessionRoleTypesCpp"); return LuaReturnHR(L, S_OK); } void LogSessionMember(const xbox::services::multiplayer::multiplayer_session_member* member) { LogToFile("member->MemberId: %d", member->member_id()); LogToFile("member->InitialTeam: %s", xbox::services::Utils::StringFromStringT(member->initial_team()).c_str()); LogToFile("member->Xuid: %s", xbox::services::Utils::StringFromStringT(member->xbox_user_id()).c_str()); LogToFile("member->CustomConstantsJson: %s", xbox::services::Utils::StringFromStringT(member->member_custom_constants_json().serialize()).c_str()); LogToFile("member->SecureDeviceBaseAddress64: %s", xbox::services::Utils::StringFromStringT(member->secure_device_base_address64()).c_str()); for (auto role : member->roles()) { LogToFile("member->roles %s : %s", xbox::services::Utils::StringFromStringT(role.first).c_str(), xbox::services::Utils::StringFromStringT(role.second).c_str()); } LogToFile("member->RolesCount: %ul", member->roles().size()); LogToFile("member->CustomPropertiesJson: %s", xbox::services::Utils::StringFromStringT(member->member_custom_properties_json().serialize()).c_str()); LogToFile("member->Gamertag: %s", xbox::services::Utils::StringFromStringT(member->gamertag()).c_str()); LogToFile("member->XblMultiplayerSessionMemberStatus: %d", member->status()); LogToFile("member->IsTurnAvailable: %d", member->is_turn_available()); LogToFile("member->IsCurrentUser: %d", member->is_current_user()); LogToFile("member->InitializeRequested: %d", member->initialize_requested()); LogToFile("member->MatchmakingResultServerMeasurementsJson: %s", xbox::services::Utils::StringFromStringT(member->matchmaking_result_server_measurements_json().serialize()).c_str()); LogToFile("member->ServerMeasurementsJson: %s", xbox::services::Utils::StringFromStringT(member->member_server_measurements_json().serialize()).c_str()); for (size_t i = 0; i < member->members_in_group().size(); i++) { LogToFile("member->MembersInGroupIds[%d]: %ul", i, member->members_in_group()[i]->member_id()); } LogToFile("member->MembersInGroupCount: %ul", member->members_in_group().size()); LogToFile("member->DeviceToken: %s", xbox::services::Utils::StringFromStringT(member->device_token()).c_str()); LogToFile("member->Nat: %d", member->nat()); LogToFile("member->ActiveTitleId: %d", member->active_title_id()); LogToFile("member->InitializationEpisode: %d", member->initialization_episode()); LogToFile("member->JoinTime: %s", xbox::services::Utils::StringFromStringT(member->join_time().to_string()).c_str()); LogToFile("member->InitializationFailureCause: %d", member->initialization_failure_cause()); for (size_t i = 0; i < member->groups().size(); i++) { LogToFile("member->Groups[%d]: %s", i, xbox::services::Utils::StringFromStringT(member->groups()[i]).c_str()); } LogToFile("member->GroupsCount: %ul", member->groups().size()); for (size_t i = 0; i < member->encounters().size(); i++) { LogToFile("member->Encounters[%d]: %s", i, xbox::services::Utils::StringFromStringT(member->encounters()[i]).c_str()); } LogToFile("member->EncountersCount: %ul", member->encounters().size()); } int MultiplayerSessionMembersCpp_Lua(lua_State *L) { auto sessionHandle = GetSessionHandleFromArgCpp(L, 1); std::vector> members = sessionHandle->members(); LogToFile("MultiplayerSessionMembersCpp"); for (auto member : members) { LogSessionMember(member.get()); } LogToFile("membersCount: %d", members.size()); return LuaReturnHR(L, S_OK); } int MultiplayerSessionGetMemberCpp_Lua(lua_State *L) { uint32_t memberId = GetUint32FromLua(L, 1, 0); LogToFile("MultiplayerSessionGetMemberCpp"); auto sessionHandle = GetSessionHandleFromArgCpp(L, 2); for (auto member : sessionHandle->members()) { if (member->member_id() == memberId) { LogSessionMember(member.get()); } } return LuaReturnHR(L, S_OK); } int MultiplayerSessionMatchmakingServerCpp_Lua(lua_State *L) { auto sessionHandle = GetSessionHandleFromArgCpp(L, 1); xbox::services::multiplayer::multiplayer_session_matchmaking_server server = sessionHandle->matchmaking_server(); LogToFile("MultiplayerSessionMatchmakingServerCpp"); if (server.is_null()) { LogToFile("server is null"); return LuaReturnHR(L, S_OK); } LogToFile("server.status: %d", server.status()); LogToFile("server.status_details: %s", xbox::services::Utils::StringFromStringT(server.status_details()).c_str()); LogToFile("server.typical_wait: %d", server.typical_wait()); auto sessionRef = server.target_session_ref(); LogSessionRef(&sessionRef); return LuaReturnHR(L, S_OK); } int MultiplayerSessionMembersAcceptedCpp_Lua(lua_State *L) { auto sessionHandle = GetSessionHandleFromArgCpp(L, 1); uint32_t membersAccepted = sessionHandle->members_accepted(); LogToFile("MultiplayerSessionMembersAcceptedCpp"); LogToFile("membersAccepted: %d", membersAccepted); return LuaReturnHR(L, S_OK); } int MultiplayerSessionServersJsonCpp_Lua(lua_State *L) { auto sessionHandle = GetSessionHandleFromArgCpp(L, 1); string_t serversJsonString = sessionHandle->servers_json().serialize(); LogToFile("MultiplayerSessionServersJsonCpp"); LogToFile("json: %s", xbox::services::Utils::StringFromStringT(serversJsonString).c_str()); return LuaReturnHR(L, S_OK); } int MultiplayerSessionSetServersJsonCpp_Lua(lua_State *L) { string_t jsonString = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 1, "{}").c_str()); web::json::value json = web::json::value::parse(jsonString); auto sessionHandle = GetSessionHandleFromArgCpp(L, 2); sessionHandle->set_servers_json(json); LogToFile("MultiplayerSessionSetServersJsonCpp"); return LuaReturnHR(L, S_OK); } int MultiplayerSessionEtagCpp_Lua(lua_State *L) { auto sessionHandle = GetSessionHandleFromArgCpp(L, 1); string_t etag = sessionHandle->e_tag(); LogToFile("MultiplayerSessionEtagCpp"); LogToFile("etag: %s", xbox::services::Utils::StringFromStringT(etag).c_str()); return LuaReturnHR(L, S_OK); } int MultiplayerSessionCurrentUserCpp_Lua(lua_State *L) { auto sessionHandle = GetSessionHandleFromArgCpp(L, 1); std::shared_ptr currentUser = sessionHandle->current_user(); LogToFile("MultiplayerSessionCurrentUserCpp"); if (currentUser == nullptr) { LogToFile("currentUser == nullptr"); return LuaReturnHR(L, S_OK); } LogSessionMember(currentUser.get()); return LuaReturnHR(L, S_OK); } int MultiplayerSessionGetInfoCpp_Lua(lua_State *L) { auto sessionHandle = GetSessionHandleFromArgCpp(L, 1); LogToFile("MultiplayerSessionGetInfoCpp:"); LogToFile("branch: %s", xbox::services::Utils::StringFromStringT(sessionHandle->branch()).c_str()); LogToFile("change_number: %ul", sessionHandle->change_number()); LogToFile("multiplayer_correlation_id: %s", xbox::services::Utils::StringFromStringT(sessionHandle->multiplayer_correlation_id()).c_str()); LogToFile("start_time: %s", xbox::services::Utils::StringFromStringT(sessionHandle->start_time().to_string()).c_str()); LogToFile("date_of_next_timer: %s", xbox::services::Utils::StringFromStringT(sessionHandle->date_of_next_timer().to_string()).c_str()); LogToFile("search_handle_id: %s", xbox::services::Utils::StringFromStringT(sessionHandle->search_handle_id()).c_str()); return LuaReturnHR(L, S_OK); } int MultiplayerSessionWriteStatusCpp_Lua(lua_State *L) { auto sessionHandle = GetSessionHandleFromArgCpp(L, 1); xbox::services::multiplayer::write_session_status writeStatus = sessionHandle->write_status(); LogToFile("MultiplayerSessionWriteStatusCpp"); LogToFile("writeStatus: %d", writeStatus); return LuaReturnHR(L, S_OK); } int MultiplayerSessionSetInitializationStatusCpp_Lua(lua_State *L) { bool initSucceded = GetBoolFromLua(L, 1, false); auto sessionHandle = GetSessionHandleFromArgCpp(L, 2); sessionHandle->set_initialization_status(initSucceded); LogToFile("MultiplayerSessionSetInitializationStatusCpp %d", initSucceded); return LuaReturnHR(L, S_OK); } int MultiplayerSessionSetHostDeviceTokenCpp_Lua(lua_State *L) { string_t hostDeviceToken = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 1, "DefaultHost").c_str()); auto sessionHandle = GetSessionHandleFromArgCpp(L, 2); sessionHandle->set_host_device_token(hostDeviceToken); LogToFile("MultiplayerSessionSetHostDeviceTokenCpp host:%s", xbox::services::Utils::StringFromStringT(hostDeviceToken).c_str()); return LuaReturnHR(L, S_OK); } int MultiplayerSessionSetMatchmakingServerConnectionPathCpp_Lua(lua_State *L) { string_t path = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 1, "DefaultPath").c_str()); auto sessionHandle = GetSessionHandleFromArgCpp(L, 2); sessionHandle->set_matchmaking_server_connection_path(path); LogToFile("MultiplayerSessionSetMatchmakingServerConnectionPathCpp path:%s", xbox::services::Utils::StringFromStringT(path).c_str()); return LuaReturnHR(L, S_OK); } int MultiplayerSessionSetClosedCpp_Lua(lua_State *L) { bool closed = GetBoolFromLua(L, 1, true); auto sessionHandle = GetSessionHandleFromArgCpp(L, 2); sessionHandle->set_closed(closed); LogToFile("MultiplayerSessionSetClosedCpp %d", closed); return LuaReturnHR(L, S_OK); } int MultiplayerSessionSetLockedCpp_Lua(lua_State *L) { bool locked = GetBoolFromLua(L, 1, false); auto sessionHandle = GetSessionHandleFromArgCpp(L, 2); sessionHandle->set_locked(locked); LogToFile("MultiplayerSessionSetLockedCpp %d", locked); return LuaReturnHR(L, S_OK); } int MultiplayerSessionSetAllocateCloudComputeCpp_Lua(lua_State *L) { bool allocate = GetBoolFromLua(L, 1, false); auto sessionHandle = GetSessionHandleFromArgCpp(L, 2); sessionHandle->set_allocate_cloud_compute(allocate); LogToFile("MultiplayerSessionSetAllocateCloudComputeCpp %d", allocate); return LuaReturnHR(L, S_OK); } int MultiplayerSessionSetMatchmakingResubmitCpp_Lua(lua_State *L) { bool matchResubmit = GetBoolFromLua(L, 1, false); auto sessionHandle = GetSessionHandleFromArgCpp(L, 2); sessionHandle->set_matchmaking_resubmit(matchResubmit); LogToFile("MultiplayerSessionSetMatchmakingResubmitCpp %d", matchResubmit); return LuaReturnHR(L, S_OK); } int MultiplayerSessionSetServerConnectionStringCandidatesCpp_Lua(lua_State *L) { string_t candidate1 = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 1, "Candidate1").c_str()); string_t candidate2 = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 2, "Candidate2").c_str()); std::vector candidates{ candidate1, candidate2 }; auto sessionHandle = GetSessionHandleFromArgCpp(L, 3); sessionHandle->set_server_connection_string_candidates(candidates); LogToFile("MultiplayerSessionSetServerConnectionStringCandidatesCpp"); return LuaReturnHR(L, S_OK); } int MultiplayerSessionSetSessionChangeSubscriptionCpp_Lua(lua_State *L) { auto changeTypes = static_cast(GetUint32FromLua(L, 1, static_cast(xbox::services::multiplayer::multiplayer_session_change_types::everything))); auto sessionHandle = GetSessionHandleFromArgCpp(L, 2); std::error_code err = sessionHandle->set_session_change_subscription(changeTypes); HRESULT hr = ConvertXboxLiveErrorCodeToHresult(err); LogToFile("MultiplayerSessionSetSessionChangeSubscriptionCpp: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int MultiplayerSessionLeaveCpp_Lua(lua_State *L) { auto sessionHandle = GetSessionHandleFromArgCpp(L, 1); std::error_code err = sessionHandle->leave(); HRESULT hr = ConvertXboxLiveErrorCodeToHresult(err); LogToFile("MultiplayerSessionLeaveCpp: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int MultiplayerSessionSetCurrentUserStatusCpp_Lua(lua_State *L) { auto status = static_cast(GetUint32FromLua(L, 1, 3)); auto sessionHandle = GetSessionHandleFromArgCpp(L, 2); ENSURE_IS_TRUE(sessionHandle != nullptr, "No valid multiplayer session."); std::error_code err = sessionHandle->set_current_user_status(status); HRESULT hr = ConvertXboxLiveErrorCodeToHresult(err); LogToFile("MultiplayerSessionSetCurrentUserStatusCpp: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int MultiplayerSessionSetCurrentUserSecureDeviceAddressBase64Cpp_Lua(lua_State *L) { string_t deviceAddress = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 1, "ExampleDeviceAddress").c_str()); auto sessionHandle = GetSessionHandleFromArgCpp(L, 2); ENSURE_IS_TRUE(sessionHandle != nullptr, "No valid multiplayer session."); std::error_code err = sessionHandle->set_current_user_secure_device_address_base64(deviceAddress); HRESULT hr = ConvertXboxLiveErrorCodeToHresult(err); LogToFile("MultiplayerSessionSetCurrentUserSecureDeviceAddressBase64Cpp: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int MultiplayerSessionSetCurrentUserRolesCpp_Lua(lua_State *L) { string_t roleTypeName1 = _T("roleTypeName1"); string_t roleName1 = _T("roleName1"); string_t roleTypeName2 = _T("roleTypeName2"); string_t roleName2 = _T("roleName2"); std::unordered_map roleInfo{ std::pair(roleTypeName1, roleName1), std::pair(roleTypeName2, roleName2) }; auto sessionHandle = GetSessionHandleFromArgCpp(L, 1); std::error_code err = sessionHandle->set_current_user_role_info(roleInfo); HRESULT hr = ConvertXboxLiveErrorCodeToHresult(err); LogToFile("MultiplayerSessionSetCurrentUserRolesCpp: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int MultiplayerSessionCurrentUserSetGroupsCpp_Lua(lua_State *L) { string_t group1 = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 1, "group1").c_str()); string_t group2 = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 2, "group2").c_str()); std::vector groups{ group1, group2 }; auto sessionHandle = GetSessionHandleFromArgCpp(L, 3); ENSURE_IS_TRUE(sessionHandle != nullptr, "No valid multiplayer session."); sessionHandle->current_user()->set_groups(groups); LogToFile("MultiplayerSessionCurrentUserSetGroupsCpp"); return LuaReturnHR(L, S_OK); } int MultiplayerSessionCurrentUserSetEncountersCpp_Lua(lua_State *L) { string_t encounter1 = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 1, "encounter1").c_str()); string_t encounter2 = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 2, "encounter2").c_str()); std::vector encounters{ encounter1, encounter2 }; auto sessionHandle = GetSessionHandleFromArgCpp(L, 3); ENSURE_IS_TRUE(sessionHandle != nullptr, "No valid multiplayer session."); sessionHandle->current_user()->set_encounters(encounters); LogToFile("MultiplayerSessionCurrentUserSetEncountersCpp"); return LuaReturnHR(L, S_OK); } int MultiplayerSessionSetCurrentUserQosMeasurementsJsonCpp_Lua(lua_State *L) { string_t measurementsJsonString = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 1, "{\"measurements1\":5}").c_str()); web::json::value measurementsJson = web::json::value::parse(measurementsJsonString); auto sessionHandle = GetSessionHandleFromArgCpp(L, 2); std::error_code err = sessionHandle->set_current_user_quality_of_service_measurements_json(measurementsJson); HRESULT hr = ConvertXboxLiveErrorCodeToHresult(err); LogToFile("MultiplayerSessionSetCurrentUserQosMeasurementsCpp: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int MultiplayerSessionSetCurrentUserMemberCustomPropertyJsonCpp_Lua(lua_State *L) { string_t name = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 1, "name1").c_str()); string_t jsonString = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 2, "{\"myscore\":123}").c_str()); web::json::value json = web::json::value::parse(jsonString); auto sessionHandle = GetSessionHandleFromArgCpp(L, 3); ENSURE_IS_TRUE(sessionHandle != nullptr, "No valid multiplayer session."); std::error_code err = sessionHandle->set_current_user_member_custom_property_json(name, json); HRESULT hr = ConvertXboxLiveErrorCodeToHresult(err); LogToFile("MultiplayerSessionSetCurrentUserMemberCustomPropertyJsonCpp: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int MultiplayerSessionDeleteCurrentUserMemberCustomPropertyJsonCpp_Lua(lua_State *L) { string_t name = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 1, "name1").c_str()); auto sessionHandle = GetSessionHandleFromArgCpp(L, 2); std::error_code err = sessionHandle->delete_current_user_member_custom_property_json(name); HRESULT hr = ConvertXboxLiveErrorCodeToHresult(err); LogToFile("MultiplayerSessionDeleteCurrentUserMemberCustomPropertyJsonCpp: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int MultiplayerSessionSetMatchmakingTargetSessionConstantsJsonCpp_Lua(lua_State *L) { string_t constsString = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 1, "{}").c_str()); web::json::value consts = web::json::value::parse(constsString); auto sessionHandle = GetSessionHandleFromArgCpp(L, 2); std::error_code err = sessionHandle->set_matchmaking_target_session_constants_json(consts); HRESULT hr = ConvertXboxLiveErrorCodeToHresult(err); LogToFile("MultiplayerSessionSetMatchmakingTargetSessionConstantsJsonCpp: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int MultiplayerSessionSetSessionCustomPropertyJsonCpp_Lua(lua_State *L) { string_t name = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 1, "name1").c_str()); string_t jsonString = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 2, "{}").c_str()); web::json::value json = web::json::value::parse(jsonString); auto sessionHandle = GetSessionHandleFromArgCpp(L, 3); std::error_code err = sessionHandle->set_session_custom_property_json(name, json); HRESULT hr = ConvertXboxLiveErrorCodeToHresult(err); LogToFile("MultiplayerSessionSetSessionCustomPropertyJsonCpp: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int MultiplayerSessionDeleteSessionCustomPropertyJsonCpp_Lua(lua_State *L) { string_t name = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 1, "name1").c_str()); auto sessionHandle = GetSessionHandleFromArgCpp(L, 2); std::error_code err = sessionHandle->delete_session_custom_property_json(name); HRESULT hr = ConvertXboxLiveErrorCodeToHresult(err); LogToFile("MultiplayerSessionDeleteSessionCustomPropertyJsonCpp: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int MultiplayerSessionCompareCpp_Lua(lua_State *L) { auto sessionHandle = GetSessionHandleFromArgCpp(L, 1); auto sessionHandle2 = sessionHandle; xbox::services::xbox_live_result result = xbox::services::multiplayer::multiplayer_session::compare_multiplayer_sessions(sessionHandle, sessionHandle2); HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); LogToFile("MultiplayerSessionCompareCpp: hr=%s", ConvertHR(hr).c_str()); if (SUCCEEDED(hr)) { xbox::services::multiplayer::multiplayer_session_change_types changeTypes = result.payload(); LogToFile("changeTypes: %d", changeTypes); } return LuaReturnHR(L, hr); } //multiplayer_service int MultiplayerServiceWriteSession_Lua(lua_State *L) { uint64_t sessionIndex; auto sessionHandle = GetSessionHandleFromArgCpp(L, 1, &sessionIndex); ENSURE_IS_TRUE(sessionHandle != nullptr, "No valid multiplayer session."); auto sessionWriteMode = ConvertStringToCppMultiplayerSessionWriteMode(GetStringFromLua(L, 2, "multiplayer_session_write_mode::update_or_create_new").c_str()); std::shared_ptr xblc = std::make_shared(Data()->xboxLiveContext); xblc->multiplayer_service().write_session( sessionHandle, sessionWriteMode ).then( [sessionIndex](xbox::services::xbox_live_result> result) { HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); LogToFile("MultiplayerServiceWriteSession: hr=%s", ConvertHR(hr).c_str()); if (SUCCEEDED(hr)) { MPStateCpp()->sessionHandles[static_cast(sessionIndex)] = result.payload(); } CallLuaFunctionWithHr(hr, "OnMultiplayerServiceWriteSession"); }); return LuaReturnHR(L, S_OK); } int MultiplayerServiceWriteSessionByHandle_Lua(lua_State *L) { auto writeMode = static_cast(GetUint32FromLua(L, 1, static_cast(xbox::services::multiplayer::multiplayer_session_write_mode::update_existing))); string_t handleId = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 2, MPStateCpp()->activityHandle).c_str()); auto sessionHandle = GetSessionHandleFromArgCpp(L, 3); LogToFile("MultiplayerServiceWriteSessionByHandle"); LogToFile("writeMode: %d", writeMode); LogToFile("handleId: %s", xbox::services::Utils::StringFromStringT(handleId).c_str()); std::shared_ptr xblc = std::make_shared(Data()->xboxLiveContext); xblc->multiplayer_service().write_session_by_handle( sessionHandle, writeMode, handleId ).then( [](xbox::services::xbox_live_result> result) { HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); LogToFile("MultiplayerServiceWriteSessionByHandle: hr=%s", ConvertHR(hr).c_str()); if (SUCCEEDED(hr)) { std::shared_ptr session = result.payload(); UNREFERENCED_PARAMETER(session); } CallLuaFunctionWithHr(hr, "OnMultiplayerServiceWriteSessionByHandle"); }); return LuaReturnHR(L, S_OK); } int MultiplayerServiceGetCurrentSession_Lua(lua_State *L) { string_t scid = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 1, Data()->scid).c_str()); string_t sessionTemplateName = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 2, "MinGameSession").c_str()); uint64_t sessionIndex{ GetUint64FromLua(L, 4, 0) }; string_t sessionName = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 3, MultiplayerStateCpp::GetSessionName(sessionIndex)).c_str()); xbox::services::multiplayer::multiplayer_session_reference sessionRef{ scid, sessionTemplateName, sessionName }; std::shared_ptr xblc = std::make_shared(Data()->xboxLiveContext); xblc->multiplayer_service().get_current_session(sessionRef).then( [sessionIndex](xbox::services::xbox_live_result> result) { HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); LogToFile("MultiplayerServiceGetCurrentSession: hr=%s", ConvertHR(hr).c_str()); if (SUCCEEDED(hr)) { MPStateCpp()->sessionHandles[static_cast(sessionIndex)] = result.payload(); } CallLuaFunctionWithHr(hr, "OnMultiplayerServiceGetCurrentSession"); }); return LuaReturnHR(L, S_OK); } int MultiplayerServiceGetCurrentSessionByHandle_Lua(lua_State *L) { string_t handleId = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 1, MPStateCpp()->inviteHandle).c_str()); auto sessionIndex{ GetUint64FromLua(L, 2, 0) }; if (handleId.empty()) { handleId = _T("86191619-4002-044f-4846-f8f903c71512"); } LogToFile("handleId: %s", xbox::services::Utils::StringFromStringT(handleId).c_str()); std::shared_ptr xblc = std::make_shared(Data()->xboxLiveContext); xblc->multiplayer_service().get_current_session_by_handle(handleId).then( [sessionIndex](xbox::services::xbox_live_result> result) { HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); LogToFile("MultiplayerServiceGetCurrentSessionByHandle: hr=%s", ConvertHR(hr).c_str()); if (SUCCEEDED(hr)) { MPStateCpp()->sessionHandles[static_cast(sessionIndex)] = result.payload(); } CallLuaFunctionWithHr(hr, "OnMultiplayerServiceGetCurrentSessionByHandle"); }); return LuaReturnHR(L, S_OK); } int MultiplayerServiceGetSessions_Lua(lua_State *L) { string_t scid = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 1, Data()->scid).c_str()); uint32_t maxItems = GetUint32FromLua(L, 2, 0); bool includePrivateSessions = GetBoolFromLua(L, 3, false); bool includeReservations = GetBoolFromLua(L, 4, false); bool includeInactiveSessions = GetBoolFromLua(L, 5, false); string_t keywordFilter = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 6, "killzone").c_str()); string_t sessionTemplateNameFilter = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 7, "").c_str()); xbox::services::multiplayer::multiplayer_session_visibility visibilityFilter = static_cast(GetUint32FromLua(L, 8, 5)); uint32_t contractVersionFilter = GetUint32FromLua(L, 9, 0); xbox::services::multiplayer::multiplayer_get_sessions_request request(scid, maxItems); request.set_include_private_sessions(includePrivateSessions); request.set_include_reservations(includeReservations); request.set_include_inactive_sessions(includeInactiveSessions); request.set_keyword_filter(keywordFilter); request.set_session_template_name_filter(sessionTemplateNameFilter); request.set_visibility_filter(visibilityFilter); request.set_contract_version_filter(contractVersionFilter); std::shared_ptr xblc = std::make_shared(Data()->xboxLiveContext); xblc->multiplayer_service().get_sessions(request).then( [](xbox::services::xbox_live_result> result) { HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); LogToFile("MultiplayerServiceGetSessions: hr=%s", ConvertHR(hr).c_str()); if (SUCCEEDED(hr)) { std::vector sessionStates = result.payload(); } CallLuaFunctionWithHr(hr, "OnMultiplayerServiceGetSessions"); }); return LuaReturnHR(L, S_OK); } int MultiplayerServiceSetActivity_Lua(lua_State *L) { std::shared_ptr xblc = std::make_shared(Data()->xboxLiveContext); xblc->multiplayer_service().set_activity(MPStateCpp()->sessionRef).then( [](xbox::services::xbox_live_result result) { HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); LogToFile("MultiplayerServiceSetActivity: hr=%s", ConvertHR(hr).c_str()); CallLuaFunctionWithHr(hr, "OnMultiplayerServiceSetActivity"); }); return LuaReturnHR(L, S_OK); } int MultiplayerServiceClearActivity_Lua(lua_State *L) { string_t serviceConfigurationID = xbox::services::Utils::StringTFromUtf8(Data()->scid); std::shared_ptr xblc = std::make_shared(Data()->xboxLiveContext); xblc->multiplayer_service().clear_activity(serviceConfigurationID).then( [](xbox::services::xbox_live_result result) { HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); LogToFile("MultiplayerServiceClearActivity: hr=%s", ConvertHR(hr).c_str()); CallLuaFunctionWithHr(hr, "OnMultiplayerServiceClearActivity"); }); return LuaReturnHR(L, S_OK); } int MultiplayerServiceSendInvites_Lua(lua_State *L) { string_t contextStringId = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 1, "contextStringId1").c_str()); string_t customActivationContext = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 2, "customActivationContext1").c_str()); string_t targetXuid = xbox::services::Utils::StringTFromUint64(GetUint64FromLua(L, 3, 2814679169942680)); xbox::services::multiplayer::multiplayer_session_reference sessionReference = MPStateCpp()->sessionRef; std::vector xuids{ targetXuid }; uint32_t titleId = Data()->titleId; std::shared_ptr xblc = std::make_shared(Data()->xboxLiveContext); xblc->multiplayer_service().send_invites( sessionReference, xuids, titleId, contextStringId, customActivationContext ).then( [](xbox::services::xbox_live_result> result) { HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); LogToFile("MultiplayerServiceSendInvites: hr=%s", ConvertHR(hr).c_str()); if (SUCCEEDED(hr)) { std::vector inviteHandles = result.payload(); LogToFile("MultiplayerServiceSendInvites got %d invite handles", inviteHandles.size()); MPStateCpp()->inviteHandle = xbox::services::Utils::StringFromStringT(inviteHandles[0]); } CallLuaFunctionWithHr(hr, "OnMultiplayerServiceSendInvites"); }); return LuaReturnHR(L, S_OK); } int MultiplayerServiceGetActivitiesForSocialGroup_Lua(lua_State *L) { string_t serviceConfigurationID = xbox::services::Utils::StringTFromUtf8(Data()->scid); string_t socialGroupOwnerXuid = xbox::services::Utils::StringTFromUint64(GetUint64FromLua(L, 1, 2814632956486799)); string_t socialGroup = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 2, "people").c_str()); std::shared_ptr xblc = std::make_shared(Data()->xboxLiveContext); xblc->multiplayer_service().get_activities_for_social_group( serviceConfigurationID, socialGroupOwnerXuid, socialGroup ).then( [](xbox::services::xbox_live_result> result) { HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); LogToFile("MultiplayerServiceGetActivitiesForSocialGroup: hr=%s", ConvertHR(hr).c_str()); if (SUCCEEDED(hr)) { std::vector activityDetails = result.payload(); UNREFERENCED_PARAMETER(activityDetails); } CallLuaFunctionWithHr(hr, "OnMultiplayerServiceGetActivitiesForSocialGroup"); }); return LuaReturnHR(L, S_OK); } int MultiplayerServiceGetActivitiesForUsers_Lua(lua_State *L) { uint64_t xuid1 = GetUint64FromLua(L, 1, Data()->m_multiDeviceManager->GetRemoteXuid()); if (xuid1 == 0) xuid1 = 2814636782672891; string_t serviceConfigurationID = xbox::services::Utils::StringTFromUtf8(Data()->scid); std::vector xuids{ xbox::services::Utils::StringTFromUint64(xuid1) }; std::shared_ptr xblc = std::make_shared(Data()->xboxLiveContext); xblc->multiplayer_service().get_activities_for_users( serviceConfigurationID, xuids ).then( [](xbox::services::xbox_live_result> result) { HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); LogToFile("MultiplayerServiceGetActivitiesForUsers: hr=%s", ConvertHR(hr).c_str()); if (SUCCEEDED(hr)) { std::vector activityDetails = result.payload(); if (activityDetails.size() > 0) { std::string handleIdStr = xbox::services::Utils::StringFromStringT(activityDetails[0].handle_id()); LogToScreen("Joining lobby via handle %s", handleIdStr.c_str()); MPStateCpp()->activityHandle = handleIdStr; // CODE SNIP SKIP } else { if (Data()->m_multiDeviceManager->GetRemoteXuid() != 0) { LogToScreen("No activity handle to join. Failing..."); hr = E_FAIL; } } } CallLuaFunctionWithHr(hr, "OnMultiplayerServiceGetActivitiesForUsers"); }); return LuaReturnHR(L, S_OK); } int MultiplayerServiceEnableMultiplayerSubscriptions_Lua(lua_State *L) { std::shared_ptr xblc = std::make_shared(Data()->xboxLiveContext); std::error_code err = xblc->multiplayer_service().enable_multiplayer_subscriptions(); HRESULT hr = ConvertXboxLiveErrorCodeToHresult(err); LogToFile("MultiplayerServiceEnableMultiplayerSubscriptions: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int MultiplayerServiceDisableMultiplayerSubscriptions_Lua(lua_State *L) { std::shared_ptr xblc = std::make_shared(Data()->xboxLiveContext); xblc->multiplayer_service().disable_multiplayer_subscriptions(); LogToFile("MultiplayerServiceDisableMultiplayerSubscriptions"); return LuaReturnHR(L, S_OK); } int MultiplayerServiceMultiplayerSubscriptionsEnabled_Lua(lua_State *L) { std::shared_ptr xblc = std::make_shared(Data()->xboxLiveContext); bool enabled = xblc->multiplayer_service().subscriptions_enabled(); LogToFile("MultiplayerServiceMultiplayerSubscriptionsEnabled"); LogToFile("enabled: %d", enabled); return LuaReturnHR(L, S_OK); } int MultiplayerServiceAddMultiplayerSessionChangedHandler_Lua(lua_State *L) { std::shared_ptr xblc = std::make_shared(Data()->xboxLiveContext); function_context fnContext = xblc->multiplayer_service().add_multiplayer_session_changed_handler( [](const xbox::services::multiplayer::multiplayer_session_change_event_args& args) { LogToFile("MultiplayerServiceAddMultiplayerSessionChangedHandler"); LogToFile("ChangeNumber: %d", args.change_number()); CallLuaFunctionWithHr(S_OK, "OnMultiplayerServiceAddMultiplayerSessionChangedHandler"); }); MPStateCpp()->sessionChangedContext = fnContext; LogToFile("MultiplayerServiceAddMultiplayerSessionChangedHandler"); return LuaReturnHR(L, S_OK); } int MultiplayerServiceRemoveMultiplayerSessionChangedHandler_Lua(lua_State *L) { function_context fnContext = MPStateCpp()->sessionChangedContext; std::shared_ptr xblc = std::make_shared(Data()->xboxLiveContext); xblc->multiplayer_service().remove_multiplayer_session_changed_handler(fnContext); MPStateCpp()->sessionChangedContext = nullptr; LogToFile("MultiplayerServiceRemoveMultiplayerSessionChangedHandler"); return LuaReturnHR(L, S_OK); } int MultiplayerServiceAddMultiplayerSubscriptionLostHandler_Lua(lua_State *L) { std::shared_ptr xblc = std::make_shared(Data()->xboxLiveContext); function_context fnContext = xblc->multiplayer_service().add_multiplayer_subscription_lost_handler( []() { LogToFile("MultiplayerServiceAddMultiplayerSubscriptionLostHandler"); CallLuaFunctionWithHr(S_OK, "OnMultiplayerServiceAddMultiplayerSubscriptionLostHandler"); }); MPStateCpp()->subscriptionLostContext = fnContext; LogToFile("MultiplayerServiceAddMultiplayerSubscriptionLostHandler"); return LuaReturnHR(L, S_OK); } int MultiplayerServiceRemoveMultiplayerSubscriptionLostHandler_Lua(lua_State *L) { function_context fnContext = MPStateCpp()->subscriptionLostContext; std::shared_ptr xblc = std::make_shared(Data()->xboxLiveContext); xblc->multiplayer_service().remove_multiplayer_subscription_lost_handler(fnContext); MPStateCpp()->subscriptionLostContext = nullptr; LogToFile("MultiplayerServiceRemoveMultiplayerSubscriptionLostHandler"); return LuaReturnHR(L, S_OK); } int MultiplayerServiceAddMultiplayerConnectionIdChangedHandler_Lua(lua_State *L) { std::shared_ptr xblc = std::make_shared(Data()->xboxLiveContext); function_context fnContext = xblc->multiplayer_service().add_multiplayer_connection_id_changed_handler( []() { LogToFile("MultiplayerServiceAddMultiplayerConnectionIdChangedHandler"); CallLuaFunctionWithHr(S_OK, "OnMultiplayerServiceAddMultiplayerConnectionIdChangedHandler"); }); MPStateCpp()->connectionIdChangedContext = fnContext; LogToFile("MultiplayerServiceAddMultiplayerConnectionIdChangedHandler"); return LuaReturnHR(L, S_OK); } int MultiplayerServiceRemoveMultiplayerConnectionIdChangedHandler_Lua(lua_State *L) { function_context fnContext = MPStateCpp()->connectionIdChangedContext; std::shared_ptr xblc = std::make_shared(Data()->xboxLiveContext); xblc->multiplayer_service().remove_multiplayer_connection_id_changed_handler(fnContext); MPStateCpp()->connectionIdChangedContext = nullptr; LogToFile("MultiplayerServiceRemoveMultiplayerConnectionIdChangedHandler"); return LuaReturnHR(L, S_OK); } int MultiplayerServiceSetTransferHandle_Lua(lua_State* L) { uint64_t targetIndex{ GetUint64FromLua(L, 1, 0) }; uint64_t originIndex{ GetUint64FromLua(L, 2, 0) }; auto targetReference = MPStateCpp()->sessionHandles[static_cast(targetIndex)]->session_reference(); auto originReference = MPStateCpp()->sessionHandles[static_cast(originIndex)]->session_reference(); if (!originReference.is_null() && !targetReference.is_null()) { std::shared_ptr xblc = std::make_shared(Data()->xboxLiveContext); xblc->multiplayer_service().set_transfer_handle( targetReference, originReference ).then( [](xbox::services::xbox_live_result result) { HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); LogToFile("MultiplayerServiceSetTransferHandle: hr=%s", ConvertHR(hr).c_str()); if (SUCCEEDED(hr)) { string_t transferId = result.payload(); LogToFile("Successfully set transfer handle, ID = %s", xbox::services::Utils::StringFromStringT(transferId).c_str()); } CallLuaFunctionWithHr(hr, "OnMultiplayerServiceSetTransferHandle"); }); } return LuaReturnHR(L, S_OK); } int MultiplayerServiceSetSearchHandle_Lua(lua_State *L) { std::vector tags{ _T("SessionTag") }; std::unordered_map numbersMetadata{ std::pair(_T("numberattributename"), 1.1) }; std::unordered_map stringsMetadata{ std::pair(_T("stringattributename"), _T("string attribute value")) }; xbox::services::multiplayer::multiplayer_session_reference sessionRef = MPStateCpp()->sessionRef; xbox::services::multiplayer::multiplayer_search_handle_request searchRequest(sessionRef); searchRequest.set_tags(tags); searchRequest.set_numbers_metadata(numbersMetadata); searchRequest.set_strings_metadata(stringsMetadata); std::shared_ptr xblc = std::make_shared(Data()->xboxLiveContext); xblc->multiplayer_service().set_search_handle(searchRequest).then( [](xbox::services::xbox_live_result result) { HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); LogToFile("MultiplayerServiceSetSearchHandle: hr=%s", ConvertHR(hr).c_str()); if (SUCCEEDED(hr)) { } CallLuaFunctionWithHr(hr, "OnMultiplayerServiceSetSearchHandle"); }); return LuaReturnHR(L, S_OK); } int MultiplayerServiceClearSearchHandle_Lua(lua_State *L) { string_t handleId; std::shared_ptr xblc = std::make_shared(Data()->xboxLiveContext); xblc->multiplayer_service().clear_search_handle(handleId).then( [](xbox::services::xbox_live_result result) { HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); LogToFile("MultiplayerServiceClearSearchHandle: hr=%s", ConvertHR(hr).c_str()); CallLuaFunctionWithHr(hr, "OnMultiplayerServiceClearSearchHandle"); }); return LuaReturnHR(L, S_OK); } int MultiplayerServiceGetSearchHandles_Lua(lua_State *L) { string_t serviceConfigurationId = xbox::services::Utils::StringTFromUtf8(Data()->scid); bool orderAscending{ false }; string_t sessionTemplateName = _T("MinGameSession"); string_t orderByAttribute = _T(""); string_t searchFilter = _T(""); std::shared_ptr xblc = std::make_shared(Data()->xboxLiveContext); xblc->multiplayer_service().get_search_handles( serviceConfigurationId, sessionTemplateName, orderByAttribute, orderAscending, searchFilter ).then( [](xbox::services::xbox_live_result> result) { HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); LogToFile("MultiplayerServiceGetSearchHandles: hr=%s", ConvertHR(hr).c_str()); if (SUCCEEDED(hr)) { std::vector searchHandles = result.payload(); LogToFile("Got %u search handles:", searchHandles.size()); for (auto searchHandle : searchHandles) { LogToFile("\t%s", xbox::services::Utils::StringFromStringT(searchHandle.handle_id()).c_str()); } if (searchHandles.size() > 0) { MPStateCpp()->searchHandleDetails = std::make_shared< xbox::services::multiplayer::multiplayer_search_handle_details>(searchHandles[0]); } } CallLuaFunctionWithHr(hr, "OnMultiplayerServiceGetSearchHandles"); }); return LuaReturnHR(L, S_OK); } //multiplayer_search_handle_detail int MultiplayerSearchHandleDetailsCloseHandle_Lua(lua_State *L) { MPStateCpp()->searchHandleDetails = nullptr; LogToFile("MultiplayerSearchHandleDetailsCloseHandle"); return LuaReturnHR(L, S_OK); } int MultiplayerSearchHandleDetailsSessionReference_Lua(lua_State *L) { auto sessionRef = MPStateCpp()->searchHandleDetails->session_reference(); LogToFile("MultiplayerSearchHandleDetailsSessionReference"); LogSessionRef(&sessionRef); return LuaReturnHR(L, S_OK); } int MultiplayerSearchHandleDetailsHandleId_Lua(lua_State *L) { string_t handleId = MPStateCpp()->searchHandleDetails->handle_id(); LogToFile("MultiplayerSearchHandleDetailsHandleId"); LogToFile("Search Handle Id: %s", xbox::services::Utils::StringFromStringT(handleId).c_str()); return LuaReturnHR(L, S_OK); } int MultiplayerSearchHandleDetailsSessionOwnerXuids_Lua(lua_State *L) { std::vector xuids; if (MPStateCpp()->searchHandleDetails) { xuids = MPStateCpp()->searchHandleDetails->session_owner_xbox_user_ids(); } LogToFile("MultiplayerSearchHandleDetailsSessionOwnerXuids"); LogToFile("There are %u session owners:", xuids.size()); for (string_t xuid : xuids) { LogToFile("\t%s", xbox::services::Utils::StringFromStringT(xuid).c_str()); } return LuaReturnHR(L, S_OK); } int MultiplayerSearchHandleDetailsTags_Lua(lua_State *L) { if (MPStateCpp()->searchHandleDetails == nullptr) { return LuaReturnHR(L, S_OK); } std::vector tags = MPStateCpp()->searchHandleDetails->tags(); LogToFile("MultiplayerSearchHandleDetailsTags"); LogToFile("There are %u tags for the session:", tags.size()); for (string_t tag : tags) { LogToFile("\t%s", xbox::services::Utils::StringFromStringT(tag).c_str()); } return LuaReturnHR(L, S_OK); } int MultiplayerSearchHandleDetailsStringsMetadata_Lua(lua_State *L) { if (MPStateCpp()->searchHandleDetails == nullptr) { return LuaReturnHR(L, S_OK); } auto stringsMetadata = MPStateCpp()->searchHandleDetails->strings_metadata(); LogToFile("MultiplayerSearchHandleDetailsStringsMetadata"); LogToFile("There are %u string metadata for the session:", stringsMetadata.size()); for (std::pair stringMetadata : stringsMetadata) { LogToFile("\t%s : %s", xbox::services::Utils::StringFromStringT(stringMetadata.first).c_str(), xbox::services::Utils::StringFromStringT(stringMetadata.second).c_str()); } return LuaReturnHR(L, S_OK); } int MultiplayerSearchHandleDetailsNumbersMetadata_Lua(lua_State *L) { if (MPStateCpp()->searchHandleDetails == nullptr) { return LuaReturnHR(L, S_OK); } auto numbersMetadata = MPStateCpp()->searchHandleDetails->numbers_metadata(); LogToFile("MultiplayerSearchHandleDetailsNumbersMetadata"); LogToFile("There are %u number metadata for the session:", numbersMetadata.size()); for (std::pair numberMetadata : numbersMetadata) { LogToFile("\t%s : %f", xbox::services::Utils::StringFromStringT(numberMetadata.first).c_str(), numberMetadata.second); } return LuaReturnHR(L, S_OK); } int MultiplayerSearchHandleDetailsVisibility_Lua(lua_State *L) { if (MPStateCpp()->searchHandleDetails == nullptr) { return LuaReturnHR(L, S_OK); } xbox::services::multiplayer::multiplayer_session_visibility sessionVisibility = MPStateCpp()->searchHandleDetails->visibility(); LogToFile("MultiplayerSearchHandleDetailsVisibility: visibility=%u", sessionVisibility); return LuaReturnHR(L, S_OK); } int MultiplayerSearchHandleDetailsJoinRestriction_Lua(lua_State *L) { if (MPStateCpp()->searchHandleDetails == nullptr) { return LuaReturnHR(L, S_OK); } xbox::services::multiplayer::multiplayer_session_restriction joinRestriction = MPStateCpp()->searchHandleDetails->join_restriction(); LogToFile("MultiplayerSearchHandleDetailsJoinRestriction: restriction=%u", joinRestriction); return LuaReturnHR(L, S_OK); } int MultiplayerSearchHandleDetailsClosed_Lua(lua_State *L) { if (MPStateCpp()->searchHandleDetails == nullptr) { return LuaReturnHR(L, S_OK); } bool sessionClosed = MPStateCpp()->searchHandleDetails->closed(); LogToFile("MultiplayerSearchHandleDetailsClosed: closed=%d", sessionClosed); return LuaReturnHR(L, S_OK); } int MultiplayerSearchHandleDetailsMemberCounts_Lua(lua_State *L) { if (MPStateCpp()->searchHandleDetails == nullptr) { return LuaReturnHR(L, S_OK); } uint32_t membersCount = MPStateCpp()->searchHandleDetails->members_count(); uint32_t maxMembersCount = MPStateCpp()->searchHandleDetails->max_members_count(); LogToFile("MultiplayerSearchHandleDetailsMemberCounts: max members=%u, current members=%u ", maxMembersCount, membersCount); return LuaReturnHR(L, S_OK); } int MultiplayerSearchHandleDetailsHandleCreationTime_Lua(lua_State *L) { if (MPStateCpp()->searchHandleDetails == nullptr) { return LuaReturnHR(L, S_OK); } auto creationTime = MPStateCpp()->searchHandleDetails->handle_creation_time(); LogToFile("MultiplayerSearchHandleDetailsHandleCreationTime: creation time=%s", xbox::services::Utils::StringFromStringT(creationTime.to_string()).c_str()); return LuaReturnHR(L, S_OK); } int MultiplayerSearchHandleDetailsCustomSessionPropertiesJson_Lua(lua_State *L) { if (MPStateCpp()->searchHandleDetails == nullptr) { return LuaReturnHR(L, S_OK); } string_t customPropertiesJsonStr = MPStateCpp()->searchHandleDetails->custom_session_properties_json().serialize(); LogToFile("MultiplayerSearchHandleDetailsCustomSessionPropertiesJson: properties=%s", xbox::services::Utils::StringFromStringT(customPropertiesJsonStr).c_str()); return LuaReturnHR(L, S_OK); } //matchmaking_service int MatchmakingServiceCreateTicket_Lua(lua_State* L) { string_t hopperName = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 1, "PlayerSkillNoQoS").c_str()); string_t attributesJsonStr = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 2, "{}").c_str()); std::chrono::seconds timeoutInSeconds{ GetUint32FromLua(L, 6, 100) }; auto sessionRef = MPStateCpp()->sessionRef; string_t serviceConfigurationId = xbox::services::Utils::StringTFromUtf8(Data()->scid); xbox::services::matchmaking::preserve_session_mode preserveSession = xbox::services::matchmaking::preserve_session_mode::never; web::json::value attributesJson = web::json::value::parse(attributesJsonStr); std::shared_ptr xblc = std::make_shared(Data()->xboxLiveContext); xblc->matchmaking_service().create_match_ticket( sessionRef, serviceConfigurationId, hopperName, timeoutInSeconds, preserveSession, attributesJson ).then( [](xbox::services::xbox_live_result result) { HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); LogToFile("MatchmakingServiceCreateTicket: hr=%s", ConvertHR(hr).c_str()); if (SUCCEEDED(hr)) { xbox::services::matchmaking::create_match_ticket_response ticketResponse = result.payload(); LogToScreen("create_match_ticket_response.match_ticket_id: %s", xbox::services::Utils::StringFromStringT(ticketResponse.match_ticket_id()).c_str()); LogToScreen("create_match_ticket_response.estimated_wait_time: %d", ticketResponse.estimated_wait_time()); Data()->matchTicketResponseCpp = ticketResponse; } CallLuaFunctionWithHr(hr, "OnMatchmakingServiceCreateTicket"); }); return LuaReturnHR(L, S_OK); } int MatchmakingServiceGetMatchTicketDetails_Lua(lua_State* L) { string_t hopperName = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 1, "PlayerSkillNoQoS").c_str()); string_t serviceConfigurationId = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 2, Data()->scid).c_str()); string_t ticketId = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 3, "").c_str()); if (ticketId.empty()) { ticketId = Data()->matchTicketResponseCpp.match_ticket_id(); } if (Data()->xalUser == nullptr) { return LuaReturnHR(L, E_UNEXPECTED); } std::shared_ptr xblc = std::make_shared(Data()->xboxLiveContext); xblc->matchmaking_service().get_match_ticket_details( serviceConfigurationId, hopperName, ticketId ).then( [](xbox::services::xbox_live_result result) { HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); LogToFile("MatchmakingServiceGetMatchTicketDetails: hr=%s", ConvertHR(hr).c_str()); if (SUCCEEDED(hr)) { xbox::services::matchmaking::match_ticket_details_response ticketDetailsResponse = result.payload(); LogToScreen("match_ticket_details_response.match_status: %d", ticketDetailsResponse.match_status()); LogToScreen("match_ticket_details_response.estimated_wait_time: %d", ticketDetailsResponse.estimated_wait_time()); LogToScreen("match_ticket_details_response.preserve_session: %d", ticketDetailsResponse.preserve_session()); LogToScreen("match_ticket_details_response.ticketSession: SCID: %s, Session Name: %s, Session Template Name: %s", xbox::services::Utils::StringFromStringT(ticketDetailsResponse.ticket_session().service_configuration_id()).c_str(), xbox::services::Utils::StringFromStringT(ticketDetailsResponse.ticket_session().session_name()).c_str(), xbox::services::Utils::StringFromStringT(ticketDetailsResponse.ticket_session().session_template_name()).c_str()); LogToScreen("match_ticket_details_response.targetSession: SCID: %s, Session Name: %s, Session Template Name: %s", xbox::services::Utils::StringFromStringT(ticketDetailsResponse.target_session().service_configuration_id()).c_str(), xbox::services::Utils::StringFromStringT(ticketDetailsResponse.target_session().session_name()).c_str(), xbox::services::Utils::StringFromStringT(ticketDetailsResponse.target_session().session_template_name()).c_str()); if (!ticketDetailsResponse.ticket_attributes().is_null()) { LogToScreen("match_ticket_details_response.ticket_attributes: %d", xbox::services::Utils::StringFromStringT(ticketDetailsResponse.ticket_attributes().serialize()).c_str()); } } CallLuaFunctionWithHr(hr, "OnMatchmakingServiceGetMatchTicketDetails"); }); return LuaReturnHR(L, S_OK); } int MatchmakingServiceGetHopperStatistics_Lua(lua_State* L) { string_t hopperName = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 1, "PlayerSkillNoQoS").c_str()); string_t serviceConfigurationId = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 2, Data()->scid).c_str()); std::shared_ptr xblc = std::make_shared(Data()->xboxLiveContext); xblc->matchmaking_service().get_hopper_statistics(serviceConfigurationId, hopperName).then( [](xbox::services::xbox_live_result result) { HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); LogToFile("MatchmakingServiceGetHopperStatistics: hr=%s", ConvertHR(hr).c_str()); if (SUCCEEDED(hr)) { xbox::services::matchmaking::hopper_statistics_response hopperStatisticsResponse = result.payload(); LogToScreen("hopper_statistics_response.hopper_name: %s", xbox::services::Utils::StringFromStringT(hopperStatisticsResponse.hopper_name()).c_str()); LogToScreen("hopper_statistics_response.estimated_wait_time: %d", hopperStatisticsResponse.estimated_wait_time()); LogToScreen("hopper_statistics_response.players_waiting_to_match: %d", hopperStatisticsResponse.players_waiting_to_match()); } CallLuaFunctionWithHr(hr, "OnMatchmakingServiceGetHopperStatistics"); }); return LuaReturnHR(L, S_OK); } int MatchmakingServiceDeleteMatchTicket_Lua(lua_State* L) { string_t hopperName = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 1, "PlayerSkillNoQoS").c_str()); string_t serviceConfigurationId = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 2, Data()->scid).c_str()); string_t ticketId = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 3, "").c_str()); if (ticketId.empty()) { ticketId = Data()->matchTicketResponseCpp.match_ticket_id(); } std::shared_ptr xblc = std::make_shared(Data()->xboxLiveContext); xblc->matchmaking_service().delete_match_ticket( serviceConfigurationId, hopperName, ticketId ).then( [](xbox::services::xbox_live_result result) { HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); LogToFile("MatchmakingServiceDeleteMatchTicket: hr=%s", ConvertHR(hr).c_str()); CallLuaFunctionWithHr(hr, "OnMatchmakingServiceDeleteMatchTicket"); }); return LuaReturnHR(L, S_OK); } #else //CPP_TESTS_ENABLED //multiplayer_session_reference int MultiplayerSessionReferenceIsValidCpp_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerSessionReferenceCreateCpp_Lua(lua_State* L) { return LuaReturnHR(L, S_OK); } int MultiplayerSessionReferenceParseFromUriPathCpp_Lua(lua_State* L) { return LuaReturnHR(L, S_OK); } //multitplayer_session int MultiplayerSessionCreateCpp_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerSessionJoinCpp_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerSessionAddMemberReservationCpp_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerSessionTimeOfSessionCpp_Lua(lua_State* L) { return LuaReturnHR(L, S_OK); } int MultiplayerSessionGetInitializationInfoCpp_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerSessionSubscribedChangeTypesCpp_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerSessionHostCandidatesCpp_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerSessionSessionReferenceCpp_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerSessionSessionConstantsCpp_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerSessionSetVisibilityCpp_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerSessionSetMaxMembersInSessionCpp_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerSessionSetTimeoutsCpp_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerSessionSetQosConnectivityMetricsCpp_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerSessionSetMemberInitializationCpp_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerSessionSetPeerToPeerRequirementsCpp_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerSessionSetPeerToHostRequirementsCpp_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerSessionSetSessionCapabilitiesCpp_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerSessionSetCloudComputePackageJsonCpp_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerSessionSessionPropertiesCpp_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerSessionPropertiesSetKeywordsCpp_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerSessionPropertiesSetJoinRestrictionCpp_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerSessionPropertiesSetReadRestrictionCpp_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerSessionRoleTypesCpp_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerSessionMembersCpp_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerSessionGetMemberCpp_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerSessionMatchmakingServerCpp_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerSessionMembersAcceptedCpp_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerSessionServersJsonCpp_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerSessionSetServersJsonCpp_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerSessionEtagCpp_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerSessionCurrentUserCpp_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerSessionGetInfoCpp_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerSessionWriteStatusCpp_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerSessionSetInitializationStatusCpp_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerSessionSetHostDeviceTokenCpp_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerSessionSetMatchmakingServerConnectionPathCpp_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerSessionSetClosedCpp_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerSessionSetLockedCpp_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerSessionSetAllocateCloudComputeCpp_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerSessionSetMatchmakingResubmitCpp_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerSessionSetServerConnectionStringCandidatesCpp_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerSessionSetSessionChangeSubscriptionCpp_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerSessionLeaveCpp_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerSessionSetCurrentUserStatusCpp_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerSessionSetCurrentUserSecureDeviceAddressBase64Cpp_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerSessionSetCurrentUserRolesCpp_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerSessionCurrentUserSetGroupsCpp_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerSessionCurrentUserSetEncountersCpp_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerSessionSetCurrentUserQosMeasurementsJsonCpp_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerSessionSetCurrentUserMemberCustomPropertyJsonCpp_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerSessionDeleteCurrentUserMemberCustomPropertyJsonCpp_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerSessionSetMatchmakingTargetSessionConstantsJsonCpp_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerSessionSetSessionCustomPropertyJsonCpp_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerSessionDeleteSessionCustomPropertyJsonCpp_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerSessionCompareCpp_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } //multiplayer_service int MultiplayerServiceWriteSession_Lua(lua_State *L) { CallLuaFunctionWithHr(S_OK, "OnMultiplayerServiceWriteSession"); return LuaReturnHR(L, S_OK); } int MultiplayerServiceWriteSessionByHandle_Lua(lua_State *L) { CallLuaFunctionWithHr(S_OK, "OnMultiplayerServiceWriteSessionByHandle"); return LuaReturnHR(L, S_OK); } int MultiplayerServiceGetCurrentSession_Lua(lua_State *L) { CallLuaFunctionWithHr(S_OK, "OnMultiplayerServiceGetCurrentSession"); return LuaReturnHR(L, S_OK); } int MultiplayerServiceGetCurrentSessionByHandle_Lua(lua_State *L) { CallLuaFunctionWithHr(S_OK, "OnMultiplayerServiceGetCurrentSessionByHandle"); return LuaReturnHR(L, S_OK); } int MultiplayerServiceGetSessions_Lua(lua_State *L) { CallLuaFunctionWithHr(S_OK, "OnMultiplayerServiceGetSessions"); return LuaReturnHR(L, S_OK); } int MultiplayerServiceSetActivity_Lua(lua_State *L) { CallLuaFunctionWithHr(S_OK, "OnMultiplayerServiceSetActivity"); return LuaReturnHR(L, S_OK); } int MultiplayerServiceClearActivity_Lua(lua_State *L) { CallLuaFunctionWithHr(S_OK, "OnMultiplayerServiceClearActivity"); return LuaReturnHR(L, S_OK); } int MultiplayerServiceSendInvites_Lua(lua_State *L) { CallLuaFunctionWithHr(S_OK, "OnMultiplayerServiceSendInvites"); return LuaReturnHR(L, S_OK); } int MultiplayerServiceGetActivitiesForSocialGroup_Lua(lua_State *L) { CallLuaFunctionWithHr(S_OK, "OnMultiplayerServiceGetActivitiesForSocialGroup"); return LuaReturnHR(L, S_OK); } int MultiplayerServiceGetActivitiesForUsers_Lua(lua_State *L) { CallLuaFunctionWithHr(S_OK, "OnMultiplayerServiceGetActivitiesForUsers"); return LuaReturnHR(L, S_OK); } int MultiplayerServiceEnableMultiplayerSubscriptions_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerServiceDisableMultiplayerSubscriptions_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerServiceMultiplayerSubscriptionsEnabled_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerServiceAddMultiplayerSessionChangedHandler_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerServiceRemoveMultiplayerSessionChangedHandler_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerServiceAddMultiplayerSubscriptionLostHandler_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerServiceRemoveMultiplayerSubscriptionLostHandler_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerServiceAddMultiplayerConnectionIdChangedHandler_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerServiceRemoveMultiplayerConnectionIdChangedHandler_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerServiceSetTransferHandle_Lua(lua_State* L) { CallLuaFunctionWithHr(S_OK, "OnMultiplayerServiceSetTransferHandle"); return LuaReturnHR(L, S_OK); } int MultiplayerServiceSetSearchHandle_Lua(lua_State *L) { CallLuaFunctionWithHr(S_OK, "OnMultiplayerServiceSetSearchHandle"); return LuaReturnHR(L, S_OK); } int MultiplayerServiceClearSearchHandle_Lua(lua_State *L) { CallLuaFunctionWithHr(S_OK, "OnMultiplayerServiceClearSearchHandle"); return LuaReturnHR(L, S_OK); } int MultiplayerServiceGetSearchHandles_Lua(lua_State *L) { CallLuaFunctionWithHr(S_OK, "OnMultiplayerServiceGetSearchHandles"); return LuaReturnHR(L, S_OK); } //multiplayer_search_handle_detail int MultiplayerSearchHandleDetailsCloseHandle_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerSearchHandleDetailsSessionReference_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerSearchHandleDetailsHandleId_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerSearchHandleDetailsSessionOwnerXuids_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerSearchHandleDetailsTags_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerSearchHandleDetailsStringsMetadata_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerSearchHandleDetailsNumbersMetadata_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerSearchHandleDetailsVisibility_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerSearchHandleDetailsJoinRestriction_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerSearchHandleDetailsClosed_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerSearchHandleDetailsMemberCounts_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerSearchHandleDetailsHandleCreationTime_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int MultiplayerSearchHandleDetailsCustomSessionPropertiesJson_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } //matchmaking_service int MatchmakingServiceCreateTicket_Lua(lua_State* L) { CallLuaFunctionWithHr(S_OK, "OnMatchmakingServiceCreateTicket"); return LuaReturnHR(L, S_OK); } int MatchmakingServiceGetMatchTicketDetails_Lua(lua_State* L) { CallLuaFunctionWithHr(S_OK, "OnMatchmakingServiceGetMatchTicketDetails"); return LuaReturnHR(L, S_OK); } int MatchmakingServiceGetHopperStatistics_Lua(lua_State* L) { CallLuaFunctionWithHr(S_OK, "OnMatchmakingServiceGetHopperStatistics"); return LuaReturnHR(L, S_OK); } int MatchmakingServiceDeleteMatchTicket_Lua(lua_State* L) { CallLuaFunctionWithHr(S_OK, "OnMatchmakingServiceDeleteMatchTicket"); return LuaReturnHR(L, S_OK); } #endif //CPP_TESTS_ENABLED void SetupAPIs_CppMultiplayer() { //multiplayer_session_reference lua_register(Data()->L, "MultiplayerSessionReferenceCreateCpp", MultiplayerSessionReferenceCreateCpp_Lua); lua_register(Data()->L, "MultiplayerSessionReferenceParseFromUriPathCpp", MultiplayerSessionReferenceParseFromUriPathCpp_Lua); lua_register(Data()->L, "MultiplayerSessionReferenceIsValidCpp", MultiplayerSessionReferenceIsValidCpp_Lua); //multiplayer_session lua_register(Data()->L, "MultiplayerSessionCreateCpp", MultiplayerSessionCreateCpp_Lua); lua_register(Data()->L, "MultiplayerSessionJoinCpp", MultiplayerSessionJoinCpp_Lua); lua_register(Data()->L, "MultiplayerSessionAddMemberReservationCpp", MultiplayerSessionAddMemberReservationCpp_Lua); lua_register(Data()->L, "MultiplayerSessionTimeOfSessionCpp", MultiplayerSessionTimeOfSessionCpp_Lua); lua_register(Data()->L, "MultiplayerSessionGetInitializationInfoCpp", MultiplayerSessionGetInitializationInfoCpp_Lua); lua_register(Data()->L, "MultiplayerSessionSubscribedChangeTypesCpp", MultiplayerSessionSubscribedChangeTypesCpp_Lua); lua_register(Data()->L, "MultiplayerSessionHostCandidatesCpp", MultiplayerSessionHostCandidatesCpp_Lua); lua_register(Data()->L, "MultiplayerSessionSessionReferenceCpp", MultiplayerSessionSessionReferenceCpp_Lua); lua_register(Data()->L, "MultiplayerSessionSessionConstantsCpp", MultiplayerSessionSessionConstantsCpp_Lua); lua_register(Data()->L, "MultiplayerSessionSetVisibilityCpp", MultiplayerSessionSetVisibilityCpp_Lua); lua_register(Data()->L, "MultiplayerSessionSetMaxMembersInSessionCpp", MultiplayerSessionSetMaxMembersInSessionCpp_Lua); lua_register(Data()->L, "MultiplayerSessionSetTimeoutsCpp", MultiplayerSessionSetTimeoutsCpp_Lua); lua_register(Data()->L, "MultiplayerSessionSetQosConnectivityMetricsCpp", MultiplayerSessionSetQosConnectivityMetricsCpp_Lua); lua_register(Data()->L, "MultiplayerSessionSetMemberInitializationCpp", MultiplayerSessionSetMemberInitializationCpp_Lua); lua_register(Data()->L, "MultiplayerSessionSetPeerToPeerRequirementsCpp", MultiplayerSessionSetPeerToPeerRequirementsCpp_Lua); lua_register(Data()->L, "MultiplayerSessionSetPeerToHostRequirementsCpp", MultiplayerSessionSetPeerToHostRequirementsCpp_Lua); lua_register(Data()->L, "MultiplayerSessionSetSessionCapabilitiesCpp", MultiplayerSessionSetSessionCapabilitiesCpp_Lua); lua_register(Data()->L, "MultiplayerSessionSetCloudComputePackageJsonCpp", MultiplayerSessionSetCloudComputePackageJsonCpp_Lua); lua_register(Data()->L, "MultiplayerSessionSessionPropertiesCpp", MultiplayerSessionSessionPropertiesCpp_Lua); lua_register(Data()->L, "MultiplayerSessionPropertiesSetKeywordsCpp", MultiplayerSessionPropertiesSetKeywordsCpp_Lua); lua_register(Data()->L, "MultiplayerSessionPropertiesSetJoinRestrictionCpp", MultiplayerSessionPropertiesSetJoinRestrictionCpp_Lua); lua_register(Data()->L, "MultiplayerSessionPropertiesSetReadRestrictionCpp", MultiplayerSessionPropertiesSetReadRestrictionCpp_Lua); lua_register(Data()->L, "MultiplayerSessionRoleTypesCpp", MultiplayerSessionRoleTypesCpp_Lua); lua_register(Data()->L, "MultiplayerSessionMembersCpp", MultiplayerSessionMembersCpp_Lua); lua_register(Data()->L, "MultiplayerSessionGetMemberCpp", MultiplayerSessionGetMemberCpp_Lua); lua_register(Data()->L, "MultiplayerSessionMatchmakingServerCpp", MultiplayerSessionMatchmakingServerCpp_Lua); lua_register(Data()->L, "MultiplayerSessionMembersAcceptedCpp", MultiplayerSessionMembersAcceptedCpp_Lua); lua_register(Data()->L, "MultiplayerSessionServersJsonCpp", MultiplayerSessionServersJsonCpp_Lua); lua_register(Data()->L, "MultiplayerSessionSetServersJsonCpp", MultiplayerSessionSetServersJsonCpp_Lua); lua_register(Data()->L, "MultiplayerSessionEtagCpp", MultiplayerSessionEtagCpp_Lua); lua_register(Data()->L, "MultiplayerSessionCurrentUserCpp", MultiplayerSessionCurrentUserCpp_Lua); lua_register(Data()->L, "MultiplayerSessionGetInfoCpp", MultiplayerSessionGetInfoCpp_Lua); lua_register(Data()->L, "MultiplayerSessionWriteStatusCpp", MultiplayerSessionWriteStatusCpp_Lua); lua_register(Data()->L, "MultiplayerSessionSetInitializationStatusCpp", MultiplayerSessionSetInitializationStatusCpp_Lua); lua_register(Data()->L, "MultiplayerSessionSetHostDeviceTokenCpp", MultiplayerSessionSetHostDeviceTokenCpp_Lua); lua_register(Data()->L, "MultiplayerSessionSetMatchmakingServerConnectionPathCpp", MultiplayerSessionSetMatchmakingServerConnectionPathCpp_Lua); lua_register(Data()->L, "MultiplayerSessionSetClosedCpp", MultiplayerSessionSetClosedCpp_Lua); lua_register(Data()->L, "MultiplayerSessionSetLockedCpp", MultiplayerSessionSetLockedCpp_Lua); lua_register(Data()->L, "MultiplayerSessionSetAllocateCloudComputeCpp", MultiplayerSessionSetAllocateCloudComputeCpp_Lua); lua_register(Data()->L, "MultiplayerSessionSetMatchmakingResubmitCpp", MultiplayerSessionSetMatchmakingResubmitCpp_Lua); lua_register(Data()->L, "MultiplayerSessionSetServerConnectionStringCandidatesCpp", MultiplayerSessionSetServerConnectionStringCandidatesCpp_Lua); lua_register(Data()->L, "MultiplayerSessionSetSessionChangeSubscriptionCpp", MultiplayerSessionSetSessionChangeSubscriptionCpp_Lua); lua_register(Data()->L, "MultiplayerSessionLeaveCpp", MultiplayerSessionLeaveCpp_Lua); lua_register(Data()->L, "MultiplayerSessionSetCurrentUserStatusCpp", MultiplayerSessionSetCurrentUserStatusCpp_Lua); lua_register(Data()->L, "MultiplayerSessionSetCurrentUserSecureDeviceAddressBase64Cpp", MultiplayerSessionSetCurrentUserSecureDeviceAddressBase64Cpp_Lua); lua_register(Data()->L, "MultiplayerSessionSetCurrentUserRolesCpp", MultiplayerSessionSetCurrentUserRolesCpp_Lua); lua_register(Data()->L, "MultiplayerSessionCurrentUserSetGroupsCpp", MultiplayerSessionCurrentUserSetGroupsCpp_Lua); lua_register(Data()->L, "MultiplayerSessionCurrentUserSetEncountersCpp", MultiplayerSessionCurrentUserSetEncountersCpp_Lua); lua_register(Data()->L, "MultiplayerSessionSetCurrentUserQosMeasurementsJsonCpp", MultiplayerSessionSetCurrentUserQosMeasurementsJsonCpp_Lua); lua_register(Data()->L, "MultiplayerSessionSetCurrentUserMemberCustomPropertyJsonCpp", MultiplayerSessionSetCurrentUserMemberCustomPropertyJsonCpp_Lua); lua_register(Data()->L, "MultiplayerSessionDeleteCurrentUserMemberCustomPropertyJsonCpp", MultiplayerSessionDeleteCurrentUserMemberCustomPropertyJsonCpp_Lua); lua_register(Data()->L, "MultiplayerSessionSetMatchmakingTargetSessionConstantsJsonCpp", MultiplayerSessionSetMatchmakingTargetSessionConstantsJsonCpp_Lua); lua_register(Data()->L, "MultiplayerSessionSetSessionCustomPropertyJsonCpp", MultiplayerSessionSetSessionCustomPropertyJsonCpp_Lua); lua_register(Data()->L, "MultiplayerSessionDeleteSessionCustomPropertyJsonCpp", MultiplayerSessionDeleteSessionCustomPropertyJsonCpp_Lua); lua_register(Data()->L, "MultiplayerSessionCompareCpp", MultiplayerSessionCompareCpp_Lua); //multiplayer_service lua_register(Data()->L, "MultiplayerServiceWriteSession", MultiplayerServiceWriteSession_Lua); lua_register(Data()->L, "MultiplayerServiceWriteSessionByHandle", MultiplayerServiceWriteSessionByHandle_Lua); lua_register(Data()->L, "MultiplayerServiceGetCurrentSession", MultiplayerServiceGetCurrentSession_Lua); lua_register(Data()->L, "MultiplayerServiceGetCurrentSessionByHandle", MultiplayerServiceGetCurrentSessionByHandle_Lua); lua_register(Data()->L, "MultiplayerServiceGetSessions", MultiplayerServiceGetSessions_Lua); lua_register(Data()->L, "MultiplayerServiceSetActivity", MultiplayerServiceSetActivity_Lua); lua_register(Data()->L, "MultiplayerServiceClearActivity", MultiplayerServiceClearActivity_Lua); lua_register(Data()->L, "MultiplayerServiceSendInvites", MultiplayerServiceSendInvites_Lua); lua_register(Data()->L, "MultiplayerServiceGetActivitiesForSocialGroup", MultiplayerServiceGetActivitiesForSocialGroup_Lua); lua_register(Data()->L, "MultiplayerServiceGetActivitiesForUsers", MultiplayerServiceGetActivitiesForUsers_Lua); lua_register(Data()->L, "MultiplayerServiceEnableMultiplayerSubscriptions", MultiplayerServiceEnableMultiplayerSubscriptions_Lua); lua_register(Data()->L, "MultiplayerServiceDisableMultiplayerSubscriptions", MultiplayerServiceDisableMultiplayerSubscriptions_Lua); lua_register(Data()->L, "MultiplayerServiceMultiplayerSubscriptionsEnabled", MultiplayerServiceMultiplayerSubscriptionsEnabled_Lua); lua_register(Data()->L, "MultiplayerServiceAddMultiplayerSessionChangedHandler", MultiplayerServiceAddMultiplayerSessionChangedHandler_Lua); lua_register(Data()->L, "MultiplayerServiceRemoveMultiplayerSessionChangedHandler", MultiplayerServiceRemoveMultiplayerSessionChangedHandler_Lua); lua_register(Data()->L, "MultiplayerServiceAddMultiplayerSubscriptionLostHandler", MultiplayerServiceAddMultiplayerSubscriptionLostHandler_Lua); lua_register(Data()->L, "MultiplayerServiceRemoveMultiplayerSubscriptionLostHandler", MultiplayerServiceRemoveMultiplayerSubscriptionLostHandler_Lua); lua_register(Data()->L, "MultiplayerServiceAddMultiplayerConnectionIdChangedHandler", MultiplayerServiceAddMultiplayerConnectionIdChangedHandler_Lua); lua_register(Data()->L, "MultiplayerServiceRemoveMultiplayerConnectionIdChangedHandler", MultiplayerServiceRemoveMultiplayerConnectionIdChangedHandler_Lua); lua_register(Data()->L, "MultiplayerServiceSetTransferHandle", MultiplayerServiceSetTransferHandle_Lua); lua_register(Data()->L, "MultiplayerServiceSetSearchHandle", MultiplayerServiceSetSearchHandle_Lua); lua_register(Data()->L, "MultiplayerServiceClearSearchHandle", MultiplayerServiceClearSearchHandle_Lua); lua_register(Data()->L, "MultiplayerServiceGetSearchHandles", MultiplayerServiceGetSearchHandles_Lua); //multiplayer_search_handle_details lua_register(Data()->L, "MultiplayerSearchHandleDetailsCloseHandle", MultiplayerSearchHandleDetailsCloseHandle_Lua); lua_register(Data()->L, "MultiplayerSearchHandleDetailsSessionReference", MultiplayerSearchHandleDetailsSessionReference_Lua); lua_register(Data()->L, "MultiplayerSearchHandleDetailsHandleId", MultiplayerSearchHandleDetailsHandleId_Lua); lua_register(Data()->L, "MultiplayerSearchHandleDetailsSessionOwnerXuids", MultiplayerSearchHandleDetailsSessionOwnerXuids_Lua); lua_register(Data()->L, "MultiplayerSearchHandleDetailsTags", MultiplayerSearchHandleDetailsTags_Lua); lua_register(Data()->L, "MultiplayerSearchHandleDetailsStringsMetadata", MultiplayerSearchHandleDetailsStringsMetadata_Lua); lua_register(Data()->L, "MultiplayerSearchHandleDetailsNumbersMetadata", MultiplayerSearchHandleDetailsNumbersMetadata_Lua); lua_register(Data()->L, "MultiplayerSearchHandleDetailsVisibility", MultiplayerSearchHandleDetailsVisibility_Lua); lua_register(Data()->L, "MultiplayerSearchHandleDetailsJoinRestriction", MultiplayerSearchHandleDetailsJoinRestriction_Lua); lua_register(Data()->L, "MultiplayerSearchHandleDetailsClosed", MultiplayerSearchHandleDetailsClosed_Lua); lua_register(Data()->L, "MultiplayerSearchHandleDetailsMemberCounts", MultiplayerSearchHandleDetailsMemberCounts_Lua); lua_register(Data()->L, "MultiplayerSearchHandleDetailsHandleCreationTime", MultiplayerSearchHandleDetailsHandleCreationTime_Lua); lua_register(Data()->L, "MultiplayerSearchHandleDetailsCustomSessionPropertiesJson", MultiplayerSearchHandleDetailsCustomSessionPropertiesJson_Lua); //matchmaking_service lua_register(Data()->L, "MatchmakingServiceCreateTicket", MatchmakingServiceCreateTicket_Lua); lua_register(Data()->L, "MatchmakingServiceGetMatchTicketDetails", MatchmakingServiceGetMatchTicketDetails_Lua); lua_register(Data()->L, "MatchmakingServiceGetHopperStatistics", MatchmakingServiceGetHopperStatistics_Lua); lua_register(Data()->L, "MatchmakingServiceDeleteMatchTicket", MatchmakingServiceDeleteMatchTicket_Lua); } ================================================ FILE: Tests/ApiExplorer/APIs/apis_cpp_presence.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #if CPP_TESTS_ENABLED static struct PresenceStateCpp { PresenceStateCpp() = default; ~PresenceStateCpp() { //assert(!presenceRecord); assert(!devicePresenceChangeSubscription); assert(!titlePresenceChangeSubscription); assert(!devicePresenceChangedHandlerContext); assert(!titlePresenceChangedHandlerContext); } std::shared_ptr presenceRecord{ nullptr }; std::shared_ptr devicePresenceChangeSubscription{ nullptr }; std::shared_ptr titlePresenceChangeSubscription{ nullptr }; function_context devicePresenceChangedHandlerContext{ nullptr }; function_context titlePresenceChangedHandlerContext{ nullptr }; } presenceStateCpp; #endif int PresenceRecordGetXuidCpp_Lua(lua_State *L) { #if CPP_TESTS_ENABLED string_t xuid = presenceStateCpp.presenceRecord->xbox_user_id(); LogToScreen("PresenceRecordGetXuidCpp xuid=%s", xbox::services::Utils::StringFromStringT(xuid).c_str()); #else LogToFile("PresenceRecordGetXuidCpp is disabled for this platform."); #endif return LuaReturnHR(L, S_OK); } int PresenceRecordGetUserStateCpp_Lua(lua_State *L) { #if CPP_TESTS_ENABLED xbox::services::presence::user_presence_state userState = presenceStateCpp.presenceRecord->user_state(); LogToScreen("PresenceRecordGetUserStateCpp state=%u", userState); #else LogToFile("PresenceRecordGetUserStateCpp is disabled for this platform."); #endif return LuaReturnHR(L, S_OK); } int PresenceRecordGetDeviceRecordsCpp_Lua(lua_State *L) { #if CPP_TESTS_ENABLED std::vector deviceRecords = presenceStateCpp.presenceRecord->presence_device_records(); for (auto deviceRecord : deviceRecords) { LogToScreen("Got presence_device_record with device type %u and %u title records", deviceRecord.device_type(), deviceRecord.presence_title_records().size()); for (auto titleRecord : deviceRecord.presence_title_records()) { LogToScreen("Title record titleId %u", titleRecord.title_id()); } } LogToFile("PresenceRecordGetDeviceRecordsCpp"); #else LogToFile("PresenceRecordGetDeviceRecordsCpp is disabled for this platform."); #endif return LuaReturnHR(L, S_OK); } int PresenceRecordCloseHandleCpp_Lua(lua_State* L) { #if CPP_TESTS_ENABLED presenceStateCpp.presenceRecord = nullptr; LogToFile("PresenceRecordCloseHandleCpp"); #else LogToFile("PresenceRecordCloseHandleCpp is disabled for this platform."); #endif return LuaReturnHR(L, S_OK); } int PresenceServiceSetPresence_Lua(lua_State *L) { #if CPP_TESTS_ENABLED bool isActiveInTitle = GetUint64FromLua(L, 1, 1); std::shared_ptr xblc = std::make_shared(Data()->xboxLiveContext); xblc->presence_service().set_presence(isActiveInTitle).then( [](xbox::services::xbox_live_result result) { HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); LogToFile("PresenceServiceSetPresence: hr=%s", ConvertHR(hr).data()); if (SUCCEEDED(hr)) { } if (hr == HTTP_E_STATUS_429_TOO_MANY_REQUESTS) { LogToFile("PresenceServiceSetPresence returned 429. Ignoring failure"); hr = S_OK; } CallLuaFunctionWithHr(hr, "OnPresenceServiceSetPresence"); }); #else CallLuaFunctionWithHr(S_OK, "OnPresenceServiceSetPresence"); LogToFile("PresenceServiceSetPresence is disabled on this platform."); #endif return LuaReturnHR(L, S_OK); } int PresenceServiceGetPresence_Lua(lua_State *L) { #if CPP_TESTS_ENABLED uint64_t xuid = Data()->xboxUserId; if (Data()->m_multiDeviceManager->GetRemoteXuid() != 0) { xuid = Data()->m_multiDeviceManager->GetRemoteXuid(); } string_t xboxUserId = xbox::services::Utils::StringTFromUint64(GetUint64FromLua(L, 1, xuid)); std::shared_ptr xblc = std::make_shared(Data()->xboxLiveContext); xblc->presence_service().get_presence(xboxUserId).then( [](xbox::services::xbox_live_result result) { HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); LogToFile("PresenceServiceGetPresence: hr=%s", ConvertHR(hr).data()); if (SUCCEEDED(hr)) { presenceStateCpp.presenceRecord = std::make_shared(result.payload()); } CallLuaFunctionWithHr(hr, "OnPresenceServiceGetPresence"); }); #else LogToFile("PresenceServiceGetPresence is disabled for this platform"); CallLuaFunctionWithHr(S_OK, "OnPresenceServiceGetPresence"); #endif return LuaReturnHR(L, S_OK); } int PresenceServiceGetPresenceForMultipleUsers_Lua(lua_State* L) { #if CPP_TESTS_ENABLED std::vector xuids{ _T("2814639011617876"), _T("2814641789541994") }; std::vector deviceTypes; std::vector titleIds; xbox::services::presence::presence_detail_level detailLevel = xbox::services::presence::presence_detail_level::all; bool onlineOnly = true; bool broadcastingOnly = false; std::shared_ptr xblc = std::make_shared(Data()->xboxLiveContext); xblc->presence_service().get_presence_for_multiple_users( xuids, deviceTypes, titleIds, detailLevel, onlineOnly, broadcastingOnly ).then([](xbox::services::xbox_live_result> result) { HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); LogToFile("PresenceServiceGetPresenceForMultipleUsers: hr=%s", ConvertHR(hr).data()); if (SUCCEEDED(hr)) { LogToFile("Got %u presence records", result.payload().size()); } CallLuaFunctionWithHr(hr, "OnPresenceServiceGetPresenceForMultipleUsers"); }); #else LogToFile("PresenceServiceGetPresenceForMultipleUsers is disabled for this platform."); CallLuaFunctionWithHr(S_OK, "OnPresenceServiceGetPresenceForMultipleUsers"); #endif return LuaReturnHR(L, S_OK); } int PresenceServiceGetPresenceForSocialGroup_Lua(lua_State *L) { #if CPP_TESTS_ENABLED string_t socialGroup = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 1, "Friends").c_str()); std::shared_ptr xblc = std::make_shared(Data()->xboxLiveContext); xblc->presence_service().get_presence_for_social_group(socialGroup).then( [](xbox::services::xbox_live_result> result) { HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); LogToFile("PresenceServiceGetPresenceForSocialGroup: hr=%s", ConvertHR(hr).data()); if (SUCCEEDED(hr)) { LogToFile("Got %u presence records", result.payload().size()); } CallLuaFunctionWithHr(hr, "OnPresenceServiceGetPresenceForMultipleUsers"); }); #else LogToFile("PresenceServiceGetPresenceForSocialGroup is disabled for this platform."); CallLuaFunctionWithHr(S_OK, "OnPresenceServiceGetPresenceForMultipleUsers"); #endif return LuaReturnHR(L, S_OK); } int PresenceServiceSubscribeToDevicePresenceChange_Lua(lua_State* L) { #if CPP_TESTS_ENABLED string_t xuid{ _T("2814639011617876") }; std::shared_ptr xblc = std::make_shared(Data()->xboxLiveContext); xbox::services::xbox_live_result> result = xblc->presence_service().subscribe_to_device_presence_change(xuid); HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); LogToFile("PresenceServiceSubscribeToDevicePresenceChange: hr=%s", ConvertHR(hr).c_str()); if (SUCCEEDED(hr)) { presenceStateCpp.devicePresenceChangeSubscription = result.payload(); } return LuaReturnHR(L, hr); #else LogToFile("PresenceServiceSubscribeToDevicePresenceChange is disabled for this platform."); return LuaReturnHR(L, S_OK); #endif } int PresenceServiceUnsubscribeFromDevicePresenceChange_Lua(lua_State* L) { #if CPP_TESTS_ENABLED std::shared_ptr xblc = std::make_shared(Data()->xboxLiveContext); xbox::services::xbox_live_result result = xblc->presence_service().unsubscribe_from_device_presence_change(presenceStateCpp.devicePresenceChangeSubscription); HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); LogToFile("PresenceServiceUnsubscribeFromDevicePresenceChange: hr=%s", ConvertHR(hr).c_str()); if (SUCCEEDED(hr)) { presenceStateCpp.devicePresenceChangeSubscription = nullptr; } return LuaReturnHR(L, hr); #else LogToFile("PresenceServiceUnsubscribeFromDevicePresenceChange is disabled for this platform"); return LuaReturnHR(L, S_OK); #endif } int PresenceServiceSubscribeToTitlePresenceChange_Lua(lua_State* L) { #if CPP_TESTS_ENABLED string_t xuid{ _T("2814639011617876") }; std::shared_ptr xblc = std::make_shared(Data()->xboxLiveContext); xbox::services::xbox_live_result> result = xblc->presence_service().subscribe_to_title_presence_change(xuid, Data()->titleId); HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); LogToFile("PresenceServiceSubscribeToTitlePresenceChange: hr=%s", ConvertHR(hr).c_str()); if (SUCCEEDED(hr)) { presenceStateCpp.titlePresenceChangeSubscription = result.payload(); } return LuaReturnHR(L, hr); #else LogToFile("PresenceServiceSubscribeToTitlePresenceChange is disabled for this platform."); return LuaReturnHR(L, S_OK); #endif } int PresenceServiceUnsubscribeFromTitlePresenceChange_Lua(lua_State* L) { #if CPP_TESTS_ENABLED std::shared_ptr xblc = std::make_shared(Data()->xboxLiveContext); xbox::services::xbox_live_result result = xblc->presence_service().unsubscribe_from_title_presence_change(presenceStateCpp.titlePresenceChangeSubscription); HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); LogToFile("PresenceServiceUnsubscribeFromTitlePresenceChange: hr=%s", ConvertHR(hr).c_str()); if (SUCCEEDED(hr)) { presenceStateCpp.titlePresenceChangeSubscription = nullptr; } return LuaReturnHR(L, hr); #else LogToFile("PresenceServiceUnsubscribeFromTitlePresenceChange is disabled for this platform"); return LuaReturnHR(L, S_OK); #endif } int PresenceServiceAddDevicePresenceChangedHandler_Lua(lua_State* L) { #if CPP_TESTS_ENABLED std::shared_ptr xblc = std::make_shared(Data()->xboxLiveContext); presenceStateCpp.devicePresenceChangedHandlerContext = xblc->presence_service().add_device_presence_changed_handler( [](xbox::services::presence::device_presence_change_event_args args) { LogToFile("Device presence change notification received:"); LogToFile("Xuid = %s, device_type = %u, is_user_logged_on_device = %u", xbox::services::Utils::StringFromStringT(args.xbox_user_id()).c_str(), args.device_type(), args.is_user_logged_on_device()); }); LogToFile("PresenceServiceAddDevicePresenceChangedHandler"); #else LogToFile("PresenceServiceAddDevicePresenceChangedHandler is disabled for this platform."); #endif return LuaReturnHR(L, S_OK); } int PresenceServiceRemoveDevicePresenceChangedHandler_Lua(lua_State* L) { #if CPP_TESTS_ENABLED std::shared_ptr xblc = std::make_shared(Data()->xboxLiveContext); xblc->presence_service().remove_device_presence_changed_handler(presenceStateCpp.devicePresenceChangedHandlerContext); presenceStateCpp.devicePresenceChangedHandlerContext = nullptr; LogToFile("PresenceServiceRemoveDevicePresenceChangedHandler"); #else LogToFile("PresenceServiceRemoveDevicePresenceChangedHandler is disabled on this platform."); #endif return LuaReturnHR(L, S_OK); } int PresenceServiceAddTitlePresenceChangedHandler_Lua(lua_State* L) { #if CPP_TESTS_ENABLED std::shared_ptr xblc = std::make_shared(Data()->xboxLiveContext); presenceStateCpp.titlePresenceChangedHandlerContext = xblc->presence_service().add_title_presence_changed_handler( [](xbox::services::presence::title_presence_change_event_args args) { LogToFile("Title presence change notification received:"); LogToFile("Xuid = %s, title_id = %u, title_state = %u", xbox::services::Utils::StringFromStringT(args.xbox_user_id()).c_str(), args.title_id(), args.title_state()); }); LogToFile("PresenceServiceAddTitlePresenceChangedHandler"); #else LogToFile("PresenceServiceAddTitlePresenceChangedHandler is disabled for this platform."); #endif return LuaReturnHR(L, S_OK); } int PresenceServiceRemoveTitlePresenceChangedHandler_Lua(lua_State* L) { #if CPP_TESTS_ENABLED std::shared_ptr xblc = std::make_shared(Data()->xboxLiveContext); xblc->presence_service().remove_title_presence_changed_handler(presenceStateCpp.titlePresenceChangedHandlerContext); presenceStateCpp.titlePresenceChangedHandlerContext = nullptr; LogToFile("PresenceServiceRemoveTitlePresenceChangedHandler"); #else LogToFile("PresenceServiceRemoveTitlePresenceChangedHandler is disabled on this platform."); #endif return LuaReturnHR(L, S_OK); } void SetupAPIs_CppPresence() { lua_register(Data()->L, "PresenceRecordGetXuidCpp", PresenceRecordGetXuidCpp_Lua); lua_register(Data()->L, "PresenceRecordGetUserStateCpp", PresenceRecordGetUserStateCpp_Lua); lua_register(Data()->L, "PresenceRecordGetDeviceRecordsCpp", PresenceRecordGetDeviceRecordsCpp_Lua); lua_register(Data()->L, "PresenceRecordCloseHandleCpp", PresenceRecordCloseHandleCpp_Lua); lua_register(Data()->L, "PresenceServiceSetPresence", PresenceServiceSetPresence_Lua); lua_register(Data()->L, "PresenceServiceGetPresence", PresenceServiceGetPresence_Lua); lua_register(Data()->L, "PresenceServiceGetPresenceForSocialGroup", PresenceServiceGetPresenceForSocialGroup_Lua); lua_register(Data()->L, "PresenceServiceGetPresenceForMultipleUsers", PresenceServiceGetPresenceForMultipleUsers_Lua); lua_register(Data()->L, "PresenceServiceSubscribeToDevicePresenceChange", PresenceServiceSubscribeToDevicePresenceChange_Lua); lua_register(Data()->L, "PresenceServiceUnsubscribeFromDevicePresenceChange", PresenceServiceUnsubscribeFromDevicePresenceChange_Lua); lua_register(Data()->L, "PresenceServiceSubscribeToTitlePresenceChange", PresenceServiceSubscribeToTitlePresenceChange_Lua); lua_register(Data()->L, "PresenceServiceUnsubscribeFromTitlePresenceChange", PresenceServiceUnsubscribeFromTitlePresenceChange_Lua); lua_register(Data()->L, "PresenceServiceAddDevicePresenceChangedHandler", PresenceServiceAddDevicePresenceChangedHandler_Lua); lua_register(Data()->L, "PresenceServiceRemoveDevicePresenceChangedHandler", PresenceServiceRemoveDevicePresenceChangedHandler_Lua); lua_register(Data()->L, "PresenceServiceAddTitlePresenceChangedHandler", PresenceServiceAddTitlePresenceChangedHandler_Lua); lua_register(Data()->L, "PresenceServiceRemoveTitlePresenceChangedHandler", PresenceServiceRemoveTitlePresenceChangedHandler_Lua); } ================================================ FILE: Tests/ApiExplorer/APIs/apis_cpp_privacy.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #if CPP_TESTS_ENABLED string_t ConvertXblPermissionToStringT(XblPermission permissionType) { switch (permissionType) { case XblPermission::BroadcastWithTwitch: return xbox::services::privacy::permission_id_constants::broadcast_with_twitch(); case XblPermission::CommunicateUsingText: return xbox::services::privacy::permission_id_constants::communicate_using_text(); case XblPermission::CommunicateUsingVideo: return xbox::services::privacy::permission_id_constants::communicate_using_video(); case XblPermission::CommunicateUsingVoice: return xbox::services::privacy::permission_id_constants::communicate_using_voice(); case XblPermission::PlayMultiplayer: return xbox::services::privacy::permission_id_constants::play_multiplayer(); //case XblPermission::ShareItem: //unsupported //case XblPermission::ShareTargetContentToExternalNetworks: //unsupported case XblPermission::ViewTargetExerciseInfo: return xbox::services::privacy::permission_id_constants::view_target_exercise_info(); case XblPermission::ViewTargetGameHistory: return xbox::services::privacy::permission_id_constants::view_target_game_history(); case XblPermission::ViewTargetMusicHistory: return xbox::services::privacy::permission_id_constants::view_target_music_history(); case XblPermission::ViewTargetMusicStatus: return xbox::services::privacy::permission_id_constants::view_target_music_status(); case XblPermission::ViewTargetPresence: return xbox::services::privacy::permission_id_constants::view_target_presence(); case XblPermission::ViewTargetProfile: return xbox::services::privacy::permission_id_constants::view_target_profile(); case XblPermission::ViewTargetUserCreatedContent: return xbox::services::privacy::permission_id_constants::view_target_user_created_content(); case XblPermission::ViewTargetVideoHistory: return xbox::services::privacy::permission_id_constants::view_target_video_history(); case XblPermission::ViewTargetVideoStatus: return xbox::services::privacy::permission_id_constants::view_target_video_status(); case XblPermission::WriteComment: case XblPermission::ShareItem: case XblPermission::ShareTargetContentToExternalNetworks: case XblPermission::Unknown: default: return _T("Unknown"); } } #endif int PrivacyServiceGetAvoidList_Lua(lua_State* L) { #if CPP_TESTS_ENABLED std::shared_ptr xblc = std::make_shared(Data()->xalUser); xblc->privacy_service().get_avoid_list().then( [](xbox::services::xbox_live_result> result) { HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); LogToFile("PrivacyServiceGetAvoidList: hr=%s", ConvertHR(hr).c_str()); if (SUCCEEDED(hr)) { std::vector avoidList = result.payload(); LogToFile("PrivacyServiceGetAvoidList avoided xuids count=%d", avoidList.size()); } CallLuaFunctionWithHr(hr, "OnPrivacyServiceGetAvoidList"); }); #else LogToFile("PrivacyServiceGetAvoidList is disabled on this platform"); CallLuaFunctionWithHr(S_OK, "OnPrivacyServiceGetAvoidList"); #endif return LuaReturnHR(L, S_OK); } int PrivacyServiceCheckPermissionWithTargetUser_Lua(lua_State* L) { #if CPP_TESTS_ENABLED string_t permissionToCheck = ConvertXblPermissionToStringT(static_cast(GetUint32FromLua(L, 1, (uint32_t)XblPermission::ViewTargetProfile))); string_t targetXuid = xbox::services::Utils::StringTFromUint64(GetUint64FromLua(L, 2, 2743710844428572)); std::shared_ptr xblc = std::make_shared(Data()->xalUser); xblc->privacy_service().check_permission_with_target_user( permissionToCheck, targetXuid ).then( [](xbox::services::xbox_live_result result) { HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); LogToFile("PrivacyServiceCheckPermissionWithTargetUser: hr=%s", ConvertHR(hr).c_str()); if (SUCCEEDED(hr)) { xbox::services::privacy::permission_check_result permissionResult = result.payload(); LogToFile("PrivacyServiceCheckPermissionWithTargetUser: isAllowed=%d", permissionResult.is_allowed()); } CallLuaFunctionWithHr(hr, "OnPrivacyServiceCheckPermissionWithTargetUser"); }); #else LogToFile("PrivacyServiceCheckPermissionWithTargetUser is disabled for this platform."); CallLuaFunctionWithHr(S_OK, "OnPrivacyServiceCheckPermissionWithTargetUser"); #endif return LuaReturnHR(L, S_OK); } int PrivacyServiceCheckMultiplePermissionsWithMultipleTargetUsers_Lua(lua_State* L) { #if CPP_TESTS_ENABLED std::vector permissionsToCheck{ xbox::services::privacy::permission_id_constants::communicate_using_text(), xbox::services::privacy::permission_id_constants::communicate_using_video(), xbox::services::privacy::permission_id_constants::communicate_using_voice(), xbox::services::privacy::permission_id_constants::view_target_profile(), xbox::services::privacy::permission_id_constants::view_target_game_history(), xbox::services::privacy::permission_id_constants::view_target_video_history(), xbox::services::privacy::permission_id_constants::view_target_music_history(), xbox::services::privacy::permission_id_constants::view_target_exercise_info(), xbox::services::privacy::permission_id_constants::view_target_presence(), xbox::services::privacy::permission_id_constants::view_target_video_status(), xbox::services::privacy::permission_id_constants::view_target_music_status(), xbox::services::privacy::permission_id_constants::play_multiplayer(), xbox::services::privacy::permission_id_constants::view_target_user_created_content(), xbox::services::privacy::permission_id_constants::broadcast_with_twitch() }; std::vector targetXuids{ _T("2743710844428572"), _T("2533274819720636"), xbox::services::privacy::anonymous_user_type_constants::cross_network_user(), xbox::services::privacy::anonymous_user_type_constants::crost_network_friend() }; //the multiple_permissions_check_result for an anonymous type returns results for both anonymous types, is this expected? size_t expectedResultCount{ permissionsToCheck.size() * (targetXuids.size() + 2) }; std::shared_ptr xblc = std::make_shared(Data()->xalUser); xblc->privacy_service().check_multiple_permissions_with_multiple_target_users( permissionsToCheck, targetXuids ).then( [expectedResultCount](xbox::services::xbox_live_result> result) { HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); LogToFile("PrivacyServiceCheckMultiplePermissionsWithMultipleTargetUsers: hr=%s", ConvertHR(hr).c_str()); if (SUCCEEDED(hr)) { std::vector multiplePermissionsResults = result.payload(); size_t resultCount = 0; for (xbox::services::privacy::multiple_permissions_check_result multiplePermissionsResult : multiplePermissionsResults) { resultCount += multiplePermissionsResult.items().size(); } LogToFile("PrivacyServiceCheckMultiplePermissionsWithMultipleTargetUsers: expectedResultCount=%d resultCount=%d", expectedResultCount, resultCount); assert(resultCount == expectedResultCount); } CallLuaFunctionWithHr(hr, "OnPrivacyServiceCheckMultiplePermissionsWithMultipleTargetUsers"); }); #else LogToFile("PrivacyServiceCheckMultiplePermissionsWithMultipleTargetUsers is disabled for this platform"); CallLuaFunctionWithHr(S_OK, "OnPrivacyServiceCheckMultiplePermissionsWithMultipleTargetUsers"); #endif return LuaReturnHR(L, S_OK); } int PrivacyServiceGetMuteList_Lua(lua_State* L) { #if CPP_TESTS_ENABLED std::shared_ptr xblc = std::make_shared(Data()->xalUser); xblc->privacy_service().get_mute_list().then( [](xbox::services::xbox_live_result> result) { HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); LogToFile("PrivacyServiceGetMuteList: hr=%s", ConvertHR(hr).c_str()); if (SUCCEEDED(hr)) { std::vector muteList = result.payload(); LogToFile("PrivacyServiceGetMuteList muted xuids count=%d", muteList.size()); } CallLuaFunctionWithHr(hr, "OnPrivacyServiceGetMuteList"); }); #else LogToFile("PrivacyServiceGetMuteList is disabled on this platform"); CallLuaFunctionWithHr(S_OK, "OnPrivacyServiceGetMuteList"); #endif return LuaReturnHR(L, S_OK); } int PrivacyServiceGetAvoidOrMuteList_Lua(lua_State* L) { #if CPP_TESTS_ENABLED std::shared_ptr xblc = std::make_shared(Data()->xalUser); xblc->privacy_service().get_avoid_or_mute_list( _T("avoid") ).then( [](xbox::services::xbox_live_result> result) { HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); LogToFile("PrivacyServiceGetAvoidOrMuteList: hr=%s", ConvertHR(hr).c_str()); if (SUCCEEDED(hr)) { std::vector avoidList = result.payload(); LogToFile("PrivacyServiceGetAvoidOrMuteList xuids count=%d", avoidList.size()); } CallLuaFunctionWithHr(hr, "OnPrivacyServiceGetAvoidOrMuteList"); }); #else LogToFile("PrivacyServiceGetAvoidOrMuteList is disabled on this platform"); CallLuaFunctionWithHr(S_OK, "OnPrivacyServiceGetAvoidOrMuteList"); #endif return LuaReturnHR(L, S_OK); } void SetupAPIs_CppPrivacy() { lua_register(Data()->L, "PrivacyServiceGetAvoidList", PrivacyServiceGetAvoidList_Lua); lua_register(Data()->L, "PrivacyServiceCheckPermissionWithTargetUser", PrivacyServiceCheckPermissionWithTargetUser_Lua); lua_register(Data()->L, "PrivacyServiceCheckMultiplePermissionsWithMultipleTargetUsers", PrivacyServiceCheckMultiplePermissionsWithMultipleTargetUsers_Lua); lua_register(Data()->L, "PrivacyServiceGetMuteList", PrivacyServiceGetMuteList_Lua); lua_register(Data()->L, "PrivacyServiceGetAvoidOrMuteList", PrivacyServiceGetAvoidOrMuteList_Lua); } ================================================ FILE: Tests/ApiExplorer/APIs/apis_cpp_profile.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" int ProfileServiceGetUserProfile_Lua(lua_State* L) { #if CPP_TESTS_ENABLED string_t xboxUserId = xbox::services::Utils::StringTFromUint64(Data()->xboxUserId); std::shared_ptr xblc = std::make_shared(Data()->xalUser); xblc->profile_service().get_user_profile( xboxUserId ).then( [](xbox::services::xbox_live_result result) { HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); LogToFile("ProfileServiceGetUserProfile: hr=%s", ConvertHR(hr).c_str()); if (SUCCEEDED(hr)) { xbox::services::social::xbox_user_profile userProfile = result.payload(); LogToFile("ProfileServiceGetUserProfile: gamertag=%s", ConvertHR(hr).c_str(), xbox::services::Utils::StringFromStringT(userProfile.gamertag()).c_str()); } CallLuaFunctionWithHr(hr, "OnProfileServiceGetUserProfile"); }); #else LogToFile("ProfileServiceGetUserProfile is disabled on this platform."); CallLuaFunctionWithHr(S_OK, "OnProfileServiceGetUserProfile"); #endif return LuaReturnHR(L, S_OK); } int ProfileServiceGetUserProfiles_Lua(lua_State* L) { #if CPP_TESTS_ENABLED std::vector userIds; string_t xboxUserId = xbox::services::Utils::StringTFromUint64(Data()->xboxUserId); userIds.push_back(xboxUserId); std::shared_ptr xblc = std::make_shared(Data()->xalUser); xblc->profile_service().get_user_profiles( userIds ).then( [](xbox::services::xbox_live_result> result) { HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); LogToFile("ProfileServiceGetUserProfiles: hr=%s", ConvertHR(hr).c_str()); if (SUCCEEDED(hr)) { std::vector profiles = result.payload(); LogToFile("ProfileServiceGetUserProfiles: gamertag=%s", ConvertHR(hr).c_str(), xbox::services::Utils::StringFromStringT(profiles[0].gamertag()).c_str()); } CallLuaFunctionWithHr(hr, "OnProfileServiceGetUserProfiles"); }); #else LogToFile("ProfileServiceGetUserProfiles is disabled on this platform."); CallLuaFunctionWithHr(S_OK, "OnProfileServiceGetUserProfiles"); #endif return LuaReturnHR(L, S_OK); } int ProfileServiceGetUserProfilesForSocialGroup_Lua(lua_State* L) { #if CPP_TESTS_ENABLED string_t socialGroup = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 1, "People").c_str()); std::shared_ptr xblc = std::make_shared(Data()->xalUser); xblc->profile_service().get_user_profiles_for_social_group( socialGroup ).then( [](xbox::services::xbox_live_result> result) { HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); LogToFile("ProfileServiceGetUserProfilesForSocialGroup: hr=%s", ConvertHR(hr).c_str()); if (SUCCEEDED(hr)) { std::vector profiles = result.payload(); LogToFile("ProfileServiceGetUserProfilesForSocialGroup: profilesCount=%d", profiles.size()); for (auto profile : profiles) { LogToFile("\tgamertag=%s", xbox::services::Utils::StringFromStringT(profile.gamertag()).c_str()); } } CallLuaFunctionWithHr(hr, "OnProfileServiceGetUserProfilesForSocialGroup"); }); #else LogToFile("ProfileServiceGetUserProfiles is disabled on this platform."); CallLuaFunctionWithHr(S_OK, "OnProfileServiceGetUserProfilesForSocialGroup"); #endif return LuaReturnHR(L, S_OK); } void SetupAPIs_CppProfile() { lua_register(Data()->L, "ProfileServiceGetUserProfile", ProfileServiceGetUserProfile_Lua); lua_register(Data()->L, "ProfileServiceGetUserProfiles", ProfileServiceGetUserProfiles_Lua); lua_register(Data()->L, "ProfileServiceGetUserProfilesForSocialGroup", ProfileServiceGetUserProfilesForSocialGroup_Lua); } ================================================ FILE: Tests/ApiExplorer/APIs/apis_cpp_real_time_activity.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" XBL_WARNING_DISABLE_DEPRECATED #if CPP_TESTS_ENABLED static function_context s_connectionStateHandlerContextCpp{ nullptr }; static function_context s_subscriptionErrorHandlerContextCpp{ nullptr }; static function_context s_resyncHandlerContextCpp{ nullptr }; #endif int RealTimeActivityServiceActivate_Lua(lua_State *L) { #if CPP_TESTS_ENABLED std::shared_ptr xblc = std::make_shared(Data()->xboxLiveContext); xblc->real_time_activity_service()->activate(); LogToFile("RealTimeActivityServiceActivate"); #else LogToFile("RealTimeActivityServiceActivate is disabled for this platform."); #endif return LuaReturnHR(L, S_OK); } int RealTimeActivityServiceDeactivate_Lua(lua_State *L) { #if CPP_TESTS_ENABLED std::shared_ptr xblc = std::make_shared(Data()->xboxLiveContext); xblc->real_time_activity_service()->deactivate(); LogToFile("RealTimeActivityServiceDeactivate"); #else LogToFile("RealTimeActivityServiceDeactivate is disabled for this platform."); #endif return LuaReturnHR(L, S_OK); } int RealTimeActivityServiceAddConnectionStateChangeHandler_Lua(lua_State *L) { #if CPP_TESTS_ENABLED std::shared_ptr xblc = std::make_shared(Data()->xboxLiveContext); s_connectionStateHandlerContextCpp = xblc->real_time_activity_service()->add_connection_state_change_handler( [](xbox::services::real_time_activity::real_time_activity_connection_state connectionState) { LogToFile("RealTimeActivityServiceConnectionState changed to %d", connectionState); switch (connectionState) { case xbox::services::real_time_activity::real_time_activity_connection_state::connecting: LogToFile("RealTimeActivityServiceAddConnectionStateChangeHandler: Connecting\n"); CallLuaFunction("OnRealTimeActivityServiceAddConnectionStateChangeHandler_Connecting"); break; case xbox::services::real_time_activity::real_time_activity_connection_state::connected: LogToFile("RealTimeActivityServiceAddConnectionStateChangeHandler: Connected\n"); CallLuaFunction("OnRealTimeActivityServiceAddConnectionStateChangeHandler_Connected"); break; case xbox::services::real_time_activity::real_time_activity_connection_state::disconnected: LogToFile("RealTimeActivityServiceAddConnectionStateChangeHandler: Connected\n"); CallLuaFunction("OnRealTimeActivityServiceAddConnectionStateChangeHandler_Disconnected"); break; } }); LogToFile("RealTimeActivityServiceAddConnectionStateChangeHandler"); #else LogToFile("RealTimeActivityServiceAddConnectionStateChangeHandler is disabled on this platform."); CallLuaFunction("OnRealTimeActivityServiceAddConnectionStateChangeHandler_Disabled"); #endif return LuaReturnHR(L, S_OK); } int RealTimeActivityServiceRemoveConnectionStateChangeHandler_Lua(lua_State *L) { #if CPP_TESTS_ENABLED if (s_connectionStateHandlerContextCpp) { std::shared_ptr xblc = std::make_shared(Data()->xboxLiveContext); xblc->real_time_activity_service()->remove_connection_state_change_handler(s_connectionStateHandlerContextCpp); s_connectionStateHandlerContextCpp = nullptr; } LogToFile("RealTimeActivityServiceRemoveConnectionStateChangeHandler"); #else LogToFile("RealTimeActivityServiceRemoveConnectionStateChangeHandler is disabled for this platform."); #endif return LuaReturnHR(L, S_OK); } int RealTimeActivityServiceAddSubscriptionErrorHandler_Lua(lua_State *L) { #if CPP_TESTS_ENABLED std::shared_ptr xblc = std::make_shared(Data()->xboxLiveContext); s_subscriptionErrorHandlerContextCpp = xblc->real_time_activity_service()->add_subscription_error_handler( [](xbox::services::real_time_activity::real_time_activity_subscription_error_event_args subscriptionError) { LogToFile("Rta subscription error %s", ConvertHR(ConvertXboxLiveErrorCodeToHresult(subscriptionError.err())).c_str()); }); LogToFile("RealTimeActivityServiceAddSubscriptionErrorHandler"); #else LogToFile("RealTimeActivityServiceAddSubscriptionErrorHandler is disabled for this platform."); #endif return LuaReturnHR(L, S_OK); } int RealTimeActivityServiceRemoveSubscriptionErrorHandler_Lua(lua_State *L) { #if CPP_TESTS_ENABLED if (s_subscriptionErrorHandlerContextCpp) { std::shared_ptr xblc = std::make_shared(Data()->xboxLiveContext); xblc->real_time_activity_service()->remove_subscription_error_handler(s_subscriptionErrorHandlerContextCpp); s_subscriptionErrorHandlerContextCpp = nullptr; } LogToFile("RealTimeActivityServiceRemoveSubscriptionErrorHandler"); #else LogToFile("RealTimeActivityServiceRemoveSubscriptionErrorHandler is disabled for this platform."); #endif return LuaReturnHR(L, S_OK); } int RealTimeActivityServiceAddResyncHandler_Lua(lua_State *L) { #if CPP_TESTS_ENABLED std::shared_ptr xblc = std::make_shared(Data()->xboxLiveContext); s_resyncHandlerContextCpp = xblc->real_time_activity_service()->add_resync_handler( []() { LogToFile("RealTimeActivityServiceResyncHandler called"); }); LogToFile("RealTimeActivityServiceAddResyncHandler"); #else LogToFile("RealTimeActivityServiceAddResyncHandler is disabled for this platform"); #endif return LuaReturnHR(L, S_OK); } int RealTimeActivityServiceRemoveResyncHandler_Lua(lua_State *L) { #if CPP_TESTS_ENABLED if (s_resyncHandlerContextCpp) { std::shared_ptr xblc = std::make_shared(Data()->xboxLiveContext); xblc->real_time_activity_service()->remove_resync_handler(s_resyncHandlerContextCpp); s_resyncHandlerContextCpp = nullptr; } LogToFile("RealTimeActivityServiceRemoveResyncHandler"); #else LogToFile("RealTimeActivityServiceRemoveResyncHandler is disabled for this platform."); #endif return LuaReturnHR(L, S_OK); } void SetupAPIs_CppRta() { lua_register(Data()->L, "RealTimeActivityServiceActivate", RealTimeActivityServiceActivate_Lua); lua_register(Data()->L, "RealTimeActivityServiceDeactivate", RealTimeActivityServiceDeactivate_Lua); lua_register(Data()->L, "RealTimeActivityServiceAddConnectionStateChangeHandler", RealTimeActivityServiceAddConnectionStateChangeHandler_Lua); lua_register(Data()->L, "RealTimeActivityServiceRemoveConnectionStateChangeHandler", RealTimeActivityServiceRemoveConnectionStateChangeHandler_Lua); lua_register(Data()->L, "RealTimeActivityServiceAddSubscriptionErrorHandler", RealTimeActivityServiceAddSubscriptionErrorHandler_Lua); lua_register(Data()->L, "RealTimeActivityServiceRemoveSubscriptionErrorHandler", RealTimeActivityServiceRemoveSubscriptionErrorHandler_Lua); lua_register(Data()->L, "RealTimeActivityServiceAddResyncHandler", RealTimeActivityServiceAddResyncHandler_Lua); lua_register(Data()->L, "RealTimeActivityServiceRemoveResyncHandler", RealTimeActivityServiceRemoveResyncHandler_Lua); } ================================================ FILE: Tests/ApiExplorer/APIs/apis_cpp_social.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" XBL_WARNING_DISABLE_DEPRECATED #if CPP_TESTS_ENABLED xbox::services::social::xbox_social_relationship_filter ConvertStringToCppSocialRelationshipFilter(const char* str) { xbox::services::social::xbox_social_relationship_filter filter = xbox::services::social::xbox_social_relationship_filter::all; if (pal::stricmp(str, "xbox_social_relationship_filter::all") == 0) filter = xbox::services::social::xbox_social_relationship_filter::all; else if (pal::stricmp(str, "xbox_social_relationship_filter::Favorite") == 0) filter = xbox::services::social::xbox_social_relationship_filter::favorite; else if (pal::stricmp(str, "xbox_social_relationship_filter::LegacyXboxLiveFriends") == 0) filter = xbox::services::social::xbox_social_relationship_filter::legacy_xbox_live_friends; return filter; } #endif int SocialServiceGetSocialRelationships_Lua(lua_State *L) { #if CPP_TESTS_ENABLED xbox::services::social::xbox_social_relationship_filter socialRelationshipFilter = ConvertStringToCppSocialRelationshipFilter(GetStringFromLua(L, 1, "xbox_social_relationship_filter::all").c_str()); uint32_t startIndex = 0; uint32_t maxItems = 0; std::shared_ptr xblc = std::make_shared(Data()->xboxLiveContext); auto task = xblc->social_service().get_social_relationships( socialRelationshipFilter, startIndex, maxItems ).then([xblc](xbox::services::xbox_live_result result) { HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); LogToFile("SocialServiceGetSocialRelationships: hr=%s", ConvertHR(hr).c_str()); if (SUCCEEDED(hr)) { xbox::services::social::xbox_social_relationship_result socialRelationshipResult = result.payload(); Data()->socialRelationshipResult = socialRelationshipResult; auto relationships = socialRelationshipResult.items(); size_t relationshipsCount = relationships.size(); LogToFile("Got %u SocialRelationships:", relationshipsCount);; for (size_t i = 0; i < relationshipsCount; i++) { LogToFile("Xuid = %s, isFollowingCaller = %u", xbox::services::Utils::StringFromStringT(relationships[i].xbox_user_id()).c_str(), relationships[i].is_following_caller()); } } CallLuaFunctionWithHr(hr, "OnSocialServiceGetSocialRelationships"); }); #else CallLuaFunctionWithHr(S_OK, "OnSocialServiceGetSocialRelationships"); LogToFile("SocialServiceGetSocialRelationships is disabled for this platform."); #endif return LuaReturnHR(L, S_OK); } int SocialRelationshipResultHasNextCpp_Lua(lua_State *L) { bool hasNext{ false }; #if CPP_TESTS_ENABLED hasNext = Data()->socialRelationshipResult.has_next(); LogToFile("SocialRelationshipResultHasNextCpp: hasNext=%s", hasNext ? "true" : "false"); #else LogToFile("SocialRelationshipResultHasNextCpp is disabled for this platform."); #endif lua_pushnumber(L, (int)hasNext); return LuaReturnHR(L, S_OK, 1); } int SocialRelationshipResultGetNextCpp_Lua(lua_State *L) { #if CPP_TESTS_ENABLED uint32_t maxItems = 100; LogToFile("SocialRelationshipResultGetNextCpp MaxItems: %d", maxItems); Data()->socialRelationshipResult.get_next(maxItems).then( [](xbox::services::xbox_live_result< xbox::services::social::xbox_social_relationship_result > result) { HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); if (SUCCEEDED(hr)) { xbox::services::social::xbox_social_relationship_result socialRelationshipResult = result.payload(); Data()->socialRelationshipResult = socialRelationshipResult; auto relationships = socialRelationshipResult.items(); size_t relationshipsCount = relationships.size(); LogToFile("Got %u SocialRelationships:", relationshipsCount);; for (size_t i = 0; i < relationshipsCount; i++) { LogToFile("Xuid = %s, isFollowingCaller = %u", xbox::services::Utils::StringFromStringT(relationships[i].xbox_user_id()).c_str(), relationships[i].is_following_caller()); } } CallLuaFunctionWithHr(hr, "OnSocialRelationshipsResultGetNextCpp"); }); #else LogToFile("SocialRelationshipResultGetNextCpp is disabled for this platform."); CallLuaFunctionWithHr(S_OK, "OnSocialRelationshipsResultGetNextCpp"); #endif return LuaReturnHR(L, S_OK); } int SocialRelationshipResultCloseHandleCpp_Lua(lua_State* L) { //The relationship result needs to be cleaned up before the lua script ends and global state is cleaned up, //otherwise it will be the last reference to the xblContext, and cause the xblContext and xalUser to cleanup //without global state. #if CPP_TESTS_ENABLED //Force the relationship result to release it's handles early. Realistically this should never be called in a real use case scenario. Data()->socialRelationshipResult.~xbox_social_relationship_result(); #else #endif return LuaReturnHR(L, S_OK); } int SocialServiceSubscribeToSocialRelationshipChange_Lua(lua_State *L) { #if CPP_TESTS_ENABLED string_t xboxUserID = xbox::services::Utils::StringTFromUint64(Data()->xboxUserId); std::shared_ptr xblc = std::make_shared(Data()->xboxLiveContext); auto result = xblc->social_service().subscribe_to_social_relationship_change(xboxUserID); HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); if (SUCCEEDED(hr)) { Data()->socialRelationshipChangeSubscription = result.payload(); } LogToFile("SocialServiceSubscribeToSocialRelationshipChange: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); #else LogToFile("SocialServiceSubscribeToSocialRelationshipChange is disabled on this platform."); return LuaReturnHR(L, S_OK); #endif } int SocialServiceUnsubscribeFromSocialRelationshipChange_Lua(lua_State *L) { #if CPP_TESTS_ENABLED std::shared_ptr xblc = std::make_shared(Data()->xboxLiveContext); auto result = xblc->social_service().unsubscribe_from_social_relationship_change(Data()->socialRelationshipChangeSubscription); HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); if (SUCCEEDED(hr)) { Data()->socialRelationshipChangeSubscription = nullptr; } LogToFile("SocialServiceUnsubscribeFromSocialRelationshipChange: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); #else LogToFile("SocialServiceUnsubscribeFromSocialRelationshipChange is disabled on this platform."); return LuaReturnHR(L, S_OK); #endif } int SocialServiceAddSocialRelationshipChangedHandler_Lua(lua_State *L) { #if CPP_TESTS_ENABLED std::shared_ptr xblc = std::make_shared(Data()->xboxLiveContext); Data()->socialRelationshipChangedHandlerContext = xblc->social_service().add_social_relationship_changed_handler( [](xbox::services::social::social_relationship_change_event_args args) { LogToFile("Social relationship changed:"); std::stringstream ss; for (size_t i = 0; i < args.xbox_user_ids().size(); ++i) { if (i > 0) { ss << ", "; } ss << xbox::services::Utils::StringFromStringT(args.xbox_user_ids()[i]); } LogToFile("socialNotification = %u, affectedXuids = %s", args.social_notification(), ss.str().data()); }); LogToFile("SocialServiceAddSocialRelationshipChangedHandler"); #else LogToFile("SocialServiceAddSocialRelationshipChangedHandler is disabled on this platform."); #endif return LuaReturnHR(L, S_OK); } int SocialServiceRemoveSocialRelationshipChangedHandler_Lua(lua_State *L) { #if CPP_TESTS_ENABLED std::shared_ptr xblc = std::make_shared(Data()->xboxLiveContext); xblc->social_service().remove_social_relationship_changed_handler(Data()->socialRelationshipChangedHandlerContext); Data()->socialRelationshipChangedHandlerContext = nullptr; LogToFile("SocialServiceRemoveSocialRelationshipChangedHandler"); #else LogToFile("SocialServiceRemoveSocialRelationshipChangedHandler is disabled on this platform"); #endif return LuaReturnHR(L, S_OK); } int ReputationServiceSubmitReputationFeedback_Lua(lua_State* L) { #if CPP_TESTS_ENABLED xbox::services::social::reputation_feedback_type reputationFeedbackType = xbox::services::social::reputation_feedback_type::positive_helpful_player; string_t xuid = _T("2814639011617876"); string_t sessionName = _T(""); string_t reasonMessage = _T("Helpful player"); string_t evidenceResourceId = _T(""); std::shared_ptr xblc = std::make_shared(Data()->xboxLiveContext); xblc->reputation_service().submit_reputation_feedback( xuid, reputationFeedbackType, sessionName, reasonMessage, evidenceResourceId ).then([](xbox::services::xbox_live_result result) { HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); LogToFile("ReputationServiceSubmitReputationFeedback: hr=%s", ConvertHR(hr).c_str()); CallLuaFunctionWithHr(hr, "OnReputationServiceSubmitReputationFeedback"); }); #else LogToFile("ReputationServiceSubmitReputationFeedback is disabled for this platform."); CallLuaFunctionWithHr(S_OK, "OnReputationServiceSubmitReputationFeedback"); #endif return LuaReturnHR(L, S_OK); } int ReputationServiceSubmitBatchReputationFeedback_Lua(lua_State *L) { #if CPP_TESTS_ENABLED std::vector feedbackItems; feedbackItems.push_back(xbox::services::social::reputation_feedback_item { _T("2814639011617876"), xbox::services::social::reputation_feedback_type::positive_helpful_player, xbox::services::multiplayer::multiplayer_session_reference(), _T("Helpful player"), _T("") }); // Add any additional feedback items here std::shared_ptr xblc = std::make_shared(Data()->xboxLiveContext); xblc->reputation_service().submit_batch_reputation_feedback(feedbackItems).then( [](xbox::services::xbox_live_result result) { HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); LogToFile("ReputationServiceSubmitBatchReputationFeedback: hr=%s", ConvertHR(hr).c_str()); CallLuaFunctionWithHr(hr, "OnReputationServiceSubmitBatchReputationFeedback"); }); #else LogToFile("ReputationServiceSubmitBatchReputationFeedback is disabled for this platform."); CallLuaFunctionWithHr(S_OK, "OnReputationServiceSubmitBatchReputationFeedback"); #endif return LuaReturnHR(L, S_OK); } void SetupAPIs_CppSocial() { lua_register(Data()->L, "SocialServiceGetSocialRelationships", SocialServiceGetSocialRelationships_Lua); lua_register(Data()->L, "SocialRelationshipResultHasNextCpp", SocialRelationshipResultHasNextCpp_Lua); lua_register(Data()->L, "SocialRelationshipResultGetNextCpp", SocialRelationshipResultGetNextCpp_Lua); lua_register(Data()->L, "SocialRelationshipResultCloseHandleCpp", SocialRelationshipResultCloseHandleCpp_Lua); lua_register(Data()->L, "SocialServiceSubscribeToSocialRelationshipChange", SocialServiceSubscribeToSocialRelationshipChange_Lua); lua_register(Data()->L, "SocialServiceUnsubscribeFromSocialRelationshipChange", SocialServiceUnsubscribeFromSocialRelationshipChange_Lua); lua_register(Data()->L, "SocialServiceAddSocialRelationshipChangedHandler", SocialServiceAddSocialRelationshipChangedHandler_Lua); lua_register(Data()->L, "SocialServiceRemoveSocialRelationshipChangedHandler", SocialServiceRemoveSocialRelationshipChangedHandler_Lua); lua_register(Data()->L, "ReputationServiceSubmitReputationFeedback", ReputationServiceSubmitReputationFeedback_Lua); lua_register(Data()->L, "ReputationServiceSubmitBatchReputationFeedback", ReputationServiceSubmitBatchReputationFeedback_Lua); } ================================================ FILE: Tests/ApiExplorer/APIs/apis_cpp_social_manager.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include #if CPP_TESTS_ENABLED static struct SocialManagerCppState { SocialManagerCppState() = default; ~SocialManagerCppState() { // Validate that our tests cleaned up correctly assert(!doWork); assert(groups.empty()); } std::map> groups; std::thread doWorkThread{}; std::atomic doWork{ false }; std::atomic doWorkJoinWhenDone{ false }; } socialManagerCppState; HRESULT SocialManagerDoWorkCpp() { std::shared_ptr socialManager = xbox::services::social::manager::social_manager::get_singleton_instance(); std::vector events = socialManager->do_work(); for (auto& socialEvent : events) { static std::unordered_map eventTypesMap { { xbox::services::social::manager::social_event_type::users_added_to_social_graph, "users_added_to_social_graph" }, { xbox::services::social::manager::social_event_type::users_removed_from_social_graph, "users_removed_from_social_graph" }, { xbox::services::social::manager::social_event_type::presence_changed, "presence_changed" }, { xbox::services::social::manager::social_event_type::profiles_changed, "profiles_changed" }, { xbox::services::social::manager::social_event_type::social_relationships_changed, "social_relationships_changed" }, { xbox::services::social::manager::social_event_type::local_user_added, "local_user_added" }, { xbox::services::social::manager::social_event_type::social_user_group_loaded, "social_user_group_loaded" }, { xbox::services::social::manager::social_event_type::social_user_group_updated, "social_user_group_updated" }, { xbox::services::social::manager::social_event_type::unknown, "unknown" } }; std::stringstream ss; ss << "social_manager::do_work: Event of type " << eventTypesMap[socialEvent.event_type()] << std::endl; ss << "Users affected: " << std::endl; for (auto userContainer : socialEvent.users_affected()) { char xuid[17]; xbox::services::Utils::Utf8FromCharT(userContainer.xbox_user_id(), xuid, sizeof(xuid)); ss << "\t" << xuid << std::endl; } LogToFile(ss.str().c_str()); switch (socialEvent.event_type()) { case xbox::services::social::manager::social_event_type::users_added_to_social_graph: LogToFile("social_manager::do_work: users_added_to_social_graph event"); CallLuaFunctionWithHr(S_OK, "OnSocialManagerDoWorkCpp_UsersAddedToSocialGraphEvent"); break; case xbox::services::social::manager::social_event_type::users_removed_from_social_graph: LogToFile("social_manager::do_work: users_removed_from_social_graph event"); CallLuaFunctionWithHr(S_OK, "OnSocialManagerDoWorkCpp_UsersRemovedFromSocialGraphEvent"); break; case xbox::services::social::manager::social_event_type::presence_changed: LogToFile("social_manager::do_work: presence_changed event"); CallLuaFunctionWithHr(S_OK, "OnSocialManagerDoWorkCpp_PresenceChangedEvent"); break; case xbox::services::social::manager::social_event_type::profiles_changed: LogToFile("social_manager::do_work: profiles_changed event"); CallLuaFunctionWithHr(S_OK, "OnSocialManagerDoWorkCpp_ProfilesChangedEvent"); break; case xbox::services::social::manager::social_event_type::social_relationships_changed: LogToFile("social_manager::do_work: social_relationships_changed event"); CallLuaFunctionWithHr(S_OK, "OnSocialManagerDoWorkCpp_SocialRelationshipsChangedEvent"); break; case xbox::services::social::manager::social_event_type::local_user_added: LogToFile("social_manager::do_work: local_user_added event"); CallLuaFunctionWithHr(S_OK, "OnSocialManagerDoWorkCpp_LocalUserAddedEvent"); break; case xbox::services::social::manager::social_event_type::local_user_removed: LogToFile("social_manager::do_work: local_user_removed event"); CallLuaFunctionWithHr(S_OK, "OnSocialManagerDoWorkCpp_LocalUserRemovedEvent"); break; case xbox::services::social::manager::social_event_type::social_user_group_loaded: LogToFile("social_manager::do_work: social_user_group_loaded event"); CallLuaFunctionWithHr(S_OK, "OnSocialManagerDoWorkCpp_SocialUserGroupLoadedEvent"); break; case xbox::services::social::manager::social_event_type::social_user_group_updated: LogToFile("social_manager::do_work: social_user_group_updated event"); CallLuaFunctionWithHr(S_OK, "OnSocialManagerDoWorkCpp_SocialUserGroupUpdatedEvent"); break; case xbox::services::social::manager::social_event_type::unknown: default: LogToFile("social_manager::do_work: unknown event"); CallLuaFunctionWithHr(S_OK, "OnSocialManagerDoWorkCpp_UnknownEvent"); break; } } return S_OK; } void StopSocialManagerDoWorkHelperCpp() { if (socialManagerCppState.doWorkJoinWhenDone) { socialManagerCppState.doWork = false; socialManagerCppState.doWorkJoinWhenDone = false; socialManagerCppState.doWorkThread.join(); } } xbox::services::social::manager::social_manager_extra_detail_level ConvertStringToCppSocialManagerExtraDetailLevel(const char* str) { xbox::services::social::manager::social_manager_extra_detail_level detailLevel = xbox::services::social::manager::social_manager_extra_detail_level::no_extra_detail; if (pal::stricmp(str, "social_manager_extra_detail_level::title_history_leve_T(") == 0) detailLevel = xbox::services::social::manager::social_manager_extra_detail_level::title_history_level; else if (pal::stricmp(str, "social_manager_extra_detail_level::preferred_color_leve_T(") == 0) detailLevel = xbox::services::social::manager::social_manager_extra_detail_level::preferred_color_level; return detailLevel; } xbox::services::social::manager::presence_filter ConvertStringToCppPresenceFilter(const char* str) { xbox::services::social::manager::presence_filter filter = xbox::services::social::manager::presence_filter::unknown; if (pal::stricmp(str, "presence_filter::title_online") == 0) filter = xbox::services::social::manager::presence_filter::title_online; else if (pal::stricmp(str, "presence_filter::title_offline") == 0) filter = xbox::services::social::manager::presence_filter::title_offline; #if XSAPI_BUILT_FROM_SOURCE else if (pal::stricmp(str, "presence_filter::title_online_outside_title") == 0) filter = xbox::services::social::manager::presence_filter::title_online_outside_title; #endif else if (pal::stricmp(str, "presence_filter::all_online") == 0) filter = xbox::services::social::manager::presence_filter::all_online; else if (pal::stricmp(str, "presence_filter::all_offline") == 0) filter = xbox::services::social::manager::presence_filter::all_offline; else if (pal::stricmp(str, "presence_filter::all_title") == 0) filter = xbox::services::social::manager::presence_filter::all_title; else if (pal::stricmp(str, "presence_filter::al_T(") == 0) filter = xbox::services::social::manager::presence_filter::all; return filter; } xbox::services::social::manager::relationship_filter ConvertStringToCppRelationshipFilter(const char* str) { xbox::services::social::manager::relationship_filter filter = xbox::services::social::manager::relationship_filter::friends; if (pal::stricmp(str, "relationship_filter::friends") == 0) filter = xbox::services::social::manager::relationship_filter::friends; else if (pal::stricmp(str, "relationship_filter::favorite") == 0) filter = xbox::services::social::manager::relationship_filter::favorite; return filter; } // Pool of XDKS.1 xuids to create social groups from std::vector listXuidStrings { _T("2814639011617876"),_T("2814641789541994"),_T("2814644008675844"),_T("2814644210052185"),_T("2814645164579523"),_T("2814646075485729"),_T("2814649783195402"),_T("2814650260879943"), _T("2814652370182940"),_T("2814652714045777"),_T("2814654391560620"),_T("2814654975417728"),_T("2814656000993855"),_T("2814660006763195"),_T("2814666715930430"),_T("2814667316080600"), _T("2814669550092398"),_T("2814669684179632"),_T("2814669733667211"),_T("2814671180786692"),_T("2814679901432274"),_T("2814613501048225"),_T("2814614352529204"),_T("2814615856126401"), _T("2814616641363830"),_T("2814617883586813"),_T("2814618053453081"),_T("2814629752527080"),_T("2814631255161151"),_T("2814632477267887"),_T("2814633284389038"),_T("2814635732495522"), _T("2814635779785472"),_T("2814635974475208"),_T("2814636979708499"),_T("2814618092438397"),_T("2814618260480530"),_T("2814618319551907"),_T("2814619559360314"),_T("2814620368929739"), _T("2814620769042115"),_T("2814621007349381"),_T("2814623088399025"),_T("2814623825448960"),_T("2814624220291971"),_T("2814624961587858"),_T("2814626394212372"),_T("2814626639518570"), _T("2814628203722867"),_T("2814629143923154"),_T("2814614382301082"),_T("2814614959737919"),_T("2814615558140392"),_T("2814618401629514"),_T("2814618701087902"),_T("2814619300882392"), _T("2814623785189962"),_T("2814623956387698"),_T("2814625066090704"),_T("2814625471782204"),_T("2814626946705530"),_T("2814627006318591"),_T("2814628046127456"),_T("2814631487749991"), _T("2814631517599783"),_T("2814632798310691"),_T("2814633582140204"),_T("2814634204785789"),_T("2814634895412664"),_T("2814635439049207"),_T("2814638609354868"),_T("2814639589885754"), _T("2814641670947751"),_T("2814643512602566"),_T("2814646137630843"),_T("2814648499394446"),_T("2814651465227139"),_T("2814652150012664"),_T("2814653926747608"),_T("2814655098938516"), _T("2814655264861214"),_T("2814655417678099"),_T("2814655883565306"),_T("2814656031821923"),_T("2814656159501072"),_T("2814656780954834"),_T("2814660657970845"),_T("2814661604435490"), _T("2814663444319727"),_T("2814663818015575"),_T("2814665274839967"),_T("2814667273133504"),_T("2814670761542037"),_T("2814672762886609"),_T("2814673772488023"),_T("2814674096344056"), _T("2814674229538758"),_T("2814678943953289"),_T("2814680898042782") }; #endif //lua commands int StartSocialManagerDoWorkLoopCpp_Lua(lua_State* L) { #if CPP_TESTS_ENABLED socialManagerCppState.doWork = true; socialManagerCppState.doWorkJoinWhenDone = true; std::lock_guard lock(Data()->m_luaLock); socialManagerCppState.doWorkThread = std::thread([]() { Data()->m_socialDoWorkDone = false; while (socialManagerCppState.doWork && !Data()->m_quit) { { std::lock_guard lock(Data()->m_luaLock); SocialManagerDoWorkCpp(); } pal::Sleep(10); } Data()->m_socialDoWorkDone = true; LogToFile("Exiting do work thread"); }); #else LogToFile("StartSocialManagerDoWorkLoopCpp is disabled for this platform."); //Call a 'disabled' function so tests that are waiting on do work events can still terminate CallLuaFunctionWithHr(S_OK, "OnStartSocialManagerDoWorkLoopCppDisabled"); #endif return LuaReturnHR(L, S_OK); } int StopSocialManagerDoWorkLoopCpp_Lua(lua_State* L) { #if CPP_TESTS_ENABLED LogToFile("StopSocialManagerDoWorkLoopCpp"); socialManagerCppState.doWorkJoinWhenDone = true; socialManagerCppState.doWork = false; #else LogToFile("StopSocialManagerDoWorkLoopCpp is disabled on this platform."); #endif return LuaReturnHR(L, S_OK); } int SocialManagerPresenceRecordIsUserPlayingTitleCpp_Lua(lua_State *L) { #if CPP_TESTS_ENABLED using xbox::services::social::manager::xbox_social_user_group; using xbox::services::social::manager::xbox_social_user; std::shared_ptr group{ nullptr }; uint64_t luaGroup = GetUint64FromLua(L, 1, 0); if (luaGroup) { group = (*socialManagerCppState.groups.find(luaGroup)).second; } else { group = (*socialManagerCppState.groups.begin()).second; } if (group == nullptr) { LogToFile("SocialManagerPresenceRecordIsUserPlayingTitleCpp: No xbox_social_user_group Loaded"); return S_OK; } uint32_t titleId = GetUint32FromLua(L, 1, 174925616); std::vector userVec = group->users(); for (auto user : userVec) { xbox::services::social::manager::social_manager_presence_record presenceRecord = user->presence_record(); bool playingTitle = presenceRecord.is_user_playing_title(titleId); LogToFile("SocialManagerPresenceRecordIsUserPlayingTitleCpp: TitleId: %d, playing: %u", titleId, playingTitle); } #else LogToFile("SocialManagerPresenceRecordIsUserPlayingTitleCpp is disabled on this platform."); #endif return LuaReturnHR(L, S_OK); } int SocialManagerUserGroupGetTypeCpp_Lua(lua_State* L) { #if CPP_TESTS_ENABLED std::shared_ptr group{ nullptr }; uint64_t luaGroup = GetUint64FromLua(L, 1, 0); if (luaGroup) { group = (*socialManagerCppState.groups.find(luaGroup)).second; } else { group = (*socialManagerCppState.groups.begin()).second; } if (group == nullptr) { LogToFile("SocialManagerUserGroupGetTypeCpp: No xbox_social_user_group Loaded"); return S_OK; } xbox::services::social::manager::social_user_group_type type = group->social_user_group_type(); LogToFile("SocialManagerUserGroupGetTypeCpp: type=%u", type); #else LogToFile("SocialManagerUserGroupGetTypeCpp is disabled on this platform."); #endif return LuaReturnHR(L, S_OK); } int SocialManagerUserGroupGetLocalUserCpp_Lua(lua_State* L) { #if CPP_TESTS_ENABLED std::shared_ptr group{ nullptr }; uint64_t luaGroup = GetUint64FromLua(L, 1, 0); if (luaGroup) { group = (*socialManagerCppState.groups.find(luaGroup)).second; } else { group = (*socialManagerCppState.groups.begin()).second; } if (group == nullptr) { LogToFile("SocialManagerUserGroupGetLocalUserCpp: No xbox_social_user_group Loaded"); return S_OK; } xbox_live_user_t user = group->local_user(); LogToFile("SocialManagerUserGroupGetLocalUserCpp: user=%llu", user); #else LogToFile("SocialManagerUserGroupGetLocalUserCpp is disabled on this platform."); #endif return LuaReturnHR(L, S_OK); } int SocialManagerUserGroupGetFiltersCpp_Lua(lua_State* L) { #if CPP_TESTS_ENABLED std::shared_ptr group{ nullptr }; uint64_t luaGroup = GetUint64FromLua(L, 1, 0); if (luaGroup) { group = (*socialManagerCppState.groups.find(luaGroup)).second; } else { group = (*socialManagerCppState.groups.begin()).second; } if (group == nullptr) { LogToFile("SocialManagerUserGroupGetFiltersCpp: No xbox_social_user_group Loaded"); return S_OK; } xbox::services::social::manager::presence_filter presenceFilter = group->presence_filter_of_group(); xbox::services::social::manager::relationship_filter relationshipFilter = group->relationship_filter_of_group(); LogToFile("SocialManagerUserGroupGetFiltersCpp: presenceFilter=%u, relationshipFilter=%u", presenceFilter, relationshipFilter); #else LogToFile("SocialManagerUserGroupGetFiltersCpp is disabled for this platform."); #endif return LuaReturnHR(L, S_OK); } int SocialManagerUserGroupGetUsersCpp_Lua(lua_State *L) { #if CPP_TESTS_ENABLED std::shared_ptr group{ nullptr }; uint64_t luaGroup = GetUint64FromLua(L, 1, 0); if (luaGroup) { group = (*socialManagerCppState.groups.find(luaGroup)).second; } else { group = (*socialManagerCppState.groups.begin()).second; } if (group == nullptr) { LogToFile("SocialManagerUserGroupGetUsersCpp: No xbox_social_user_group Loaded"); return S_OK; } LogToFile("SocialManagerUserGroupGetUsersCpp: usersCount: %d", group->users().size()); for (auto user : group->users()) { char buffer[17]; xbox::services::Utils::Utf8FromCharT(user->gamertag(), buffer, sizeof(buffer)); LogToFile("\t%s", buffer); } #else LogToFile("SocialManagerUserGroupGetUsersCpp is not supported on this platform."); #endif return LuaReturnHR(L, S_OK); } int SocialManagerUserGroupGetUsersTrackedByGroupCpp_Lua(lua_State *L) { #if CPP_TESTS_ENABLED std::shared_ptr group{ nullptr }; uint64_t luaGroup = GetUint64FromLua(L, 1, 0); if (luaGroup) { group = (*socialManagerCppState.groups.find(luaGroup)).second; } else { group = (*socialManagerCppState.groups.begin()).second; } if (group == nullptr) { LogToFile("SocialManagerUserGroupGetUsersTrackedByGroupCpp: No xbox_social_user_group Loaded"); return S_OK; } LogToFile("SocialManagerUserGroupGetUsersTrackedByGroupCpp trackedUsersCount: %d", group->users_tracked_by_social_user_group().size()); for (auto user : group->users_tracked_by_social_user_group()) { LogToFile("\t%s", user.xbox_user_id()); } #else LogToFile("SocialManagerUserGroupGetUsersTrackByGroupCpp is disabled on this platform."); #endif return LuaReturnHR(L, S_OK); } int SocialManagerAddLocalUserCpp_Lua(lua_State *L) { #if CPP_TESTS_ENABLED xbox::services::social::manager::social_manager_extra_detail_level extraDetailLevel = ConvertStringToCppSocialManagerExtraDetailLevel( GetStringFromLua(L, 1, "social_manager_extra_detail_level::no_extra_detai_T(").c_str()); LogToFile("SocialManagerAddLocalUserCpp: social_manager_extra_detail_level: %d", extraDetailLevel); xbox_live_user_t user = Data()->xalUser; std::shared_ptr socialManager = xbox::services::social::manager::social_manager::get_singleton_instance(); socialManager->add_local_user(user, extraDetailLevel); LogToFile("SocialManagerAddLocalUserCpp"); #else LogToFile("SocialManagerAddLocalUserCpp is disabled for this platform."); #endif return LuaReturnHR(L, S_OK); } int SocialManagerRemoveLocalUserCpp_Lua(lua_State *L) { #if CPP_TESTS_ENABLED std::shared_ptr socialManager = xbox::services::social::manager::social_manager::get_singleton_instance(); xbox_live_user_t user = Data()->xalUser; socialManager->remove_local_user(user); LogToFile("SocialManagerRemoveLocalUserCpp"); #else LogToFile("SocialManagerRemoveLocalUserCpp is disabled for this platform."); #endif return LuaReturnHR(L, S_OK); } int SocialManagerCreateSocialUserGroupFromFiltersCpp_Lua(lua_State *L) { #if CPP_TESTS_ENABLED xbox_live_user_t user = Data()->xalUser; xbox::services::social::manager::presence_filter presenceFilter = ConvertStringToCppPresenceFilter(GetStringFromLua(L, 1, "presence_filter::al_T(").c_str()); xbox::services::social::manager::relationship_filter relationshipFilter = ConvertStringToCppRelationshipFilter(GetStringFromLua(L, 2, "relationship_filter::friends").c_str()); LogToFile("SocialManagerCreateSocialUserGroupFromFiltersCpp: presence_filter: %d", presenceFilter); LogToFile("SocialManagerCreateSocialUserGroupFromFiltersCpp: relationship_filter: %d", relationshipFilter); std::shared_ptr socialManager = xbox::services::social::manager::social_manager::get_singleton_instance(); xbox::services::xbox_live_result> result = socialManager->create_social_user_group_from_filters(user, presenceFilter, relationshipFilter); std::shared_ptr group{ nullptr }; HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); if (SUCCEEDED(hr)) { group = result.payload(); socialManagerCppState.groups.insert(std::pair>(reinterpret_cast(group.get()), group)); } lua_pushinteger(L, reinterpret_cast(group.get())); LogToFile("SocialManagerCreateSocialUserGroupFromFiltersCpp: %s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr, 1); #else LogToFile("SocialManagerCreateSocialUserGroupFromFiltersCpp is disabled for this platform."); return LuaReturnHR(L, S_OK); #endif } int SocialManagerDestroySocialUserGroupCpp_Lua(lua_State* L) { #if CPP_TESTS_ENABLED std::shared_ptr group{ nullptr }; uint64_t luaGroup = GetUint64FromLua(L, 1, 0); if (luaGroup) { group = (*socialManagerCppState.groups.find(luaGroup)).second; } else { group = (*socialManagerCppState.groups.begin()).second; } if (group == nullptr) { LogToFile("SocialManagerDestroySocialUserGroupCpp: No xbox_social_user_group Loaded"); return S_OK; } std::shared_ptr socialManager = xbox::services::social::manager::social_manager::get_singleton_instance(); xbox::services::xbox_live_result result = socialManager->destroy_social_user_group(group); HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); if (SUCCEEDED(hr)) { socialManagerCppState.groups.erase(reinterpret_cast(group.get())); } LogToFile("SocialManagerDestroySocialUserGroupCpp: %s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); #else LogToFile("SocialManagerDestroySocialUserGroupCpp is disabled for this platform."); return LuaReturnHR(L, S_OK); #endif } int SocialManagerCreateSocialUserGroupFromListCpp_Lua(lua_State *L) { #if CPP_TESTS_ENABLED // Params: // 1) number of xuids to include in list // 2) offset in the above vector of first xuid uint64_t count{ GetUint64FromLua(L, 1, 10) }; uint64_t offset{ GetUint64FromLua(L, 2, 0) }; assert(offset + count <= listXuidStrings.size()); xbox_live_user_t user = Data()->xalUser; std::vector xuids { listXuidStrings.begin() + static_cast(offset), listXuidStrings.begin() + static_cast(offset + count) }; std::shared_ptr socialManager = xbox::services::social::manager::social_manager::get_singleton_instance(); xbox::services::xbox_live_result> result = socialManager->create_social_user_group_from_list(user, xuids); std::shared_ptr group; HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); if (SUCCEEDED(hr)) { group = result.payload(); socialManagerCppState.groups.insert(std::pair>(reinterpret_cast(group.get()), group)); } lua_pushinteger(L, reinterpret_cast(group.get())); LogToFile("SocialManagerCreateSocialUserGroupFromListCpp: %s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr, 1); #else LogToFile("SocialManagerCreateSocialUserGroupFromListCpp is disabled on this platform."); return LuaReturnHR(L, S_OK); #endif } int SocialManagerGetLocalUsersCpp_Lua(lua_State *L) { #if CPP_TESTS_ENABLED std::shared_ptr socialManager = xbox::services::social::manager::social_manager::get_singleton_instance(); LogToFile("XblSocialManagerGetLocalUsers localUsersCount: %d", socialManager->local_users().size()); HRESULT hr = S_OK; for (xbox_live_user_t user : socialManager->local_users()) { size_t gamertagSize = XalUserGetGamertagSize(user, XalGamertagComponent_Classic); std::vector gamertag(gamertagSize, '\0'); size_t bufferUsed; hr = XalUserGetGamertag(user, XalGamertagComponent_Classic, gamertagSize, gamertag.data(), &bufferUsed); if (SUCCEEDED(hr)) { LogToFile("\t%s", gamertag.data()); } } LogToFile("SocialManagerGetLocalUsersCpp: %s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); #else LogToFile("SocialManagerGetLocalUsersCpp is disabled for this platform."); return LuaReturnHR(L, S_OK); #endif } int SocialManagerUpdateSocialUserGroupCpp_Lua(lua_State *L) { #if CPP_TESTS_ENABLED // Params: // 1) group pointer // 1) number of xuids to include in list // 2) offset in the above vector of first xuid std::shared_ptr group{ nullptr }; uint64_t luaGroup = GetUint64FromLua(L, 1, 0); if (luaGroup) { group = (*socialManagerCppState.groups.find(luaGroup)).second; } else { group = (*socialManagerCppState.groups.begin()).second; } if (group == nullptr) { LogToFile("SocialManagerUpdateSocialUserGroupCpp: No xbox_social_user_group Loaded"); return S_OK; } auto count{ GetUint64FromLua(L, 2, 15) }; auto offset{ GetUint64FromLua(L, 3, 0) }; // CODE SNIPPET START: XblSocialManagerUpdateSocialUserGroup_C std::vector xuids { listXuidStrings.begin() + static_cast(offset), listXuidStrings.begin() + static_cast(offset + count) }; std::shared_ptr socialManager = xbox::services::social::manager::social_manager::get_singleton_instance(); xbox::services::xbox_live_result result = socialManager->update_social_user_group(group, xuids); HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); LogToFile("SocialManagerUpdateSocialUserGroupCpp: %s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); #else LogToFile("SocialManagerUpdateSocialUserGroupCpp is disabled for this platform."); return LuaReturnHR(L, S_OK); #endif } int SocialManagerSetRichPresencePollingStatusCpp_Lua(lua_State *L) { #if CPP_TESTS_ENABLED xbox_live_user_t user = Data()->xalUser; bool shouldEnablePolling = GetBoolFromLua(L, 1, false); LogToFile("SocialManagerSetRichPresencePollingStatusCpp: ShouldEnablePolling: %s", shouldEnablePolling ? "true" : "false"); std::shared_ptr socialManager = xbox::services::social::manager::social_manager::get_singleton_instance(); xbox::services::xbox_live_result result = socialManager->set_rich_presence_polling_status(user, shouldEnablePolling); HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); LogToFile("SocialManagerSetRichPresencePollingStatusCpp: %s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); #else LogToFile("SocialManagerSetRichPresencePollingStatusCpp is disabled for this platform."); return LuaReturnHR(L, S_OK); #endif } int SocialManagerDoWorkCpp_Lua(lua_State *L) { #if CPP_TESTS_ENABLED HRESULT hr = SocialManagerDoWorkCpp(); LogToFile("SocialManagerDoWorkCpp: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); #else LogToFile("SocialManagerDoWorkCpp is disabled on this platform."); return LuaReturnHR(L, S_OK); #endif } void SetupAPIs_CppSocialManager() { lua_register(Data()->L, "StartSocialManagerDoWorkLoopCpp", StartSocialManagerDoWorkLoopCpp_Lua); lua_register(Data()->L, "StopSocialManagerDoWorkLoopCpp", StopSocialManagerDoWorkLoopCpp_Lua); lua_register(Data()->L, "SocialManagerPresenceRecordIsUserPlayingTitleCpp", SocialManagerPresenceRecordIsUserPlayingTitleCpp_Lua); lua_register(Data()->L, "SocialManagerUserGroupGetTypeCpp", SocialManagerUserGroupGetTypeCpp_Lua); lua_register(Data()->L, "SocialManagerUserGroupGetLocalUserCpp", SocialManagerUserGroupGetLocalUserCpp_Lua); lua_register(Data()->L, "SocialManagerUserGroupGetFiltersCpp", SocialManagerUserGroupGetFiltersCpp_Lua); lua_register(Data()->L, "SocialManagerUserGroupGetUsersCpp", SocialManagerUserGroupGetUsersCpp_Lua); lua_register(Data()->L, "SocialManagerUserGroupGetUsersTrackedByGroupCpp", SocialManagerUserGroupGetUsersTrackedByGroupCpp_Lua); lua_register(Data()->L, "SocialManagerAddLocalUserCpp", SocialManagerAddLocalUserCpp_Lua); lua_register(Data()->L, "SocialManagerRemoveLocalUserCpp", SocialManagerRemoveLocalUserCpp_Lua); lua_register(Data()->L, "SocialManagerCreateSocialUserGroupFromFiltersCpp", SocialManagerCreateSocialUserGroupFromFiltersCpp_Lua); lua_register(Data()->L, "SocialManagerCreateSocialUserGroupFromListCpp", SocialManagerCreateSocialUserGroupFromListCpp_Lua); lua_register(Data()->L, "SocialManagerGetLocalUsersCpp", SocialManagerGetLocalUsersCpp_Lua); lua_register(Data()->L, "SocialManagerUpdateSocialUserGroupCpp", SocialManagerUpdateSocialUserGroupCpp_Lua); lua_register(Data()->L, "SocialManagerSetRichPresencePollingStatusCpp", SocialManagerSetRichPresencePollingStatusCpp_Lua); lua_register(Data()->L, "SocialManagerDoWorkCpp", SocialManagerDoWorkCpp_Lua); lua_register(Data()->L, "SocialManagerDestroySocialUserGroupCpp", SocialManagerDestroySocialUserGroupCpp_Lua); } ================================================ FILE: Tests/ApiExplorer/APIs/apis_cpp_statistics.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #pragma warning(disable:4996) int UserStatisticsServiceGetSingleUserStatistic_Lua(lua_State* L) { #if CPP_TESTS_ENABLED string_t xboxUserId = xbox::services::Utils::StringTFromUint64(Data()->xboxUserId); string_t serviceConfigurationId = xbox::services::Utils::StringTFromUtf8(Data()->scid); string_t statisticName = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 1, "TotalPuzzlesSolved").c_str()); LogToFile("UserStatisticsServiceGetSingleUserStatistic: statisticName: %s", xbox::services::Utils::StringFromStringT(statisticName).c_str()); std::shared_ptr xblc = std::make_shared(Data()->xboxLiveContext); xblc->user_statistics_service().get_single_user_statistics( xboxUserId, serviceConfigurationId, statisticName ).then( [](xbox::services::xbox_live_result result) { HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); LogToFile("UserStatisticsServiceGetSingleUserStatistic: hr=%s", ConvertHR(hr).data()); if (SUCCEEDED(hr)) { xbox::services::user_statistics::user_statistics_result userStatisticsResult{ result.payload() }; VERIFY_IS_TRUE(userStatisticsResult.service_configuration_statistics().size() == 1, "service_configuration_statistics size"); VERIFY_IS_TRUE(userStatisticsResult.service_configuration_statistics()[0].statistics().size() == 1, "statistics size"); if (userStatisticsResult.service_configuration_statistics().size() > 0 && userStatisticsResult.service_configuration_statistics()[0].statistics().size() > 0) { xbox::services::user_statistics::statistic stat = userStatisticsResult.service_configuration_statistics()[0].statistics()[0]; //Data()->lastUserStat = stat.value(); LogToScreen("%s's stat %s is %s. Note: With GDK, ensure fiddler isn't running for stat upload to work", Data()->gamertag.c_str(), xbox::services::Utils::StringFromStringT(stat.statistic_name()).c_str(), xbox::services::Utils::StringFromStringT(stat.value()).c_str() ); } } CallLuaFunctionWithHr(hr, "OnUserStatisticsServiceGetSingleUserStatistic"); }); #else LogToFile("UserStatisticsServiceGetSingleUserStatistic is disabled for this platform."); CallLuaFunctionWithHr(S_OK, "OnUserStatisticsServiceGetSingleUserStatistic"); #endif return LuaReturnHR(L, S_OK); } int UserStatisticsServiceGetSingleUserStatistics_Lua(lua_State *L) { #if CPP_TESTS_ENABLED string_t statisticName1 = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 1, "TotalPuzzlesSolved").c_str()); string_t statisticName2 = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 2, "TotalRoundsStarted").c_str()); LogToFile("UserStatisticsServiceGetSingleUserStatistics: statisticName1: %s", xbox::services::Utils::StringFromStringT(statisticName1).c_str()); LogToFile("UserStatisticsServiceGetSingleUserStatistics: statisticName2: %s", xbox::services::Utils::StringFromStringT(statisticName2).c_str()); string_t xboxUserId = xbox::services::Utils::StringTFromUint64(Data()->xboxUserId); string_t serviceConfigurationId = xbox::services::Utils::StringTFromUtf8(Data()->scid); std::vector statisticNames{ statisticName1, statisticName2 }; std::shared_ptr xblc = std::make_shared(Data()->xboxLiveContext); xblc->user_statistics_service().get_single_user_statistics( xboxUserId, serviceConfigurationId, statisticNames ).then( [](xbox::services::xbox_live_result result) { HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); LogToFile("UserStatisticsServiceGetSingleUserStatistics: hr=%s", ConvertHR(hr).data()); if (SUCCEEDED(hr)) { xbox::services::user_statistics::user_statistics_result userStatisticsResult{ result.payload() }; if (userStatisticsResult.service_configuration_statistics().size() > 0) { std::vector stats = userStatisticsResult.service_configuration_statistics()[0].statistics(); LogToFile("Got %u statistics for %s", stats.size(), Data()->gamertag.c_str()); for (auto stat : stats) { LogToScreen("stat %s is %s", xbox::services::Utils::StringFromStringT(stat.statistic_name()).c_str(), xbox::services::Utils::StringFromStringT(stat.value()).c_str() ); } } } CallLuaFunctionWithHr(hr, "OnUserStatisticsServiceGetSingleUserStatistics"); }); #else LogToFile("UserStatisticsServiceGetSingleUserStatistics is disabled for this platform."); CallLuaFunctionWithHr(S_OK, "OnUserStatisticsServiceGetSingleUserStatistics"); #endif return LuaReturnHR(L, S_OK); } int UserStatisticsServiceGetMultipleUserStatistics_Lua(lua_State *L) { #if CPP_TESTS_ENABLED string_t statisticName1 = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 1, "TotalPuzzlesSolved").c_str()); string_t statisticName2 = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 2, "TotalRoundsStarted").c_str()); LogToFile("UserStatisticsServiceGetMultipleUserStatistics: statisticName1: %s", xbox::services::Utils::StringFromStringT(statisticName1).c_str()); LogToFile("UserStatisticsServiceGetMultipleUserStatistics: statisticName2: %s", xbox::services::Utils::StringFromStringT(statisticName2).c_str()); string_t xuid1 = xbox::services::Utils::StringTFromUint64(GetUint64FromLua(L, 3, Data()->xboxUserId)); string_t xuid2 = xbox::services::Utils::StringTFromUint64(GetUint64FromLua(L, 4, 2814634367189975)); LogToFile("UserStatisticsServiceGetMultipleUserStatistics: xuid1: %ul", xbox::services::Utils::StringFromStringT(xuid1).c_str()); LogToFile("UserStatisticsServiceGetMultipleUserStatistics: xuid2: %ul", xbox::services::Utils::StringFromStringT(xuid2).c_str()); std::vector xboxUserIds{ xuid1, xuid2 }; string_t serviceConfigurationId = xbox::services::Utils::StringTFromUtf8(Data()->scid); std::vector statisticNames{ statisticName1, statisticName2 }; std::shared_ptr xblc = std::make_shared(Data()->xboxLiveContext); xblc->user_statistics_service().get_multiple_user_statistics( xboxUserIds, serviceConfigurationId, statisticNames ).then( [](xbox::services::xbox_live_result> result) { HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); LogToFile("UserStatisticsServiceGetMultipleUserStatistics: hr=%s", ConvertHR(hr).data()); if (SUCCEEDED(hr)) { std::vector userStatisticsResults = result.payload(); for (auto statResult : userStatisticsResults) { LogToFile("XUID: %d", xbox::services::Utils::StringFromStringT(statResult.xbox_user_id()).c_str()); for (auto scidStats : statResult.service_configuration_statistics()) { LogToFile("SCID: %s", xbox::services::Utils::StringFromStringT(scidStats.service_configuration_id()).c_str()); for (auto stat : scidStats.statistics()) { LogToFile("Stat name:%s value:%s type:%s", xbox::services::Utils::StringFromStringT(stat.statistic_name()).c_str(), xbox::services::Utils::StringFromStringT(stat.value()).c_str(), xbox::services::Utils::StringFromStringT(stat.statistic_type()).c_str() ); } } } } CallLuaFunctionWithHr(hr, "OnUserStatisticsServiceGetMultipleUserStatistics"); }); #else LogToFile("UserStatisticsServiceGetMultipleUserStatistics is disabled for this platform"); CallLuaFunctionWithHr(S_OK, "OnUserStatisticsServiceGetMultipleUserStatistics"); #endif return LuaReturnHR(L, S_OK); } int UserStatisticsServiceGetMultipleUserStatisticsForMultipleServiceConfigurations_Lua(lua_State *L) { #if CPP_TESTS_ENABLED string_t statisticName1 = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 1, "TotalPuzzlesSolved").c_str()); string_t statisticName2 = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 2, "TotalRoundsStarted").c_str()); LogToFile("UserStatisticsServiceGetMultipleUserStatisticsForMultipleServiceConfigurations: statisticName1: %s", xbox::services::Utils::StringFromStringT(statisticName1).c_str()); LogToFile("UserStatisticsServiceGetMultipleUserStatisticsForMultipleServiceConfigurations: statisticName2: %s", xbox::services::Utils::StringFromStringT(statisticName2).c_str()); std::vector statisticNames{ statisticName1, statisticName2 }; string_t serviceConfigurationId = xbox::services::Utils::StringTFromUtf8(Data()->scid); xbox::services::user_statistics::requested_statistics requestedStatistic1{ serviceConfigurationId, statisticNames }; string_t xuid1 = xbox::services::Utils::StringTFromUint64(GetUint64FromLua(L, 3, Data()->xboxUserId)); string_t xuid2 = xbox::services::Utils::StringTFromUint64(GetUint64FromLua(L, 4, 2814634367189975)); LogToFile("UserStatisticsServiceGetMultipleUserStatisticsForMultipleServiceConfigurations: xuid1: %ul", xbox::services::Utils::StringFromStringT(xuid1).c_str()); LogToFile("UserStatisticsServiceGetMultipleUserStatisticsForMultipleServiceConfigurations: xuid2: %ul", xbox::services::Utils::StringFromStringT(xuid2).c_str()); std::vector xboxUserIds{ xuid1, xuid2 }; std::vector requestedStatistics{ requestedStatistic1 }; std::shared_ptr xblc = std::make_shared(Data()->xboxLiveContext); xblc->user_statistics_service().get_multiple_user_statistics_for_multiple_service_configurations( xboxUserIds, requestedStatistics ).then( [](xbox::services::xbox_live_result> result) { HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); LogToFile("UserStatisticsServiceGetMultipleUserStatisticsForMultipleServiceConfigurations: hr=%s", ConvertHR(hr).data()); if (SUCCEEDED(hr)) { std::vector userStatisticsResults = result.payload(); for (auto statResult : userStatisticsResults) { LogToFile("Xbox User ID: %d", xbox::services::Utils::StringFromStringT(statResult.xbox_user_id()).c_str()); for (auto scidStats : statResult.service_configuration_statistics()) { LogToFile("SCID: %s", xbox::services::Utils::StringFromStringT(scidStats.service_configuration_id()).c_str()); for (auto stat : scidStats.statistics()) { LogToFile("Stat name:%s value:%s type:%s", xbox::services::Utils::StringFromStringT(stat.statistic_name()).c_str(), xbox::services::Utils::StringFromStringT(stat.value()).c_str(), xbox::services::Utils::StringFromStringT(stat.statistic_type()).c_str() ); } } } } CallLuaFunctionWithHr(hr, "OnUserStatisticsServiceGetMultipleUserStatisticsForMultipleServiceConfigurations"); }); #else LogToFile("UserStatisticsServiceGetMultipleUserStatisticsForMultipleServiceConfigurations is disabled for this platform"); CallLuaFunctionWithHr(S_OK, "OnUserStatisticsServiceGetMultipleUserStatisticsForMultipleServiceConfigurations"); #endif return LuaReturnHR(L, S_OK); } int UserStatisticsServiceSubscribeToStatisticChange_Lua(lua_State *L) { #if CPP_TESTS_ENABLED if (Data()->statisticChangeSubscriptionCpp != nullptr) { return LuaReturnHR(L, E_FAIL); } string_t xboxUserId = xbox::services::Utils::StringTFromUint64(Data()->xboxUserId); string_t serviceConfigurationId = xbox::services::Utils::StringTFromUtf8(Data()->scid); string_t statisticName = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 1, "TotalPuzzlesSolved").c_str()); LogToFile("UserStatisticsServiceSubscribeToStatisticChange: statisticName: %s", xbox::services::Utils::StringFromStringT(statisticName).c_str()); std::shared_ptr xblc = std::make_shared(Data()->xboxLiveContext); auto result = xblc->user_statistics_service().subscribe_to_statistic_change(xboxUserId, serviceConfigurationId, statisticName); HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); if (SUCCEEDED(hr)) { Data()->statisticChangeSubscriptionCpp = result.payload(); } LogToFile("UserStatisticsServiceSubscribeToStatisticChange: hr=%s", ConvertHR(hr).data()); return LuaReturnHR(L, hr); #else LogToFile("UserStatisticsServiceSubscribeToStatisticChange is disabled for this platform."); return LuaReturnHR(L, S_OK); #endif } int UserStatisticsServiceUnsubscribeFromStatisticChange_Lua(lua_State *L) { #if CPP_TESTS_ENABLED std::shared_ptr xblc = std::make_shared(Data()->xboxLiveContext); auto result = xblc->user_statistics_service().unsubscribe_from_statistic_change(Data()->statisticChangeSubscriptionCpp); HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); if (SUCCEEDED(hr)) { Data()->statisticChangeSubscriptionCpp = nullptr; } LogToFile("UserStatisticsServiceUnsubscribeFromStatisticChange: hr=%s", ConvertHR(hr).data()); return LuaReturnHR(L, hr); #else LogToFile("UserStatisticsServiceUnsubscribeFromStatisticChange is disabled for this platform"); return LuaReturnHR(L, S_OK); #endif } int UserStatisticsServiceAddStatisticChangedHandler_Lua(lua_State *L) { #if CPP_TESTS_ENABLED std::shared_ptr xblc = std::make_shared(Data()->xboxLiveContext); Data()->statisticChangedFunctionContextCpp = xblc->user_statistics_service().add_statistic_changed_handler( [](xbox::services::user_statistics::statistic_change_event_args args) { // Handle stat change LogToScreen("Statistic changed callback: stat changed (%s = %s)", xbox::services::Utils::StringFromStringT(args.latest_statistic().statistic_name()).c_str(), xbox::services::Utils::StringFromStringT(args.latest_statistic().value()).c_str()); CallLuaFunctionWithHr(S_OK, "OnStatisticChangedHandlerCpp"); }); LogToFile("UserStatisticsServiceAddStatisticChangedHandler"); #else LogToFile("UserStatisticsServiceAddStatisticChangedHandler is disabled on this platform."); #endif return LuaReturnHR(L, S_OK); } int UserStatisticsServiceRemoveStatisticChangedHandler_Lua(lua_State *L) { #if CPP_TESTS_ENABLED std::shared_ptr xblc = std::make_shared(Data()->xboxLiveContext); xblc->user_statistics_service().remove_statistic_changed_handler(Data()->statisticChangedFunctionContextCpp); Data()->statisticChangedFunctionContextCpp = nullptr; LogToFile("UserStatisticsServiceRemoveStatisticChangedHandler"); #else LogToFile("UserStatisticsServiceRemoveStatisticChangedHandler is disabled for this platform."); #endif return LuaReturnHR(L, S_OK); } int StatisticChangeSubscriptionGetStateCpp_Lua(lua_State* L) { #if CPP_TESTS_ENABLED if (Data()->statisticChangeSubscriptionCpp) { xbox::services::real_time_activity::real_time_activity_subscription_state subscriptionState = Data()->statisticChangeSubscriptionCpp->state(); LogToFile("StatisticChangeSubscriptionGetStateCpp: subscriptionState=%u", subscriptionState); } else { LogToFile("StatisticChangeSubscriptionGetStateCpp: No subscription found"); } #else LogToFile("StatisticChangeSubscriptionGetStateCpp is disabled for this platform."); #endif return LuaReturnHR(L, S_OK); } int StatisticChangeSubscriptionGetIdCpp_Lua(lua_State* L) { #if CPP_TESTS_ENABLED if (Data()->statisticChangeSubscriptionCpp) { uint32_t subscriptionId = Data()->statisticChangeSubscriptionCpp->subscription_id(); LogToFile("StatisticChangeSubscriptionGetStateCpp: subscriptionState=%u", subscriptionId); } else { LogToFile("StatisticChangeSubscriptionGetIdCpp: No subscription found"); } #else LogToFile("StatisticChangeSubscriptionGetIdCpp is disabled for this platform."); #endif return LuaReturnHR(L, S_OK); } void SetupAPIs_CppStatistics() { lua_register(Data()->L, "UserStatisticsServiceGetSingleUserStatistic", UserStatisticsServiceGetSingleUserStatistic_Lua); lua_register(Data()->L, "UserStatisticsServiceGetSingleUserStatistics", UserStatisticsServiceGetSingleUserStatistics_Lua); lua_register(Data()->L, "UserStatisticsServiceGetMultipleUserStatistics", UserStatisticsServiceGetMultipleUserStatistics_Lua); lua_register(Data()->L, "UserStatisticsServiceGetMultipleUserStatisticsForMultipleServiceConfigurations", UserStatisticsServiceGetMultipleUserStatisticsForMultipleServiceConfigurations_Lua); lua_register(Data()->L, "UserStatisticsServiceSubscribeToStatisticChange", UserStatisticsServiceSubscribeToStatisticChange_Lua); lua_register(Data()->L, "UserStatisticsServiceUnsubscribeFromStatisticChange", UserStatisticsServiceUnsubscribeFromStatisticChange_Lua); lua_register(Data()->L, "UserStatisticsServiceAddStatisticChangedHandler", UserStatisticsServiceAddStatisticChangedHandler_Lua); lua_register(Data()->L, "UserStatisticsServiceRemoveStatisticChangedHandler", UserStatisticsServiceRemoveStatisticChangedHandler_Lua); lua_register(Data()->L, "StatisticChangeSubscriptionGetStateCpp", StatisticChangeSubscriptionGetStateCpp_Lua); lua_register(Data()->L, "StatisticChangeSubscriptionGetIdCpp", StatisticChangeSubscriptionGetIdCpp_Lua); } ================================================ FILE: Tests/ApiExplorer/APIs/apis_cpp_stringverify.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" int StringServiceVerifyString_Lua(lua_State *L) { #if CPP_TESTS_ENABLED string_t testString = _T("Shltstring"); std::shared_ptr xblc = std::make_shared(Data()->xalUser); xblc->string_service().verify_string( testString ).then( [](xbox::services::xbox_live_result result) { HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); LogToFile("StringServiceVerifyString: hr=%s", ConvertHR(hr).c_str()); if (SUCCEEDED(hr)) { xbox::services::system::verify_string_result verifyStringResult = result.payload(); LogToFile( "Result: Result Code: %d - First Offending String: %s", verifyStringResult.result_code(), (verifyStringResult.result_code() == xbox::services::system::verify_string_result_code::offensive) ? xbox::services::Utils::StringFromStringT(verifyStringResult.first_offending_substring()).c_str() : ""); } CallLuaFunctionWithHr(hr, "OnStringServiceVerifyString"); }); #else LogToFile("StringServiceVerifyString is disabled on this platform."); CallLuaFunctionWithHr(S_OK, "OnStringServiceVerifyString"); #endif return LuaReturnHR(L, S_OK); } int StringServiceVerifyStrings_Lua(lua_State* L) { #if CPP_TESTS_ENABLED string_t testString1 = _T("Shltstring"); string_t testString2 = _T("Goodstring"); string_t testString3 = _T("Shltstring2"); std::vector testStrings{ testString1, testString2, testString3 }; std::shared_ptr xblc = std::make_shared(Data()->xalUser); xblc->string_service().verify_strings( testStrings ).then( [](xbox::services::xbox_live_result> result) { HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); LogToFile("StringServiceVerifyStrings: hr=%s", ConvertHR(hr).c_str()); if (SUCCEEDED(hr)) { std::vector verifyStringResults = result.payload(); LogToScreen("StringServiceVerifyStrings: count=%d", verifyStringResults.size()); assert(verifyStringResults.size() == 3); for (xbox::services::system::verify_string_result verifyStringResult : verifyStringResults) { LogToFile( "Result: Result Code: %d - First Offending String: %s", verifyStringResult.result_code(), (verifyStringResult.result_code() == xbox::services::system::verify_string_result_code::offensive) ? xbox::services::Utils::StringFromStringT(verifyStringResult.first_offending_substring()).c_str() : ""); } } CallLuaFunctionWithHr(hr, "OnStringServiceVerifyStrings"); }); #else LogToFile("StringServiceVerifyStrings is disabled on this platform."); CallLuaFunctionWithHr(S_OK, "OnStringServiceVerifyStrings"); #endif return LuaReturnHR(L, S_OK); } void SetupAPIs_CppStringVerify() { lua_register(Data()->L, "StringServiceVerifyString", StringServiceVerifyString_Lua); lua_register(Data()->L, "StringServiceVerifyStrings", StringServiceVerifyStrings_Lua); } ================================================ FILE: Tests/ApiExplorer/APIs/apis_cpp_title_storage.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #if CPP_TESTS_ENABLED xbox::services::title_storage::title_storage_type ConvertStringToCppTitleStorageType(const char* str) { xbox::services::title_storage::title_storage_type storageType = xbox::services::title_storage::title_storage_type::trusted_platform_storage; if (pal::stricmp(str, "title_storage_type::trusted_platform_storage") == 0) storageType = xbox::services::title_storage::title_storage_type::trusted_platform_storage; else if (pal::stricmp(str, "title_storage_type::global_storage") == 0) storageType = xbox::services::title_storage::title_storage_type::global_storage; else if (pal::stricmp(str, "title_storage_type::universal") == 0) storageType = xbox::services::title_storage::title_storage_type::universal; return storageType; } xbox::services::title_storage::title_storage_blob_type ConvertStringToCppTitleStorageBlobType(const char* str) { xbox::services::title_storage::title_storage_blob_type blobType = xbox::services::title_storage::title_storage_blob_type::unknown; if (pal::stricmp(str, "title_storage_blob_type::binary") == 0) blobType = xbox::services::title_storage::title_storage_blob_type::binary; else if(pal::stricmp(str, "title_storage_blob_type::json") == 0) blobType = xbox::services::title_storage::title_storage_blob_type::json; else if (pal::stricmp(str, "title_storage_blob_type::config") == 0) blobType = xbox::services::title_storage::title_storage_blob_type::config; return blobType; } xbox::services::title_storage::title_storage_e_tag_match_condition ConvertStringToCppETagMatchCondition(const char* str) { xbox::services::title_storage::title_storage_e_tag_match_condition matchCondition = xbox::services::title_storage::title_storage_e_tag_match_condition::not_used; if (pal::stricmp(str, "title_storage_e_tag_match_condition::if_match") == 0) matchCondition = xbox::services::title_storage::title_storage_e_tag_match_condition::if_match; else if (pal::stricmp(str, "title_storage_e_tag_match_condition::if_not_match") == 0) matchCondition = xbox::services::title_storage::title_storage_e_tag_match_condition::if_not_match; return matchCondition; } #endif int TitleStorageServiceGetQuota_Lua(lua_State* L) { #if CPP_TESTS_ENABLED xbox::services::title_storage::title_storage_type storageType = ConvertStringToCppTitleStorageType(GetStringFromLua(L, 1, "title_storage_type::universal").c_str()); std::shared_ptr xblc = std::make_shared(Data()->xalUser); xblc->title_storage_service().get_quota( xbox::services::Utils::StringTFromUtf8(Data()->scid), storageType ).then( [](xbox::services::xbox_live_result < xbox::services::title_storage::title_storage_quota> result) { HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); LogToFile("TitleStorageServiceGetQuota: hr=%s", ConvertHR(hr).c_str()); if (SUCCEEDED(hr)) { xbox::services::title_storage::title_storage_quota storageQuota = result.payload(); LogToScreen("XblTitleStorageGetQuotaResult: usedBytes=%u quotaBytes=%u", storageQuota.used_bytes(), storageQuota.quota_bytes()); } CallLuaFunctionWithHr(hr, "OnTitleStorageServiceGetQuota"); }); #else LogToFile("TitleStorageServiceGetQuota is disabled for this platform."); CallLuaFunctionWithHr(S_OK, "OnTitleStorageServiceGetQuota"); #endif return LuaReturnHR(L, S_OK); } int TitleStorageServiceGetBlobMetadata_Lua(lua_State* L) { #if CPP_TESTS_ENABLED xbox::services::title_storage::title_storage_type storageType = ConvertStringToCppTitleStorageType(GetStringFromLua(L, 1, "title_storage_type::universal").c_str()); string_t blobPath = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 2, "").c_str()); string_t xboxUserId = xbox::services::Utils::StringTFromUint64(GetUint64FromLua(L, 3, 0)); uint32_t skipItems = GetUint32FromLua(L, 4, 0); uint32_t maxItems = GetUint32FromLua(L, 5, 0); std::shared_ptr xblc = std::make_shared(Data()->xalUser); xblc->title_storage_service().get_blob_metadata( xbox::services::Utils::StringTFromUtf8(Data()->scid), storageType, blobPath, xboxUserId, skipItems, maxItems ).then( [xblc](xbox::services::xbox_live_result result) { HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); LogToFile("TitleStorageServiceGetBlobMetadata: hr=%s", ConvertHR(hr).c_str()); if (SUCCEEDED(hr)) { xbox::services::title_storage::title_storage_blob_metadata_result metadataResult = result.payload(); Data()->blobMetadataResultCpp = metadataResult; LogToScreen("TitleStorageServiceGetBlobMetadata result count: %d", metadataResult.items().size()); Data()->blobMetadataCpp = metadataResult.items()[0]; } CallLuaFunctionWithHr(hr, "OnTitleStorageServiceGetBlobMetadata"); }); #else LogToFile("TitleStorageServiceGetBlobMetadata is disabled for this platform."); CallLuaFunctionWithHr(S_OK, "OnTitleStorageServiceGetBlobMetadata"); #endif return LuaReturnHR(L, S_OK); } int TitleStorageBlobMetadataResultHasNextCpp_Lua(lua_State* L) { bool hasNext = false; #if CPP_TESTS_ENABLED hasNext = Data()->blobMetadataResultCpp.has_next(); LogToFile("TitleStorageBlobMetadataResultHasNextCpp: hasNext=%s", hasNext ? "true" : "false"); #else LogToFile("TitleStorageBlobMetadataResultHasNextCpp is disabled form this platform."); #endif lua_pushnumber(L, (int)hasNext); return LuaReturnHR(L, S_OK, 1); } int TitleStorageBlobMetadataResultGetNextCpp_Lua(lua_State* L) { #if CPP_TESTS_ENABLED uint32_t maxItems = GetUint32FromLua(L, 1, 100); LogToFile("TitleStorageBlobMetadataResultGetNextCpp: MaxItems: %d", maxItems); Data()->blobMetadataResultCpp.get_next(maxItems).then( [](xbox::services::xbox_live_result result) { HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); LogToFile("TitleStorageBlobMetadataResultGetNextCpp: hr=%s", ConvertHR(hr).c_str()); if (SUCCEEDED(hr)) { xbox::services::title_storage::title_storage_blob_metadata_result metadataResult = result.payload(); Data()->blobMetadataResultCpp = metadataResult; LogToScreen("TitleStorageBlobMetadataResultGetNext count: %d", metadataResult.items().size()); } CallLuaFunctionWithHr(hr, "OnTitleStorageBlobMetadataResultGetNextCpp"); }); #else LogToFile("TitleStorageBlobMetadataResultGetNextCpp is disabled for this platform."); CallLuaFunctionWithHr(S_OK, "OnTitleStorageBlobMetadataResultGetNextCpp"); #endif return LuaReturnHR(L, S_OK); } int TitleStorageServiceDeleteBlob_Lua(lua_State* L) { #if CPP_TESTS_ENABLED bool deleteOnlyIfEtagMatches = GetBoolFromLua(L, 1, false); std::shared_ptr xblc = std::make_shared(Data()->xalUser); xblc->title_storage_service().delete_blob( Data()->blobMetadataCpp, deleteOnlyIfEtagMatches ).then( [](xbox::services::xbox_live_result result) { HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); LogToScreen("TitleStorageServiceDeleteBlob: hr=%s", ConvertHR(hr).c_str()); CallLuaFunctionWithHr(hr, "OnTitleStorageServiceDeleteBlob"); }); #else LogToFile("TitleStorageServiceDeleteBlob is disabled for this platform."); CallLuaFunctionWithHr(S_OK, "OnTitleStorageServiceDeleteBlob"); #endif return LuaReturnHR(L, S_OK); } int TitleStorageServiceDownloadBlob_Lua(lua_State* L) { #if CPP_TESTS_ENABLED xbox::services::title_storage::title_storage_e_tag_match_condition eTagMatchCondition{ xbox::services::title_storage::title_storage_e_tag_match_condition::not_used }; string_t selectQuery = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 1, "").c_str()); uint32_t preferredDownloadBlockSize = GetUint32FromLua(L, 2, 1024 * 256); std::shared_ptr> downloadBlobBuffer = std::make_shared>((unsigned int)(Data()->blobMetadataCpp.length())); std::shared_ptr xblc = std::make_shared(Data()->xalUser); xblc->title_storage_service().download_blob( Data()->blobMetadataCpp, downloadBlobBuffer, eTagMatchCondition, selectQuery, preferredDownloadBlockSize ).then( [downloadBlobBuffer](xbox::services::xbox_live_result result) { HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); LogToScreen("TitleStorageServiceDownloadBlob: hr=%s", ConvertHR(hr).c_str()); CallLuaFunctionWithHr(hr, "OnTitleStorageServiceDownloadBlob"); }); #else LogToFile("TitleStorageServiceDownloadBlob is disabled for this platform."); CallLuaFunctionWithHr(S_OK, "OnTitleStorageServiceDownloadBlob"); #endif return LuaReturnHR(L, S_OK); } int TitleStorageServiceUploadBlob_Lua(lua_State* L) { #if CPP_TESTS_ENABLED xbox::services::title_storage::title_storage_type storageType = ConvertStringToCppTitleStorageType(GetStringFromLua(L, 1, "title_storage_type::universal").c_str()); string_t blobPath = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 2, "apirunner/test/path.txt").c_str()); xbox::services::title_storage::title_storage_blob_type blobType = ConvertStringToCppTitleStorageBlobType(GetStringFromLua(L, 3, "title_storage_blob_type::binary").c_str()); string_t displayName = xbox::services::Utils::StringTFromUtf8(GetStringFromLua(L, 4, "Test Binary Data").c_str()); string_t eTag = _T("TestETag"); xbox::services::title_storage::title_storage_blob_metadata blobMetadata( xbox::services::Utils::StringTFromUtf8(Data()->scid), storageType, blobPath, blobType, xbox::services::Utils::StringTFromUint64(Data()->xboxUserId), displayName, eTag ); std::shared_ptr> blobBuffer = std::make_shared>(); xbox::services::title_storage::title_storage_e_tag_match_condition eTagMatchCondition = ConvertStringToCppETagMatchCondition(GetStringFromLua(L, 5, "title_storage_e_tag_match_condition::not_used").c_str()); uint32_t preferredUploadBlockSize = 1024 * 256; size_t blobBufferSize = 1024 * 600; // requires 3 chunk uploads blobBuffer->resize(blobBufferSize); char zero = '0'; for (size_t i = 0; i < blobBufferSize; i++) { (*blobBuffer)[i] = static_cast(zero + i % 10); } std::shared_ptr xblc = std::make_shared(Data()->xalUser); xblc->title_storage_service().upload_blob( blobMetadata, blobBuffer, eTagMatchCondition, preferredUploadBlockSize ).then( [blobBuffer](xbox::services::xbox_live_result result) { HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); LogToScreen("TitleStorageServiceUploadBlob: hr=%s", ConvertHR(hr).c_str()); if (SUCCEEDED(hr)) { Data()->blobMetadataCpp = result.payload(); } CallLuaFunctionWithHr(hr, "OnTitleStorageServiceUploadBlob"); }); #else LogToFile("TitleStorageServiceUploadBlob is disabled for this platform."); CallLuaFunctionWithHr(S_OK, "OnTitleStorageServiceUploadBlob"); #endif return LuaReturnHR(L, S_OK); } void SetupAPIs_CppTitleStorage() { lua_register(Data()->L, "TitleStorageServiceGetQuota", TitleStorageServiceGetQuota_Lua); lua_register(Data()->L, "TitleStorageServiceGetBlobMetadata", TitleStorageServiceGetBlobMetadata_Lua); lua_register(Data()->L, "TitleStorageBlobMetadataResultHasNextCpp", TitleStorageBlobMetadataResultHasNextCpp_Lua); lua_register(Data()->L, "TitleStorageBlobMetadataResultGetNextCpp", TitleStorageBlobMetadataResultGetNextCpp_Lua); lua_register(Data()->L, "TitleStorageServiceDeleteBlob", TitleStorageServiceDeleteBlob_Lua); lua_register(Data()->L, "TitleStorageServiceDownloadBlob", TitleStorageServiceDownloadBlob_Lua); lua_register(Data()->L, "TitleStorageServiceUploadBlob", TitleStorageServiceUploadBlob_Lua); } ================================================ FILE: Tests/ApiExplorer/APIs/apis_docs.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" XBL_WARNING_DISABLE_DEPRECATED void CodeSnippets() { // Non-functional code used to illustrate examples in docs { auto asyncBlock = std::make_unique(); // CODE SNIPPET START: XblPresenceSetPresenceAsyncWithIDs_C XblPresenceRichPresenceIds ids{}; pal::strcpy(ids.scid, sizeof(ids.scid), Data()->scid); ids.presenceId = "playingMap"; std::vector tokenIds{ "CurrentMap" }; ids.presenceTokenIds = tokenIds.data(); ids.presenceTokenIdsCount = tokenIds.size(); HRESULT hr = XblPresenceSetPresenceAsync(Data()->xboxLiveContext, true, &ids, asyncBlock.get()); // CODE SNIPPET END UNREFERENCED_PARAMETER(hr); } { std::string scid = "123"; std::string leaderboardName = "123"; // CODE SNIPPET START: XblLeaderboardGetLeaderboardAsync-Rank XblLeaderboardQuery leaderboardQuery = {}; pal::strcpy(leaderboardQuery.scid, sizeof(leaderboardQuery.scid), scid.c_str()); leaderboardQuery.leaderboardName = leaderboardName.c_str(); leaderboardQuery.skipResultToRank = 100; leaderboardQuery.maxItems = 100; // CODE SNIPPET END } { std::string scid = "123"; std::string leaderboardName = "123"; uint64_t xboxUserId = 123; // CODE SNIPPET START: XblLeaderboardGetLeaderboardAsync-User XblLeaderboardQuery leaderboardQuery = {}; pal::strcpy(leaderboardQuery.scid, sizeof(leaderboardQuery.scid), scid.c_str()); leaderboardQuery.leaderboardName = leaderboardName.c_str(); leaderboardQuery.skipToXboxUserId = xboxUserId; leaderboardQuery.maxItems = 100; // CODE SNIPPET END } { #if HC_PLATFORM != HC_PLATFORM_XDK // CODE SNIPPET START: XblEventsWriteInGameEvent HRESULT hr = XblEventsWriteInGameEvent( Data()->xboxLiveContext, "PuzzleSolved", R"({"DifficultyLevelId":100, "GameplayModeId":"Adventure"})", R"({"LocationX":1,"LocationY":1})" ); // CODE SNIPPET END UNREFERENCED_PARAMETER(hr); #endif } { auto xblContextHandle = Data()->xboxLiveContext; auto xboxUserId = Data()->xboxUserId; auto scid = Data()->scid; { // CODE SNIPPET START: DocsSubscribeToStatisticChange // Subscribe for statistic change events std::string statisticName = "totalPuzzlesSolved"; XblRealTimeActivitySubscriptionHandle subscriptionHandle{ nullptr }; HRESULT hr = XblUserStatisticsSubscribeToStatisticChange( xblContextHandle, xboxUserId, scid, statisticName.c_str(), &subscriptionHandle ); // Add a statistic changed handler void* context{ nullptr }; XblFunctionContext statisticChangedFunctionContext = XblUserStatisticsAddStatisticChangedHandler( Data()->xboxLiveContext, [](XblStatisticChangeEventArgs eventArgs, void* context) { // Handle stat change LogToScreen("Statistic changed callback: stat changed (%s = %s)", eventArgs.latestStatistic.statisticName, eventArgs.latestStatistic.value); UNREFERENCED_PARAMETER(context); // CODE SNIP SKIP }, context ); // CODE SNIPPET END UNREFERENCED_PARAMETER(statisticChangedFunctionContext); UNREFERENCED_PARAMETER(statisticName); UNREFERENCED_PARAMETER(hr); } { XblFunctionContext statisticChangedFunctionContext = 0; // CODE SNIPPET START: DocsUnsubscribeFromStatisticChange // Remove the statistic changed handler XblUserStatisticsRemoveStatisticChangedHandler( xblContextHandle, statisticChangedFunctionContext ); // Unsubscribe for statistic change events HRESULT hr = XblUserStatisticsUnsubscribeFromStatisticChange( Data()->xboxLiveContext, Data()->statisticChangeSubscriptionHandle ); // CODE SNIPPET END UNREFERENCED_PARAMETER(statisticChangedFunctionContext); UNREFERENCED_PARAMETER(hr); } { // CODE SNIPPET START: DocsAddConnectionIdChangedHandler void* context{ nullptr }; XblFunctionContext connectionIdChangedFunctionContext = XblMultiplayerAddConnectionIdChangedHandler( xblContextHandle, [](void* context) { UNREFERENCED_PARAMETER(context); // CODE SNIP SKIP XTaskQueueHandle queue{ nullptr }; // CODE SNIP SKIP auto xblContextHandle = Data()->xboxLiveContext; // CODE SNIP SKIP XblMultiplayerSessionHandle sessionHandle; // Retrieve the MPSD session to update sessionHandle = nullptr; // CODE SNIP SKIP XblMultiplayerSessionCurrentUserSetStatus(sessionHandle, XblMultiplayerSessionMemberStatus::Active); auto asyncBlock = std::make_unique(); asyncBlock->queue = queue; asyncBlock->context = nullptr; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; XblMultiplayerSessionHandle sessionHandle; HRESULT hr = XblMultiplayerWriteSessionResult(asyncBlock, &sessionHandle); if (SUCCEEDED(hr)) { // If the write call succeeds, the connection id has been updated and no further action is needed. } else { // If the write call fails, it is likely the user has been removed from the session. } }; auto hr = XblMultiplayerWriteSessionAsync(xblContextHandle, sessionHandle, XblMultiplayerSessionWriteMode::UpdateExisting, asyncBlock.get()); if (SUCCEEDED(hr)) { asyncBlock.release(); } }, context); // CODE SNIPPET END UNREFERENCED_PARAMETER(connectionIdChangedFunctionContext); } } } void DocsMultiplayerCreateSession() { auto xblContextHandle = Data()->xboxLiveContext; auto XUID = Data()->xboxUserId; const char* SCID{ Data()->scid }; const char* SESSION_TEMPLATE_NAME{ "MinGameSession" }; const char* SESSION_NAME{ "" }; auto queue = Data()->queue; // CODE SNIPPET START: DocsMultiplayerCreateSession_C auto asyncBlock = std::make_unique(); asyncBlock->queue = queue; asyncBlock->context = nullptr; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; XblMultiplayerSessionHandle sessionHandle; HRESULT hr = XblMultiplayerWriteSessionResult(asyncBlock, &sessionHandle); if (SUCCEEDED(hr)) { // Process multiplayer session handle } else { // Handle failure } }; XblMultiplayerSessionReference ref; pal::strcpy(ref.Scid, sizeof(ref.Scid), SCID); pal::strcpy(ref.SessionTemplateName, sizeof(ref.SessionTemplateName), SESSION_TEMPLATE_NAME); pal::strcpy(ref.SessionName, sizeof(ref.SessionName), SESSION_NAME); XblMultiplayerSessionInitArgs args = {}; XblMultiplayerSessionHandle sessionHandle = XblMultiplayerSessionCreateHandle(XUID, &ref, &args); auto hr = XblMultiplayerWriteSessionAsync(xblContextHandle, sessionHandle, XblMultiplayerSessionWriteMode::CreateNew, asyncBlock.get()); if (SUCCEEDED(hr)) { asyncBlock.release(); } // CODE SNIPPET END } void DocsMultiplayerJoinSessionFromSearchHandle() { auto xblContextHandle = Data()->xboxLiveContext; auto scid = Data()->scid; // CODE SNIPPET START: DocsMultiplayerJoinSessionFromSearchHandle_C auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->context = nullptr; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; size_t resultCount{ 0 }; auto hr = XblMultiplayerGetSearchHandlesResultCount(asyncBlock, &resultCount); if (SUCCEEDED(hr) && resultCount > 0) { auto handles = new XblMultiplayerSearchHandle[resultCount]; hr = XblMultiplayerGetSearchHandlesResult(asyncBlock, handles, resultCount); if (SUCCEEDED(hr)) { // Join the game session const char* handleId{ nullptr }; XblMultiplayerSearchHandleGetId(handles[0], &handleId); XblMultiplayerSessionReference multiplayerSessionReference; XblMultiplayerSearchHandleGetSessionReference(handles[0], &multiplayerSessionReference); XblMultiplayerSessionHandle gameSession = XblMultiplayerSessionCreateHandle(Data()->xboxUserId, &multiplayerSessionReference, nullptr); XblMultiplayerSessionJoin(gameSession, nullptr, true, true); // TODO finish // XblMultiplayerWriteSessionByHandleAsync(Data()->xboxLiveContext, gameSession, XblMultiplayerSessionWriteMode::UpdateExisting, handleId, async); // XblMultiplayerManagerJoinGame(handleId, Data()->xalUser); // Close handles for (auto i = 0u; i < resultCount; ++i) { XblMultiplayerSearchHandleCloseHandle(handles[i]); } } } }; const char* sessionName{ "MinGameSession" }; const char* orderByAttribute{ nullptr }; bool orderAscending{ false }; const char* searchFilter{ nullptr }; const char* socialGroup{ nullptr }; HRESULT hr = XblMultiplayerGetSearchHandlesAsync( xblContextHandle, scid, sessionName, orderByAttribute, orderAscending, searchFilter, socialGroup, asyncBlock.get() ); if (SUCCEEDED(hr)) { asyncBlock.release(); } // CODE SNIPPET END } void DocsMultiplayerManagerAddLocalUser_Single() { auto xblUserHandle = Data()->xalUser; void* context = nullptr; // CODE SNIPPET START: DocsMultiplayerManagerAddLocalUser_Single_C HRESULT hr = XblMultiplayerManagerLobbySessionAddLocalUser(xblUserHandle); if (!SUCCEEDED(hr)) { // Handle failure } // Set member connection address const char* connectionAddress = "1.1.1.1"; hr = XblMultiplayerManagerLobbySessionSetLocalMemberConnectionAddress( xblUserHandle, connectionAddress, context); if (!SUCCEEDED(hr)) { // Handle failure } // Set custom member properties const char* propName = "Name"; const char* propValueJson = "{}"; hr = XblMultiplayerManagerLobbySessionSetProperties(propName, propValueJson, context); if (!SUCCEEDED(hr)) { // Handle failure } // DOTS // CODE SNIPPET END } void DocsMultiplayerManagerAddLocalUser_Multiple() { void* context = nullptr; // CODE SNIPPET START: DocsMultiplayerManagerAddLocalUser_Multiple_C std::vector xblUsers; for (XblUserHandle xblUserHandle : xblUsers) { HRESULT hr = XblMultiplayerManagerLobbySessionAddLocalUser(xblUserHandle); if (!SUCCEEDED(hr)) { // Handle failure } // Set member connection address const char* connectionAddress = "1.1.1.1"; hr = XblMultiplayerManagerLobbySessionSetLocalMemberConnectionAddress( xblUserHandle, connectionAddress, context); if (!SUCCEEDED(hr)) { // Handle failure } // Set custom member properties const char* propName = "Name"; const char* propValueJson = "{}"; hr = XblMultiplayerManagerLobbySessionSetProperties(propName, propValueJson, context); if (!SUCCEEDED(hr)) { // Handle failure } // DOTS } // CODE SNIPPET END } void DocsMultiplayerManagerJoinLobby() { auto xblUserHandle = Data()->xalUser; auto inviteHandleId = "8907df69-558a-43cc-93ce-34a0a219da63"; void* context = nullptr; // CODE SNIPPET START: DocsMultiplayerManagerJoinLobby_C HRESULT hr = XblMultiplayerManagerJoinLobby(inviteHandleId, xblUserHandle); if (!SUCCEEDED(hr)) { // Handle failure } // Set member connection address const char* connectionAddress = "1.1.1.1"; hr = XblMultiplayerManagerLobbySessionSetLocalMemberConnectionAddress( xblUserHandle, connectionAddress, context); // CODE SNIPPET END } ================================================ FILE: Tests/ApiExplorer/APIs/apis_grts_gameinvite.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #if HC_PLATFORM == HC_PLATFORM_GDK #include "XGameInvite.h" XTaskQueueRegistrationToken g_token = { 0 }; void CALLBACK MyXGameInviteEventCallback( _In_opt_ void* context, _In_ const char* inviteUri) { UNREFERENCED_PARAMETER(context); if (inviteUri != nullptr) { LogToScreen("GRTS: Game: MyXGameInviteEventCallback inviteUri:%s\n", inviteUri); std::string inviteString(inviteUri); auto pos = inviteString.find("handle="); auto handleString = inviteString.substr(pos + 7, 36); Data()->inviteHandle = handleString; CallLuaFunctionWithHr(S_OK, "OnXGameInviteRegisterForEvent"); } else { LogToScreen("GRTS: Game: MyXGameInviteEventCallback inviteUri:nullptr\n"); } } int XGameInviteRegisterForEvent_Lua(lua_State* L) { CreateQueueIfNeeded(); HRESULT hr = XGameInviteRegisterForEvent( Data()->queue, nullptr, MyXGameInviteEventCallback, &g_token); LogToFile("GRTS: Game: XGameInviteRegisterForEvent hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XGameInviteUnregisterForEvent_Lua(lua_State* L) { bool result = XGameInviteUnregisterForEvent(g_token, true); LogToFile("GRTS: Game: XGameInviteUnregisterForEvent result=%d", result); return LuaReturnHR(L, S_OK); } #endif void SetupAPIs_GrtsGameInvite() { #if HC_PLATFORM == HC_PLATFORM_GDK lua_register(Data()->L, "XGameInviteRegisterForEvent", XGameInviteRegisterForEvent_Lua); lua_register(Data()->L, "XGameInviteUnregisterForEvent", XGameInviteUnregisterForEvent_Lua); #endif } ================================================ FILE: Tests/ApiExplorer/APIs/apis_libhttp.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" int HCInitialize_Lua(lua_State *L) { // CODE SNIPPET START: HCInitialize #if HC_PLATFORM == HC_PLATFORM_ANDROID HRESULT hr = HCInitialize(&(Data()->initArgs)); #else HRESULT hr = HCInitialize(nullptr); #endif // CODE SNIPPET END HCSettingsSetTraceLevel(HCTraceLevel::Verbose); HCTraceSetTraceToDebugger(true); LogToFile("HCInitialize: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int HCCleanup_Lua(lua_State *L) { // CODE SNIPPET START: HCCleanup HCCleanup(); // CODE SNIPPET END LogToFile("HCCleanup"); return LuaReturnHR(L, S_OK); } int HCCleanupAsync_Lua(lua_State* L) { CreateQueueIfNeeded(); // CODE SNIPPET START: HCCleanupAsync auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* HRESULT hr = XAsyncGetStatus(asyncBlock, false); LogToFile("HCCleanupAsync result: hr=%s", ConvertHR(hr).c_str()); CallLuaFunctionWithHr(hr, "OnHCCleanupAsync"); // CODE SNIP SKIP }; HRESULT hr = HCCleanupAsync(asyncBlock.get()); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } // CODE SNIPPET END LogToFile("HCCleanupAsync: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int HCGetLibVersion_Lua(lua_State *L) { // CODE SNIPPET START: HCGetLibVersion const char* ver = nullptr; HRESULT hr = HCGetLibVersion(&ver); // CODE SNIPPET END LogToFile("HCGetLibVersion: hr=%s %s", ConvertHR(hr).c_str(), ver); return LuaReturnHR(L, hr); } int HCTrace_Lua(lua_State* L) { LogToFile("HCTrace: testing small message"); HCTraceImplArea apiExplorerArea{ "ApiExplorer", HCTraceLevel::Important }; HCTraceImplMessage(&apiExplorerArea, apiExplorerArea.Verbosity, "testing small message"); LogToFile("HCTrace: succeeded with small message"); return LuaReturnHR(L, S_OK); } int HCTraceLarge_Lua(lua_State* L) { LogToFile("HCTraceLarge: testing large message"); HCTraceImplArea apiExplorerArea{ "ApiExplorer", HCTraceLevel::Important }; std::vector largeMessage(8192, 'w'); HCTraceImplMessage(&apiExplorerArea, apiExplorerArea.Verbosity, largeMessage.data()); LogToFile("HCTraceLarge: succeeded with large message"); return LuaReturnHR(L, S_OK); } int HCHttpCallCreate_Lua(lua_State *L) { // CODE SNIPPET START: HCHttpCallCreate HCCallHandle call = nullptr; HRESULT hr = HCHttpCallCreate(&call); // CODE SNIPPET END Data()->httpCall = call; LogToFile("HCHttpCallCreate: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int HCHttpCallPerformAsync_Lua(lua_State *L) { CreateQueueIfNeeded(); // CODE SNIPPET START: HCHttpCallPerformAsync auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->context = Data()->httpCall; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* HRESULT hr = XAsyncGetStatus(asyncBlock, false); LogToFile("HCHttpCallPerformAsync result: hr=%s", ConvertHR(hr).c_str()); CallLuaFunctionWithHr(hr, "OnHCHttpCallPerformAsync"); // CODE SNIP SKIP }; HRESULT hr = HCHttpCallPerformAsync(Data()->httpCall, asyncBlock.get()); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } // CODE SNIPPET END LogToFile("HCHttpCallPerformAsync: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int HCHttpCallDuplicateHandle_Lua(lua_State *L) { // CODE SNIPPET START: HCHttpCallDuplicateHandle HCCallHandle newHttpCall = HCHttpCallDuplicateHandle(Data()->httpCall); // CODE SNIPPET END HCHttpCallCloseHandle(newHttpCall); LogToFile("HCHttpCallDuplicateHandle"); return LuaReturnHR(L, S_OK); } int HCHttpCallCloseHandle_Lua(lua_State *L) { // CODE SNIPPET START: HCHttpCallCloseHandle HCHttpCallCloseHandle(Data()->httpCall); // CODE SNIPPET END Data()->httpCall = nullptr; LogToFile("HCHttpCallCloseHandle"); return LuaReturnHR(L, S_OK); } int HCHttpCallGetId_Lua(lua_State *L) { // CODE SNIPPET START: HCHttpCallGetId uint64_t httpId = HCHttpCallGetId(Data()->httpCall); // CODE SNIPPET END UNREFERENCED_PARAMETER(httpId); LogToFile("HCHttpCallGetId"); return LuaReturnHR(L, S_OK); } int HCHttpCallSetTracing_Lua(lua_State *L) { bool traceCall = GetBoolFromLua(L, 1, true); // CODE SNIPPET START: HCHttpCallSetTracing HRESULT hr = HCHttpCallSetTracing(Data()->httpCall, traceCall); // CODE SNIPPET END LogToFile("HCHttpCallSetTracing: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int HCHttpCallGetRequestUrl_Lua(lua_State *L) { // CODE SNIPPET START: HCHttpCallGetRequestUrl const char* url = nullptr; HRESULT hr = HCHttpCallGetRequestUrl(Data()->httpCall, &url); // CODE SNIPPET END LogToFile("HCHttpCallGetRequestUrl: hr=%s %s", ConvertHR(hr).c_str(), url); return LuaReturnHR(L, hr); } int HCHttpCallRequestSetUrl_Lua(lua_State *L) { std::string methodName = GetStringFromLua(L, 1, "GET"); std::string url = GetStringFromLua(L, 2, "https://www.bing.com"); // CODE SNIPPET START: HCHttpCallRequestSetUrl HRESULT hr = HCHttpCallRequestSetUrl(Data()->httpCall, methodName.c_str(), url.c_str()); // CODE SNIPPET END LogToFile("HCHttpCallRequestSetUrl: hr=%s %s", ConvertHR(hr).c_str(), url.c_str()); return LuaReturnHR(L, hr); } int HCHttpCallRequestSetRequestBodyString_Lua(lua_State *L) { std::string requestBodyString = GetStringFromLua(L, 1, "Test"); // CODE SNIPPET START: HCHttpCallRequestSetRequestBodyString HRESULT hr = HCHttpCallRequestSetRequestBodyString(Data()->httpCall, requestBodyString.c_str()); // CODE SNIPPET END LogToFile("HCHttpCallRequestSetRequestBodyString: hr=%s %s", ConvertHR(hr).c_str(), requestBodyString.c_str()); return LuaReturnHR(L, hr); } int HCHttpCallRequestSetHeader_Lua(lua_State *L) { std::string headerName = GetStringFromLua(L, 1, "TestName"); std::string headerValue = GetStringFromLua(L, 2, "TestValue"); bool allowTracing = GetBoolFromLua(L, 3, true); // CODE SNIPPET START: HCHttpCallRequestSetHeader HRESULT hr = HCHttpCallRequestSetHeader(Data()->httpCall, headerName.c_str(), headerValue.c_str(), allowTracing); // CODE SNIPPET END LogToFile("HCHttpCallRequestSetHeader: hr=%s %s:%s allowTracing=%d", ConvertHR(hr).c_str(), headerName.c_str(), headerValue.c_str(), allowTracing); return LuaReturnHR(L, hr); } int HCHttpCallRequestSetTimeout_Lua(lua_State *L) { uint32_t timeoutInSeconds = GetUint32FromLua(L, 1, 100); // CODE SNIPPET START: HCHttpCallRequestSetTimeout HRESULT hr = HCHttpCallRequestSetTimeout(Data()->httpCall, timeoutInSeconds); // CODE SNIPPET END LogToFile("HCHttpCallRequestSetTimeout: hr=%s %d", ConvertHR(hr).c_str(), timeoutInSeconds); return LuaReturnHR(L, hr); } int HCHttpCallRequestSetRetryDelay_Lua(lua_State *L) { uint32_t retryDelayInSeconds = GetUint32FromLua(L, 1, 100); // CODE SNIPPET START: HCHttpCallRequestSetRetryDelay HRESULT hr = HCHttpCallRequestSetRetryDelay(Data()->httpCall, retryDelayInSeconds); // CODE SNIPPET END LogToFile("HCHttpCallRequestSetRetryDelay: hr=%s %d", ConvertHR(hr).c_str(), retryDelayInSeconds); return LuaReturnHR(L, hr); } int HCHttpCallRequestSetTimeoutWindow_Lua(lua_State *L) { uint32_t timeoutWindowInSeconds = GetUint32FromLua(L, 1, 100); // CODE SNIPPET START: HCHttpCallRequestSetTimeoutWindow HRESULT hr = HCHttpCallRequestSetTimeoutWindow(Data()->httpCall, timeoutWindowInSeconds); // CODE SNIPPET END LogToFile("HCHttpCallRequestSetTimeoutWindow: hr=%s %d", ConvertHR(hr).c_str(), timeoutWindowInSeconds); return LuaReturnHR(L, hr); } int HCHttpCallRequestSetRetryCacheId_Lua(lua_State *L) { uint32_t retryAfterCacheId = GetUint32FromLua(L, 1, 1); // CODE SNIPPET START: HCHttpCallRequestSetRetryCacheId HRESULT hr = HCHttpCallRequestSetRetryCacheId(Data()->httpCall, retryAfterCacheId); // CODE SNIPPET END LogToFile("HCHttpCallRequestSetRetryCacheId: hr=%s %d", ConvertHR(hr).c_str(), retryAfterCacheId); return LuaReturnHR(L, hr); } int HCHttpCallRequestSetRetryAllowed_Lua(lua_State *L) { bool retryAllowed = GetBoolFromLua(L, 1, true); // CODE SNIPPET START: HCHttpCallRequestSetRetryAllowed HRESULT hr = HCHttpCallRequestSetRetryAllowed(Data()->httpCall, retryAllowed); // CODE SNIPPET END LogToFile("HCHttpCallRequestSetRetryAllowed: hr=%s %d", ConvertHR(hr).c_str(), retryAllowed); return LuaReturnHR(L, hr); } int HCHttpCallResponseGetResponseString_Lua(lua_State *L) { // CODE SNIPPET START: HCHttpCallResponseGetResponseString const char* response = nullptr; HRESULT hr = HCHttpCallResponseGetResponseString(Data()->httpCall, &response); // CODE SNIPPET END LogToFile("HCHttpCallResponseGetResponseString: hr=%s %s", ConvertHR(hr).c_str(), response); return LuaReturnHR(L, hr); } int HCHttpCallResponseGetResponseBodyBytesSize_Lua(lua_State *L) { size_t bufferSize = 0; // CODE SNIPPET START: HCHttpCallResponseGetResponseBodyBytesSize HRESULT hr = HCHttpCallResponseGetResponseBodyBytesSize(Data()->httpCall, &bufferSize); // CODE SNIPPET END LogToFile("HCHttpCallResponseGetResponseBodyBytesSize: hr=%s %zu", ConvertHR(hr).c_str(), bufferSize); return LuaReturnHR(L, hr); } int HCHttpCallResponseGetStatusCode_Lua(lua_State *L) { uint32_t statusCode = 0; // CODE SNIPPET START: HCHttpCallResponseGetStatusCode HRESULT hr = HCHttpCallResponseGetStatusCode(Data()->httpCall, &statusCode); // CODE SNIPPET END LogToFile("HCHttpCallResponseGetStatusCode: hr=%s %d", ConvertHR(hr).c_str(), statusCode); return LuaReturnHR(L, hr); } int HCHttpCallResponseGetNetworkErrorCode_Lua(lua_State *L) { // CODE SNIPPET START: HCHttpCallResponseGetNetworkErrorCode HRESULT networkErrorCode = S_OK; uint32_t platErrorCode = 0; HRESULT hr = HCHttpCallResponseGetNetworkErrorCode(Data()->httpCall, &networkErrorCode, &platErrorCode); // CODE SNIPPET END LogToFile("HCHttpCallResponseGetNetworkErrorCode: hr=%s networkError=%d platError=%d", ConvertHR(hr).c_str(), networkErrorCode, platErrorCode); return LuaReturnHR(L, hr); } int HCHttpCallResponseGetHeader_Lua(lua_State *L) { std::string headerName = GetStringFromLua(L, 1, "UserAgent"); // CODE SNIPPET START: HCHttpCallResponseGetHeader const char* headerValue = nullptr; HRESULT hr = HCHttpCallResponseGetHeader(Data()->httpCall, headerName.c_str(), &headerValue); // CODE SNIPPET END LogToFile("HCHttpCallResponseGetHeader: hr=%s %s:%s", ConvertHR(hr).c_str(), headerName.c_str(), headerValue); return LuaReturnHR(L, hr); } int HCHttpCallResponseGetNumHeaders_Lua(lua_State *L) { uint32_t numHeaders = 0; // CODE SNIPPET START: HCHttpCallResponseGetNumHeaders HRESULT hr = HCHttpCallResponseGetNumHeaders(Data()->httpCall, &numHeaders); // CODE SNIPPET END LogToFile("HCHttpCallResponseGetNumHeaders: hr=%s %d", ConvertHR(hr).c_str(), numHeaders); return LuaReturnHR(L, hr); } int HCHttpCallResponseGetHeaderAtIndex_Lua(lua_State *L) { uint32_t headerIndex = GetUint32FromLua(L, 1, 0); // CODE SNIPPET START: HCHttpCallResponseGetHeaderAtIndex const char* headerName = nullptr; const char* headerValue = nullptr; HRESULT hr = HCHttpCallResponseGetHeaderAtIndex(Data()->httpCall, headerIndex, &headerName, &headerValue); // CODE SNIPPET END LogToFile("HCHttpCallResponseGetHeaderAtIndex: hr=%s %d=%s:%s", ConvertHR(hr).c_str(), headerIndex, headerName, headerValue); return LuaReturnHR(L, hr); } void CALLBACK ExampleWebsocketMessageReceived( _In_ HCWebsocketHandle websocket, _In_z_ const char* incomingBodyString, _In_ void* functionContext ) { UNREFERENCED_PARAMETER(websocket); UNREFERENCED_PARAMETER(functionContext); LogToFile("Received message from websocket: %s", incomingBodyString); CallLuaFunction("OnHCWebsocketMessageReceived"); } void CALLBACK ExampleWebsocketClosed( _In_ HCWebsocketHandle websocket, _In_ HCWebSocketCloseStatus closeStatus, _In_ void* functionContext ) { UNREFERENCED_PARAMETER(websocket); UNREFERENCED_PARAMETER(functionContext); LogToFile("Websocket closed with status %u", closeStatus); CallLuaFunction("OnHCWebsocketClosed"); } int HCWebSocketCreate_Lua(lua_State *L) { // CODE SNIPPET START: HCWebSocketCreate HCWebsocketHandle websocket = nullptr; HRESULT hr = HCWebSocketCreate(&websocket, ExampleWebsocketMessageReceived, nullptr, ExampleWebsocketClosed, nullptr); // CODE SNIPPET END Data()->websocket = websocket; LogToFile("HCWebSocketCreate: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int HCWebSocketSetProxyUri_Lua(lua_State *L) { std::string proxyUri = GetStringFromLua(L, 1, "Proxy"); // CODE SNIPPET START: HCWebSocketSetProxyUri HRESULT hr = HCWebSocketSetProxyUri(Data()->websocket, proxyUri.c_str()); // CODE SNIPPET END LogToFile("HCWebSocketSetProxyUri: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int HCWebSocketSetHeader_Lua(lua_State *L) { std::string headerName = GetStringFromLua(L, 1, "TestName"); std::string headerValue = GetStringFromLua(L, 2, "TestValue"); // CODE SNIPPET START: HCWebSocketSetHeader HRESULT hr = HCWebSocketSetHeader(Data()->websocket, headerName.c_str(), headerValue.c_str()); // CODE SNIPPET END LogToFile("HCWebSocketSetHeader: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int HCWebSocketConnectAsync_Lua(lua_State *L) { std::string uri = GetStringFromLua(L, 1, "ws://localhost:9002"); std::string subProtocol = GetStringFromLua(L, 2, ""); // CODE SNIPPET START: HCWebSocketConnectAsync auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->context = nullptr; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* WebSocketCompletionResult result{}; HRESULT hr = HCGetWebSocketConnectResult(asyncBlock, &result); LogToFile("HCGetWebSocketConnectResult: hr=%s errorCode=0x%0.8x", ConvertHR(hr).c_str(), result.errorCode); // CODE SNIP SKIP CallLuaFunctionWithHr(hr, "OnHCWebSocketConnectAsync"); // CODE SNIP SKIP }; HRESULT hr = HCWebSocketConnectAsync(uri.c_str(), subProtocol.c_str(), Data()->websocket, asyncBlock.get()); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } // CODE SNIPPET END LogToFile("HCWebSocketConnectAsync: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int HCWebSocketSendMessageAsync_Lua(lua_State *L) { std::string message = GetStringFromLua(L, 1, "Test"); // CODE SNIPPET START: HCWebSocketSendMessageAsync auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->context = nullptr; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* WebSocketCompletionResult result{}; HRESULT hr = HCGetWebSocketSendMessageResult(asyncBlock, &result); LogToFile("HCGetWebSocketSendMessageResult: hr=%s errorCode=0x%0.8x", ConvertHR(hr).c_str(), result.errorCode); // CODE SNIP SKIP CallLuaFunctionWithHr(hr, "OnHCWebSocketSendMessageAsync"); // CODE SNIP SKIP }; HRESULT hr = HCWebSocketSendMessageAsync(Data()->websocket, message.c_str(), asyncBlock.get()); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } // CODE SNIPPET END LogToFile("HCWebSocketSendMessageAsync: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int HCWebSocketDisconnect_Lua(lua_State *L) { // CODE SNIPPET START: HCWebSocketDisconnect HRESULT hr = HCWebSocketDisconnect(Data()->websocket); // CODE SNIPPET END LogToFile("HCWebSocketDisconnect: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int HCWebSocketDuplicateHandle_Lua(lua_State *L) { // CODE SNIPPET START: HCWebSocketDuplicateHandle HCWebsocketHandle newWebsocket = HCWebSocketDuplicateHandle(Data()->websocket); // CODE SNIPPET END HCWebSocketCloseHandle(newWebsocket); LogToFile("HCWebSocketDuplicateHandle"); return LuaReturnHR(L, S_OK); } int HCWebSocketCloseHandle_Lua(lua_State *L) { // CODE SNIPPET START: HCWebSocketCloseHandle HRESULT hr = HCWebSocketCloseHandle(Data()->websocket); // CODE SNIPPET END LogToFile("HCWebSocketCloseHandle: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int HCTraceSetTraceToDebugger_Lua(lua_State *L) { // CODE SNIPPET START: HCTraceSetTraceToDebugger HCSettingsSetTraceLevel(HCTraceLevel::Verbose); // See HCTraceLevel enum for various levels // TODO: jasonsa - confirm works HCTraceSetTraceToDebugger(true); // CODE SNIPPET END LogToFile("HCTraceSetTraceToDebugger"); return LuaReturnHR(L, S_OK); } int HCSettingsSetTraceLevel_Lua(lua_State *L) { // CODE SNIPPET START: HCSettingsSetTraceLevel HCSettingsSetTraceLevel(HCTraceLevel::Verbose); // CODE SNIPPET END LogToFile("HCSettingsSetTraceLevel"); return LuaReturnHR(L, S_OK); } int HCMockCallCreate_Lua(lua_State *L) { // CODE SNIPPET START: HCMockCallCreate HRESULT hr = HCMockCallCreate(&Data()->mockHttpCall); // CODE SNIPPET END LogToFile("HCMockCallCreate"); return LuaReturnHR(L, hr); } int HCMockAddMock_Lua(lua_State *L) { auto method = GetStringFromLua(L, 1, std::string{}); auto url = GetStringFromLua(L, 2, std::string{}); // CODE SNIPPET START: HCMockAddMock HRESULT hr = HCMockAddMock( Data()->mockHttpCall, method.empty() ? nullptr : method.data(), url.empty() ? nullptr : url.data(), nullptr, 0 ); // CODE SNIPPET END LogToFile("HCMockAddMock"); return LuaReturnHR(L, hr); } int HCMockClearMocks_Lua(lua_State *L) { // CODE SNIPPET START: HCMockClearMocks HRESULT hr = HCMockClearMocks(); // CODE SNIPPET END Data()->mockHttpCall = nullptr; LogToFile("HCMockClearMocks"); return LuaReturnHR(L, hr); } int HCMockResponseSetNetworkErrorCode_Lua(lua_State *L) { auto platformErrorCode = GetUint32FromLua(L, 1, 0); // CODE SNIPPET START: HCMockResponseSetNetworkErrorCode HRESULT hr = HCMockResponseSetNetworkErrorCode(Data()->mockHttpCall, E_FAIL, platformErrorCode); // CODE SNIPPET END LogToFile("HCMockResponseSetNetworkErrorCode"); return LuaReturnHR(L, hr); } #if HC_PLATFORM == HC_PLATFORM_GDK namespace xbox { namespace httpclient { extern void HCWinHttpSuspend(); extern void HCWinHttpResume(); } } int HCWinHttpSuspend_lua(lua_State *L) { UNREFERENCED_PARAMETER(L); xbox::httpclient::HCWinHttpSuspend(); return 0; } int HCWinHttpResume_lua(lua_State *L) { UNREFERENCED_PARAMETER(L); xbox::httpclient::HCWinHttpResume(); return 0; } #endif void SetupAPIs_LibHttp() { //lua_register(Data()->L, "HCMemSetFunctions", HCMemSetFunctions_Lua); //lua_register(Data()->L, "HCMemGetFunctions", HCMemGetFunctions_Lua); lua_register(Data()->L, "HCInitialize", HCInitialize_Lua); lua_register(Data()->L, "HCCleanup", HCCleanup_Lua); lua_register(Data()->L, "HCCleanupAsync", HCCleanupAsync_Lua); lua_register(Data()->L, "HCGetLibVersion", HCGetLibVersion_Lua); lua_register(Data()->L, "HCTrace", HCTrace_Lua); lua_register(Data()->L, "HCTraceLarge", HCTraceLarge_Lua); //lua_register(Data()->L, "HCAddCallRoutedHandler", HCAddCallRoutedHandler_Lua); //lua_register(Data()->L, "HCRemoveCallRoutedHandler", HCRemoveCallRoutedHandler_Lua); lua_register(Data()->L, "HCHttpCallCreate", HCHttpCallCreate_Lua); lua_register(Data()->L, "HCHttpCallPerformAsync", HCHttpCallPerformAsync_Lua); lua_register(Data()->L, "HCHttpCallDuplicateHandle", HCHttpCallDuplicateHandle_Lua); lua_register(Data()->L, "HCHttpCallCloseHandle", HCHttpCallCloseHandle_Lua); lua_register(Data()->L, "HCHttpCallGetId", HCHttpCallGetId_Lua); lua_register(Data()->L, "HCHttpCallSetTracing", HCHttpCallSetTracing_Lua); lua_register(Data()->L, "HCHttpCallGetRequestUrl", HCHttpCallGetRequestUrl_Lua); lua_register(Data()->L, "HCHttpCallRequestSetUrl", HCHttpCallRequestSetUrl_Lua); //lua_register(Data()->L, "HCHttpCallRequestSetRequestBodyBytes", HCHttpCallRequestSetRequestBodyBytes_Lua); lua_register(Data()->L, "HCHttpCallRequestSetRequestBodyString", HCHttpCallRequestSetRequestBodyString_Lua); lua_register(Data()->L, "HCHttpCallRequestSetHeader", HCHttpCallRequestSetHeader_Lua); lua_register(Data()->L, "HCHttpCallRequestSetRetryAllowed", HCHttpCallRequestSetRetryAllowed_Lua); lua_register(Data()->L, "HCHttpCallRequestSetRetryCacheId", HCHttpCallRequestSetRetryCacheId_Lua); lua_register(Data()->L, "HCHttpCallRequestSetTimeout", HCHttpCallRequestSetTimeout_Lua); lua_register(Data()->L, "HCHttpCallRequestSetRetryDelay", HCHttpCallRequestSetRetryDelay_Lua); lua_register(Data()->L, "HCHttpCallRequestSetTimeoutWindow", HCHttpCallRequestSetTimeoutWindow_Lua); lua_register(Data()->L, "HCHttpCallResponseGetResponseString", HCHttpCallResponseGetResponseString_Lua); lua_register(Data()->L, "HCHttpCallResponseGetResponseBodyBytesSize", HCHttpCallResponseGetResponseBodyBytesSize_Lua); //lua_register(Data()->L, "HCHttpCallResponseGetResponseBodyBytes", HCHttpCallResponseGetResponseBodyBytes_Lua); lua_register(Data()->L, "HCHttpCallResponseGetStatusCode", HCHttpCallResponseGetStatusCode_Lua); lua_register(Data()->L, "HCHttpCallResponseGetNetworkErrorCode", HCHttpCallResponseGetNetworkErrorCode_Lua); lua_register(Data()->L, "HCHttpCallResponseGetHeader", HCHttpCallResponseGetHeader_Lua); lua_register(Data()->L, "HCHttpCallResponseGetNumHeaders", HCHttpCallResponseGetNumHeaders_Lua); lua_register(Data()->L, "HCHttpCallResponseGetHeaderAtIndex", HCHttpCallResponseGetHeaderAtIndex_Lua); lua_register(Data()->L, "HCWebSocketCreate", HCWebSocketCreate_Lua); lua_register(Data()->L, "HCWebSocketSetProxyUri", HCWebSocketSetProxyUri_Lua); lua_register(Data()->L, "HCWebSocketSetHeader", HCWebSocketSetHeader_Lua); lua_register(Data()->L, "HCWebSocketConnectAsync", HCWebSocketConnectAsync_Lua); lua_register(Data()->L, "HCWebSocketSendMessageAsync", HCWebSocketSendMessageAsync_Lua); lua_register(Data()->L, "HCWebSocketDisconnect", HCWebSocketDisconnect_Lua); lua_register(Data()->L, "HCWebSocketDuplicateHandle", HCWebSocketDuplicateHandle_Lua); lua_register(Data()->L, "HCWebSocketCloseHandle", HCWebSocketCloseHandle_Lua); lua_register(Data()->L, "HCTraceSetTraceToDebugger", HCTraceSetTraceToDebugger_Lua); lua_register(Data()->L, "HCSettingsSetTraceLevel", HCSettingsSetTraceLevel_Lua); lua_register(Data()->L, "HCMockCallCreate", HCMockCallCreate_Lua); lua_register(Data()->L, "HCMockAddMock", HCMockAddMock_Lua); lua_register(Data()->L, "HCMockClearMocks", HCMockClearMocks_Lua); lua_register(Data()->L, "HCMockResponseSetNetworkErrorCode", HCMockResponseSetNetworkErrorCode_Lua); #if HC_PLATFORM == HC_PLATFORM_GDK lua_register(Data()->L, "HCWinHttpSuspend", HCWinHttpSuspend_lua); lua_register(Data()->L, "HCWinHttpResume", HCWinHttpResume_lua); #endif } ================================================ FILE: Tests/ApiExplorer/APIs/apis_xal.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" int XalCleanupAsync_Lua(lua_State *L) { if (!Data()->m_isXalInitialized) return LuaReturnHR(L, S_OK); if (Data()->nsalMockCall != nullptr) { HCMockRemoveMock(Data()->nsalMockCall); //Close the handle we're holding onto for API runner state HCMockCallCloseHandle(Data()->nsalMockCall); Data()->nsalMockCall = nullptr; if (Data()->libHttpClientInit) // Set on GDK where XAL is just a wrapper around XUser { HCCleanup(); Data()->libHttpClientInit = false; } } #if CPP_TESTS_ENABLED Data()->blobMetadataResultCpp = xbox::services::title_storage::title_storage_blob_metadata_result{}; Data()->blobMetadataCpp = xbox::services::title_storage::title_storage_blob_metadata{}; #endif auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->context = nullptr; HRESULT hr = XalCleanupAsync(asyncBlock.get()); if (SUCCEEDED(hr)) { // Synchronously wait for cleanup to complete hr = XAsyncGetStatus(asyncBlock.get(), true); Data()->m_isXalInitialized = false; } LogToFile("XalCleanup: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } void SetupXalNsalMock() { // Mock the NSAL call so XAL doesn't 429 due to repeated fast XAL init/cleanup during testing // GET https://title.mgt.xboxlive.com/titles/current/endpoints HTTP/1.1 // HTTP/1.1 200 OK // {"EndPoints":[{"Protocol":"http","Host":"*","HostType":"wildcard"},{"Protocol":"https","Host":"*","HostType":"wildcard"}]} if (Data()->nsalMockCall != nullptr) { HCMockRemoveMock(Data()->nsalMockCall); Data()->nsalMockCall = nullptr; } HRESULT hr = HCMockCallCreate(&(Data()->nsalMockCall)); if (hr == E_HC_NOT_INITIALISED) { // This happens on GDK where XAL is just a wrapper around XUser LogToScreen("Calling HCInitialize()"); #if HC_PLATFORM == HC_PLATFORM_ANDROID hr = HCInitialize(&(Data()->initArgs)); #else hr = HCInitialize(nullptr); #endif LogToScreen("HCInitialize done"); assert(SUCCEEDED(hr)); Data()->libHttpClientInit = true; hr = HCMockCallCreate(&(Data()->nsalMockCall)); } assert(SUCCEEDED(hr)); hr = HCMockAddMock(Data()->nsalMockCall, "GET", "https://title.mgt.xboxlive.com/titles/current/endpoints", nullptr, 0); assert(SUCCEEDED(hr)); hr = HCMockResponseSetStatusCode(Data()->nsalMockCall, 200); assert(SUCCEEDED(hr)); std::string responseBodyString = "{\"EndPoints\":[{\"Protocol\":\"http\",\"Host\":\"*\",\"HostType\":\"wildcard\"},{\"Protocol\":\"https\",\"Host\":\"*\",\"HostType\":\"wildcard\"}]}"; std::vector bodyBytes{ responseBodyString.begin(), responseBodyString.end() }; hr = HCMockResponseSetResponseBodyBytes(Data()->nsalMockCall, bodyBytes.data(), static_cast(bodyBytes.size())); assert(SUCCEEDED(hr)); } int XalInitialize_Lua(lua_State *L) { std::string clientId = GetStringFromLua(L, 1, "000000004C251635"); uint32_t titleId = (uint32_t)GetUint64FromLua(L, 2, 1626464874); std::string sandbox = GetStringFromLua(L, 3, "RETAIL"); // Alternate config for XboxLiveE2E Stats 2017 //std::string clientId = GetStringFromLua(L, 1, "0000000044296E10"); //uint32_t titleId = (uint32_t)GetUint64FromLua(L, 2, 2025855259); Data()->titleId = titleId; CreateQueueIfNeeded(); // CODE SNIPPET START: XalInitialize XalInitArgs xalInitArgs = {}; #if HC_PLATFORM == HC_PLATFORM_UWP xalInitArgs.titleId = titleId; xalInitArgs.packageFamilyName = u8"41336MicrosoftATG.XboxLiveE2E_dspnxghe87tn0"; //xalInitArgs.correlationVector; // optional //xalInitArgs.flags; // optional //xalInitArgs.launchUser; // optional //xalInitArgs.mainWindow; // optional #elif HC_PLATFORM == HC_PLATFORM_GDK || HC_PLATFORM == HC_PLATFORM_XDK // No args on GDK / XDK #else // Args for iOS / Android / Win32 xalInitArgs.clientId = clientId.c_str(); xalInitArgs.titleId = titleId; xalInitArgs.sandbox = sandbox.c_str(); #if HC_PLATFORM == HC_PLATFORM_IOS || HC_PLATFORM == HC_PLATFORM_ANDROID // Extra args on Mobile std::string redirectUri{ "ms-xal-" + clientId + "://auth" }; xalInitArgs.redirectUri = redirectUri.data(); #if HC_PLATFORM == HC_PLATFORM_ANDROID // Extra args on Android xalInitArgs.disableDiagnosticTelemetry = false; JNIEnv* jniEnv = nullptr; jint result = Data()->javaVM->GetEnv(reinterpret_cast(&jniEnv), JNI_VERSION_1_6); if (result != JNI_OK) { LogToScreen("Failed to retrieve the JNIEnv from the JavaVM."); } assert(jniEnv != nullptr); jobject res = jniEnv->CallObjectMethod(Data()->m_mainActivityClassInstance, Data()->m_getApplicationContext); xalInitArgs.javaVM = Data()->javaVM; xalInitArgs.appContext = res; #endif #endif #endif HCTraceSetTraceToDebugger(true); HCSettingsSetTraceLevel(HCTraceLevel::Verbose); HRESULT hr = XalInitialize(&xalInitArgs, Data()->queue); // CODE SNIPPET END Data()->m_isXalInitialized = true; LogToScreen("XalInitialize: hr=%s", ConvertHR(hr).c_str()); LogToFile("XalInitialize: hr=%s", ConvertHR(hr).c_str()); SetupXalNsalMock(); return LuaReturnHR(L, hr); } int XalTryAddFirstUserSilentlyAsync_Lua(lua_State *L) { CreateQueueIfNeeded(); // CODE SNIPPET START: XalTryAddFirstUserSilentlyAsync auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->context = nullptr; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* XalUserHandle newUser = nullptr; HRESULT hr = XalTryAddDefaultUserSilentlyResult(asyncBlock, &newUser); // TODO: Store and use newUser LogToScreen("XalTryAddFirstUserSilentlyResult: hr=%s user=0x%0.8x", ConvertHR(hr).c_str(), newUser); LogToFile("XalTryAddFirstUserSilentlyResult: hr=%s user=0x%0.8x", ConvertHR(hr).c_str(), newUser); // CODE SNIP SKIP if (Data()->xalUser != nullptr) { XalUserCloseHandle(Data()->xalUser); Data()->xalUser = nullptr; } Data()->xalUser = newUser; // CODE SNIP SKIP if (Data()->xalUser) // CODE SNIP SKIP { // CODE SNIP SKIP Data()->gotXalUser = true; // CODE SNIP SKIP } // CODE SNIP SKIP LuaStopTestIfFailed(hr); CallLuaString("common = require 'common'; common.OnXalTryAddFirstUserSilentlyAsync()"); // CODE SNIP SKIP }; HRESULT hr = XalTryAddDefaultUserSilentlyAsync(0u, asyncBlock.get()); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } // CODE SNIPPET END LogToScreen("XalTryAddFirstUserSilentlyAsync: hr=%s", ConvertHR(hr).c_str()); LogToFile("XalTryAddFirstUserSilentlyAsync: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XalGetMaxUsers_Lua(lua_State *L) { // CODE SNIPPET START: XalGetMaxUsers uint32_t maxUsers = 0; HRESULT hr = XalGetMaxUsers(&maxUsers); // CODE SNIPPET END LogToScreen("XalGetMaxUsers: hr=%s. result=%d", ConvertHR(hr).c_str(), maxUsers); LogToFile("XalGetMaxUsers: hr=%s. result=%d", ConvertHR(hr).c_str(), maxUsers); // CODE SNIP SKIP return LuaReturnHR(L, hr); } int XalAddUserWithUiAsync_Lua(lua_State *L) { CreateQueueIfNeeded(); // CODE SNIPPET START: XalAddUserWithUiAsync auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->context = nullptr; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* XalUserHandle newUser = nullptr; HRESULT hr = XalAddUserWithUiResult(asyncBlock, &newUser); // TODO: Store and use newUser LogToScreen("XalAddUserWithUiResult: hr=%s user=0x%0.8x", ConvertHR(hr).c_str(), newUser); LogToFile("XalAddUserWithUiResult: hr=%s user=0x%0.8x", ConvertHR(hr).c_str(), newUser); // CODE SNIP SKIP if (Data()->xalUser != nullptr) { XalUserCloseHandle(Data()->xalUser); Data()->xalUser = nullptr; } Data()->xalUser = newUser; // CODE SNIP SKIP if (Data()->xalUser) // CODE SNIP SKIP { // CODE SNIP SKIP Data()->gotXalUser = true; // CODE SNIP SKIP } // CODE SNIP SKIP CallLuaFunctionWithHr(hr, "OnXalAddUserWithUiAsync"); // CODE SNIP SKIP }; HRESULT hr = XalAddUserWithUiAsync(0u, asyncBlock.get()); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } // CODE SNIPPET END LogToScreen("XalAddUserWithUiAsync: hr=%s", ConvertHR(hr).c_str()); LogToFile("XalAddUserWithUiAsync: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XalGetDeviceUserIsPresent_Lua(lua_State *L) { // CODE SNIPPET START: XalGetDeviceUserIsPresent bool result = XalGetDeviceUserIsPresent(); // CODE SNIPPET END LogToScreen("XalGetDeviceUserIsPresent: result=%d", result); LogToFile("XalGetDeviceUserIsPresent: result=%d", result); return LuaReturnHR(L, S_OK); } int XalGetDeviceUser_Lua(lua_State *L) { // CODE SNIPPET START: XalGetDeviceUser XalUserHandle deviceUser = nullptr; HRESULT hr = XalGetDeviceUser(&deviceUser); // CODE SNIPPET END LogToScreen("XalGetDeviceUser: hr=%s", ConvertHR(hr).c_str()); LogToFile("XalGetDeviceUser: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XalSignOutUserAsyncIsPresent_Lua(lua_State *L) { // CODE SNIPPET START: XalSignOutUserAsyncIsPresent bool result = XalSignOutUserAsyncIsPresent(); // CODE SNIPPET END LogToScreen("XalSignOutUserAsyncIsPresent: result=%d", result); LogToFile("XalSignOutUserAsyncIsPresent: result=%d", result); return LuaReturnHR(L, S_OK); } int XalSignOutUserAsync_Lua(lua_State *L) { CreateQueueIfNeeded(); // CODE SNIPPET START: XalSignOutUserAsync auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->context = nullptr; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* HRESULT hr = XAsyncGetStatus(asyncBlock, false); // TODO: Store and use newUser LogToScreen("OnXalSignOutUserAsync: hr=%s", ConvertHR(hr).c_str()); LogToFile("OnXalSignOutUserAsync: hr=%s", ConvertHR(hr).c_str()); // CODE SNIP SKIP XalUserCloseHandle(Data()->xalUser); // CODE SNIP SKIP Data()->xalUser = nullptr; // CODE SNIP SKIP CallLuaFunctionWithHr(hr, "OnXalSignOutUserAsync"); // CODE SNIP SKIP }; HRESULT hr = XalSignOutUserAsync(Data()->xalUser, asyncBlock.get()); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } // CODE SNIPPET END LogToScreen("XalSignOutUserAsync: hr=%s", ConvertHR(hr).c_str()); LogToFile("XalSignOutUserAsync: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XalUserDuplicateHandle_Lua(lua_State *L) { // CODE SNIPPET START: XalUserDuplicateHandle XalUserHandle dupUser = nullptr; HRESULT hr = XalUserDuplicateHandle(Data()->xalUser, &dupUser); // CODE SNIPPET END LogToScreen("XalUserDuplicateHandle: hr=%s", ConvertHR(hr).c_str()); LogToFile("XalUserDuplicateHandle: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XalUserCloseHandle_Lua(lua_State *L) { XalUserHandle xalUser = Data()->xalUser; if (xalUser != nullptr) { // CODE SNIPPET START: XalUserCloseHandle XalUserCloseHandle(xalUser); xalUser = nullptr; // CODE SNIPPET END Data()->xalUser = nullptr; LogToScreen("XalUserCloseHandle"); LogToFile("XalUserCloseHandle"); } return LuaReturnHR(L, S_OK); } int XalUserGetId_Lua(lua_State *L) { if (Data()->xalUser == nullptr) { return LuaReturnHR(L, S_OK); } // CODE SNIPPET START: XalUserGetId uint64_t xboxUserId = 0; HRESULT hr = XalUserGetId(Data()->xalUser, &xboxUserId); // CODE SNIPPET END LogToScreen("XalUserGetId: hr=%s xboxUserId=%llu", ConvertHR(hr).c_str(), xboxUserId); LogToFile("XalUserGetId: hr=%s xboxUserId=%llu", ConvertHR(hr).c_str(), xboxUserId); Data()->xboxUserId = xboxUserId; return LuaReturnHR(L, hr); } int XalUserIsDevice_Lua(lua_State *L) { // CODE SNIPPET START: XalUserIsDevice bool result = XalUserIsDevice(Data()->xalUser); // CODE SNIPPET END LogToScreen("XalUserIsDevice: result=%d", result); LogToFile("XalUserIsDevice: result=%d", result); return LuaReturnHR(L, S_OK); } int XalUserIsGuest_Lua(lua_State *L) { // CODE SNIPPET START: XalUserIsGuest bool result = XalUserIsGuest(Data()->xalUser); // CODE SNIPPET END LogToScreen("XalUserIsGuest: result=%d", result); LogToFile("XalUserIsGuest: result=%d", result); return LuaReturnHR(L, S_OK); } int XalUserGetState_Lua(lua_State *L) { // CODE SNIPPET START: XalUserGetState XalUserState state = {}; HRESULT hr = XalUserGetState(Data()->xalUser, &state); // CODE SNIPPET END LogToScreen("XalUserGetState: hr=%s", ConvertHR(hr).c_str()); LogToFile("XalUserGetState: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XalUserGetGamertag_Lua(lua_State *L) { // CODE SNIPPET START: XalUserGetGamertag size_t gamerTagSize = XalUserGetGamertagSize(Data()->xalUser, XalGamertagComponent_Classic); std::vector gamerTag(gamerTagSize, '\0'); size_t bufferUsed; HRESULT hr = XalUserGetGamertag(Data()->xalUser, XalGamertagComponent_Classic, gamerTagSize, gamerTag.data(), &bufferUsed); // CODE SNIPPET END LogToScreen("XalUserGetGamertag %s: hr=%s gamerTag=%s", gamerTag.data(), ConvertHR(hr).c_str(), gamerTag.data()); LogToFile("XalUserGetGamertag %s: hr=%s gamerTag=%s", gamerTag.data(), ConvertHR(hr).c_str(), gamerTag.data()); Data()->gamertag = std::string(gamerTag.data()); return LuaReturnHR(L, hr); } int XalUserGetAgeGroup_Lua(lua_State *L) { // CODE SNIPPET START: XalUserGetAgeGroup XalAgeGroup ageGroup = {}; HRESULT hr = XalUserGetAgeGroup(Data()->xalUser, &ageGroup); // CODE SNIPPET END LogToScreen("XalUserGetAgeGroup: hr=%s", ConvertHR(hr).c_str()); LogToFile("XalUserGetAgeGroup: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XalUserCheckPrivilege_Lua(lua_State *L) { // CODE SNIPPET START: XalUserCheckPrivilege XalPrivilege privilege = XalPrivilege_Multiplayer; bool hasPrivilege = false; XalPrivilegeCheckDenyReasons reasons = { }; HRESULT hr = XalUserCheckPrivilege(Data()->xalUser, privilege, &hasPrivilege, &reasons); // CODE SNIPPET END LogToScreen("XalUserCheckPrivilege: hr=%s", ConvertHR(hr).c_str()); LogToFile("XalUserCheckPrivilege: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XalUserGetGamerPictureAsync_Lua(lua_State *L) { CreateQueueIfNeeded(); // CODE SNIPPET START: XalUserGetGamerPictureAsync auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->context = nullptr; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* size_t bufferSize; HRESULT hr = XalUserGetGamerPictureResultSize(asyncBlock, &bufferSize); LogToScreen("XalUserGetGamerPictureResultSize: hr=%s bufferSize=%d", ConvertHR(hr).c_str(), bufferSize); LogToFile("XalUserGetGamerPictureResultSize: hr=%s bufferSize=%d", ConvertHR(hr).c_str(), bufferSize); // CODE SNIP SKIP std::vector buffer(bufferSize); hr = XalUserGetGamerPictureResult(asyncBlock, bufferSize, buffer.data()); LogToScreen("XalUserGetGamerPictureResult: hr=%s", ConvertHR(hr).c_str()); LogToFile("XalUserGetGamerPictureResult: hr=%s", ConvertHR(hr).c_str()); // CODE SNIP SKIP CallLuaFunctionWithHr(hr, "OnXalUserGetGamerPictureAsync"); // CODE SNIP SKIP }; HRESULT hr = XalUserGetGamerPictureAsync(Data()->xalUser, XalGamerPictureSize_Small, asyncBlock.get()); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } // CODE SNIPPET END LogToScreen("XalUserGetGamerPictureAsync: hr=%s", ConvertHR(hr).c_str()); LogToFile("XalUserGetGamerPictureAsync: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XalUserGetTokenAndSignatureSilentlyAsync_Lua(lua_State *L) { CreateQueueIfNeeded(); std::string method = GetStringFromLua(L, 1, ""); std::string url = GetStringFromLua(L, 2, ""); // CODE SNIPPET START: XalUserGetTokenAndSignatureSilentlyAsync auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->context = nullptr; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* size_t bufferSize; HRESULT hr = XalUserGetTokenAndSignatureSilentlyResultSize(asyncBlock, &bufferSize); LogToScreen("XalUserGetTokenAndSignatureSilentlyResultSize: hr=%s bufferSize=%d", ConvertHR(hr).c_str(), bufferSize); LogToFile("XalUserGetTokenAndSignatureSilentlyResultSize: hr=%s bufferSize=%d", ConvertHR(hr).c_str(), bufferSize); // CODE SNIP SKIP std::vector buffer(bufferSize); XalUserGetTokenAndSignatureData* result; hr = XalUserGetTokenAndSignatureSilentlyResult(asyncBlock, bufferSize, buffer.data(), &result, nullptr); LogToScreen("XalUserGetTokenAndSignatureSilentlyResult: hr=%s", ConvertHR(hr).c_str()); LogToFile("XalUserGetTokenAndSignatureSilentlyResult: hr=%s", ConvertHR(hr).c_str()); // CODE SNIP SKIP CallLuaFunctionWithHr(hr, "OnXalUserGetTokenAndSignatureSilentlyAsync"); // CODE SNIP SKIP }; XalUserGetTokenAndSignatureArgs args{}; args.method = method.data(); args.url = url.data(); HRESULT hr = XalUserGetTokenAndSignatureSilentlyAsync(Data()->xalUser, &args, asyncBlock.get()); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } // CODE SNIPPET END LogToScreen("XalUserCheckPrivilegesWithUiAsync: hr=%s", ConvertHR(hr).c_str()); LogToFile("XalUserCheckPrivilegesWithUiAsync: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XalUserResolveIssueWithUiAsync_Lua(lua_State *L) { CreateQueueIfNeeded(); // CODE SNIPPET START: XalUserResolveIssueWithUiAsync auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->context = nullptr; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* HRESULT hr = XAsyncGetStatus(asyncBlock, false); LogToScreen("XAsyncGetStatus: hr=%s", ConvertHR(hr).c_str()); LogToFile("XAsyncGetStatus: hr=%s", ConvertHR(hr).c_str()); // CODE SNIP SKIP CallLuaFunctionWithHr(hr, "OnXalUserResolveIssueWithUiAsync"); // CODE SNIP SKIP }; HRESULT hr = XalUserResolveIssueWithUiAsync(Data()->xalUser, "https://www.xboxlive.com", asyncBlock.get()); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } // CODE SNIPPET END LogToScreen("XalUserResolveIssueWithUiAsync: hr=%s", ConvertHR(hr).c_str()); LogToFile("XalUserResolveIssueWithUiAsync: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } // CODE SNIPPET START: XalUserRegisterChangeEventHandler void OnXalUserChangeEventHandler( _In_opt_ void* context, _In_ XalUserLocalId userId, _In_ XalUserChangeType change) { UNREFERENCED_PARAMETER(context); UNREFERENCED_PARAMETER(userId); UNREFERENCED_PARAMETER(change); } #if HC_PLATFORM == HC_PLATFORM_GDK void CALLBACK OnXalUserChangeEventHandler_GDK( _In_opt_ void* context, _In_ XUserLocalId userLocalId, _In_ XUserChangeEvent event) { UNREFERENCED_PARAMETER(context); UNREFERENCED_PARAMETER(userLocalId); UNREFERENCED_PARAMETER(event); } #endif // CODE SNIPPET END int XalUserRegisterChangeEventHandler_Lua(lua_State *L) { CreateQueueIfNeeded(); void* context = nullptr; XalRegistrationToken token = { 0 }; // CODE SNIPPET START: XalUserRegisterChangeEventHandler #if HC_PLATFORM == HC_PLATFORM_GDK HRESULT hr = XalUserRegisterChangeEventHandler(Data()->queue, context, OnXalUserChangeEventHandler_GDK, &token); #else HRESULT hr = XalUserRegisterChangeEventHandler(Data()->queue, context, OnXalUserChangeEventHandler, &token); #endif // CODE SNIPPET END LogToScreen("XalUserRegisterChangeEventHandler: hr=%s", ConvertHR(hr).c_str()); LogToFile("XalUserRegisterChangeEventHandler: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XalUserUnregisterChangeEventHandler_Lua(lua_State *L) { XalRegistrationToken token{0}; // CODE SNIPPET START: XalUserUnregisterChangeEventHandler XalUserUnregisterChangeEventHandler(token); // CODE SNIPPET END LogToScreen("XalUserUnregisterChangeEventHandler"); LogToFile("XalUserUnregisterChangeEventHandler"); return LuaReturnHR(L, S_OK); } #if HC_PLATFORM == HC_PLATFORM_GDK || HC_PLATFORM == HC_PLATFORM_IOS || HC_PLATFORM == HC_PLATFORM_UWP || HC_PLATFORM == HC_PLATFORM_XDK || HC_PLATFORM == HC_PLATFORM_ANDROID int XalPlatformWebSetEventHandler_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } int XalPlatformStorageSetEventHandlers_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } #endif void SetupAPIs_Xal() { lua_register(Data()->L, "XalInitialize", XalInitialize_Lua); lua_register(Data()->L, "XalCleanupAsync", XalCleanupAsync_Lua); lua_register(Data()->L, "XalPlatformWebSetEventHandler", XalPlatformWebSetEventHandler_Lua); lua_register(Data()->L, "XalPlatformStorageSetEventHandlers", XalPlatformStorageSetEventHandlers_Lua); lua_register(Data()->L, "XalGetMaxUsers", XalGetMaxUsers_Lua); lua_register(Data()->L, "XalTryAddFirstUserSilentlyAsync", XalTryAddFirstUserSilentlyAsync_Lua); lua_register(Data()->L, "XalAddUserWithUiAsync", XalAddUserWithUiAsync_Lua); lua_register(Data()->L, "XalGetDeviceUserIsPresent", XalGetDeviceUserIsPresent_Lua); lua_register(Data()->L, "XalGetDeviceUser", XalGetDeviceUser_Lua); lua_register(Data()->L, "XalSignOutUserAsyncIsPresent", XalSignOutUserAsyncIsPresent_Lua); lua_register(Data()->L, "XalSignOutUserAsync", XalSignOutUserAsync_Lua); lua_register(Data()->L, "XalUserDuplicateHandle", XalUserDuplicateHandle_Lua); lua_register(Data()->L, "XalUserCloseHandle", XalUserCloseHandle_Lua); lua_register(Data()->L, "XalUserGetId", XalUserGetId_Lua); lua_register(Data()->L, "XalUserIsDevice", XalUserIsDevice_Lua); lua_register(Data()->L, "XalUserIsGuest", XalUserIsGuest_Lua); lua_register(Data()->L, "XalUserGetState", XalUserGetState_Lua); lua_register(Data()->L, "XalUserGetGamertag", XalUserGetGamertag_Lua); lua_register(Data()->L, "XalUserGetGamerPictureAsync", XalUserGetGamerPictureAsync_Lua); lua_register(Data()->L, "XalUserGetAgeGroup", XalUserGetAgeGroup_Lua); lua_register(Data()->L, "XalUserCheckPrivilege", XalUserCheckPrivilege_Lua); lua_register(Data()->L, "XalUserGetTokenAndSignatureSilentlyAsync", XalUserGetTokenAndSignatureSilentlyAsync_Lua); lua_register(Data()->L, "XalUserResolveIssueWithUiAsync", XalUserResolveIssueWithUiAsync_Lua); lua_register(Data()->L, "XalUserRegisterChangeEventHandler", XalUserRegisterChangeEventHandler_Lua); lua_register(Data()->L, "XalUserUnregisterChangeEventHandler", XalUserUnregisterChangeEventHandler_Lua); } ================================================ FILE: Tests/ApiExplorer/APIs/apis_xblc.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" void StopSocialManagerDoWorkHelper(); void StopAchievementsManagerDoWorkHelper(); #if CPP_TESTS_ENABLED void StopSocialManagerDoWorkHelperCpp(); #endif int XblInitialize_Lua(lua_State *L) { bool bSetQueue = GetBoolFromLua(L, 1, true); CreateQueueIfNeeded(); // CODE SNIPPET START: XblInitialize XblInitArgs args = { }; args.queue = Data()->queue; // CODE SKIP START if(!bSetQueue) { //Use a default task queue. If the global task queue has been initialized to null, //trying to use a default task queue for XblInitialize will return E_NO_TASK_QUEUE. args.queue = nullptr; } // CODE SKIP END #if !(HC_PLATFORM == HC_PLATFORM_XDK || HC_PLATFORM == HC_PLATFORM_UWP) args.scid = "00000000-0000-0000-0000-000076029b4d"; // Alternate SCID for XboxLiveE2E Stats 2017 config // args.scid = "00000000-0000-0000-0000-000078c0191b" #endif #if HC_PLATFORM == HC_PLATFORM_WIN32 char pathArray[MAX_PATH + 1]; GetCurrentDirectoryA(MAX_PATH + 1, pathArray); auto pathString = std::string{ pathArray } + '\\'; args.localStoragePath = pathString.data(); #endif #if HC_PLATFORM == HC_PLATFORM_ANDROID args.applicationContext = Data()->applicationContext; args.javaVM = Data()->javaVM; #endif HRESULT hr = XblInitialize(&args); // CODE SNIPPET END if (SUCCEEDED(hr)) { XblDisableAssertsForXboxLiveThrottlingInDevSandboxes(XblConfigSetting::ThisCodeNeedsToBeChanged); } LogToFile("XblInitialize: %s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblGetScid_Lua(lua_State *L) { CreateQueueIfNeeded(); // CODE SNIPPET START: XblGetScid HRESULT hr = XblGetScid(&Data()->scid); // CODE SNIPPET END LogToFile("XblGetScid: %s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblDisableAssertsForXboxLiveThrottlingInDevSandboxes_Lua(lua_State *L) { // CODE SNIPPET START: XblDisableAssertsForXboxLiveThrottlingInDevSandboxes XblDisableAssertsForXboxLiveThrottlingInDevSandboxes(XblConfigSetting::ThisCodeNeedsToBeChanged); // CODE SNIPPET END LogToFile("XblDisableAssertsForXboxLiveThrottlingInDevSandboxes Complete."); return LuaReturnHR(L, S_OK); } _Ret_maybenull_ _Post_writable_byte_size_(size) void* STDAPIVCALLTYPE ApiRunnerMemAlloc( _In_ size_t size, _In_ HCMemoryType ) { return new (std::nothrow) char[size]; } void STDAPIVCALLTYPE ApiRunnerMemFree( _In_ _Post_invalid_ void* pointer, _In_ HCMemoryType ) { delete[] pointer; } XblMemAllocFunction g_apiRunnerMemAllocFunc = nullptr; XblMemFreeFunction g_apiRunnerMemFreeFunc = nullptr; int XblMemSetFunctions_Lua(lua_State *L) { // CODE SNIPPET START: XblMemSetFunctions HRESULT hr = XblMemSetFunctions(&ApiRunnerMemAlloc, &ApiRunnerMemFree); // CODE SNIPPET END LogToFile("XblMemSetFunctions: %s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblMemGetFunctions_Lua(lua_State *L) { // CODE SNIPPET START: XblMemGetFunctions XblMemAllocFunction memAllocFunc = nullptr; XblMemFreeFunction memFreeFunc = nullptr; HRESULT hr = XblMemGetFunctions(&memAllocFunc, &memFreeFunc); // CODE SNIPPET END g_apiRunnerMemAllocFunc = memAllocFunc; g_apiRunnerMemFreeFunc = memFreeFunc; LogToFile("XblMemGetFunctions: %s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblCleanupAsync_Lua(lua_State *L) { #if HC_PLATFORM == HC_PLATFORM_UWP Sleep(1000); #endif // CODE SNIPPET START: XblCleanupAsync auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; HRESULT hr = XblCleanupAsync(asyncBlock.get()); if (SUCCEEDED(hr)) { // Synchronously wait for cleanup to complete hr = XAsyncGetStatus(asyncBlock.get(), true); } // CODE SNIPPET END LogToFile("XblCleanup: hr=%s", ConvertHR(hr).data()); // Ignore not init'd error if (hr == E_XBL_NOT_INITIALIZED) { hr = S_OK; } return LuaReturnHR(L, S_OK); } int XblGetErrorCondition_Lua(lua_State *L) { HRESULT hrIn = static_cast(GetUint32FromLua(L, 1, static_cast(E_FAIL))); XblErrorCondition errc = XblGetErrorCondition(hrIn); lua_pushinteger(L, (int)errc); return LuaReturnHR(L, S_OK, 1); } int XblContextCreateHandle_Lua(lua_State *L) { // CODE SNIPPET START: XblContextCreateHandle XblContextHandle contextHandle{ nullptr }; HRESULT hr = XblContextCreateHandle(Data()->xalUser, &contextHandle); // CODE SNIPPET END // Cache the created handle for use in other APIs if we don't have one already if (Data()->xboxLiveContext == nullptr) { Data()->xboxLiveContext = contextHandle; } else { LogToFile("XblContextCreateHandle called but an existing handle was cached"); } lua_pushinteger(L, (int64_t)contextHandle); LogToFile("XblContextCreateHandle: %s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr, 1); } int XblContextDuplicateHandle_Lua(lua_State *L) { // CODE SNIPPET START: XblContextCreateHandle XblContextHandle duplicate{}; HRESULT hr = XblContextDuplicateHandle(Data()->xboxLiveContext, &duplicate); // CODE SNIPPET END if (SUCCEEDED(hr)) { XblContextCloseHandle(Data()->xboxLiveContext); Data()->xboxLiveContext = duplicate; } LogToFile("XblContextDuplicateHandle: %s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblContextCloseHandle_Lua(lua_State *L) { StopSocialManagerDoWorkHelper(); StopAchievementsManagerDoWorkHelper(); #if CPP_TESTS_ENABLED StopSocialManagerDoWorkHelperCpp(); #endif // Get the XblContextHandle XblContextHandle handleToClose = (XblContextHandle)GetUint64FromLua(L, 1, (uint64_t)Data()->xboxLiveContext); // CODE SNIPPET START: XblContextCloseHandle if (handleToClose != nullptr) { XblContextCloseHandle(handleToClose); } // CODE SNIPPET END if (handleToClose == Data()->xboxLiveContext) { Data()->xboxLiveContext = nullptr; } LogToFile("OnXblContextCloseHandle called."); return LuaReturnHR(L, S_OK); } // CODE SNIPPET START: XblCallRoutedHandler void Test_XblCallRoutedHandler( _In_ XblServiceCallRoutedArgs, _In_ void*) { } // CODE SNIPPET START: XblCallRoutedHandler int XblAddServiceCallRoutedHandler_Lua(lua_State *L) { // CODE SNIPPET START: XblContextSettingsAddServiceCallRoutedHandler XblFunctionContext fn = XblAddServiceCallRoutedHandler(Test_XblCallRoutedHandler, nullptr); // CODE SNIPPET END Data()->fnAddServiceCallRoutedHandler = fn; LogToFile("XblContextSettingsAddServiceCallRoutedHandler_Lua"); return LuaReturnHR(L, S_OK); } int XblSetOverrideLocale_Lua(lua_State *L) { // CODE SNIPPET START: XblSetOverrideLocale XblSetOverrideLocale("fr-FR"); // CODE SNIPPET END LogToFile("XblSetOverrideLocale_Lua"); return LuaReturnHR(L, S_OK); } int XblRemoveServiceCallRoutedHandler_Lua(lua_State *L) { // CODE SNIPPET START: XblContextSettingsRemoveServiceCallRoutedHandler XblRemoveServiceCallRoutedHandler(Data()->fnAddServiceCallRoutedHandler); // CODE SNIPPET END Data()->fnAddServiceCallRoutedHandler = 0; LogToFile("XblContextSettingsRemoveServiceCallRoutedHandler_Lua"); return LuaReturnHR(L, S_OK); } int XblContextSettingsGetLongHttpTimeout_Lua(lua_State *L) { // CODE SNIPPET START: XblContextSettingsGetLongHttpTimeout_Lua uint32_t timeoutInSeconds = 0; XblContextSettingsGetLongHttpTimeout(Data()->xboxLiveContext, &timeoutInSeconds); // CODE SNIPPET END LogToFile("XblContextSettingsGetLongHttpTimeout_Lua: %d", timeoutInSeconds); return LuaReturnHR(L, S_OK); } int XblContextSettingsSetLongHttpTimeout_Lua(lua_State *L) { uint32_t timeoutInSeconds = GetUint32FromLua(L, 1, 500); // CODE SNIPPET START: XblContextSettingsSetLongHttpTimeout_Lua HRESULT hr = XblContextSettingsSetLongHttpTimeout(Data()->xboxLiveContext, timeoutInSeconds); // CODE SNIPPET END LogToFile("XblContextSettingsSetLongHttpTimeout_Lua"); return LuaReturnHR(L, hr); } int XblContextSettingsGetHttpRetryDelay_Lua(lua_State *L) { // CODE SNIPPET START: XblContextSettingsGetHttpRetryDelay_Lua uint32_t delayInSeconds = 0; HRESULT hr = XblContextSettingsGetHttpRetryDelay(Data()->xboxLiveContext, &delayInSeconds); // CODE SNIPPET END UNREFERENCED_PARAMETER(hr); LogToFile("XblContextSettingsGetHttpRetryDelay_Lua: %ul", delayInSeconds); return LuaReturnHR(L, S_OK); } int XblContextSettingsSetHttpRetryDelay_Lua(lua_State *L) { uint32_t delayInSeconds = GetUint32FromLua(L, 1, 300); // CODE SNIPPET START: XblContextSettingsSetHttpRetryDelay_Lua HRESULT hr = XblContextSettingsSetHttpRetryDelay(Data()->xboxLiveContext, delayInSeconds); // CODE SNIPPET END LogToFile("XblContextSettingsSetHttpRetryDelay_Lua"); return LuaReturnHR(L, hr); } int XblContextSettingsGetHttpTimeoutWindow_Lua(lua_State *L) { // CODE SNIPPET START: XblContextSettingsGetHttpTimeoutWindow_Lua uint32_t delayInSeconds = 0; HRESULT hr = XblContextSettingsGetHttpTimeoutWindow(Data()->xboxLiveContext, &delayInSeconds); // CODE SNIPPET END LogToFile("XblContextSettingsGetHttpTimeoutWindow_Lua %d", delayInSeconds); return LuaReturnHR(L, hr); } int XblContextSettingsSetHttpTimeoutWindow_Lua(lua_State *L) { uint32_t timeoutWindowInSeconds = GetUint32FromLua(L, 1, 200); // CODE SNIPPET START: XblContextSettingsSetHttpTimeoutWindow_Lua HRESULT hr = XblContextSettingsSetHttpTimeoutWindow(Data()->xboxLiveContext, timeoutWindowInSeconds); // CODE SNIPPET END LogToFile("XblContextSettingsSetHttpTimeoutWindow_Lua: %d", timeoutWindowInSeconds); return LuaReturnHR(L, hr); } int XblContextSettingsGetWebsocketTimeoutWindow_Lua(lua_State *L) { // CODE SNIPPET START: XblContextSettingsGetWebsocketTimeoutWindow_Lua uint32_t timeoutWindowInSeconds = 0; HRESULT hr = XblContextSettingsGetHttpTimeoutWindow(Data()->xboxLiveContext, &timeoutWindowInSeconds); // CODE SNIPPET END LogToFile("XblContextSettingsGetWebsocketTimeoutWindow_Lua: %d", timeoutWindowInSeconds); return LuaReturnHR(L, hr); } int XblContextSettingsSetWebsocketTimeoutWindow_Lua(lua_State *L) { uint32_t timeoutWindowInSeconds = GetUint32FromLua(L, 1, 200); // CODE SNIPPET START: XblContextSettingsSetWebsocketTimeoutWindow_Lua HRESULT hr = XblContextSettingsSetWebsocketTimeoutWindow(Data()->xboxLiveContext, timeoutWindowInSeconds); // CODE SNIPPET END LogToFile("XblContextSettingsSetWebsocketTimeoutWindow_Lua"); return LuaReturnHR(L, hr); } int XblContextSettingsGetUseCrossPlatformQosServers_Lua(lua_State *L) { // CODE SNIPPET START: XblContextSettingsGetUseCrossPlatformQosServers_Lua bool useQosServers = 0; HRESULT hr = XblContextSettingsGetUseCrossPlatformQosServers(Data()->xboxLiveContext, &useQosServers); // CODE SNIPPET END UNREFERENCED_PARAMETER(hr); LogToFile("XblContextSettingsGetUseCrossPlatformQosServers_Lua: %d", useQosServers); return LuaReturnHR(L, S_OK); } int XblContextSettingsSetUseCrossPlatformQosServers_Lua(lua_State *L) { bool useQosServers = GetBoolFromLua(L, 1, true); // CODE SNIPPET START: XblContextSettingsSetUseCrossPlatformQosServers_Lua HRESULT hr = XblContextSettingsGetUseCrossPlatformQosServers(Data()->xboxLiveContext, &useQosServers); // CODE SNIPPET END UNREFERENCED_PARAMETER(hr); LogToFile("XblContextSettingsSetUseCrossPlatformQosServers_Lua"); return LuaReturnHR(L, S_OK); } void SetupAPIs_Xbl() { // xbox_live_global_c.h lua_register(Data()->L, "XblInitialize", XblInitialize_Lua); lua_register(Data()->L, "XblGetScid", XblGetScid_Lua); lua_register(Data()->L, "XblCleanupAsync", XblCleanupAsync_Lua); lua_register(Data()->L, "XblMemSetFunctions", XblMemSetFunctions_Lua); lua_register(Data()->L, "XblMemGetFunctions", XblMemGetFunctions_Lua); lua_register(Data()->L, "XblDisableAssertsForXboxLiveThrottlingInDevSandboxes", XblDisableAssertsForXboxLiveThrottlingInDevSandboxes_Lua); lua_register(Data()->L, "XblAddServiceCallRoutedHandler", XblAddServiceCallRoutedHandler_Lua); lua_register(Data()->L, "XblRemoveServiceCallRoutedHandler", XblRemoveServiceCallRoutedHandler_Lua); lua_register(Data()->L, "XblSetOverrideLocale", XblSetOverrideLocale_Lua); // errors_c.h lua_register(Data()->L, "XblGetErrorCondition", XblGetErrorCondition_Lua); // xbox_live_context_c.h lua_register(Data()->L, "XblContextCreateHandle", XblContextCreateHandle_Lua); lua_register(Data()->L, "XblContextDuplicateHandle", XblContextDuplicateHandle_Lua); lua_register(Data()->L, "XblContextCloseHandle", XblContextCloseHandle_Lua); // TBD: XblContextGetUser // TBD: XblContextGetXboxUserId // xbox_live_context_settings_c.h lua_register(Data()->L, "XblContextSettingsGetLongHttpTimeout", XblContextSettingsGetLongHttpTimeout_Lua); lua_register(Data()->L, "XblContextSettingsSetLongHttpTimeout", XblContextSettingsSetLongHttpTimeout_Lua); lua_register(Data()->L, "XblContextSettingsGetHttpRetryDelay", XblContextSettingsGetHttpRetryDelay_Lua); lua_register(Data()->L, "XblContextSettingsSetHttpRetryDelay", XblContextSettingsSetHttpRetryDelay_Lua); lua_register(Data()->L, "XblContextSettingsGetHttpTimeoutWindow", XblContextSettingsGetHttpTimeoutWindow_Lua); lua_register(Data()->L, "XblContextSettingsSetHttpTimeoutWindow", XblContextSettingsSetHttpTimeoutWindow_Lua); lua_register(Data()->L, "XblContextSettingsGetWebsocketTimeoutWindow", XblContextSettingsGetWebsocketTimeoutWindow_Lua); lua_register(Data()->L, "XblContextSettingsSetWebsocketTimeoutWindow", XblContextSettingsSetWebsocketTimeoutWindow_Lua); lua_register(Data()->L, "XblContextSettingsGetUseCrossPlatformQosServers", XblContextSettingsGetUseCrossPlatformQosServers_Lua); lua_register(Data()->L, "XblContextSettingsSetUseCrossPlatformQosServers", XblContextSettingsSetUseCrossPlatformQosServers_Lua); } ================================================ FILE: Tests/ApiExplorer/APIs/apis_xblc_achievement_unlock_notification.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #if HC_PLATFORM == HC_PLATFORM_WIN32 namespace xbl { namespace apirunner { enum status { OK = 0, ERROR_NO_MSG }; static status errorCode; HRESULT GetAchievementLockStatus(const std::string& id, bool& isLocked) { LogToScreen("GetAchievement [%s]", id.c_str()); CreateQueueIfNeeded(); std::unique_ptr asyncBlock = std::make_unique(); HRESULT hr = XblAchievementsGetAchievementAsync( Data()->xboxLiveContext, Data()->xboxUserId, Data()->scid, id.c_str(), asyncBlock.get()); // wait for get achievement to complete XAsyncGetStatus(asyncBlock.get(), true); XblAchievementsResultHandle resultHandle; hr = XblAchievementsGetAchievementResult(asyncBlock.get(), &resultHandle); if (hr < 0) { return hr; } const XblAchievement* achievements = nullptr; size_t achievementsCount = 0; hr = XblAchievementsResultGetAchievements(resultHandle, &achievements, &achievementsCount); LogToFile("Got achievementsCount: %d", achievementsCount); if (achievementsCount != 1) { return hr; } if (achievements[0].progressState == XblAchievementProgressState::Achieved) { isLocked = false; } else { isLocked = true; } const char * state = (isLocked) ? "Not achieved" : "Achieved"; LogToScreen( "Achievement id=[%s] name=[%s] state[%s]", achievements[0].id, achievements[0].name, state); return hr; } HRESULT UnlockAchievement(std::string id) { LogToScreen("UnlockAchievement [%s]", id.c_str()); CreateQueueIfNeeded(); std::unique_ptr asyncBlock = std::make_unique(); uint32_t percent = 100; HRESULT hr = XblAchievementsUpdateAchievementAsync( Data()->xboxLiveContext, Data()->xboxUserId, id.c_str(), percent, asyncBlock.get()); XAsyncGetStatus(asyncBlock.get(), true); XblAchievementsResultHandle resultHandle; hr = XblAchievementsGetAchievementResult(asyncBlock.get(), &resultHandle); return hr; } void CALLBACK AchievementUnlockHandler(const XblAchievementUnlockEvent* args, void* context) { UNREFERENCED_PARAMETER(context); LogToScreen("Achievement Unlock Notification"); LogToScreen("titleId: %u", args->titleId); LogToScreen("achievementName: %s", args->achievementName); LogToScreen("achievementIcon: %s", args->achievementIcon); LogToScreen("gamerscore: %u", args->gamerscore); LogToScreen("rarityPercentage: %f", args->rarityPercentage); LogToScreen("rarityCategory: %u", static_cast(args->rarityCategory)); errorCode = status::OK; } XblFunctionContext SetHandler() { XblFunctionContext id = XblAchievementUnlockAddNotificationHandler(Data()->xboxLiveContext, AchievementUnlockHandler, nullptr); return id; } void UnsetHandler(XblFunctionContext id) { XblAchievementUnlockRemoveNotificationHandler(Data()->xboxLiveContext, id); } namespace lua { int XblAchievementUnlockAddNotificationHandler(lua_State *L) { auto id = SetHandler(); lua_pushinteger(L, id); LuaReturnHR(L, S_OK, 1); return 0; } int XblAchievementUnlockRemoveNotificationHandler(lua_State *L) { uint32_t id = GetUint32FromLua(L, 1, 0); UnsetHandler(id); return 0; } int RunAchievementUnlock(lua_State *L) { std::string achievementId = luaL_checkstring(L, 1); HRESULT hr = 0; errorCode = status::ERROR_NO_MSG; Sleep(1000); hr = UnlockAchievement(achievementId); return LuaReturnHR(L, hr); } int Cleanup(lua_State *L) { uint32_t id = GetUint32FromLua(L, 1, 0); LogToScreen("Got %d", id); UnsetHandler(id); return 0; } int CheckStatus(lua_State *L) { lua_pushinteger(L, errorCode); return 1; } int IsAchievementLocked(lua_State *L) { std::string achievementId = luaL_checkstring(L, 1); bool isLocked = false; HRESULT hr = GetAchievementLockStatus(achievementId, isLocked); lua_pushboolean(L, isLocked); return LuaReturnHR(L, hr, 1); } } } } void SetupAPIs_XblAchievementUnlockNotification() { lua_register(Data()->L, "RunAchievementUnlock", xbl::apirunner::lua::RunAchievementUnlock); lua_register(Data()->L, "Cleanup", xbl::apirunner::lua::Cleanup); lua_register(Data()->L, "CheckStatus", xbl::apirunner::lua::CheckStatus); lua_register(Data()->L, "IsAchievementLocked", xbl::apirunner::lua::IsAchievementLocked); lua_register(Data()->L, "XblAchievementUnlockAddNotificationHandler", xbl::apirunner::lua::XblAchievementUnlockAddNotificationHandler); lua_register(Data()->L, "XblAchievementUnlockRemoveNotificationHandler", xbl::apirunner::lua::XblAchievementUnlockRemoveNotificationHandler); } #endif ================================================ FILE: Tests/ApiExplorer/APIs/apis_xblc_achievements.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" std::string AchievementProgressToString(XblAchievement achievement) { std::stringstream stream; if (achievement.progressState == XblAchievementProgressState::Achieved) { stream << "Achieved (" << achievement.progression.timeUnlocked << ")"; } else if (achievement.progressState == XblAchievementProgressState::InProgress) { stream << "Started"; stream << "(" << achievement.progression.requirements[0].currentProgressValue << "/" << achievement.progression.requirements[0].targetProgressValue << ")"; } else if (achievement.progressState == XblAchievementProgressState::NotStarted) { stream << "Not Started"; } else if (achievement.progressState == XblAchievementProgressState::Unknown) { stream << "Unknown"; } return stream.str(); } XblAchievementType ConvertStringToXblAchievementType(const char* str) { XblAchievementType type = XblAchievementType::Unknown; if (pal::stricmp(str, "XblAchievementType::Unknown") == 0) type = XblAchievementType::Unknown; else if (pal::stricmp(str, "XblAchievementType::All") == 0) type = XblAchievementType::All; else if (pal::stricmp(str, "XblAchievementType::Persistent") == 0) type = XblAchievementType::Persistent; else if (pal::stricmp(str, "XblAchievementType::Challenge") == 0) type = XblAchievementType::Challenge; return type; } XblAchievementOrderBy ConvertStringToXblAchievementOrderBy(const char* str) { XblAchievementOrderBy orderBy = XblAchievementOrderBy::DefaultOrder; if (pal::stricmp(str, "XblAchievementOrderBy::DefaultOrder") == 0) orderBy = XblAchievementOrderBy::DefaultOrder; else if (pal::stricmp(str, "XblAchievementOrderBy::TitleId") == 0) orderBy = XblAchievementOrderBy::TitleId; else if (pal::stricmp(str, "XblAchievementOrderBy::UnlockTime") == 0) orderBy = XblAchievementOrderBy::UnlockTime; return orderBy; } // commands int XblAchievementsResultGetAchievements_Lua(lua_State *L) { // CODE SNIPPET START: XblAchievementsResultGetAchievements const XblAchievement* achievements; size_t achievementsCount; HRESULT hr = XblAchievementsResultGetAchievements(Data()->achievementsResult, &achievements, &achievementsCount); // CODE SNIPPET END LogToFile("XblAchievementsResultGetAchievements: hr=%s", ConvertHR(hr).c_str()); if (SUCCEEDED(hr)) { std::stringstream stream; for (uint32_t i = 0; i < achievementsCount; i++) { stream << "\t" << achievements[i].name << "(" << AchievementProgressToString(achievements[i]) << ")\n"; } LogToFile(stream.str().c_str()); } return LuaReturnHR(L, hr); } int XblAchievementsResultHasNext_Lua(lua_State *L) { // CODE SNIPPET START: XblAchievementsResultHasNext HRESULT hr = S_OK; bool hasNext = false; if (Data()->achievementsResult != nullptr) { hr = XblAchievementsResultHasNext(Data()->achievementsResult, &hasNext); } // CODE SNIPPET END LogToFile("XblAchievementsResultHasNext: hr=%s hasNext=%s", ConvertHR(hr).c_str(), hasNext ? "true" : "false"); lua_pushnumber(L, (int)hasNext); return LuaReturnHR(L, hr, 1); } int XblAchievementsResultGetNextAsync_Lua(lua_State *L) { CreateQueueIfNeeded(); uint32_t maxItems = GetUint32FromLua(L, 1, 100); LogToFile("XblAchievementsResultGetNextAsync: MaxItems: %d", maxItems); // CODE SNIPPET START: XblAchievementsResultGetNextAsync auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->context = nullptr; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* XblAchievementsResultHandle resultHandle; auto hr = XblAchievementsResultGetNextResult(asyncBlock, &resultHandle); LogToFile("XblAchievementsResultGetNextResult: hr=%s", ConvertHR(hr).c_str()); // CODE SNIP SKIP if (SUCCEEDED(hr)) { // CODE SKIP START if (Data()->achievementsResult) { XblAchievementsResultCloseHandle(Data()->achievementsResult); } XblAchievementsResultDuplicateHandle(resultHandle, &Data()->achievementsResult); // CODE SKIP END const XblAchievement* achievements = nullptr; size_t achievementsCount = 0; hr = XblAchievementsResultGetAchievements(resultHandle, &achievements, &achievementsCount); LogToFile("OnXblAchievementsGetAchievementsForTitleIdAsync: Got achievementsCount: %d", achievementsCount); // CODE SNIP SKIP for (size_t i = 0; i < achievementsCount; i++) { LogToScreen("Achievement %s: %s = %s", achievements[i].id, achievements[i].name, (achievements[i].progressState == XblAchievementProgressState::Achieved) ? "Achieved" : "Not achieved"); } XblAchievementsResultCloseHandle(resultHandle); // when done with handle, close it achievements = nullptr; // Clear array after calling XblAchievementsResultCloseHandle to pointer to freed memory } CallLuaFunctionWithHr(hr, "OnXblAchievementsResultGetNextAsync"); // CODE SNIP SKIP }; HRESULT hr = XblAchievementsResultGetNextAsync( Data()->achievementsResult, maxItems, asyncBlock.get() ); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } // CODE SNIPPET END LogToFile("XblAchievementsResultGetNextAsync: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblAchievementsGetAchievementsForTitleIdAsync_Lua(lua_State *L) { CreateQueueIfNeeded(); XblAchievementType achievementType = ConvertStringToXblAchievementType(GetStringFromLua(L, 1, "XblAchievementType::All").c_str()); bool unlockedOnly = GetBoolFromLua(L, 2, false); XblAchievementOrderBy orderBy = ConvertStringToXblAchievementOrderBy(GetStringFromLua(L, 3, "XblAchievementOrderBy::DefaultOrder").c_str()); uint32_t skipItems = GetUint32FromLua(L, 4, 0); uint32_t maxItems = GetUint32FromLua(L, 5, 100); LogToFile("XblAchievementsGetAchievementsForTitleIdAsync: AchievementType: %d", achievementType); LogToFile("XblAchievementsGetAchievementsForTitleIdAsync: unlockedOnly: %s", unlockedOnly ? "true" : "false"); LogToFile("XblAchievementsGetAchievementsForTitleIdAsync: OrderBy: %d", orderBy); LogToFile("XblAchievementsGetAchievementsForTitleIdAsync: SkipItems: %d", skipItems); LogToFile("XblAchievementsGetAchievementsForTitleIdAsync: MaxItems: %d", maxItems); // CODE SNIPPET START: XblAchievementsGetAchievementsForTitleIdAsync auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->context = nullptr; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* XblAchievementsResultHandle resultHandle; auto hr = XblAchievementsGetAchievementsForTitleIdResult(asyncBlock, &resultHandle); LogToFile("XblAchievementsGetAchievementsForTitleIdResult: hr=%s", ConvertHR(hr).c_str()); // CODE SNIP SKIP if (SUCCEEDED(hr)) { // CODE SKIP START if (Data()->achievementsResult) { XblAchievementsResultCloseHandle(Data()->achievementsResult); } XblAchievementsResultDuplicateHandle(resultHandle, &Data()->achievementsResult); // CODE SKIP END const XblAchievement* achievements = nullptr; size_t achievementsCount = 0; hr = XblAchievementsResultGetAchievements(resultHandle, &achievements, &achievementsCount); LogToFile("OnXblAchievementsGetAchievementsForTitleIdAsync: Got achievementsCount: %d", achievementsCount); // CODE SNIP SKIP for (size_t i = 0; i < achievementsCount; i++) { LogToScreen("Achievement %s: %s = %s", achievements[i].id, achievements[i].name, (achievements[i].progressState == XblAchievementProgressState::Achieved) ? "Achieved" : "Not achieved"); } XblAchievementsResultCloseHandle(resultHandle); // when done with handle, close it achievements = nullptr; // Clear array after calling XblAchievementsResultCloseHandle to pointer to freed memory // Instead you could not close the handle and store it. Then // if you needed to copy the handle, call XblAchievementsResultDuplicateHandle() } CallLuaFunctionWithHr(hr, "OnXblAchievementsGetAchievementsForTitleIdAsync"); // CODE SNIP SKIP }; HRESULT hr = XblAchievementsGetAchievementsForTitleIdAsync( Data()->xboxLiveContext, Data()->xboxUserId, Data()->titleId, achievementType, unlockedOnly, orderBy, skipItems, maxItems, asyncBlock.get() ); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } // CODE SNIPPET END LogToFile("XblAchievementsGetAchievementsForTitleIdAsync: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblAchievementsResultCloseHandle_Lua(lua_State *L) { if (Data()->achievementsResult) { XblAchievementsResultCloseHandle(Data()->achievementsResult); Data()->achievementsResult = nullptr; } return LuaReturnHR(L, S_OK); } int XblAchievementsGetAchievementAsync_Lua(lua_State *L) { CreateQueueIfNeeded(); auto achievementId = GetStringFromLua(L, 1, "1"); LogToFile("XblAchievementsGetAchievementAsync: AchievementId: %s", achievementId.c_str()); // CODE SNIPPET START: XblAchievementsGetAchievementAsync auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->context = nullptr; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* XblAchievementsResultHandle resultHandle; auto hr = XblAchievementsGetAchievementResult(asyncBlock, &resultHandle); LogToFile("XblAchievementsGetAchievementResult: hr=%s", ConvertHR(hr).c_str()); // CODE SNIP SKIP if (SUCCEEDED(hr)) { // CODE SKIP START if (Data()->achievementsResult) { XblAchievementsResultCloseHandle(Data()->achievementsResult); } XblAchievementsResultDuplicateHandle(resultHandle, &Data()->achievementsResult); // CODE SKIP END const XblAchievement* achievements = nullptr; size_t achievementsCount = 0; hr = XblAchievementsResultGetAchievements( resultHandle, &achievements, &achievementsCount ); for (size_t i = 0; i < achievementsCount; i++) { LogToScreen("Achievement %s: %s = %s", achievements[i].id, achievements[i].name, (achievements[i].progressState == XblAchievementProgressState::Achieved) ? "Achieved" : "Not achieved"); } XblAchievementsResultCloseHandle(resultHandle); // when done with handle, close it achievements = nullptr; // Clear array after calling XblAchievementsResultCloseHandle to pointer to freed memory } CallLuaFunctionWithHr(hr, "OnXblAchievementsGetAchievementAsync"); // CODE SNIP SKIP }; HRESULT hr = XblAchievementsGetAchievementAsync( Data()->xboxLiveContext, Data()->xboxUserId, Data()->scid, achievementId.c_str(), asyncBlock.get() ); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } // CODE SNIPPET END LogToFile("XblAchievementsGetAchievementAsync: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblAchievementsUpdateAchievementAsync_Lua(lua_State *L) { CreateQueueIfNeeded(); auto achievementId = GetStringFromLua(L, 1, "1"); uint32_t percentComplete = GetUint32FromLua(L, 2, 100); LogToFile("XblAchievementsUpdateAchievementAsync: AchievementId: %s", achievementId.c_str()); LogToFile("XblAchievementsUpdateAchievementAsync: PercentComplete: %d", percentComplete); // CODE SNIPPET START: XblAchievementsUpdateAchievementAsync auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->context = nullptr; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* auto result = XAsyncGetStatus(asyncBlock, false); LogToFile("XAsyncGetStatus: hr=%s", ConvertHR(result).c_str()); // CODE SNIP SKIP if (SUCCEEDED(result)) { // Achievement updated } else if (result == HTTP_E_STATUS_NOT_MODIFIED) { // Achievement not modified } else { // Achievement failed to update } CallLuaFunctionWithHr(result, "OnXblAchievementsUpdateAchievementAsync"); // CODE SNIP SKIP }; HRESULT hr = XblAchievementsUpdateAchievementAsync( Data()->xboxLiveContext, Data()->xboxUserId, achievementId.c_str(), percentComplete, asyncBlock.get() ); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } // CODE SNIPPET END LogToFile("XblAchievementsUpdateAchievementAsync: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblAchievementsUpdateAchievementForTitleIdAsync_Lua(lua_State *L) { CreateQueueIfNeeded(); auto achievementId = GetStringFromLua(L, 1, "1"); uint32_t percentComplete = GetUint32FromLua(L, 2, 100); LogToFile("XblAchievementsGetAchievementAsync: AchievementId: %s", achievementId.c_str()); LogToFile("XblAchievementsGetAchievementAsync: PercentComplete: %d", percentComplete); // CODE SNIPPET START: XblAchievementsUpdateAchievementForTitleIdAsync auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->context = nullptr; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* auto result = XAsyncGetStatus(asyncBlock, false); LogToFile("XAsyncGetStatus: hr=%s", ConvertHR(result).c_str()); // CODE SNIP SKIP if (SUCCEEDED(result)) { // Achievement updated } else if (result == HTTP_E_STATUS_NOT_MODIFIED) { // Achievement not modified } else { // Achievement failed to update } CallLuaFunctionWithHr(result, "OnXblAchievementsUpdateAchievementForTitleIdAsync"); // CODE SNIP SKIP }; HRESULT hr = XblAchievementsUpdateAchievementForTitleIdAsync( Data()->xboxLiveContext, Data()->xboxUserId, Data()->titleId, Data()->scid, achievementId.c_str(), percentComplete, asyncBlock.get() ); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } // CODE SNIPPET END LogToFile("XblAchievementsUpdateAchievementForTitleIdAsync: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } void SetupAPIs_XblAchievements() { lua_register(Data()->L, "XblAchievementsResultGetAchievements", XblAchievementsResultGetAchievements_Lua); lua_register(Data()->L, "XblAchievementsResultGetNextAsync", XblAchievementsResultGetNextAsync_Lua); lua_register(Data()->L, "XblAchievementsResultHasNext", XblAchievementsResultHasNext_Lua); lua_register(Data()->L, "XblAchievementsResultCloseHandle", XblAchievementsResultCloseHandle_Lua); lua_register(Data()->L, "XblAchievementsGetAchievementAsync", XblAchievementsGetAchievementAsync_Lua); lua_register(Data()->L, "XblAchievementsGetAchievementsForTitleIdAsync", XblAchievementsGetAchievementsForTitleIdAsync_Lua); lua_register(Data()->L, "XblAchievementsUpdateAchievementAsync", XblAchievementsUpdateAchievementAsync_Lua); lua_register(Data()->L, "XblAchievementsUpdateAchievementForTitleIdAsync", XblAchievementsUpdateAchievementForTitleIdAsync_Lua); } ================================================ FILE: Tests/ApiExplorer/APIs/apis_xblc_achievements_manager.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include static struct AchievementsManagerState { // TODO move doWork logic into this state class AchievementsManagerState() = default; ~AchievementsManagerState() { // Validate that our tests cleaned up correctly assert(!doWork); } std::thread doWorkThread{}; std::atomic doWork{ false }; std::atomic doWorkJoinWhenDone{ false }; XblAchievementsManagerResultHandle achievementsResultHandle{ nullptr }; HCMockCallHandle mockHandle{ nullptr }; } achievementsState; HRESULT AchievementsManagerDoWork() { const XblAchievementsManagerEvent* events{ nullptr }; size_t eventCount{ 0 }; HRESULT hr = XblAchievementsManagerDoWork(&events, &eventCount); if (FAILED(hr)) { return hr; } for (size_t i = 0; i < eventCount; ++i) { auto& achievementEvent = events[i]; // CODE SKIP START static std::unordered_map eventTypesMap { { XblAchievementsManagerEventType::AchievementProgressUpdated, "AchievementProgressUpdated" }, { XblAchievementsManagerEventType::AchievementUnlocked, "AchievementUnlocked" }, { XblAchievementsManagerEventType::LocalUserInitialStateSynced, "LocalUserInitialStateSynced" }, }; // CODE SKIP END std::stringstream ss; ss << "XblAchievementsManagerDoWork: Event of type " << eventTypesMap[achievementEvent.eventType] << std::endl; LogToScreen(ss.str().c_str()); switch (achievementEvent.eventType) { case XblAchievementsManagerEventType::LocalUserInitialStateSynced: LogToScreen("XblAchievementsManagerDoWork: LocalUserInitialStateSynced event"); CallLuaFunctionWithHr(hr, "OnXblAchievementsManagerDoWork_LocalUserAddedEvent"); break; case XblAchievementsManagerEventType::AchievementProgressUpdated: LogToScreen("XblAchievementsManagerDoWork: AchievementProgressUpdated event"); CallLuaFunctionWithHr(hr, "OnXblAchievementsManagerDoWork_AchievementProgressUpdatedEvent"); break; case XblAchievementsManagerEventType::AchievementUnlocked: LogToScreen("XblAchievementsManagerDoWork: AchievementUnlocked event"); CallLuaFunctionWithHr(hr, "OnXblAchievementsManagerDoWork_AchievementUnlockedEvent"); break; default: break; } } return hr; } int StartAchievementsManagerDoWorkLoop_Lua(lua_State* L) { achievementsState.doWork = true; achievementsState.doWorkJoinWhenDone = true; std::lock_guard lock(Data()->m_luaLock); achievementsState.doWorkThread = std::thread([]() { #if HC_PLATFORM == HC_PLATFORM_ANDROID JNIEnv* jniEnv = nullptr; // This should be a background thread that MUST attach a new java thread. Data()->javaVM->GetEnv(reinterpret_cast(&jniEnv), JNI_VERSION_1_6); Data()->javaVM->AttachCurrentThread(&jniEnv, nullptr); #endif Data()->m_achievementsDoWorkDone = false; while (achievementsState.doWork && !Data()->m_quit) { { std::lock_guard lock(Data()->m_luaLock); AchievementsManagerDoWork(); } pal::Sleep(10); } Data()->m_achievementsDoWorkDone = true; LogToScreen("Exiting do work thread"); #if HC_PLATFORM == HC_PLATFORM_ANDROID Data()->javaVM->DetachCurrentThread(); #endif }); return LuaReturnHR(L, S_OK); } int StopAchievementsManagerDoWorkLoop_Lua(lua_State* L) { LogToScreen("StopAchievementsManagerDoWorkLoop_Lua"); achievementsState.doWorkJoinWhenDone = true; achievementsState.doWork = false; return LuaReturnHR(L, S_OK); } void StopAchievementsManagerDoWorkHelper() { if (achievementsState.doWorkJoinWhenDone) { achievementsState.doWork = false; achievementsState.doWorkJoinWhenDone = false; achievementsState.doWorkThread.join(); } } int SetupAchievementsManagerPerformanceTestMock_Lua(lua_State *L) { #if HC_PLATFORM == HC_PLATFORM_ANDROID std::string addUserMockResponse = ReadFile("PerformanceTestMockResponse.json"); #else std::string addUserMockResponse = ReadFile("achievements\\PerformanceTestMockResponse.json"); #endif assert(!addUserMockResponse.empty()); auto hr = HCMockCallCreate(&achievementsState.mockHandle); assert(SUCCEEDED(hr)); hr = HCMockAddMock(achievementsState.mockHandle, "GET", "https://achievements.xboxlive.com", nullptr, 0); assert(SUCCEEDED(hr)); hr = HCMockResponseSetStatusCode(achievementsState.mockHandle, 200); assert(SUCCEEDED(hr)); std::vector bodyBytes{ addUserMockResponse.begin(), addUserMockResponse.end() }; hr = HCMockResponseSetResponseBodyBytes(achievementsState.mockHandle, bodyBytes.data(), static_cast(bodyBytes.size())); assert(SUCCEEDED(hr)); return LuaReturnHR(L, hr); } // commands // handles int XblAchievementsManagerResultGetAchievements_Lua(lua_State *L) { auto resultHandle{ reinterpret_cast(GetUint64FromLua(L, 1, reinterpret_cast(achievementsState.achievementsResultHandle))) }; if (resultHandle != nullptr) // might be null if original call fails { // CODE SNIPPET START: XblAchievementsManagerResultGetAchievements_C const XblAchievement* achievements = nullptr; uint64_t achievementsCount = 0; HRESULT hr = XblAchievementsManagerResultGetAchievements(resultHandle, &achievements, &achievementsCount); LogToScreen("Got %u Achievements:", achievementsCount); // CODE SNIPPET END lua_pushinteger(L, static_cast(achievementsCount)); LogToScreen("XblAchievementsManagerResultGetAchievements: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr, 1); } else { HRESULT hr = S_OK; lua_pushinteger(L, static_cast(0)); LogToScreen("XblAchievementsManagerResultGetAchievements: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr, 1); } } int XblAchievementsManagerResultDuplicateHandle_Lua(lua_State *L) { auto resultHandle{ reinterpret_cast(GetUint64FromLua(L, 1, reinterpret_cast(achievementsState.achievementsResultHandle))) }; // CODE SNIPPET START: XblAchievementsRelationshipResultDuplicateHandle_C XblAchievementsManagerResultHandle handle{}; XblAchievementsManagerResultDuplicateHandle(resultHandle, &handle); // CODE SNIPPET END XblAchievementsManagerResultCloseHandle(handle); LogToScreen("XblAchievementsManagerResultDuplicateHandle"); return LuaReturnHR(L, S_OK); } int XblAchievementsManagerResultCloseHandle_Lua(lua_State *L) { auto resultHandle{ reinterpret_cast(GetUint64FromLua(L, 1, reinterpret_cast(achievementsState.achievementsResultHandle))) }; // CODE SNIPPET START: XblAchievementsRelationshipResultCloseHandle_C if (resultHandle == achievementsState.achievementsResultHandle) { achievementsState.achievementsResultHandle = nullptr; } XblAchievementsManagerResultCloseHandle(resultHandle); // CODE SNIPPET END LogToScreen("XblAchievementsManagerResultCloseHandle"); return LuaReturnHR(L, S_OK); } // manager int XblAchievementsManagerAddLocalUser_Lua(lua_State *L) { XalUserHandle user = Data()->xalUser; HRESULT hr = XblAchievementsManagerAddLocalUser(user, nullptr); LogToScreen("XblAchievementsManagerAddLocalUser: %s", ConvertHR(hr).c_str(), nullptr); return LuaReturnHR(L, hr); } int XblAchievementsManagerRemoveLocalUser_Lua(lua_State *L) { XalUserHandle user = Data()->xalUser; HRESULT hr = XblAchievementsManagerRemoveLocalUser(user); LogToScreen("XblAchievementsManagerAddLocalUser: %s", ConvertHR(hr).c_str(), nullptr); return LuaReturnHR(L, hr); } int XblAchievementsManagerGetAchievement_Lua(lua_State *L) { auto achievementId{ GetStringFromLua(L, 1, "1") }; HRESULT hr = XblAchievementsManagerGetAchievement(Data()->xboxUserId, achievementId.c_str(), &achievementsState.achievementsResultHandle); if (FAILED(hr)) { lua_pushinteger(L, 0); return LuaReturnHR(L, hr, 1); } lua_pushinteger(L, reinterpret_cast(achievementsState.achievementsResultHandle)); LogToScreen("XblAchievementsManagerGetAchievement: %s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr, 1); } int XblAchievementsManagerGetAchievements_Lua(lua_State *L) { HRESULT hr = XblAchievementsManagerGetAchievements( Data()->xboxUserId, XblAchievementOrderBy::DefaultOrder, XblAchievementsManagerSortOrder::Unsorted, &achievementsState.achievementsResultHandle); if (FAILED(hr)) { lua_pushinteger(L, 0); return LuaReturnHR(L, hr, 1); } lua_pushinteger(L, reinterpret_cast(achievementsState.achievementsResultHandle)); return LuaReturnHR(L, hr, 1); } int XblAchievementsManagerGetAchievementsPerfTest_Lua(lua_State *L) { auto startTime = std::chrono::high_resolution_clock::now(); HRESULT hr = XblAchievementsManagerGetAchievements( Data()->xboxUserId, XblAchievementOrderBy::DefaultOrder, XblAchievementsManagerSortOrder::Unsorted, &achievementsState.achievementsResultHandle); auto endTime = std::chrono::high_resolution_clock::now(); if (FAILED(hr)) { lua_pushinteger(L, 0); return LuaReturnHR(L, hr, 1); } lua_pushinteger(L, reinterpret_cast(achievementsState.achievementsResultHandle)); std::chrono::duration duration = endTime - startTime; LogToScreen("XblAchievementsManagerGetAchievements: %s", ConvertHR(hr).c_str()); LogToScreen("XblAchievementsManagerGetAchievements Performance Test Duration: %f milliseconds", duration.count() * 1000); return LuaReturnHR(L, hr, 1); } int XblAchievementsManagerGetAchievementsByState_Lua(lua_State *L) { HRESULT hr = XblAchievementsManagerGetAchievementsByState( Data()->xboxUserId, XblAchievementOrderBy::DefaultOrder, XblAchievementsManagerSortOrder::Unsorted, XblAchievementProgressState::Achieved, &achievementsState.achievementsResultHandle ); if (FAILED(hr)) { return LuaReturnHR(L, hr, 1); } lua_pushinteger(L, reinterpret_cast(achievementsState.achievementsResultHandle)); LogToScreen("XblAchievementsManagerGetAchievementsByState: %s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr, 1); } int XblAchievementsManagerUpdateAchievement_Lua(lua_State *L) { auto achievementId{ GetStringFromLua(L, 1, "1") }; uint8_t newProgress{ static_cast(GetUint32FromLua(L, 2, 100)) }; HRESULT hr = XblAchievementsManagerUpdateAchievement(Data()->xboxUserId, achievementId.c_str(), newProgress); LogToScreen("XblAchievementsManagerUpdateAchievement: %s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } void SetupAPIs_XblAchievementsManager() { // Non XSAPI APIs lua_register(Data()->L, "StartAchievementsManagerDoWorkLoop", StartAchievementsManagerDoWorkLoop_Lua); lua_register(Data()->L, "StopAchievementsManagerDoWorkLoop", StopAchievementsManagerDoWorkLoop_Lua); lua_register(Data()->L, "SetupAchievementsManagerPerformanceTestMock", SetupAchievementsManagerPerformanceTestMock_Lua); lua_register(Data()->L, "XblAchievementsManagerGetAchievementsPerfTest", XblAchievementsManagerGetAchievementsPerfTest_Lua); // XSAPI APIs lua_register(Data()->L, "XblAchievementsManagerAddLocalUser", XblAchievementsManagerAddLocalUser_Lua); lua_register(Data()->L, "XblAchievementsManagerRemoveLocalUser", XblAchievementsManagerRemoveLocalUser_Lua); lua_register(Data()->L, "XblAchievementsManagerGetAchievement", XblAchievementsManagerGetAchievement_Lua); lua_register(Data()->L, "XblAchievementsManagerGetAchievements", XblAchievementsManagerGetAchievements_Lua); lua_register(Data()->L, "XblAchievementsManagerGetAchievementsByState", XblAchievementsManagerGetAchievementsByState_Lua); lua_register(Data()->L, "XblAchievementsManagerResultGetAchievements", XblAchievementsManagerResultGetAchievements_Lua); lua_register(Data()->L, "XblAchievementsManagerResultDuplicateHandle", XblAchievementsManagerResultDuplicateHandle_Lua); lua_register(Data()->L, "XblAchievementsManagerResultCloseHandle", XblAchievementsManagerResultCloseHandle_Lua); lua_register(Data()->L, "XblAchievementsManagerUpdateAchievement", XblAchievementsManagerUpdateAchievement_Lua); } ================================================ FILE: Tests/ApiExplorer/APIs/apis_xblc_achievements_progress_notification.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" std::string ConvertXblAchievementProgressStateToString(XblAchievementProgressState state) { std::string stateStr; switch (state) { case XblAchievementProgressState::Unknown: stateStr = "XblAchievementProgressState::Unknown"; case XblAchievementProgressState::Achieved: stateStr = "XblAchievementProgressState::Achieved"; case XblAchievementProgressState::NotStarted: stateStr = "XblAchievementProgressState::NotStarted"; case XblAchievementProgressState::InProgress: stateStr = "XblAchievementProgressState::InProgress"; } return stateStr; } int XblAchievementsAddAchievementProgressChangeHandler_Lua(lua_State *L) { CreateQueueIfNeeded(); void* context = nullptr; auto xblContext = Data()->xboxLiveContext; XblFunctionContext achievementProgressContext = XblAchievementsAddAchievementProgressChangeHandler( xblContext, [](_In_ const XblAchievementProgressChangeEventArgs* args, _In_opt_ void*) { LogToScreen("XblAchievementsAddAchievementProgressChangeHandler"); for (uint32_t progressIndex = 0; progressIndex < args->entryCount; ++progressIndex) { LogToScreen("Achievement ID:"); LogToScreen(args->updatedAchievementEntries[progressIndex].achievementId); LogToScreen("Achievement State:"); LogToScreen(ConvertXblAchievementProgressStateToString(args->updatedAchievementEntries[progressIndex].progressState).c_str()); for (uint32_t requirementIndex = 0; requirementIndex < args->updatedAchievementEntries[progressIndex].progression.requirementsCount; ++requirementIndex) { char response[256]; SPRINTF(response, 256, "Achievement Requirement %u", requirementIndex); LogToScreen(response); LogToScreen("Requirement Id:"); LogToScreen(args->updatedAchievementEntries[progressIndex].progression.requirements[requirementIndex].id); LogToScreen("Requirement Progress:"); LogToScreen(args->updatedAchievementEntries[progressIndex].progression.requirements[requirementIndex].currentProgressValue); } } CallLuaFunctionWithHr(S_OK, "OnXblAchievementsAddAchievementProgressHandler"); }, context ); Data()->m_achievementProgressNotificationFunctionContext = achievementProgressContext; LogToScreen("XblAchievementsAddAchievementProgressChangeHandler: done"); return LuaReturnHR(L, S_OK); } int XblAchievementsRemoveAchievementProgressChangeHandler_Lua(lua_State *L) { CreateQueueIfNeeded(); XblAchievementsRemoveAchievementProgressChangeHandler(Data()->xboxLiveContext, Data()->m_achievementProgressNotificationFunctionContext); Data()->m_achievementProgressNotificationFunctionContext = 0; LogToScreen("XblAchievementsRemoveAchievementProgressChangeHandler: done"); return LuaReturnHR(L, S_OK); } void SetupAPIs_XblAchievementsProgressNotifications() { lua_register(Data()->L, "XblAchievementsAddAchievementProgressChangeHandler", XblAchievementsAddAchievementProgressChangeHandler_Lua); lua_register(Data()->L, "XblAchievementsRemoveAchievementProgressChangeHandler", XblAchievementsRemoveAchievementProgressChangeHandler_Lua); } ================================================ FILE: Tests/ApiExplorer/APIs/apis_xblc_events.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #if HC_PLATFORM == HC_PLATFORM_WIN32 #include #endif int XblEventsWriteInGameEvent_Lua(lua_State *L) { #if HC_PLATFORM != HC_PLATFORM_XDK auto eventName = GetStringFromLua(L, 1, "PuzzleSolved"); auto dimensionsJson = GetStringFromLua(L, 2, "{\"DifficultyLevelId\":100,\"EnemyRoleId\":3,\"GameplayModeId\":\"gameplay mode id\",\"KillTypeId\":4,\"MultiplayerCorrelationId\":\"multiplayer correlation id\",\"PlayerRoleId\":1,\"PlayerWeaponId\":2,\"RoundId\":1}"); auto measurementsJson = GetStringFromLua(L, 3, "{\"LocationX\":1,\"LocationY\":2.12121,\"LocationZ\":-90909093}"); HRESULT hr = XblEventsWriteInGameEvent( Data()->xboxLiveContext, eventName.c_str(), dimensionsJson.c_str(), measurementsJson.c_str() ); LogToFile("XblEventsWriteInGameEvent: hr=%s", ConvertHR(hr).c_str()); #else HRESULT hr = S_OK; #endif return LuaReturnHR(L, hr); } int ValidateOfflineEventsDirectoryFileExistsAndDelete_Lua(lua_State *L) { HRESULT hr = S_OK; // Location of the offline events file is not exposed from XSAPI. We could hard code // it here but it may not always be the same, depending on platform #if HC_PLATFORM == HC_PLATFORM_WIN32 char pathArray[MAX_PATH + 1]; GetCurrentDirectoryA(MAX_PATH + 1, pathArray); std::string path{ pathArray }; std::string searchPath{ path + "\\XblEvents*" }; WIN32_FIND_DATAA fd{}; HANDLE hFind = FindFirstFileA(searchPath.data(), &fd); if (hFind != INVALID_HANDLE_VALUE) { do { DeleteFileA(std::string{ path + "\\" + fd.cFileName }.data()); } while (FindNextFileA(hFind, &fd)); } else { hr = E_FAIL; } #endif LogToFile("Validating that XblEvents.dir exists: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } void SetupAPIs_XblEvents() { lua_register(Data()->L, "XblEventsWriteInGameEvent", XblEventsWriteInGameEvent_Lua); lua_register(Data()->L, "ValidateOfflineEventsDirectoryFileExistsAndDelete", ValidateOfflineEventsDirectoryFileExistsAndDelete_Lua); } ================================================ FILE: Tests/ApiExplorer/APIs/apis_xblc_gameinvite_notifications.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" XBL_WARNING_DISABLE_DEPRECATED #if HC_PLATFORM == HC_PLATFORM_WIN32 int XblGameInviteAddNotificationHandler_Lua(lua_State *L) { CreateQueueIfNeeded(); // CODE SNIPPET START: XblGameInviteAddNotificationHandler void* context = nullptr; auto t = Data()->xboxLiveContext; XblFunctionContext gameinviteFunctionContext = XblGameInviteAddNotificationHandler( t, [](_In_ const XblGameInviteNotificationEventArgs* args, _In_opt_ void*) { LogToScreen("XblGameInviteAddNotificationHandler"); LogToScreen("Invite Handle ID:"); LogToScreen(args->inviteHandleId); LogToScreen("Invite Protocol:"); LogToScreen(args->inviteProtocol); LogToScreen("Invite Context:"); LogToScreen(args->inviteContext); LogToScreen("Sender Gamertag:"); LogToScreen(args->senderGamertag); LogToScreen("Modern Gamertag:"); LogToScreen(args->senderModernGamertag); LogToScreen("Modern Gamertag Suffix:"); LogToScreen(args->senderModernGamertagSuffix); LogToScreen("Unique Modern Gamertag:"); LogToScreen(args->senderUniqueModernGamertag); LogToScreen("Sender Gamertag:"); LogToScreen(args->senderGamertag); LogToScreen("Sender Image URL:"); LogToScreen(args->senderImageUrl); std::string s2 = std::to_string(args->senderXboxUserId); LogToScreen("Sender Xbox User ID:"); LogToScreen(s2.c_str()); CallLuaFunctionWithHr(S_OK, "OnXblGameInviteAddNotificationHandler"); // CODE SNIP SKIP }, context ); // CODE SNIPPET END Data()->gameInviteNotificationFunctionContext = gameinviteFunctionContext; LogToScreen("XblGameInviteAddNotificationHandler: done"); return LuaReturnHR(L, S_OK); } int XblGameInviteRemoveNotificationHandler_Lua(lua_State *L) { CreateQueueIfNeeded(); // CODE SNIPPET START: XblGameInviteRemoveNotificationHandler XblGameInviteRemoveNotificationHandler( Data()->xboxLiveContext, Data()->gameInviteNotificationFunctionContext ); Data()->gameInviteNotificationFunctionContext = 0; // CODE SNIPPET END LogToScreen("XblGameInviteRemoveNotificationHandler: done"); return LuaReturnHR(L, S_OK); } int XblGameInviteRegisterForEventAsync_Lua(lua_State *L) { CreateQueueIfNeeded(); // CODE SNIPPET START: XblGameInviteRegisterForEventAsync auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* XblRealTimeActivitySubscriptionHandle subscriptionHandle{ nullptr }; HRESULT hr = XblGameInviteRegisterForEventResult(asyncBlock, &subscriptionHandle); Data()->gameInviteNotificationSubscriptionHandle = subscriptionHandle; // CODE SNIP SKIP LogToFile("XblGameInviteRegisterForEventResult: hr=%s", ConvertHR(hr).c_str()); // CODE SNIP SKIP CallLuaFunctionWithHr(hr, "OnXblGameInviteRegisterForEventAsync"); // CODE SNIP SKIP }; HRESULT hr = XblGameInviteRegisterForEventAsync( Data()->xboxLiveContext, asyncBlock.get() ); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } // CODE SNIPPET END LogToScreen("XblGameInviteRegisterForEventAsync: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, S_OK); } int XblGameInviteUnregisterForEventAsync_Lua(lua_State *L) { XblRealTimeActivitySubscriptionHandle subscriptionHandle{ nullptr }; if (Data()->gameInviteNotificationSubscriptionHandle) { subscriptionHandle = Data()->gameInviteNotificationSubscriptionHandle; } if (subscriptionHandle) { // CODE SNIPPET START: XblGameInviteUnregisterForEventAsync auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* HRESULT hr = XAsyncGetStatus(asyncBlock, true); Data()->gameInviteNotificationSubscriptionHandle = nullptr; // CODE SNIP SKIP LogToFile("XblGameInviteUnregisterForEventAsync Result: hr=%s", ConvertHR(hr).c_str()); // CODE SNIP SKIP CallLuaFunctionWithHr(hr, "OnXblGameInviteUnregisterForEventAsync"); // CODE SNIP SKIP }; HRESULT hr = XblGameInviteUnregisterForEventAsync(Data()->xboxLiveContext, subscriptionHandle, asyncBlock.get()); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } // CODE SNIPPET END LogToScreen("XblGameInviteUnregisterForEventAsync: hr=%s", ConvertHR(hr).c_str()); } LogToScreen("XblGameInviteUnregisterForEventAsync: done"); return LuaReturnHR(L, S_OK); } void SetupupAPIs_XblGameInviteNotifications() { lua_register(Data()->L, "XblGameInviteAddNotificationHandler", XblGameInviteAddNotificationHandler_Lua); lua_register(Data()->L, "XblGameInviteRemoveNotificationHandler", XblGameInviteRemoveNotificationHandler_Lua); lua_register(Data()->L, "XblGameInviteRegisterForEventAsync", XblGameInviteRegisterForEventAsync_Lua); lua_register(Data()->L, "XblGameInviteUnregisterForEventAsync", XblGameInviteUnregisterForEventAsync_Lua); } #endif ================================================ FILE: Tests/ApiExplorer/APIs/apis_xblc_grts.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #if HC_PLATFORM == HC_PLATFORM_GDK #include #endif HRESULT XGameUiShowSendGameInviteAsyncHelper( const std::string& sessionTemplateName, const std::string& sessionId, const std::string& inviteText, const std::string& customActivationContext ) { #if HC_PLATFORM == HC_PLATFORM_GDK auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->context = nullptr; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* HRESULT hr = XGameUiShowSendGameInviteResult(asyncBlock); CallLuaFunctionWithHr(hr, "OnXGameUiShowSendGameInviteAsync"); }; HRESULT hr = XGameUiShowSendGameInviteAsync(asyncBlock.get(), Data()->xalUser, Data()->scid, sessionTemplateName.c_str(), sessionId.c_str(), inviteText.c_str(), customActivationContext.c_str() ); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } #else UNREFERENCED_PARAMETER(sessionTemplateName); UNREFERENCED_PARAMETER(sessionId); UNREFERENCED_PARAMETER(inviteText); UNREFERENCED_PARAMETER(customActivationContext); HRESULT hr = S_OK; #endif return hr; } int XGameUiShowSendGameInviteAsyncToMPMLobby_Lua(lua_State* L) { std::string inviteText = GetStringFromLua(L, 1, "//MPSD/custominvitestrings_JoinMyGame"); std::string customActivationContext = GetStringFromLua(L, 2, "MyCustomActivationContext"); XblMultiplayerSessionReference sessionReference{}; HRESULT hr = XblMultiplayerManagerLobbySessionSessionReference(&sessionReference); hr = XGameUiShowSendGameInviteAsyncHelper( sessionReference.SessionTemplateName, sessionReference.SessionName, inviteText, customActivationContext); return LuaReturnHR(L, hr); } int XGameUiShowSendGameInviteAsync_Lua(lua_State* L) { std::string sessionTemplateName = GetStringFromLua(L, 1, "MinGameSession"); std::string sessionId = GetStringFromLua(L, 2, "GameSession-0"); std::string inviteText = GetStringFromLua(L, 3, "Join Me In My Game!"); std::string customActivationContext = GetStringFromLua(L, 4, "MyCustomActivationContext"); HRESULT hr = XGameUiShowSendGameInviteAsyncHelper(sessionTemplateName, sessionId, inviteText, customActivationContext); return LuaReturnHR(L, hr); } int XGameUiShowMultiplayerActivityGameInviteAsync_Lua(lua_State* L) { #if HC_PLATFORM == HC_PLATFORM_GDK auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->context = nullptr; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* //HRESULT hr = XGameUiShowMultiplayerActivityGameInviteResult(asyncBlock); HRESULT hr = E_NOTIMPL; // requires GDK 2203+ CallLuaFunctionWithHr(hr, "OnXGameUiShowMultiplayerActivityGameInviteAsync"); }; //HRESULT hr = XGameUiShowMultiplayerActivityGameInviteAsync(asyncBlock.get(), Data()->xalUser); HRESULT hr = E_NOTIMPL; // requires GDK 2203+ if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } #else HRESULT hr = S_OK; #endif return LuaReturnHR(L, hr); } void SetupAPIs_GRTS() { lua_register(Data()->L, "XGameUiShowMultiplayerActivityGameInviteAsync", XGameUiShowMultiplayerActivityGameInviteAsync_Lua); lua_register(Data()->L, "XGameUiShowSendGameInviteAsync", XGameUiShowSendGameInviteAsync_Lua); lua_register(Data()->L, "XGameUiShowSendGameInviteAsyncToMPMLobby", XGameUiShowSendGameInviteAsyncToMPMLobby_Lua); } ================================================ FILE: Tests/ApiExplorer/APIs/apis_xblc_leaderboard.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" XblSocialGroupType ConvertStringToXblSocialGroupType(const char* str) { XblSocialGroupType type = XblSocialGroupType::None; if (pal::stricmp(str, "XblSocialGroupType::None") == 0) type = XblSocialGroupType::None; else if (pal::stricmp(str, "XblSocialGroupType::People") == 0) type = XblSocialGroupType::People; else if (pal::stricmp(str, "XblSocialGroupType::Favorites") == 0) type = XblSocialGroupType::Favorites; return type; } XblLeaderboardSortOrder ConvertStringToXblLeaderboardSortOrder(const char* str) { XblLeaderboardSortOrder type = XblLeaderboardSortOrder::Descending; if (pal::stricmp(str, "XblLeaderboardSortOrder::Descending") == 0) type = XblLeaderboardSortOrder::Descending; else if (pal::stricmp(str, "XblLeaderboardSortOrder::Ascending") == 0) type = XblLeaderboardSortOrder::Ascending; return type; } XblLeaderboardQueryType ConvertStringToXblLeaderboardQueryType(const std::string& queryType) { if (queryType == "XblLeaderboardQueryType::TitleManagedStatBackedGlobal") { return XblLeaderboardQueryType::TitleManagedStatBackedGlobal; } else if (queryType == "XblLeaderboardQueryType::TitleManagedStatBackedSocial") { return XblLeaderboardQueryType::TitleManagedStatBackedSocial; } else { return XblLeaderboardQueryType::UserStatBacked; } } int XblLeaderboardGetLeaderboardAsync_Lua(lua_State *L) { CreateQueueIfNeeded(); auto leaderboardName = GetStringFromLua(L, 1, "TotalPuzzlesSolvedLB"); auto statName = GetStringFromLua(L, 2, ""); XblSocialGroupType socialGroup = ConvertStringToXblSocialGroupType(GetStringFromLua(L, 3, "XblSocialGroupType::None").c_str()); auto queryType = ConvertStringToXblLeaderboardQueryType(GetStringFromLua(L, 4, "XblLeaderboardQueryType::UserStatBacked")); XblLeaderboardSortOrder order = ConvertStringToXblLeaderboardSortOrder(GetStringFromLua(L, 5, "XblLeaderboardSortOrder::Descending").c_str()); auto scid = GetStringFromLua(L, 6, Data()->scid); auto xboxUserId = GetUint64FromLua(L, 7, Data()->xboxUserId); uint32_t maxItems = GetUint32FromLua(L, 8, 0); uint64_t skipToXboxUserId = GetUint64FromLua(L, 9, 0); uint32_t skipResultToRank = GetUint32FromLua(L, 10, 0); // _Field_z_ const char** additionalColumnleaderboardNames; // size_t additionalColumnleaderboardNamesCount; LogToFile("XblLeaderboardGetLeaderboardAsync: xboxUserId: %d", xboxUserId); LogToFile("XblLeaderboardGetLeaderboardAsync: scid: %s", scid.c_str()); LogToFile("XblLeaderboardGetLeaderboardAsync: leaderboardName: %s", leaderboardName.c_str()); LogToFile("XblLeaderboardGetLeaderboardAsync: statName: %s", statName.c_str()); LogToFile("XblLeaderboardGetLeaderboardAsync: maxItems: %d", maxItems); LogToFile("XblLeaderboardGetLeaderboardAsync: skipToXboxUserId: %d", skipToXboxUserId); LogToFile("XblLeaderboardGetLeaderboardAsync: skipResultToRank: %d", skipResultToRank); LogToFile("XblLeaderboardGetLeaderboardAsync: socialGroup: %d", socialGroup); LogToFile("XblLeaderboardGetLeaderboardAsync: order: %d", order); // CODE SNIPPET START: XblLeaderboardGetLeaderboardAsync auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->context = nullptr; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* size_t resultSize; HRESULT hr = XblLeaderboardGetLeaderboardResultSize(asyncBlock, &resultSize); if (SUCCEEDED(hr)) { Data()->leaderboardBuffer.resize(resultSize); XblLeaderboardResult* leaderboard{}; hr = XblLeaderboardGetLeaderboardResult(asyncBlock, resultSize, Data()->leaderboardBuffer.data(), &leaderboard, nullptr); LogToFile("XblLeaderboardGetLeaderboardResult: %s columnsCount=%d rowsCount=%d", ConvertHR(hr).c_str(), leaderboard->columnsCount, leaderboard->rowsCount); // CODE SNIP SKIP if (SUCCEEDED(hr)) { // Use XblLeaderboardResult in result LogToScreen("Got %d rows in leaderboard", leaderboard->rowsCount); // CODE SNIP SKIP for (auto row = 0u; row < leaderboard->rowsCount; ++row) { std::stringstream rowText; rowText << leaderboard->rows[row].xboxUserId << "\t"; for (auto column = 0u; column < leaderboard->rows[row].columnValuesCount; ++column) { // Each column value is in JSON format rowText << leaderboard->rows[row].columnValues[column] << "\t"; } LogToFile(rowText.str().data()); // CODE SNIP SKIP } Data()->leaderboardResult = leaderboard; // CODE SNIP SKIP } } CallLuaFunctionWithHr(hr, "OnXblLeaderboardGetLeaderboardAsync"); // CODE SNIP SKIP }; XblLeaderboardQuery leaderboardQuery = {}; pal::strcpy(leaderboardQuery.scid, sizeof(leaderboardQuery.scid), scid.c_str()); leaderboardQuery.leaderboardName = leaderboardName.empty() ? nullptr : leaderboardName.c_str(); // See below on more options in XblLeaderboardQuery leaderboardQuery.xboxUserId = xboxUserId; // CODE SNIP SKIP leaderboardQuery.statName = statName.c_str(); // CODE SNIP SKIP leaderboardQuery.maxItems = maxItems; // CODE SNIP SKIP leaderboardQuery.skipToXboxUserId = skipToXboxUserId; // CODE SNIP SKIP leaderboardQuery.skipResultToRank = skipResultToRank; // CODE SNIP SKIP leaderboardQuery.socialGroup = socialGroup; // CODE SNIP SKIP leaderboardQuery.order = order; // CODE SNIP SKIP leaderboardQuery.queryType = queryType; // CODE SNIP SKIP HRESULT hr = XblLeaderboardGetLeaderboardAsync( Data()->xboxLiveContext, leaderboardQuery, asyncBlock.get()); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } // CODE SNIPPET END LogToFile("XblLeaderboardGetLeaderboardAsync: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblLeaderboardResultHasNext_Lua(lua_State *L) { HRESULT hr = S_OK; bool hasNext = false; if (Data()->leaderboardResult != nullptr) { hasNext = Data()->leaderboardResult->hasNext; } lua_pushnumber(L, (int)hasNext); return LuaReturnHR(L, hr, 1); } int XblLeaderboardResultGetNextAsync_Lua(lua_State *L) { CreateQueueIfNeeded(); uint32_t maxItems = GetUint32FromLua(L, 1, 0); LogToFile("XblLeaderboardGetLeaderboardAsync: maxItems: %d", maxItems); // CODE SNIPPET START: XblLeaderboardResultGetNextAsync auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->context = nullptr; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* size_t resultSize; HRESULT hr = XblLeaderboardResultGetNextResultSize(asyncBlock, &resultSize); if (SUCCEEDED(hr)) { Data()->leaderboardBuffer.resize(resultSize); XblLeaderboardResult* result{}; hr = XblLeaderboardResultGetNextResult(asyncBlock, resultSize, Data()->leaderboardBuffer.data(), &result, nullptr); // Use result to read the leaderboard results // CODE SKIP START LogToFile("XblLeaderboardResultGetNextResult: %s columnsCount=%d rowsCount=%d", ConvertHR(hr).c_str(), result->columnsCount, result->rowsCount); LogToScreen("LeaderboardName = %s", result->nextQuery.leaderboardName); Data()->leaderboardResult = result; // CODE SKIP END } CallLuaFunctionWithHr(hr, "OnXblLeaderboardResultGetNextAsync"); // CODE SNIP SKIP }; HRESULT hr = XblLeaderboardResultGetNextAsync( Data()->xboxLiveContext, Data()->leaderboardResult, maxItems, asyncBlock.get()); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } // CODE SNIPPET END LogToFile("XblLeaderboardResultGetNextAsync: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } void SetupAPIs_XblLeaderboard() { lua_register(Data()->L, "XblLeaderboardGetLeaderboardAsync", XblLeaderboardGetLeaderboardAsync_Lua); lua_register(Data()->L, "XblLeaderboardResultGetNextAsync", XblLeaderboardResultGetNextAsync_Lua); lua_register(Data()->L, "XblLeaderboardResultHasNext", XblLeaderboardResultHasNext_Lua); } ================================================ FILE: Tests/ApiExplorer/APIs/apis_xblc_multiplayer.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" struct MultiplayerState { std::vector sessionHandles; XblMultiplayerSessionReference sessionRef{}; XblMultiplayerSearchHandle searchHandle{ nullptr }; XblMultiplayerInviteHandle inviteHandle{}; XblFunctionContext sessionChange{ 0 }; XblFunctionContext lostHandler{ 0 }; std::string activityHandle; static std::string GetSessionName(uint64_t sessionId = 0) noexcept { // ID to make session names unique per API runner run #if HC_PLATFORM == HC_PLATFORM_GDK ULARGE_INTEGER largeInt; FILETIME fileTime; GetSystemTimeAsFileTime(&fileTime); largeInt.LowPart = fileTime.dwLowDateTime; largeInt.HighPart = fileTime.dwHighDateTime; static uint64_t runId{ largeInt.QuadPart }; #else static uint64_t runId{ utility::datetime::utc_now().to_interval() }; #endif std::stringstream ss; ss << "GameSession-" << runId << "-ID" << sessionId; return ss.str(); } }; std::unique_ptr g_multiplayerState; MultiplayerState* MPState() { if (g_multiplayerState == nullptr) { g_multiplayerState = std::make_unique(); g_multiplayerState->sessionHandles.resize(10); } return g_multiplayerState.get(); } XblMultiplayerSessionHandle GetSessionHandleFromArg(lua_State *L, int paramNum, uint64_t* sessionIndexOut = nullptr) { auto sessionIndex{ GetUint64FromLua(L, paramNum, 0) }; assert(MPState()->sessionHandles.size() > sessionIndex); if (sessionIndexOut != nullptr) { *sessionIndexOut = sessionIndex; } return MPState()->sessionHandles[static_cast::size_type>(sessionIndex)]; } int XblMultiplayerSessionCreateHandle_Lua(lua_State *L) { uint64_t xuid = Data()->xboxUserId; std::string scid = GetStringFromLua(L, 1, Data()->scid); std::string sessionTemplateName = GetStringFromLua(L, 2, "MinGameSession"); std::string sessionName = GetStringFromLua(L, 3, ""); auto sessionIndex{ GetUint64FromLua(L, 4, 0) }; if (sessionName.empty()) { sessionName = MultiplayerState::GetSessionName(sessionIndex); } // CODE SNIPPET START: XblMultiplayerSessionCreateHandle XblMultiplayerSessionReference ref; pal::strcpy(ref.Scid, sizeof(ref.Scid), scid.c_str()); pal::strcpy(ref.SessionTemplateName, sizeof(ref.SessionTemplateName), sessionTemplateName.c_str()); pal::strcpy(ref.SessionName, sizeof(ref.SessionName), sessionName.c_str()); XblMultiplayerSessionInitArgs args = {}; XblMultiplayerSessionHandle sessionHandle = XblMultiplayerSessionCreateHandle(xuid, &ref, &args); // CODE SNIPPET END auto state{ MPState() }; auto& session{ state->sessionHandles[static_cast(sessionIndex)] }; if (session) { XblMultiplayerSessionCloseHandle(session); } state->sessionHandles[static_cast(sessionIndex)] = sessionHandle; lua_pushinteger(L, static_cast(sessionIndex)); LogToFile("XblMultiplayerSessionCreateHandle"); return LuaReturnHR(L, S_OK, 1); } int XblMultiplayerSessionJoin_Lua(lua_State *L) { // Params: // 1) Session handle id returned when the session was created // 2) member custom constants json // 3) initialize requested // 4) join with active status auto sessionHandle = GetSessionHandleFromArg(L, 1); std::string memberCustomConstantsJson = GetStringFromLua(L, 2, "{}"); bool initializeRequested = GetBoolFromLua(L, 3, true); bool joinWithActiveStatus = GetBoolFromLua(L, 4, true); // CODE SNIPPET START: XblMultiplayerSessionJoin auto hr = XblMultiplayerSessionJoin( sessionHandle, memberCustomConstantsJson.c_str(), initializeRequested, joinWithActiveStatus); // CODE SNIPPET END LogToFile("XblMultiplayerSessionJoin: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } XblMultiplayerSessionWriteMode ConvertStringToXblMultiplayerSessionWriteMode(const char* str) { XblMultiplayerSessionWriteMode writeMode = XblMultiplayerSessionWriteMode::UpdateOrCreateNew; if (pal::stricmp(str, "XblMultiplayerSessionWriteMode::SynchronizedUpdate") == 0) writeMode = XblMultiplayerSessionWriteMode::SynchronizedUpdate; else if (pal::stricmp(str, "XblMultiplayerSessionWriteMode::CreateNew") == 0) writeMode = XblMultiplayerSessionWriteMode::CreateNew; else if (pal::stricmp(str, "XblMultiplayerSessionWriteMode::UpdateExisting") == 0) writeMode = XblMultiplayerSessionWriteMode::UpdateExisting; return writeMode; } int XblMultiplayerWriteSessionAsync_Lua(lua_State *L) { // Params: // 1) Session handle id returned when the session was created // 2) XblContextHandle to use. Defaults to Data()->xboxLiveContextse CreateQueueIfNeeded(); auto sessionIndex{ GetUint64FromLua(L, 1, 0) }; assert(MPState()->sessionHandles.size() > sessionIndex); XblContextHandle xboxLiveContext = (XblContextHandle)GetUint64FromLua(L, 2, (uint64_t)Data()->xboxLiveContext); ENSURE_IS_TRUE(MPState()->sessionHandles[static_cast(sessionIndex)] != nullptr, "No valid multiplayer session."); XblMultiplayerSessionWriteMode writeMode = ConvertStringToXblMultiplayerSessionWriteMode(GetStringFromLua(L, 2, "XblMultiplayerSessionWriteMode::UpdateOrCreateNew").c_str()); // CODE SNIPPET START: XblMultiplayerWriteSessionAsync auto asyncBlock = std::make_unique(); auto contextPtr = std::make_unique(static_cast(sessionIndex)); asyncBlock->queue = Data()->queue; asyncBlock->context = contextPtr.get(); asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* std::unique_ptr sessionIndexPtr{ static_cast(asyncBlock->context) }; auto sessionIndex{ *sessionIndexPtr }; assert(MPState()->sessionHandles.size() > sessionIndex); auto& session{ MPState()->sessionHandles[sessionIndex] }; if (session) { XblMultiplayerSessionCloseHandle(session); MPState()->sessionHandles[sessionIndex] = nullptr; } HRESULT hr = XblMultiplayerWriteSessionResult(asyncBlock, &session); if (SUCCEEDED(hr) && hr != 0x80070714) // HRESULT_FROM_WIN32(ERROR_RESOURCE_DATA_NOT_FOUND) { auto status = XblMultiplayerSessionWriteStatus(session); MPState()->sessionHandles[sessionIndex] = session; LogToFile("XblMultiplayerWriteSessionResult: hr=%s writeResult=%d", ConvertHR(hr).c_str(), status); // CODE SNIP SKIP } else { LogToFile("XblMultiplayerWriteSessionResult: hr=%s", ConvertHR(hr).c_str()); // CODE SNIP SKIP } CallLuaFunctionWithHr(hr, "OnXblMultiplayerWriteSessionAsync"); // CODE SNIP SKIP }; auto hr = XblMultiplayerWriteSessionAsync(xboxLiveContext, MPState()->sessionHandles[static_cast(sessionIndex)], writeMode, asyncBlock.get()); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); contextPtr.release(); } // CODE SNIPPET END LogToFile("XblMultiplayerWriteSessionAsync: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblMultiplayerSessionCloseHandle_Lua(lua_State *L) { uint64_t sessionIndex = 0; auto sessionHandle = GetSessionHandleFromArg(L, 1, &sessionIndex); // CODE SNIPPET START: XblMultiplayerWriteSessionAsync XblMultiplayerSessionCloseHandle(sessionHandle); // CODE SNIPPET END MPState()->sessionHandles[static_cast(sessionIndex)] = nullptr; LogToFile("XblMultiplayerSessionCloseHandle"); return LuaReturnHR(L, S_OK); } int XblMultiplayerSessionAddMemberReservation_Lua(lua_State *L) { uint64_t xuid = GetUint64FromLua(L, 1, 2814636782672891); bool initializeRequested = GetBoolFromLua(L, 2, true); auto sessionHandle = GetSessionHandleFromArg(L, 3); // CODE SNIPPET START: XblMultiplayerWriteSessionAsync auto hr = XblMultiplayerSessionAddMemberReservation(sessionHandle, xuid, nullptr, initializeRequested); // CODE SNIPPET END LogToFile("XblMultiplayerSessionAddMemberReservation: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } void LogSessionRef(const XblMultiplayerSessionReference* sessionRef) { LogToFile("Scid:%s", sessionRef->Scid); LogToFile("SessionName:%s", sessionRef->SessionName); LogToFile("SessionTemplateName:%s", sessionRef->SessionTemplateName); } int XblMultiplayerSessionReferenceCreate_Lua(lua_State *L) { std::string scid = GetStringFromLua(L, 1, Data()->scid); std::string sessionTemplateName = GetStringFromLua(L, 2, "MinGameSession"); std::string sessionName = GetStringFromLua(L, 3, MultiplayerState::GetSessionName()); // CODE SNIPPET START: XblMultiplayerSessionReferenceCreate MPState()->sessionRef = XblMultiplayerSessionReferenceCreate(scid.c_str(), sessionTemplateName.c_str(), sessionName.c_str()); // CODE SNIPPET END LogToFile("XblMultiplayerSessionReferenceCreate"); LogSessionRef(&MPState()->sessionRef); return LuaReturnHR(L, S_OK); } int XblMultiplayerSessionReferenceParseFromUriPath_Lua(lua_State* L) { std::string path = GetStringFromLua(L, 1, ""); if (path.empty()) { std::stringstream ss; ss << "/serviceconfigs/00000000-0000-0000-0000-000076029b4d/sessionTemplates/MinGameSession/sessions/"; ss << MultiplayerState::GetSessionName(); path = ss.str(); } // CODE SNIPPET START: XblMultiplayerSessionReferenceParseFromUriPath HRESULT hr = XblMultiplayerSessionReferenceParseFromUriPath(path.c_str(), &MPState()->sessionRef); // CODE SNIPPET END LogToFile("XblMultiplayerSessionReferenceParseFromUriPath: hr=%s", ConvertHR(hr).c_str()); LogToFile("Scid:%s", MPState()->sessionRef.Scid); LogToFile("SessionName:%s", MPState()->sessionRef.SessionName); LogToFile("SessionTemplateName:%s", MPState()->sessionRef.SessionTemplateName); return LuaReturnHR(L, hr); } int XblMultiplayerSessionReferenceIsValid_Lua(lua_State *L) { // CODE SNIPPET START: XblMultiplayerSessionReferenceIsValid bool isValid = XblMultiplayerSessionReferenceIsValid(&MPState()->sessionRef); // CODE SNIPPET END LogToFile("XblMultiplayerSessionReferenceIsValid isValid:%d", isValid); return LuaReturnHR(L, S_OK); } int XblMultiplayerSessionDuplicateHandle_Lua(lua_State *L) { auto sessionHandle = GetSessionHandleFromArg(L, 1); // CODE SNIPPET START: XblMultiplayerSessionDuplicateHandle XblMultiplayerSessionHandle newHandle{}; HRESULT hr = XblMultiplayerSessionDuplicateHandle(sessionHandle, &newHandle); // CODE SNIPPET END XblMultiplayerSessionCloseHandle(newHandle); LogToFile("XblMultiplayerSessionDuplicateHandle"); return LuaReturnHR(L, hr); } int XblMultiplayerSessionTimeOfSession_Lua(lua_State *L) { auto sessionHandle = GetSessionHandleFromArg(L, 1); // CODE SNIPPET START: XblMultiplayerSessionTimeOfSession time_t timeOfSession = XblMultiplayerSessionTimeOfSession(sessionHandle); // CODE SNIPPET END LogToFile("XblMultiplayerSessionTimeOfSession timeOfSession:%d", timeOfSession); return LuaReturnHR(L, S_OK); } int XblMultiplayerSessionGetInitializationInfo_Lua(lua_State *L) { auto sessionHandle = GetSessionHandleFromArg(L, 1); // CODE SNIPPET START: XblMultiplayerSessionGetInitializationInfo const XblMultiplayerSessionInitializationInfo* info = XblMultiplayerSessionGetInitializationInfo(sessionHandle); // CODE SNIPPET END LogToFile("XblMultiplayerSessionGetInitializationInfo"); LogToFile("Stage: %d", info->Stage); LogToFile("StageStartTime: %ul", info->StageStartTime); LogToFile("Episode: %d", info->Episode); return LuaReturnHR(L, S_OK); } int XblMultiplayerSessionSubscribedChangeTypes_Lua(lua_State *L) { auto sessionHandle = GetSessionHandleFromArg(L, 1); // CODE SNIPPET START: XblMultiplayerSessionSubscribedChangeTypes XblMultiplayerSessionChangeTypes changeTypes = XblMultiplayerSessionSubscribedChangeTypes(sessionHandle); // CODE SNIPPET END LogToFile("XblMultiplayerSessionSubscribedChangeTypes"); LogToFile("changeTypes: 0x%0.4x", changeTypes); return LuaReturnHR(L, S_OK); } int XblMultiplayerSessionHostCandidates_Lua(lua_State *L) { auto sessionHandle = GetSessionHandleFromArg(L, 1); // CODE SNIPPET START: XblMultiplayerSessionHostCandidates const XblDeviceToken* deviceTokens = nullptr; size_t deviceTokensCount = 0; HRESULT hr = XblMultiplayerSessionHostCandidates(sessionHandle, &deviceTokens, &deviceTokensCount); // CODE SNIPPET END LogToFile("XblMultiplayerSessionHostCandidates: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblMultiplayerSessionSessionReference_Lua(lua_State *L) { auto sessionHandle = GetSessionHandleFromArg(L, 1); // CODE SNIPPET START: XblMultiplayerSessionSessionReference const XblMultiplayerSessionReference* sessionRef = XblMultiplayerSessionSessionReference(sessionHandle); // CODE SNIPPET END LogToFile("XblMultiplayerSessionSessionReference"); LogToFile("Scid:%s", sessionRef->Scid); LogToFile("SessionName:%s", sessionRef->SessionName); LogToFile("SessionTemplateName:%s", sessionRef->SessionTemplateName); memcpy(&MPState()->sessionRef, sessionRef, sizeof(XblMultiplayerSessionReference)); return LuaReturnHR(L, S_OK); } int XblMultiplayerSessionSessionConstants_Lua(lua_State *L) { auto sessionHandle = GetSessionHandleFromArg(L, 1); // CODE SNIPPET START: XblMultiplayerSessionSessionConstants const XblMultiplayerSessionConstants* consts = XblMultiplayerSessionSessionConstants(sessionHandle); // CODE SNIPPET END LogToFile("XblMultiplayerSessionSessionConstants"); LogToFile("MaxMembersInSession: %d", consts->MaxMembersInSession); LogToFile("Visibility: %d", consts->Visibility); //uint64_t* InitiatorXuids; LogToFile("InitiatorXuidsCount: %ul", consts->InitiatorXuidsCount); if (consts->CustomJson) LogToFile("CustomJson: %s", consts->CustomJson); if (consts->SessionCloudComputePackageConstantsJson) LogToFile("SessionCloudComputePackageConstantsJson: %s", consts->SessionCloudComputePackageConstantsJson); LogToFile("MemberReservedTimeout: %ul", consts->MemberReservedTimeout); LogToFile("MemberInactiveTimeout: %ul", consts->MemberInactiveTimeout); LogToFile("MemberReadyTimeout: %ul", consts->MemberReadyTimeout); LogToFile("SessionEmptyTimeout: %ul", consts->SessionEmptyTimeout); LogToFile("EnableMetricsLatency: %d", consts->EnableMetricsLatency); LogToFile("EnableMetricsBandwidthDown: %d", consts->EnableMetricsBandwidthDown); LogToFile("EnableMetricsBandwidthUp: %d", consts->EnableMetricsBandwidthUp); LogToFile("EnableMetricsCustom: %d", consts->EnableMetricsCustom); //XblMultiplayerMemberInitialization* MemberInitialization; LogToFile("PeerToPeerRequirements->LatencyMaximum: %ul", consts->PeerToPeerRequirements.LatencyMaximum); LogToFile("PeerToPeerRequirements->BandwidthMinimumInKbps: %ul", consts->PeerToPeerRequirements.BandwidthMinimumInKbps); LogToFile("PeerToHostRequirements->LatencyMaximum: %ul", consts->PeerToHostRequirements.LatencyMaximum); LogToFile("PeerToHostRequirements->BandwidthMinimumInKbps: %ul", consts->PeerToHostRequirements.BandwidthDownMinimumInKbps); LogToFile("PeerToHostRequirements->BandwidthUpMinimumInKbps: %ul", consts->PeerToHostRequirements.BandwidthUpMinimumInKbps); LogToFile("PeerToHostRequirements->HostSelectionMetric: %ul", consts->PeerToHostRequirements.HostSelectionMetric); if (consts->MeasurementServerAddressesJson) LogToFile("MeasurementServerAddressesJson: %s", consts->MeasurementServerAddressesJson); LogToFile("ClientMatchmakingCapable: %d", consts->ClientMatchmakingCapable); LogToFile("EnableMetricsCustom: %d", consts->EnableMetricsCustom); LogToFile("SessionCapabilities->Connectivity: %d", consts->SessionCapabilities.Connectivity); LogToFile("SessionCapabilities->SuppressPresenceActivityCheck: %d", consts->SessionCapabilities.SuppressPresenceActivityCheck); LogToFile("SessionCapabilities->Gameplay: %d", consts->SessionCapabilities.Gameplay); LogToFile("SessionCapabilities->Large: %d", consts->SessionCapabilities.Large); LogToFile("SessionCapabilities->ConnectionRequiredForActiveMembers: %d", consts->SessionCapabilities.ConnectionRequiredForActiveMembers); LogToFile("SessionCapabilities->UserAuthorizationStyle: %d", consts->SessionCapabilities.UserAuthorizationStyle); LogToFile("SessionCapabilities->Crossplay: %d", consts->SessionCapabilities.Crossplay); LogToFile("SessionCapabilities->Searchable: %d", consts->SessionCapabilities.Searchable); LogToFile("SessionCapabilities->HasOwners: %d", consts->SessionCapabilities.HasOwners); return LuaReturnHR(L, S_OK); } int XblMultiplayerSessionConstantsSetMaxMembersInSession_Lua(lua_State *L) { uint32_t maxMembersInSession = GetUint32FromLua(L, 1, 10); auto sessionHandle = GetSessionHandleFromArg(L, 1); // CODE SNIPPET START: XblMultiplayerSessionConstantsSetMaxMembersInSession XblMultiplayerSessionConstantsSetMaxMembersInSession(sessionHandle, maxMembersInSession); // CODE SNIPPET END LogToFile("XblMultiplayerSessionConstantsSetMaxMembersInSession"); return LuaReturnHR(L, S_OK); } int XblMultiplayerSessionConstantsSetVisibility_Lua(lua_State *L) { XblMultiplayerSessionVisibility visibility = static_cast(GetUint32FromLua(L, 1, 3)); auto sessionHandle = GetSessionHandleFromArg(L, 2); // CODE SNIPPET START: XblMultiplayerSessionConstantsSetVisibility XblMultiplayerSessionConstantsSetVisibility(sessionHandle, visibility); // CODE SNIPPET END LogToFile("XblMultiplayerSessionConstantsSetVisibility"); return LuaReturnHR(L, S_OK); } int XblMultiplayerSessionConstantsSetTimeouts_Lua(lua_State *L) { uint64_t memberReservedTimeout = GetUint64FromLua(L, 1, 100); uint64_t memberInactiveTimeout = GetUint64FromLua(L, 2, 100); uint64_t memberReadyTimeout = GetUint64FromLua(L, 3, 100); uint64_t sessionEmptyTimeout = GetUint64FromLua(L, 4, 100); auto sessionHandle = GetSessionHandleFromArg(L, 5); // CODE SNIPPET START: XblMultiplayerSessionConstantsSetTimeouts HRESULT hr = XblMultiplayerSessionConstantsSetTimeouts( sessionHandle, memberReservedTimeout, memberInactiveTimeout, memberReadyTimeout, sessionEmptyTimeout); // CODE SNIPPET END LogToFile("XblMultiplayerSessionConstantsSetTimeouts: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblMultiplayerSessionConstantsSetQosConnectivityMetrics_Lua(lua_State *L) { bool enableLatencyMetric = GetBoolFromLua(L, 1, false); bool enableBandwidthDownMetric = GetBoolFromLua(L, 2, false); bool enableBandwidthUpMetric = GetBoolFromLua(L, 3, false); bool enableCustomMetric = GetBoolFromLua(L, 4, false); auto sessionHandle = GetSessionHandleFromArg(L, 5); // CODE SNIPPET START: XblMultiplayerSessionConstantsSetQosConnectivityMetrics HRESULT hr = XblMultiplayerSessionConstantsSetQosConnectivityMetrics( sessionHandle, enableLatencyMetric, enableBandwidthDownMetric, enableBandwidthUpMetric, enableCustomMetric); // CODE SNIPPET END LogToFile("XblMultiplayerSessionConstantsSetQosConnectivityMetrics: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblMultiplayerSessionConstantsSetMemberInitialization_Lua(lua_State *L) { XblMultiplayerMemberInitialization init = {}; init.JoinTimeout = GetUint64FromLua(L, 1, 100); init.MeasurementTimeout = GetUint64FromLua(L, 2, 100); init.EvaluationTimeout = GetUint64FromLua(L, 3, 100); init.ExternalEvaluation = GetBoolFromLua(L, 4, false); init.MembersNeededToStart = GetUint32FromLua(L, 5, 2); auto sessionHandle = GetSessionHandleFromArg(L, 6); // CODE SNIPPET START: XblMultiplayerSessionConstantsSetMemberInitialization HRESULT hr = XblMultiplayerSessionConstantsSetMemberInitialization(sessionHandle, init); // CODE SNIPPET END LogToFile("XblMultiplayerSessionConstantsSetMemberInitialization: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblMultiplayerSessionConstantsSetPeerToPeerRequirements_Lua(lua_State *L) { XblMultiplayerPeerToPeerRequirements requirements = {}; requirements.LatencyMaximum = GetUint64FromLua(L, 1, 100); requirements.BandwidthMinimumInKbps = GetUint64FromLua(L, 2, 100); auto sessionHandle = GetSessionHandleFromArg(L, 3); // CODE SNIPPET START: XblMultiplayerSessionConstantsSetPeerToPeerRequirements HRESULT hr = XblMultiplayerSessionConstantsSetPeerToPeerRequirements(sessionHandle, requirements); // CODE SNIPPET END LogToFile("XblMultiplayerSessionConstantsSetPeerToPeerRequirements: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblMultiplayerSessionConstantsSetPeerToHostRequirements_Lua(lua_State *L) { XblMultiplayerPeerToHostRequirements requirements = {}; requirements.LatencyMaximum = GetUint64FromLua(L, 1, 100); requirements.BandwidthDownMinimumInKbps = GetUint64FromLua(L, 2, 10); requirements.BandwidthUpMinimumInKbps = GetUint64FromLua(L, 3, 10); requirements.HostSelectionMetric = static_cast(GetUint64FromLua(L, 4, 1)); auto sessionHandle = GetSessionHandleFromArg(L, 5); // CODE SNIPPET START: XblMultiplayerSessionConstantsSetPeerToHostRequirements HRESULT hr = XblMultiplayerSessionConstantsSetPeerToHostRequirements(sessionHandle, requirements); // CODE SNIPPET END LogToFile("XblMultiplayerSessionConstantsSetPeerToHostRequirements: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblMultiplayerSessionConstantsSetMeasurementServerAddressesJson_Lua(lua_State *L) { std::string measurementServerAddressesJson = GetStringFromLua(L, 1, "{}"); auto sessionHandle = GetSessionHandleFromArg(L, 2); // CODE SNIPPET START: XblMultiplayerSessionConstantsSetMeasurementServerAddressesJson HRESULT hr = XblMultiplayerSessionConstantsSetMeasurementServerAddressesJson( sessionHandle, measurementServerAddressesJson.c_str()); // CODE SNIPPET END LogToFile("XblMultiplayerSessionConstantsSetMeasurementServerAddressesJson: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblMultiplayerSessionConstantsSetCapabilities_Lua(lua_State *L) { XblMultiplayerSessionCapabilities caps = {}; caps.Connectivity = GetBoolFromLua(L, 1, false); caps.SuppressPresenceActivityCheck = GetBoolFromLua(L, 4, false); caps.Gameplay = GetBoolFromLua(L, 5, false); caps.Large = GetBoolFromLua(L, 6, true); caps.ConnectionRequiredForActiveMembers = GetBoolFromLua(L, 7, false); caps.UserAuthorizationStyle = GetBoolFromLua(L, 8, false); caps.Crossplay = GetBoolFromLua(L, 9, false); caps.Searchable = GetBoolFromLua(L, 10, false); caps.HasOwners = GetBoolFromLua(L, 11, false); auto sessionHandle = GetSessionHandleFromArg(L, 12); // CODE SNIPPET START: XblMultiplayerSessionConstantsSetCapabilities HRESULT hr = XblMultiplayerSessionConstantsSetCapabilities( sessionHandle, caps); // CODE SNIPPET END LogToFile("XblMultiplayerSessionConstantsSetCapabilities: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblMultiplayerSessionConstantsSetCloudComputePackageJson_Lua(lua_State *L) { std::string sessionCloudComputePackageConstantsJson = GetStringFromLua(L, 1, "{}"); auto sessionHandle = GetSessionHandleFromArg(L, 2); // CODE SNIPPET START: XblMultiplayerSessionConstantsSetCloudComputePackageJson HRESULT hr = XblMultiplayerSessionConstantsSetCloudComputePackageJson( sessionHandle, sessionCloudComputePackageConstantsJson.c_str()); // CODE SNIPPET END LogToFile("XblMultiplayerSessionConstantsSetCloudComputePackageJson: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblMultiplayerSessionSessionProperties_Lua(lua_State *L) { auto sessionHandle = GetSessionHandleFromArg(L, 1); // CODE SNIPPET START: XblMultiplayerSessionSessionProperties const XblMultiplayerSessionProperties* props = XblMultiplayerSessionSessionProperties(sessionHandle); // CODE SNIPPET END LogToFile("XblMultiplayerSessionSessionProperties"); for (size_t i = 0; i < props->KeywordCount; i++) { LogToFile("Keywords[%ul]: %s", i, props->Keywords[i]); } LogToFile("KeywordCount: %ul", props->KeywordCount); LogToFile("JoinRestriction: %d", props->JoinRestriction); LogToFile("ReadRestriction: %d", props->ReadRestriction); for (size_t i = 0; i < props->TurnCollectionCount; i++) { LogToFile("TurnCollection[%d]: %ul", i, props->TurnCollection[i]); } LogToFile("TurnCollectionCount: %ul", props->TurnCollectionCount); LogToFile("MatchmakingTargetSessionConstantsJson: %s", props->MatchmakingTargetSessionConstantsJson); LogToFile("SessionCustomPropertiesJson: %s", props->SessionCustomPropertiesJson); LogToFile("MatchmakingServerConnectionString: %s", props->MatchmakingServerConnectionString); for (size_t i = 0; i < props->ServerConnectionStringCandidatesCount; i++) { LogToFile("ServerConnectionStringCandidates[%ul]: %s", i, props->ServerConnectionStringCandidates[i]); } LogToFile("ServerConnectionStringCandidatesCount: %ul", props->ServerConnectionStringCandidatesCount); for (size_t i = 0; i < props->SessionOwnerMemberIdsCount; i++) { LogToFile("SessionOwnerMemberIds[%ul]: %s", i, props->SessionOwnerMemberIds[i]); } LogToFile("SessionOwnerMemberIdsCount: %ul", props->SessionOwnerMemberIdsCount); LogToFile("HostDeviceToken: %s", props->HostDeviceToken.Value); LogToFile("Closed: %d", props->Closed); LogToFile("Locked: %d", props->Locked); LogToFile("AllocateCloudCompute: %d", props->AllocateCloudCompute); LogToFile("MatchmakingResubmit: %d", props->MatchmakingResubmit); return LuaReturnHR(L, S_OK); } int XblMultiplayerSessionPropertiesSetKeywords_Lua(lua_State *L) { std::string key1 = GetStringFromLua(L, 1, "Keyword1"); std::string key2 = GetStringFromLua(L, 2, "Keyword2"); auto sessionHandle = GetSessionHandleFromArg(L, 3); // CODE SNIPPET START: XblMultiplayerSessionPropertiesSetKeywords const char* keywords[2] = {}; keywords[0] = key1.c_str(); keywords[1] = key2.c_str(); size_t keywordsCount = 2; HRESULT hr = XblMultiplayerSessionPropertiesSetKeywords( sessionHandle, keywords, keywordsCount); // CODE SNIPPET END LogToFile("XblMultiplayerSessionPropertiesSetKeywords: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblMultiplayerSessionPropertiesSetJoinRestriction_Lua(lua_State *L) { XblMultiplayerSessionRestriction joinRestriction = static_cast(GetUint64FromLua(L, 1, static_cast(XblMultiplayerSessionRestriction::Followed))); auto sessionHandle = GetSessionHandleFromArg(L, 2); // CODE SNIPPET START: XblMultiplayerSessionPropertiesSetJoinRestriction XblMultiplayerSessionPropertiesSetJoinRestriction( sessionHandle, joinRestriction); // CODE SNIPPET END LogToFile("XblMultiplayerSessionPropertiesSetJoinRestriction"); return LuaReturnHR(L, S_OK); } int XblMultiplayerSessionPropertiesSetReadRestriction_Lua(lua_State *L) { XblMultiplayerSessionRestriction readRestriction = static_cast(GetUint64FromLua(L, 1, static_cast(XblMultiplayerSessionRestriction::Followed))); auto sessionHandle = GetSessionHandleFromArg(L, 2); // CODE SNIPPET START: XblMultiplayerSessionPropertiesSetReadRestriction XblMultiplayerSessionPropertiesSetReadRestriction( sessionHandle, readRestriction ); // CODE SNIPPET END LogToFile("XblMultiplayerSessionPropertiesSetReadRestriction"); return LuaReturnHR(L, S_OK); } int XblMultiplayerSessionPropertiesSetTurnCollection_Lua(lua_State *L) { uint32_t turnId1 = GetUint32FromLua(L, 1, 0); uint32_t turnId2 = GetUint32FromLua(L, 2, 1); auto sessionHandle = GetSessionHandleFromArg(L, 3); // CODE SNIPPET START: XblMultiplayerSessionPropertiesSetTurnCollection uint32_t turnCollectionMemberIds[2] = {}; turnCollectionMemberIds[0] = turnId1; turnCollectionMemberIds[1] = turnId2; size_t turnCollectionMemberIdsCount = 2; HRESULT hr = XblMultiplayerSessionPropertiesSetTurnCollection( sessionHandle, turnCollectionMemberIds, turnCollectionMemberIdsCount); // CODE SNIPPET END LogToFile("XblMultiplayerSessionPropertiesSetTurnCollection: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblMultiplayerSessionRoleTypes_Lua(lua_State *L) { auto sessionHandle = GetSessionHandleFromArg(L, 1); // CODE SNIPPET START: XblMultiplayerSessionRoleTypes const XblMultiplayerRoleType* roleTypes = nullptr; size_t roleTypesCount = 0; HRESULT hr = XblMultiplayerSessionRoleTypes( sessionHandle, &roleTypes, &roleTypesCount); // CODE SNIPPET END LogToFile("XblMultiplayerSessionRoleTypes: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblMultiplayerSessionGetRoleByName_Lua(lua_State *L) { std::string roleTypeName = GetStringFromLua(L, 1, "Role1"); std::string roleName = GetStringFromLua(L, 2, "RoleName1"); auto sessionHandle = GetSessionHandleFromArg(L, 3); // CODE SNIPPET START: XblMultiplayerSessionGetRoleByName const XblMultiplayerRole* role = nullptr; HRESULT hr = XblMultiplayerSessionGetRoleByName( sessionHandle, roleTypeName.c_str(), roleName.c_str(), &role); // CODE SNIPPET END LogToFile("XblMultiplayerSessionGetRoleByName: hr=%s", ConvertHR(hr).c_str()); if (role == nullptr) { return LuaReturnHR(L, S_OK); } //XblMultiplayerRoleType* RoleType; LogToFile("role.Name: %s", role->Name); for (uint32_t i = 0; i < role->MemberCount; i++) { LogToFile("role.MemberXuids[%ul]: %ul", i, role->MemberXuids[i]); } LogToFile("role.MemberCount: %d", role->MemberCount); LogToFile("role.TargetCount: %d", role->TargetCount); LogToFile("role.MaxMemberCount: %d", role->MaxMemberCount); return LuaReturnHR(L, hr); } int XblMultiplayerSessionSetMutableRoleSettings_Lua(lua_State *L) { std::string roleTypeName = GetStringFromLua(L, 1, "Role1"); std::string roleName = GetStringFromLua(L, 2, "RoleName1"); auto sessionHandle = GetSessionHandleFromArg(L, 3); // CODE SNIPPET START: XblMultiplayerSessionSetMutableRoleSettings uint32_t maxMemberCount = 0; uint32_t targetMemberCount = 0; HRESULT hr = XblMultiplayerSessionSetMutableRoleSettings( sessionHandle, roleTypeName.c_str(), roleName.c_str(), &maxMemberCount, &targetMemberCount); // CODE SNIPPET END LogToFile("XblMultiplayerSessionSetMutableRoleSettings: hr=%s", ConvertHR(hr).c_str()); LogToFile("maxMemberCount: %d", maxMemberCount); LogToFile("targetMemberCount: %d", targetMemberCount); return LuaReturnHR(L, hr); } void LogSessionMember(const XblMultiplayerSessionMember* member) { // TODO LogToFile("member->MemberId: %d", member->MemberId); LogToFile("member->InitialTeam: %s", member->InitialTeam); LogToFile("member->Xuid: %ul", member->Xuid); LogToFile("member->CustomConstantsJson: %s", member->CustomConstantsJson); LogToFile("member->SecureDeviceBaseAddress64: %s", member->SecureDeviceBaseAddress64); for (size_t i = 0; i < member->RolesCount; i++) { LogToFile("member->Roles[%ul].roleTypeName %s", i, member->Roles[i].roleTypeName); LogToFile("member->Roles[%ul].roleName %s", i, member->Roles[i].roleName); } LogToFile("member->RolesCount: %ul", member->RolesCount); LogToFile("member->CustomPropertiesJson: %s", member->CustomPropertiesJson); LogToFile("member->Gamertag: %s", member->Gamertag); LogToFile("member->XblMultiplayerSessionMemberStatus: %d", member->Status); LogToFile("member->IsTurnAvailable: %d", member->IsTurnAvailable); LogToFile("member->IsCurrentUser: %d", member->IsCurrentUser); LogToFile("member->InitializeRequested: %d", member->InitializeRequested); LogToFile("member->MatchmakingResultServerMeasurementsJson: %s", member->MatchmakingResultServerMeasurementsJson); LogToFile("member->ServerMeasurementsJson: %s", member->ServerMeasurementsJson); for (size_t i = 0; i < member->MembersInGroupCount; i++) { LogToFile("member->MembersInGroupIds[%d]: %ul", i, member->MembersInGroupIds[i]); } LogToFile("member->MembersInGroupCount: %ul", member->MembersInGroupCount); LogToFile("member->QosMeasurementsJson: %s", member->QosMeasurementsJson); LogToFile("member->DeviceToken: %s", member->DeviceToken.Value); LogToFile("member->Nat: %d", member->Nat); LogToFile("member->ActiveTitleId: %d", member->ActiveTitleId); LogToFile("member->InitializationEpisode: %d", member->InitializationEpisode); LogToFile("member->JoinTime: %ul", member->JoinTime); LogToFile("member->InitializationFailureCause: %d", member->InitializationFailureCause); for (size_t i = 0; i < member->GroupsCount; i++) { LogToFile("member->Groups[%d]: %s", i, member->Groups[i]); } LogToFile("member->GroupsCount: %ul", member->GroupsCount); for (size_t i = 0; i < member->EncountersCount; i++) { LogToFile("member->Encounters[%d]: %s", i, member->Encounters[i]); } LogToFile("member->EncountersCount: %ul", member->EncountersCount); } int XblMultiplayerSessionMembers_Lua(lua_State *L) { auto sessionHandle = GetSessionHandleFromArg(L, 1); // CODE SNIPPET START: XblMultiplayerSessionMembers const XblMultiplayerSessionMember* members = nullptr; size_t membersCount = 0; HRESULT hr = XblMultiplayerSessionMembers( sessionHandle, &members, &membersCount ); // CODE SNIPPET END LogToFile("XblMultiplayerSessionMembers: hr=%s", ConvertHR(hr).c_str()); for (size_t i = 0; i < membersCount; i++) { LogSessionMember(&members[i]); } LogToFile("membersCount: %d", membersCount); return LuaReturnHR(L, hr); } int XblMultiplayerSessionGetMember_Lua(lua_State *L) { uint32_t memberId = GetUint32FromLua(L, 1, 0); auto sessionHandle = GetSessionHandleFromArg(L, 2); // CODE SNIPPET START: XblMultiplayerSessionGetMember const XblMultiplayerSessionMember* member = XblMultiplayerSessionGetMember( sessionHandle, memberId); // CODE SNIPPET END LogToFile("XblMultiplayerSessionGetMember"); LogSessionMember(member); return LuaReturnHR(L, S_OK); } int XblMultiplayerSessionMatchmakingServer_Lua(lua_State *L) { auto sessionHandle = GetSessionHandleFromArg(L, 1); // CODE SNIPPET START: XblMultiplayerSessionMatchmakingServer const XblMultiplayerMatchmakingServer* server = XblMultiplayerSessionMatchmakingServer(sessionHandle); // CODE SNIPPET END LogToFile("XblMultiplayerSessionMatchmakingServer"); if (server == nullptr) { LogToFile("server == nullptr"); return LuaReturnHR(L, S_OK); } LogToFile("server->Status: %d", server->Status); LogToFile("server->StatusDetails: %s", server->StatusDetails); LogToFile("server->TypicalWaitInSeconds: %d", server->TypicalWaitInSeconds); LogSessionRef(&server->TargetSessionRef); return LuaReturnHR(L, S_OK); } int XblMultiplayerSessionMembersAccepted_Lua(lua_State *L) { auto sessionHandle = GetSessionHandleFromArg(L, 1); // CODE SNIPPET START: XblMultiplayerSessionMembersAccepted uint32_t membersAccepted = XblMultiplayerSessionMembersAccepted(sessionHandle); // CODE SNIPPET END LogToFile("XblMultiplayerSessionMembersAccepted"); LogToFile("membersAccepted: %d", membersAccepted); return LuaReturnHR(L, S_OK); } int XblMultiplayerSessionRawServersJson_Lua(lua_State *L) { auto sessionHandle = GetSessionHandleFromArg(L, 1); // CODE SNIPPET START: XblMultiplayerSessionRawServersJson const char* json = XblMultiplayerSessionRawServersJson(sessionHandle); // CODE SNIPPET END LogToFile("XblMultiplayerSessionRawServersJson"); LogToFile("json: %s", json); return LuaReturnHR(L, S_OK); } int XblMultiplayerSessionSetRawServersJson_Lua(lua_State *L) { std::string json = GetStringFromLua(L, 1, "{}"); auto sessionHandle = GetSessionHandleFromArg(L, 2); // CODE SNIPPET START: XblMultiplayerSessionSetRawServersJson HRESULT hr = XblMultiplayerSessionSetRawServersJson(sessionHandle, json.c_str()); // CODE SNIPPET END LogToFile("XblMultiplayerSessionSetRawServersJson: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblMultiplayerSessionEtag_Lua(lua_State *L) { auto sessionHandle = GetSessionHandleFromArg(L, 1); // CODE SNIPPET START: XblMultiplayerSessionEtag const char* etag = XblMultiplayerSessionEtag(sessionHandle); // CODE SNIPPET END LogToFile("XblMultiplayerSessionEtag"); LogToFile("etag: %s", etag); return LuaReturnHR(L, S_OK); } int XblMultiplayerSessionCurrentUser_Lua(lua_State *L) { auto sessionHandle = GetSessionHandleFromArg(L, 1); // CODE SNIPPET START: XblMultiplayerSessionCurrentUser const XblMultiplayerSessionMember* member = XblMultiplayerSessionCurrentUser(sessionHandle); // CODE SNIPPET END LogToFile("XblMultiplayerSessionCurrentUser"); if (member == nullptr) { LogToFile("member == nullptr"); return LuaReturnHR(L, S_OK); } LogSessionMember(member); return LuaReturnHR(L, S_OK); } int XblMultiplayerSessionGetInfo_Lua(lua_State *L) { auto sessionHandle = GetSessionHandleFromArg(L, 1); // CODE SNIPPET START: XblMultiplayerSessionGetInfo const XblMultiplayerSessionInfo * sessionInfo = XblMultiplayerSessionGetInfo(sessionHandle); // CODE SNIPPET END LogToFile("XblMultiplayerSessionGetInfo"); LogToFile("sessionInfo->ContractVersion: %d", sessionInfo->ContractVersion); LogToFile("sessionInfo->Branch: %s", sessionInfo->Branch); LogToFile("sessionInfo->ChangeNumber: %ul", sessionInfo->ChangeNumber); LogToFile("sessionInfo->CorrelationId: %s", sessionInfo->CorrelationId); LogToFile("sessionInfo->StartTime: %ul", sessionInfo->StartTime); LogToFile("sessionInfo->NextTimer: %ul", sessionInfo->NextTimer); LogToFile("sessionInfo->SearchHandleId: %s", sessionInfo->SearchHandleId); return LuaReturnHR(L, S_OK); } int XblMultiplayerSessionWriteStatus_Lua(lua_State *L) { auto sessionHandle = GetSessionHandleFromArg(L, 1); // CODE SNIPPET START: XblMultiplayerSessionWriteStatus XblWriteSessionStatus status = XblMultiplayerSessionWriteStatus(sessionHandle); // CODE SNIPPET END LogToFile("XblMultiplayerSessionWriteStatus"); LogToFile("status: %d", status); return LuaReturnHR(L, S_OK); } int XblMultiplayerSessionSetInitializationSucceeded_Lua(lua_State *L) { auto sessionHandle = GetSessionHandleFromArg(L, 1); bool initSucceded = GetBoolFromLua(L, 1, false); // CODE SNIPPET START: XblMultiplayerSessionSetInitializationSucceeded XblMultiplayerSessionSetInitializationSucceeded(sessionHandle, initSucceded); // CODE SNIPPET END LogToFile("XblMultiplayerSessionSetInitializationSucceeded %d", initSucceded); return LuaReturnHR(L, S_OK); } int XblMultiplayerSessionSetHostDeviceToken_Lua(lua_State *L) { std::string host = GetStringFromLua(L, 1, "DefaultHost"); auto sessionHandle = GetSessionHandleFromArg(L, 2); // CODE SNIPPET START: XblMultiplayerSessionSetHostDeviceToken XblDeviceToken hostDeviceToken = {}; pal::strcpy(hostDeviceToken.Value, sizeof(hostDeviceToken.Value), host.c_str()); XblMultiplayerSessionSetHostDeviceToken(sessionHandle, hostDeviceToken); // CODE SNIPPET END LogToFile("XblMultiplayerSessionSetHostDeviceToken host:%s", host.c_str()); return LuaReturnHR(L, S_OK); } int XblMultiplayerSessionSetMatchmakingServerConnectionPath_Lua(lua_State *L) { std::string path = GetStringFromLua(L, 1, "DefaultPath"); auto sessionHandle = GetSessionHandleFromArg(L, 2); // CODE SNIPPET START: XblMultiplayerSessionSetMatchmakingServerConnectionPath XblMultiplayerSessionSetMatchmakingServerConnectionPath( sessionHandle, path.c_str()); // CODE SNIPPET END LogToFile("XblMultiplayerSessionSetMatchmakingServerConnectionPath path:%s", path.c_str()); return LuaReturnHR(L, S_OK); } int XblMultiplayerSessionSetClosed_Lua(lua_State *L) { bool closed = GetBoolFromLua(L, 1, true); auto sessionHandle = GetSessionHandleFromArg(L, 2); // CODE SNIPPET START: XblMultiplayerSessionSetClosed XblMultiplayerSessionSetClosed(sessionHandle, closed); // CODE SNIPPET END LogToFile("XblMultiplayerSessionSetClosed %d", closed); return LuaReturnHR(L, S_OK); } int XblMultiplayerSessionSetLocked_Lua(lua_State *L) { bool locked = GetBoolFromLua(L, 1, false); auto sessionHandle = GetSessionHandleFromArg(L, 2); // CODE SNIPPET START: XblMultiplayerSessionSetLocked XblMultiplayerSessionSetLocked(sessionHandle, locked); // CODE SNIPPET END LogToFile("XblMultiplayerSessionSetLocked %d", locked); return LuaReturnHR(L, S_OK); } int XblMultiplayerSessionSetAllocateCloudCompute_Lua(lua_State *L) { bool allocate = GetBoolFromLua(L, 1, false); auto sessionHandle = GetSessionHandleFromArg(L, 2); // CODE SNIPPET START: XblMultiplayerSessionSetAllocateCloudCompute XblMultiplayerSessionSetAllocateCloudCompute(sessionHandle, allocate); // CODE SNIPPET END LogToFile("XblMultiplayerSessionSetAllocateCloudCompute %d", allocate); return LuaReturnHR(L, S_OK); } int XblMultiplayerSessionSetMatchmakingResubmit_Lua(lua_State *L) { bool matchResubmit = GetBoolFromLua(L, 1, false); auto sessionHandle = GetSessionHandleFromArg(L, 2); // CODE SNIPPET START: XblMultiplayerSessionSetMatchmakingResubmit XblMultiplayerSessionSetMatchmakingResubmit(sessionHandle, matchResubmit); // CODE SNIPPET END LogToFile("XblMultiplayerSessionSetMatchmakingResubmit %d", matchResubmit); return LuaReturnHR(L, S_OK); } int XblMultiplayerSessionSetServerConnectionStringCandidates_Lua(lua_State *L) { std::string candidate1 = GetStringFromLua(L, 1, "Candidate1"); std::string candidate2 = GetStringFromLua(L, 2, "Candidate1"); auto sessionHandle = GetSessionHandleFromArg(L, 3); HRESULT hr = S_OK; if (sessionHandle != nullptr) // might be null if previous call fails { // CODE SNIPPET START: XblMultiplayerSessionSetServerConnectionStringCandidates const char* serverConnectionStringCandidates[2] = {}; serverConnectionStringCandidates[0] = candidate1.c_str(); serverConnectionStringCandidates[1] = candidate2.c_str(); size_t serverConnectionStringCandidatesCount = 2; hr = XblMultiplayerSessionSetServerConnectionStringCandidates( sessionHandle, serverConnectionStringCandidates, serverConnectionStringCandidatesCount); // CODE SNIPPET END } LogToFile("XblMultiplayerSessionSetServerConnectionStringCandidates: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblMultiplayerSessionSetSessionChangeSubscription_Lua(lua_State *L) { XblMultiplayerSessionChangeTypes changeTypes = static_cast(GetUint32FromLua(L, 1, static_cast(XblMultiplayerSessionChangeTypes::Everything))); auto sessionHandle = GetSessionHandleFromArg(L, 2); // CODE SNIPPET START: XblMultiplayerSessionSetSessionChangeSubscription HRESULT hr = XblMultiplayerSessionSetSessionChangeSubscription( sessionHandle, changeTypes); // CODE SNIPPET END LogToFile("XblMultiplayerSessionSetSessionChangeSubscription: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblMultiplayerSessionLeave_Lua(lua_State *L) { auto sessionHandle = GetSessionHandleFromArg(L, 1); // CODE SNIPPET START: XblMultiplayerSessionLeave HRESULT hr = XblMultiplayerSessionLeave(sessionHandle); // CODE SNIPPET END LogToFile("XblMultiplayerSessionLeave: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblMultiplayerSessionCurrentUserSetStatus_Lua(lua_State *L) { XblMultiplayerSessionMemberStatus status = static_cast(GetUint32FromLua(L, 1, 3)); auto sessionHandle = GetSessionHandleFromArg(L, 2); ENSURE_IS_TRUE(sessionHandle != nullptr, "No valid multiplayer session."); // CODE SNIPPET START: XblMultiplayerSessionCurrentUserSetStatus HRESULT hr = XblMultiplayerSessionCurrentUserSetStatus( sessionHandle, status); // CODE SNIPPET END LogToFile("XblMultiplayerSessionCurrentUserSetStatus: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblMultiplayerSessionCurrentUserSetSecureDeviceAddressBase64_Lua(lua_State *L) { std::string deviceAddress = GetStringFromLua(L, 1, "ExampleDeviceAddress"); auto sessionHandle = GetSessionHandleFromArg(L, 2); ENSURE_IS_TRUE(sessionHandle != nullptr, "No valid multiplayer session."); // CODE SNIPPET START: XblMultiplayerSessionCurrentUserSetSecureDeviceAddressBase64 HRESULT hr = XblMultiplayerSessionCurrentUserSetSecureDeviceAddressBase64( sessionHandle, deviceAddress.c_str()); // CODE SNIPPET END LogToFile("XblMultiplayerSessionCurrentUserSetSecureDeviceAddressBase64: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblFormatSecureDeviceAddress_Lua(lua_State *L) { #if HC_PLATFORM != HC_PLATFORM_XDK && HC_PLATFORM != HC_PLATFORM_UWP std::string deviceIdStr = GetStringFromLua(L, 1, "ExampleDeviceAddress"); auto deviceId = deviceIdStr.c_str(); // CODE SNIPPET START: XblFormatSecureDeviceAddress XblFormattedSecureDeviceAddress address{ }; HRESULT hr = XblFormatSecureDeviceAddress(deviceId, &address); // CODE SNIPPET END LogToFile("XblFormatSecureDeviceAddress: hr=%s sda=%s", hr, address.value); #endif return LuaReturnHR(L, S_OK); } int XblMultiplayerSessionCurrentUserSetRoles_Lua(lua_State *L) { std::string roleTypeName1 = "roleTypeName1"; std::string roleName1 = "roleName1"; std::string roleTypeName2 = "roleTypeName2"; std::string roleName2 = "roleName2"; auto sessionHandle = GetSessionHandleFromArg(L, 1); // CODE SNIPPET START: XblMultiplayerSessionCurrentUserSetRoles XblMultiplayerSessionMemberRole roles[2] = {}; roles[0].roleTypeName = roleTypeName1.c_str(); roles[0].roleName = roleName1.c_str(); roles[1].roleTypeName = roleTypeName2.c_str(); roles[1].roleName = roleName2.c_str(); size_t rolesCount = 2; HRESULT hr = XblMultiplayerSessionCurrentUserSetRoles( sessionHandle, roles, rolesCount); // CODE SNIPPET END LogToFile("XblMultiplayerSessionCurrentUserSetRoles: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblMultiplayerSessionCurrentUserSetMembersInGroup_Lua(lua_State *L) { uint32_t memberId1 = GetUint32FromLua(L, 1, 0); auto sessionHandle = GetSessionHandleFromArg(L, 2); ENSURE_IS_TRUE(sessionHandle != nullptr, "No valid multiplayer session."); // CODE SNIPPET START: XblMultiplayerSessionCurrentUserSetMembersInGroup uint32_t memberIds[1] = {}; memberIds[0] = memberId1; size_t memberIdsCount = 1; HRESULT hr = XblMultiplayerSessionCurrentUserSetMembersInGroup( sessionHandle, memberIds, memberIdsCount); // CODE SNIPPET END LogToFile("XblMultiplayerSessionCurrentUserSetMembersInGroup: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblMultiplayerSessionCurrentUserSetGroups_Lua(lua_State *L) { std::string group1 = GetStringFromLua(L, 1, "group1"); std::string group2 = GetStringFromLua(L, 2, "group2"); auto sessionHandle = GetSessionHandleFromArg(L, 3); ENSURE_IS_TRUE(sessionHandle != nullptr, "No valid multiplayer session."); // CODE SNIPPET START: XblMultiplayerSessionCurrentUserSetGroups const char* groups[2] = {}; groups[0] = group1.c_str(); groups[1] = group2.c_str(); size_t groupsCount = 2; HRESULT hr = XblMultiplayerSessionCurrentUserSetGroups( sessionHandle, groups, groupsCount); // CODE SNIPPET END LogToFile("XblMultiplayerSessionCurrentUserSetGroups: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblMultiplayerSessionCurrentUserSetEncounters_Lua(lua_State *L) { std::string encounter1 = GetStringFromLua(L, 1, "encounter1"); std::string encounter2 = GetStringFromLua(L, 2, "encounter2"); auto sessionHandle = GetSessionHandleFromArg(L, 3); ENSURE_IS_TRUE(sessionHandle != nullptr, "No valid multiplayer session."); // CODE SNIPPET START: XblMultiplayerSessionCurrentUserSetEncounters const char* encounters[2] = {}; encounters[0] = encounter1.c_str(); encounters[1] = encounter2.c_str(); size_t encountersCount = 2; HRESULT hr = XblMultiplayerSessionCurrentUserSetEncounters( sessionHandle, encounters, encountersCount); // CODE SNIPPET END LogToFile("XblMultiplayerSessionCurrentUserSetEncounters: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblMultiplayerSessionCurrentUserSetQosMeasurements_Lua(lua_State *L) { std::string measurements = GetStringFromLua(L, 1, "{\"measurements1\":5}"); auto sessionHandle = GetSessionHandleFromArg(L, 2); // CODE SNIPPET START: XblMultiplayerSessionCurrentUserSetQosMeasurements HRESULT hr = XblMultiplayerSessionCurrentUserSetQosMeasurements( sessionHandle, measurements.c_str()); // CODE SNIPPET END LogToFile("XblMultiplayerSessionCurrentUserSetQosMeasurements: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblMultiplayerSessionCurrentUserSetServerQosMeasurements_Lua(lua_State *L) { std::string measurements = GetStringFromLua(L, 1, "{\"measurements1\":5}"); auto sessionHandle = GetSessionHandleFromArg(L, 2); // CODE SNIPPET START: XblMultiplayerSessionCurrentUserSetServerQosMeasurements HRESULT hr = XblMultiplayerSessionCurrentUserSetServerQosMeasurements( sessionHandle, measurements.c_str()); // CODE SNIPPET END LogToFile("XblMultiplayerSessionCurrentUserSetServerQosMeasurements: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblMultiplayerSessionCurrentUserSetCustomPropertyJson_Lua(lua_State *L) { std::string name = GetStringFromLua(L, 1, "name1"); std::string json = GetStringFromLua(L, 2, "{\"myscore\":123}"); auto sessionHandle = GetSessionHandleFromArg(L, 3); ENSURE_IS_TRUE(sessionHandle != nullptr, "No valid multiplayer session."); // CODE SNIPPET START: XblMultiplayerSessionCurrentUserSetCustomPropertyJson HRESULT hr = XblMultiplayerSessionCurrentUserSetCustomPropertyJson( sessionHandle, name.c_str(), json.c_str()); // CODE SNIPPET END LogToFile("XblMultiplayerSessionCurrentUserSetCustomPropertyJson: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblMultiplayerSessionCurrentUserDeleteCustomPropertyJson_Lua(lua_State *L) { std::string name = GetStringFromLua(L, 1, "name1"); auto sessionHandle = GetSessionHandleFromArg(L, 2); // CODE SNIPPET START: XblMultiplayerSessionCurrentUserDeleteCustomPropertyJson HRESULT hr = XblMultiplayerSessionCurrentUserDeleteCustomPropertyJson( sessionHandle, name.c_str()); // CODE SNIPPET END LogToFile("XblMultiplayerSessionCurrentUserDeleteCustomPropertyJson: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblMultiplayerSessionSetMatchmakingTargetSessionConstantsJson_Lua(lua_State *L) { std::string consts = GetStringFromLua(L, 1, "{}"); auto sessionHandle = GetSessionHandleFromArg(L, 2); HRESULT hr = S_OK; if (sessionHandle != nullptr) // might be null if previous call fails { // CODE SNIPPET START: XblMultiplayerSessionSetMatchmakingTargetSessionConstantsJson hr = XblMultiplayerSessionSetMatchmakingTargetSessionConstantsJson( sessionHandle, consts.c_str()); // CODE SNIPPET END } LogToFile("XblMultiplayerSessionSetMatchmakingTargetSessionConstantsJson: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblMultiplayerSessionSetCustomPropertyJson_Lua(lua_State *L) { std::string name = GetStringFromLua(L, 1, "name1"); std::string json = GetStringFromLua(L, 2, "{}"); auto sessionHandle = GetSessionHandleFromArg(L, 3); // CODE SNIPPET START: XblMultiplayerSessionSetCustomPropertyJson HRESULT hr = XblMultiplayerSessionSetCustomPropertyJson( sessionHandle, name.c_str(), json.c_str()); // CODE SNIPPET END LogToFile("XblMultiplayerSessionSetCustomPropertyJson: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblMultiplayerSessionDeleteCustomPropertyJson_Lua(lua_State *L) { std::string name = GetStringFromLua(L, 1, "name1"); auto sessionHandle = GetSessionHandleFromArg(L, 2); // CODE SNIPPET START: XblMultiplayerSessionDeleteCustomPropertyJson HRESULT hr = XblMultiplayerSessionDeleteCustomPropertyJson( sessionHandle, name.c_str()); // CODE SNIPPET END LogToFile("XblMultiplayerSessionDeleteCustomPropertyJson: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblMultiplayerSessionCompare_Lua(lua_State *L) { auto sessionHandle = GetSessionHandleFromArg(L, 1); XblMultiplayerSessionHandle sessionHandle2{}; XblMultiplayerSessionDuplicateHandle(sessionHandle, &sessionHandle2); // CODE SNIPPET START: XblMultiplayerSessionCompare XblMultiplayerSessionChangeTypes changeTypes = XblMultiplayerSessionCompare( sessionHandle, sessionHandle2); // CODE SNIPPET END XblMultiplayerSessionCloseHandle(sessionHandle2); LogToFile("XblMultiplayerSessionCompare"); LogToFile("changeTypes: %d", changeTypes); return LuaReturnHR(L, S_OK); } int XblMultiplayerWriteSessionByHandleAsync_Lua(lua_State *L) { CreateQueueIfNeeded(); XblMultiplayerSessionWriteMode writeMode = static_cast(GetUint32FromLua(L, 1, static_cast(XblMultiplayerSessionWriteMode::UpdateExisting))); std::string handleId = GetStringFromLua(L, 2, MPState()->activityHandle); auto sessionHandle = GetSessionHandleFromArg(L, 3); // CODE SNIPPET START: XblMultiplayerWriteSessionByHandleAsync auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->context = nullptr; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* XblMultiplayerSessionHandle sessionHandle; auto hr = XblMultiplayerWriteSessionByHandleResult(asyncBlock, &sessionHandle); LogToFile("XblMultiplayerWriteSessionByHandleResult: hr=%s", ConvertHR(hr).c_str()); // CODE SNIP SKIP CallLuaFunctionWithHr(hr, "OnXblMultiplayerWriteSessionByHandleAsync"); // CODE SNIP SKIP }; HRESULT hr = XblMultiplayerWriteSessionByHandleAsync( Data()->xboxLiveContext, sessionHandle, writeMode, handleId.c_str(), asyncBlock.get()); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } // CODE SNIPPET END LogToFile("XblMultiplayerWriteSessionByHandleAsync: hr=%s", ConvertHR(hr).c_str()); LogToFile("writeMode: %d", writeMode); LogToFile("handleId: %s", handleId.c_str()); return LuaReturnHR(L, hr); } int XblMultiplayerGetSessionAsync_Lua(lua_State *L) { CreateQueueIfNeeded(); XblMultiplayerSessionReference sessionRef; std::string scid = GetStringFromLua(L, 1, Data()->scid); std::string sessionTemplateName = GetStringFromLua(L, 2, "MinGameSession"); auto sessionIndex{ GetUint64FromLua(L, 4, 0) }; std::string sessionName = GetStringFromLua(L, 3, MultiplayerState::GetSessionName(sessionIndex)); pal::strcpy(sessionRef.Scid, sizeof(sessionRef.Scid), scid.c_str()); pal::strcpy(sessionRef.SessionName, sizeof(sessionRef.SessionName), sessionName.c_str()); pal::strcpy(sessionRef.SessionTemplateName, sizeof(sessionRef.SessionTemplateName), sessionTemplateName.c_str()); // CODE SNIPPET START: XblMultiplayerWriteSessionByHandleAsync auto asyncBlock = std::make_unique(); auto contextPtr = std::make_unique(static_cast(sessionIndex)); asyncBlock->queue = Data()->queue; asyncBlock->context = contextPtr.get(); asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* std::unique_ptr sessionIndexPtr{ static_cast(asyncBlock->context) }; auto sessionIndex{ *sessionIndexPtr }; auto& session{ MPState()->sessionHandles[sessionIndex] }; //CODE SNIP SKIP if (session) //CODE SNIP SKIP { XblMultiplayerSessionCloseHandle(session); //CODE SNIP SKIP MPState()->sessionHandles[sessionIndex] = nullptr; //CODE SNIP SKIP } XblMultiplayerSessionHandle sessionHandle = nullptr; auto hr = XblMultiplayerGetSessionResult(asyncBlock, &sessionHandle); LogToFile("XblMultiplayerGetSessionResult: hr=%s", ConvertHR(hr).c_str()); // CODE SNIP SKIP MPState()->sessionHandles[sessionIndex] = sessionHandle; // CODE SNIP SKIP CallLuaFunctionWithHr(hr, "OnXblMultiplayerGetSessionAsync"); // CODE SNIP SKIP }; HRESULT hr = XblMultiplayerGetSessionAsync( Data()->xboxLiveContext, &sessionRef, asyncBlock.get()); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); contextPtr.release(); } // CODE SNIPPET END LogToFile("XblMultiplayerGetSessionAsync: hr=%s", ConvertHR(hr).c_str()); LogSessionRef(&sessionRef); return LuaReturnHR(L, hr); } int XblMultiplayerGetSessionByHandleAsync_Lua(lua_State *L) { CreateQueueIfNeeded(); std::string handleId = GetStringFromLua(L, 1, MPState()->inviteHandle.Data); auto sessionIndex{ GetUint64FromLua(L, 2, 0) }; if (handleId.empty()) { handleId = "86191619-4002-044f-4846-f8f903c71512"; } // CODE SNIPPET START: XblMultiplayerGetSessionByHandleAsync auto asyncBlock = std::make_unique(); auto contextPtr = std::make_unique(static_cast(sessionIndex)); asyncBlock->queue = Data()->queue; asyncBlock->context = contextPtr.get(); asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* std::unique_ptr sessionIndexPtr{ static_cast(asyncBlock->context) }; auto sessionIndex{ *sessionIndexPtr }; auto& session{ MPState()->sessionHandles[sessionIndex] }; //CODE SNIP SKIP if (session) //CODE SNIP SKIP { XblMultiplayerSessionCloseHandle(session); //CODE SNIP SKIP MPState()->sessionHandles[sessionIndex] = nullptr; //CODE SNIP SKIP } XblMultiplayerSessionHandle sessionHandle = nullptr; auto hr = XblMultiplayerGetSessionByHandleResult(asyncBlock, &sessionHandle); LogToFile("XblMultiplayerGetSessionByHandleResult: hr=%s", ConvertHR(hr).c_str()); // CODE SNIP SKIP MPState()->sessionHandles[sessionIndex] = sessionHandle; // CODE SNIP SKIP CallLuaFunctionWithHr(hr, "OnXblMultiplayerGetSessionByHandleAsync"); // CODE SNIP SKIP }; HRESULT hr = XblMultiplayerGetSessionByHandleAsync( Data()->xboxLiveContext, handleId.c_str(), asyncBlock.get()); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); contextPtr.release(); } // CODE SNIPPET END LogToFile("XblMultiplayerGetSessionAsync: hr=%s", ConvertHR(hr).c_str()); LogToFile("handleId: %s", handleId.c_str()); return LuaReturnHR(L, hr); } int XblMultiplayerQuerySessionsAsync_Lua(lua_State *L) { CreateQueueIfNeeded(); std::string Scid = GetStringFromLua(L, 1, Data()->scid); uint32_t MaxItems = GetUint32FromLua(L, 2, 0); bool IncludePrivateSessions = GetBoolFromLua(L, 3, false); bool IncludeReservations = GetBoolFromLua(L, 4, false); bool IncludeInactiveSessions = GetBoolFromLua(L, 5, false); std::string KeywordFilter = GetStringFromLua(L, 6, "killzone"); std::string SessionTemplateNameFilter = GetStringFromLua(L, 7, ""); XblMultiplayerSessionVisibility VisibilityFilter = static_cast(GetUint32FromLua(L, 8, 5)); uint32_t ContractVersionFilter = GetUint32FromLua(L, 9, 0); //uint64_t* XuidFilters; //size_t XuidFiltersCount; // CODE SNIPPET START: XblMultiplayerQuerySessionsAsync auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->context = nullptr; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* size_t sessionCount = 0; HRESULT hr = XblMultiplayerQuerySessionsResultCount(asyncBlock, &sessionCount); LogToFile("sessionCount %d", sessionCount); if (SUCCEEDED(hr)) { std::vector sessions(sessionCount); hr = XblMultiplayerQuerySessionsResult(asyncBlock, sessionCount, sessions.data()); } CallLuaFunctionWithHr(hr, "OnXblMultiplayerQuerySessionsAsync"); // CODE SNIP SKIP }; XblMultiplayerSessionQuery sessionQuery = {}; pal::strcpy(sessionQuery.Scid, sizeof(sessionQuery.Scid), Scid.c_str()); sessionQuery.MaxItems = MaxItems; sessionQuery.IncludePrivateSessions = IncludePrivateSessions; sessionQuery.IncludeReservations = IncludeReservations; sessionQuery.IncludeInactiveSessions = IncludeInactiveSessions; sessionQuery.KeywordFilter = KeywordFilter.c_str(); pal::strcpy(sessionQuery.SessionTemplateNameFilter, sizeof(sessionQuery.SessionTemplateNameFilter), SessionTemplateNameFilter.c_str()); sessionQuery.VisibilityFilter = VisibilityFilter; sessionQuery.ContractVersionFilter = ContractVersionFilter; //sessionQuery.XuidFilters //sessionQuery.XuidFiltersCount HRESULT hr = XblMultiplayerQuerySessionsAsync( Data()->xboxLiveContext, &sessionQuery, asyncBlock.get()); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } // CODE SNIPPET END LogToFile("XblMultiplayerQuerySessionsAsync: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblMultiplayerSetActivityAsync_Lua(lua_State *L) { // CODE SNIPPET START: XblMultiplayerSetActivityAsync auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->context = nullptr; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* HRESULT hr = XAsyncGetStatus(asyncBlock, false); CallLuaFunctionWithHr(hr, "OnXblMultiplayerSetActivityAsync"); // CODE SNIP SKIP }; HRESULT hr = XblMultiplayerSetActivityAsync( Data()->xboxLiveContext, &MPState()->sessionRef, asyncBlock.get()); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } // CODE SNIPPET END LogToFile("XblMultiplayerSetActivityAsync: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblMultiplayerClearActivityAsync_Lua(lua_State *L) { // CODE SNIPPET START: XblMultiplayerClearActivityAsync auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->context = nullptr; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* HRESULT hr = XAsyncGetStatus(asyncBlock, false); CallLuaFunctionWithHr(hr, "OnXblMultiplayerClearActivityAsync"); // CODE SNIP SKIP }; HRESULT hr = XblMultiplayerClearActivityAsync( Data()->xboxLiveContext, Data()->scid, // TODO: fix type asyncBlock.get()); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } // CODE SNIPPET END LogToFile("XblMultiplayerClearActivityAsync: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblMultiplayerSendInvitesAsync_Lua(lua_State *L) { std::string contextStringIdStr = GetStringFromLua(L, 1, "contextStringId1"); std::string customActivationContextStr = GetStringFromLua(L, 2, "customActivationContext1"); uint64_t targetXuid = GetUint64FromLua(L, 3, 2814679169942680); auto xblContextHandle = Data()->xboxLiveContext; auto sessionReference = MPState()->sessionRef; auto titleId = Data()->titleId; auto contextStringId = contextStringIdStr.c_str(); auto customActivationContext = customActivationContextStr.c_str(); // CODE SNIPPET START: XblMultiplayerSendInvitesAsync_C auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->context = nullptr; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* size_t handlesCount = 1; // must be equal to invites requested XblMultiplayerInviteHandle handles[1] = {}; HRESULT hr = XblMultiplayerSendInvitesResult(asyncBlock, handlesCount, handles); MPState()->inviteHandle = handles[0]; // CODE SNIP SKIP CallLuaFunctionWithHr(hr, "OnXblMultiplayerSendInvitesAsync"); // CODE SNIP SKIP }; uint64_t xuids[1] = {}; xuids[0] = targetXuid; size_t xuidsCount = 1; HRESULT hr = XblMultiplayerSendInvitesAsync( xblContextHandle, &sessionReference, xuids, xuidsCount, titleId, contextStringId, customActivationContext, asyncBlock.get()); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } // CODE SNIPPET END LogToFile("XblMultiplayerSendInvitesAsync: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblMultiplayerGetActivitiesForSocialGroupAsync_Lua(lua_State *L) { uint64_t socialGroupOwnerXuid = GetUint64FromLua(L, 1, 2814632956486799); std::string socialGroup = GetStringFromLua(L, 2, "people"); // CODE SNIPPET START: XblMultiplayerGetActivitiesForSocialGroupAsync auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->context = nullptr; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* size_t resultCount{ 0 }; HRESULT hr = XblMultiplayerGetActivitiesForSocialGroupResultCount(asyncBlock, &resultCount); LogToFile("activityCount %d", resultCount); // CODE SNIP SKIP if (SUCCEEDED(hr)) { std::vector activityDetails(resultCount); hr = XblMultiplayerGetActivitiesForSocialGroupResult(asyncBlock, resultCount, activityDetails.data()); } else if (hr == HTTP_E_STATUS_SERVICE_UNAVAIL) { CallLuaFunctionWithHr(S_OK, "OnXblMultiplayerGetActivitiesForSocialGroupAsyncRetry"); return; } CallLuaFunctionWithHr(hr, "OnXblMultiplayerGetActivitiesForSocialGroupAsync"); // CODE SNIP SKIP }; HRESULT hr = XblMultiplayerGetActivitiesForSocialGroupAsync( Data()->xboxLiveContext, Data()->scid, socialGroupOwnerXuid, socialGroup.c_str(), asyncBlock.get()); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } // CODE SNIPPET END LogToFile("XblMultiplayerGetActivitiesForSocialGroupAsync: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblMultiplayerGetActivitiesWithPropertiesForSocialGroupAsync_Lua(lua_State *L) { uint64_t socialGroupOwnerXuid = GetUint64FromLua(L, 1, 2814632956486799); std::string socialGroup = GetStringFromLua(L, 2, "people"); // CODE SNIPPET START: XblMultiplayerGetActivitiesForSocialGroupAsync auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->context = nullptr; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* size_t resultSize{ 0 }; HRESULT hr = XblMultiplayerGetActivitiesWithPropertiesForSocialGroupResultSize(asyncBlock, &resultSize); LogToFile("activityCount %d", resultSize); // CODE SNIP SKIP if (SUCCEEDED(hr)) { if (resultSize > 0) { size_t count{ 0 }; std::vector buffer(resultSize, 0); XblMultiplayerActivityDetails* activityDetails{}; hr = XblMultiplayerGetActivitiesWithPropertiesForSocialGroupResult(asyncBlock, resultSize, buffer.data(), &activityDetails, &count, nullptr); } } CallLuaFunctionWithHr(hr, "OnXblMultiplayerGetActivitiesWithPropertiesForSocialGroupAsync"); // CODE SNIP SKIP }; HRESULT hr = XblMultiplayerGetActivitiesWithPropertiesForSocialGroupAsync( Data()->xboxLiveContext, Data()->scid, socialGroupOwnerXuid, socialGroup.c_str(), asyncBlock.get()); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } // CODE SNIPPET END LogToFile("XblMultiplayerGetActivitiesWithPropertiesForSocialGroupAsync: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblMultiplayerGetActivitiesForUsersAsync_Lua(lua_State *L) { uint64_t xuid1 = GetUint64FromLua(L, 1, Data()->m_multiDeviceManager->GetRemoteXuid()); if (xuid1 == 0) xuid1 = 2814636782672891; // CODE SNIPPET START: XblMultiplayerGetActivitiesForUsersAsync auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->context = nullptr; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* HRESULT hr = XAsyncGetStatus(asyncBlock, false); if (SUCCEEDED(hr)) { size_t resultCount{ 0 }; hr = XblMultiplayerGetActivitiesForUsersResultCount(asyncBlock, &resultCount); if (SUCCEEDED(hr)) { std::vector activityDetails(resultCount); hr = XblMultiplayerGetActivitiesForUsersResult(asyncBlock, resultCount, activityDetails.data()); if (SUCCEEDED(hr)) { if (resultCount > 0) { std::string handleIdStr = activityDetails[0].HandleId; LogToScreen("Joining lobby via handle %s", handleIdStr.c_str()); MPState()->activityHandle = handleIdStr; // CODE SNIP SKIP } else { if (Data()->m_multiDeviceManager->GetRemoteXuid() != 0) { LogToScreen("No activity handle to join. Failing..."); hr = E_FAIL; } } } } } CallLuaFunctionWithHr(hr, "OnXblMultiplayerGetActivitiesForUsersAsync"); // CODE SNIP SKIP }; uint64_t xuids[1] = {}; xuids[0] = xuid1; size_t xuidsCount = 1; HRESULT hr = XblMultiplayerGetActivitiesForUsersAsync( Data()->xboxLiveContext, Data()->scid, xuids, xuidsCount, asyncBlock.get()); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } // CODE SNIPPET END LogToFile("XblMultiplayerGetActivitiesForUsersAsync: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblMultiplayerGetActivitiesWithPropertiesForUsersAsync_Lua(lua_State *L) { uint64_t xuid1 = GetUint64FromLua(L, 1, Data()->m_multiDeviceManager->GetRemoteXuid()); if (xuid1 == 0) xuid1 = 2814636782672891; // CODE SNIPPET START: XblMultiplayerGetActivitiesForUsersAsync auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->context = nullptr; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* HRESULT hr = XAsyncGetStatus(asyncBlock, false); if (SUCCEEDED(hr)) { size_t resultSize{ 0 }; hr = XblMultiplayerGetActivitiesWithPropertiesForUsersResultSize(asyncBlock, &resultSize); if (SUCCEEDED(hr)) { size_t count{ 0 }; std::vector buffer(resultSize); XblMultiplayerActivityDetails* activityDetails{}; if (resultSize > 0) { hr = XblMultiplayerGetActivitiesWithPropertiesForUsersResult(asyncBlock, resultSize, buffer.data(), &activityDetails, &count, nullptr); if (SUCCEEDED(hr)) { std::string handleIdStr = activityDetails[0].HandleId; LogToScreen("Joining lobby via handle %s", handleIdStr.c_str()); MPState()->activityHandle = handleIdStr; // CODE SNIP SKIP } } else { if (Data()->m_multiDeviceManager->GetRemoteXuid() != 0) { LogToScreen("No activity handle to join. Failing..."); hr = E_FAIL; } } } } CallLuaFunctionWithHr(hr, "OnXblMultiplayerGetActivitiesWithPropertiesForUsersAsync"); // CODE SNIP SKIP }; uint64_t xuids[1] = {}; xuids[0] = xuid1; size_t xuidsCount = 1; HRESULT hr = XblMultiplayerGetActivitiesWithPropertiesForUsersAsync( Data()->xboxLiveContext, Data()->scid, xuids, xuidsCount, asyncBlock.get()); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } // CODE SNIPPET END LogToFile("XblMultiplayerGetActivitiesWithPropertiesForUsersAsync: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblMultiplayerSetSubscriptionsEnabled_Lua(lua_State *L) { bool enabled = GetBoolFromLua(L, 1, true); XblContextHandle xboxLiveContext = (XblContextHandle)GetUint64FromLua(L, 2, (uint64_t)Data()->xboxLiveContext); // CODE SNIPPET START: XblMultiplayerSetSubscriptionsEnabled HRESULT hr = XblMultiplayerSetSubscriptionsEnabled(xboxLiveContext, enabled); // CODE SNIPPET END LogToFile("XblMultiplayerSetSubscriptionsEnabled: hr=%s", ConvertHR(hr).c_str()); LogToFile("enabled: %d", enabled); return LuaReturnHR(L, hr); } int XblMultiplayerSubscriptionsEnabled_Lua(lua_State *L) { // CODE SNIPPET START: XblMultiplayerSubscriptionsEnabled bool enabled = XblMultiplayerSubscriptionsEnabled(Data()->xboxLiveContext); // CODE SNIPPET END LogToFile("XblMultiplayerSubscriptionsEnabled"); LogToFile("enabled: %d", enabled); return LuaReturnHR(L, S_OK); } int XblMultiplayerAddSessionChangedHandler_Lua(lua_State *L) { // CODE SNIPPET START: XblMultiplayerAddSessionChangedHandler void* context = nullptr; XblFunctionContext fnContext = XblMultiplayerAddSessionChangedHandler( Data()->xboxLiveContext, [](_In_opt_ void*, _In_ XblMultiplayerSessionChangeEventArgs args) { LogToFile("XblMultiplayerAddSessionChangedHandler"); LogToFile("ChangeNumber: %d", args.ChangeNumber); CallLuaFunctionWithHr(S_OK, "OnXblMultiplayerAddSessionChangedHandler"); // CODE SNIP SKIP }, context); // CODE SNIPPET END MPState()->sessionChange = fnContext; LogToFile("XblMultiplayerAddSessionChangedHandler"); return LuaReturnHR(L, S_OK); } int XblMultiplayerRemoveSessionChangedHandler_Lua(lua_State *L) { XblFunctionContext fnContext = MPState()->sessionChange; // CODE SNIPPET START: XblMultiplayerRemoveSessionChangedHandler XblMultiplayerRemoveSessionChangedHandler(Data()->xboxLiveContext, fnContext); // CODE SNIPPET END LogToFile("XblMultiplayerRemoveSessionChangedHandler"); return LuaReturnHR(L, S_OK); } int XblMultiplayerAddSubscriptionLostHandler_Lua(lua_State *L) { // CODE SNIPPET START: XblMultiplayerAddSubscriptionLostHandler void* context = nullptr; XblFunctionContext fnContext = XblMultiplayerAddSubscriptionLostHandler( Data()->xboxLiveContext, [](_In_opt_ void*) { LogToFile("XblMultiplayerAddSubscriptionLostHandler"); CallLuaFunctionWithHr(S_OK, "OnXblMultiplayerAddSubscriptionLostHandler"); // CODE SNIP SKIP }, context); // CODE SNIPPET END MPState()->lostHandler = fnContext; LogToFile("XblMultiplayerAddSubscriptionLostHandler"); return LuaReturnHR(L, S_OK); } int XblMultiplayerRemoveSubscriptionLostHandler_Lua(lua_State *L) { // CODE SNIPPET START: XblMultiplayerRemoveSubscriptionLostHandler XblMultiplayerRemoveSubscriptionLostHandler( Data()->xboxLiveContext, MPState()->lostHandler); // CODE SNIPPET END LogToFile("XblMultiplayerRemoveSubscriptionLostHandler"); return LuaReturnHR(L, S_OK); } int XblMultiplayerSearchHandleDuplicateHandle_Lua(lua_State *L) { // CODE SNIPPET START: XblMultiplayerSearchHandleDuplicateHandle XblMultiplayerSearchHandle duplicate{ nullptr }; HRESULT hr = XblMultiplayerSearchHandleDuplicateHandle( MPState()->searchHandle, &duplicate ); if (SUCCEEDED(hr)) { XblMultiplayerSearchHandleCloseHandle(duplicate); } // CODE SNIPPET END LogToFile("XblMultiplayerSearchHandleDuplicateHandle: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblMultiplayerSearchHandleCloseHandle_Lua(lua_State *L) { // CODE SNIPPET START: XblMultiplayerSearchHandleCloseHandle XblMultiplayerSearchHandleCloseHandle(MPState()->searchHandle); MPState()->searchHandle = nullptr; // CODE SNIPPET END LogToFile("XblMultiplayerSearchHandleCloseHandle"); return LuaReturnHR(L, S_OK); } int XblMultiplayerSearchHandleGetSessionReference_Lua(lua_State *L) { // CODE SNIPPET START: XblMultiplayerSearchHandleGetSessionReference XblMultiplayerSessionReference sessionRef{}; HRESULT hr = XblMultiplayerSearchHandleGetSessionReference( MPState()->searchHandle, &sessionRef ); // CODE SNIPPET END LogToFile("XblMultiplayerSearchHandleGetSessionReference: hr=%s", ConvertHR(hr).c_str()); LogSessionRef(&sessionRef); return LuaReturnHR(L, hr); } int XblMultiplayerSearchHandleGetId_Lua(lua_State *L) { // CODE SNIPPET START: XblMultiplayerSearchHandleGetId const char* handleId{ nullptr }; HRESULT hr = XblMultiplayerSearchHandleGetId( MPState()->searchHandle, &handleId ); // CODE SNIPPET END LogToFile("XblMultiplayerSearchHandleGetId: hr=%s", ConvertHR(hr).data()); LogToFile("Search Handle Id: %s", handleId); return LuaReturnHR(L, hr); } int XblMultiplayerSearchHandleGetSessionOwnerXuids_Lua(lua_State *L) { // CODE SNIPPET START: XblMultiplayerSearchHandleGetSessionOwnerXuids const uint64_t* xuids{ nullptr }; size_t xuidsCount{ 0 }; HRESULT hr = XblMultiplayerSearchHandleGetSessionOwnerXuids( MPState()->searchHandle, &xuids, &xuidsCount ); // CODE SNIPPET END LogToFile("XblMultiplayerSearchHandleGetSessionOwnerXuids: hr=%s", ConvertHR(hr).data()); LogToFile("There are %u session owners:", xuidsCount); for (auto i = 0u; i < xuidsCount; ++i) { LogToFile("\t%u", xuids[i]); } return LuaReturnHR(L, hr); } int XblMultiplayerSearchHandleGetTags_Lua(lua_State *L) { // CODE SNIPPET START: XblMultiplayerSearchHandleGetTags const XblMultiplayerSessionTag* tags{ nullptr }; size_t tagsCount{ 0 }; HRESULT hr = XblMultiplayerSearchHandleGetTags( MPState()->searchHandle, &tags, &tagsCount ); // CODE SNIPPET END LogToFile("XblMultiplayerSearchHandleGetTags: hr=%s", ConvertHR(hr).data()); LogToFile("There are %u tags for the session:", tagsCount); for (auto i = 0u; i < tagsCount; ++i) { LogToFile("\t%s", tags[i].value); } return LuaReturnHR(L, hr); } int XblMultiplayerSearchHandleGetStringAttributes_Lua(lua_State *L) { // CODE SNIPPET START: XblMultiplayerSearchHandleGetStringAttributes const XblMultiplayerSessionStringAttribute* attributes{ nullptr }; size_t attributesCount{ 0 }; HRESULT hr = XblMultiplayerSearchHandleGetStringAttributes( MPState()->searchHandle, &attributes, &attributesCount ); // CODE SNIPPET END LogToFile("XblMultiplayerSearchHandleGetStringAttributes: hr=%s", ConvertHR(hr).data()); LogToFile("There are %u string attributes for the session:", attributesCount); for (auto i = 0u; i < attributesCount; ++i) { LogToFile("\t%s : %s", attributes[i].name, attributes[i].value); } return LuaReturnHR(L, hr); } int XblMultiplayerSearchHandleGetNumberAttributes_Lua(lua_State *L) { // CODE SNIPPET START: XblMultiplayerSearchHandleGetNumberAttributes const XblMultiplayerSessionNumberAttribute* attributes{ nullptr }; size_t attributesCount{ 0 }; HRESULT hr = XblMultiplayerSearchHandleGetNumberAttributes( MPState()->searchHandle, &attributes, &attributesCount ); // CODE SNIPPET END LogToFile("XblMultiplayerSearchHandleGetNumberAttributes: hr=%s", ConvertHR(hr).data()); LogToFile("There are %u number attributes for the session:", attributesCount); for (auto i = 0u; i < attributesCount; ++i) { LogToFile("\t%s : %f", attributes[i].name, attributes[i].value); } return LuaReturnHR(L, hr); } int XblMultiplayerSearchHandleGetVisibility_Lua(lua_State *L) { // CODE SNIPPET START: XblMultiplayerSearchHandleGetVisibility XblMultiplayerSessionVisibility visibility{ XblMultiplayerSessionVisibility::Unknown }; HRESULT hr = XblMultiplayerSearchHandleGetVisibility( MPState()->searchHandle, &visibility ); // CODE SNIPPET END LogToFile("XblMultiplayerSearchHandleGetVisibility: visibility=%u, hr=%s", visibility, ConvertHR(hr).data()); return LuaReturnHR(L, hr); } int XblMultiplayerSearchHandleGetJoinRestriction_Lua(lua_State *L) { // CODE SNIPPET START: XblMultiplayerSearchHandleGetJoinRestriction XblMultiplayerSessionRestriction joinRestriction{ XblMultiplayerSessionRestriction::Unknown }; HRESULT hr = XblMultiplayerSearchHandleGetJoinRestriction( MPState()->searchHandle, &joinRestriction ); // CODE SNIPPET END LogToFile("XblMultiplayerSearchHandleGetJoinRestriction: restriction=%u, hr=%s", joinRestriction, ConvertHR(hr).data()); return LuaReturnHR(L, hr); } int XblMultiplayerSearchHandleGetSessionClosed_Lua(lua_State *L) { // CODE SNIPPET START: XblMultiplayerSearchHandleGetSessionClosed bool closed{ false }; HRESULT hr = XblMultiplayerSearchHandleGetSessionClosed( MPState()->searchHandle, &closed ); // CODE SNIPPET END LogToFile("XblMultiplayerSearchHandleGetSessionClosed: closed=%d, hr=%s", closed, ConvertHR(hr).data()); return LuaReturnHR(L, hr); } int XblMultiplayerSearchHandleGetMemberCounts_Lua(lua_State *L) { // CODE SNIPPET START: XblMultiplayerSearchHandleGetMemberCounts size_t currentMembers{ 0 }; size_t maxMembers{ 0 }; HRESULT hr = XblMultiplayerSearchHandleGetMemberCounts( MPState()->searchHandle, &maxMembers, ¤tMembers ); // CODE SNIPPET END LogToFile("XblMultiplayerSearchHandleGetMemberCounts: max members=%u, current members=%u hr=%s", maxMembers, currentMembers, ConvertHR(hr).data()); return LuaReturnHR(L, hr); } int XblMultiplayerSearchHandleGetCreationTime_Lua(lua_State *L) { // CODE SNIPPET START: XblMultiplayerSearchHandleGetCreationTime time_t creationTime{ 0 }; HRESULT hr = XblMultiplayerSearchHandleGetCreationTime( MPState()->searchHandle, &creationTime ); // CODE SNIPPET END LogToFile("XblMultiplayerSearchHandleGetCreationTime: creation time=%u hr=%s", creationTime, ConvertHR(hr).data()); return LuaReturnHR(L, hr); } int XblMultiplayerSearchHandleGetCustomSessionPropertiesJson_Lua(lua_State *L) { // CODE SNIPPET START: XblMultiplayerSearchHandleGetCustomSessionPropertiesJson const char* properties{ nullptr }; HRESULT hr = XblMultiplayerSearchHandleGetCustomSessionPropertiesJson( MPState()->searchHandle, &properties ); // CODE SNIPPET END LogToFile("XblMultiplayerSearchHandleGetCustomSessionPropertiesJson: properties=%s hr=%s", properties, ConvertHR(hr).data()); return LuaReturnHR(L, hr); } int XblMultiplayerCreateSearchHandleAsync_Lua(lua_State *L) { auto xblContextHandle = Data()->xboxLiveContext; auto xblMultiplayerSessionReference = MPState()->sessionRef; // CODE SNIPPET START: XblMultiplayerCreateSearchHandleAsync_C size_t tagsCount = 1; XblMultiplayerSessionTag tags[1] = {}; tags[0] = XblMultiplayerSessionTag{ "SessionTag" }; size_t numberAttributesCount = 1; XblMultiplayerSessionNumberAttribute numberAttributes[1] = {}; numberAttributes[0] = XblMultiplayerSessionNumberAttribute{ "numberattributename", 1.1 }; size_t strAttributesCount = 1; XblMultiplayerSessionStringAttribute strAttributes[1] = {}; strAttributes[0] = XblMultiplayerSessionStringAttribute{ "stringattributename", "string attribute value" }; auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->context = nullptr; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* XblMultiplayerSearchHandle searchHandle{ nullptr }; HRESULT hr = XblMultiplayerCreateSearchHandleResult(asyncBlock, &searchHandle); MPState()->searchHandle = searchHandle; // CODE SNIP SKIP LogToFile("XblMultiplayerCreateSearchHandleResult completed with result=%s", ConvertHR(hr).data()); // CODE SNIP SKIP if (SUCCEEDED(hr)) { const char* handleId{ nullptr }; XblMultiplayerSearchHandleGetId(searchHandle, &handleId); LogToFile("Search handle id = %s", handleId); // CODE SNIP SKIP } CallLuaFunctionWithHr(hr, "OnXblMultiplayerCreateSearchHandleAsync"); // CODE SNIP SKIP }; HRESULT hr = XblMultiplayerCreateSearchHandleAsync( xblContextHandle, &xblMultiplayerSessionReference, tags, tagsCount, numberAttributes, numberAttributesCount, strAttributes, strAttributesCount, asyncBlock.get() ); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } // CODE SNIPPET END LogToFile("XblMultiplayerCreateSearchHandleAsync: hr=%s", ConvertHR(hr).data()); return LuaReturnHR(L, hr); } int XblMultiplayerDeleteSearchHandleAsync_Lua(lua_State *L) { // CODE SNIPPET START: XblMultiplayerDeleteSearchHandleAsync auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->context = nullptr; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* HRESULT hr = XAsyncGetStatus(asyncBlock, true); LogToFile("XblMultiplayerDeleteSearchHandleAsync completed with result = %s", ConvertHR(hr).data()); CallLuaFunctionWithHr(hr, "OnXblMultiplayerDeleteSearchHandleAsync"); // CODE SNIP SKIP }; const char* handleId{ nullptr }; XblMultiplayerSearchHandleGetId(MPState()->searchHandle, &handleId); HRESULT hr = XblMultiplayerDeleteSearchHandleAsync( Data()->xboxLiveContext, handleId, asyncBlock.get() ); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } // CODE SNIPPET END LogToFile("XblMultiplayerDeleteSearchHandleAsync: hr=%s", ConvertHR(hr).data()); return LuaReturnHR(L, S_OK); } int XblMultiplayerGetSearchHandlesAsync_Lua(lua_State *L) { auto xblContextHandle = Data()->xboxLiveContext; auto scid = Data()->scid; // CODE SNIPPET START: XblMultiplayerGetSearchHandlesAsync_C auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->context = nullptr; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* size_t resultCount{ 0 }; auto hr = XblMultiplayerGetSearchHandlesResultCount(asyncBlock, &resultCount); if (SUCCEEDED(hr) && resultCount > 0) { auto handles = new XblMultiplayerSearchHandle[resultCount]; hr = XblMultiplayerGetSearchHandlesResult(asyncBlock, handles, resultCount); LogToFile("XblMultiplayerGetSearchHandlesResult: hr=%s", ConvertHR(hr).data()); // CODE SNIP SKIP if (SUCCEEDED(hr)) { LogToFile("Got %u search handles:", resultCount); // CODE SNIP SKIP // Process handles for (auto i = 0u; i < resultCount; ++i) { const char* handleId{ nullptr }; XblMultiplayerSearchHandleGetId(handles[i], &handleId); LogToFile("\t%s", handleId); // CODE SNIP SKIP XblMultiplayerSearchHandleCloseHandle(handles[i]); } } } CallLuaFunctionWithHr(hr, "OnXblMultiplayerGetSearchHandlesAsync"); // CODE SNIP SKIP }; const char* sessionName{ "MinGameSession" }; const char* orderByAttribute{ nullptr }; bool orderAscending{ false }; const char* searchFilter{ nullptr }; const char* socialGroup{ nullptr }; HRESULT hr = XblMultiplayerGetSearchHandlesAsync( xblContextHandle, scid, sessionName, orderByAttribute, orderAscending, searchFilter, socialGroup, asyncBlock.get() ); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } // CODE SNIPPET END LogToFile("XblMultiplayerGetSearchHandlesAsync: hr=%s", ConvertHR(hr).data()); return LuaReturnHR(L, hr); } int XblMultiplayerSetTransferHandleAsync_Lua(lua_State* L) { // Params: // 1) Target session index // 2) Origin session index auto targetIndex{ GetUint64FromLua(L, 1, 0) }; auto originIndex{ GetUint64FromLua(L, 2, 0) }; // CODE SNIPPET START: XblMultiplayerSetTransferHandleAsync auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->context = nullptr; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* XblMultiplayerSessionHandleId id{}; auto hr = XblMultiplayerSetTransferHandleResult(asyncBlock, &id); if (SUCCEEDED(hr)) { LogToFile("Sucessfully set transfer handle, ID = %s", id.value); } CallLuaFunctionWithHr(hr, "OnXblMultiplayerSetTransferHandleAsync"); // CODE SNIP SKIP }; auto targetReference = XblMultiplayerSessionSessionReference(MPState()->sessionHandles[static_cast(targetIndex)]); auto originReference = XblMultiplayerSessionSessionReference(MPState()->sessionHandles[static_cast(originIndex)]); HRESULT hr = S_OK; if (originReference != nullptr && targetReference != nullptr) { hr = XblMultiplayerSetTransferHandleAsync( Data()->xboxLiveContext, *targetReference, *originReference, asyncBlock.get() ); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } } // CODE SNIP END LogToFile("XblMultiplayerSetTransferHandleAsync: hr=%s", ConvertHR(hr).data()); return LuaReturnHR(L, hr); } int XblMatchmakingCreateTicket_Lua(lua_State* L) { // Params: // 1) matchmaking hopper name // 2) attributes json // 3) SCID // 4) session template name // 5) session name // 6) timeout in seconds auto hopperName{ GetStringFromLua(L, 1, "PlayerSkillNoQoS") }; auto attributesJson{ GetStringFromLua(L, 2, "{}") }; uint32_t timeoutInSeconds = GetUint32FromLua(L, 6, 100); CreateQueueIfNeeded(); // CODE SNIPPET START: XblMatchmakingCreateMatchTicketAsync auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->context = nullptr; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* XblCreateMatchTicketResponse result{}; auto hr = XblMatchmakingCreateMatchTicketResult(asyncBlock, &result); LogToScreen("XblMatchmakingCreateMatchTicketResult: hr=%s", ConvertHR(hr).c_str()); // CODE SNIP SKIP if (SUCCEEDED(hr)) { if (Data()->matchTicketResponse) { // CODE SNIP SKIP delete(Data()->matchTicketResponse); // CODE SNIP SKIP } // CODE SNIP SKIP Data()->matchTicketResponse = new XblCreateMatchTicketResponse(result); // CODE SNIP SKIP LogToScreen("CreateMatchTicketResponse->matchTicketId: %s", result.matchTicketId); // CODE SNIP SKIP LogToScreen("CreateMatchTicketResponse->estimatedWaitTime: %d", result.estimatedWaitTime); // CODE SNIP SKIP } CallLuaFunctionWithHr(hr, "OnXblMatchmakingCreateTicket"); // CODE SNIP SKIP }; HRESULT hr = XblMatchmakingCreateMatchTicketAsync( Data()->xboxLiveContext, MPState()->sessionRef, Data()->scid, hopperName.c_str(), timeoutInSeconds, XblPreserveSessionMode::Never, attributesJson.c_str(), asyncBlock.get() ); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } // CODE SNIPPET END LogToScreen("XblMatchmakingCreateMatchTicketAsync: hr = %s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblMatchmakingGetMatchTicketDetails_Lua(lua_State* L) { // Params: // 1) matchmaking hopper name // 2) SCID // 3) match ticket ID std::string hopperName{ GetStringFromLua(L, 1, "PlayerSkillNoQoS") }; std::string scid = GetStringFromLua(L, 2, Data()->scid); std::string ticketId = GetStringFromLua(L, 3, Data()->matchTicketResponse->matchTicketId); CreateQueueIfNeeded(); // CODE SNIPPET START: XblMatchmakingGetMatchTicketDetailsAsync auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->context = nullptr; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* size_t bufferSize; auto hr = XblMatchmakingGetMatchTicketDetailsResultSize(asyncBlock, &bufferSize); LogToFile("XblMatchmakingGetMatchTicketDetailsResultSize: hr=%s", ConvertHR(hr).c_str()); // CODE SNIP SKIP if (SUCCEEDED(hr)) { std::vector buffer(bufferSize, 0); XblMatchTicketDetailsResponse* resultPtr; hr = XblMatchmakingGetMatchTicketDetailsResult(asyncBlock, bufferSize, buffer.data(), &resultPtr, nullptr); LogToScreen("XblMatchmakingGetMatchTicketDetailsResult: hr= %s", ConvertHR(hr).c_str()); // CODE SNIP SKIP if (SUCCEEDED(hr)) // CODE SNIP SKIP { // CODE SNIP SKIP LogToScreen("XblMatchTicketDetailsResponse->matchStatus: %d", resultPtr->matchStatus); // CODE SNIP SKIP LogToScreen("XblMatchTicketDetailsResponse->estimatedWaitTime: %d", resultPtr->estimatedWaitTime); // CODE SNIP SKIP LogToScreen("XblMatchTicketDetailsResponse->preserveSession: %d", resultPtr->preserveSession); // CODE SNIP SKIP LogToScreen("XblMatchTicketDetailsResponse->ticketSession: SCID: %s, Session Name: %s, Session Template Name: %s", resultPtr->ticketSession.Scid, resultPtr->ticketSession.SessionName, resultPtr->ticketSession.SessionTemplateName); // CODE SNIP SKIP LogToScreen("XblMatchTicketDetailsResponse->targetSession: SCID: %s, Session Name: %s, Session Template Name: %s", resultPtr->targetSession.Scid, resultPtr->targetSession.SessionName, resultPtr->targetSession.SessionTemplateName); // CODE SNIP SKIP if (resultPtr->ticketAttributes != nullptr) { LogToScreen("XblMatchTicketDetailsResponse->TicketAttributes: %d", resultPtr->ticketAttributes); // CODE SNIP SKIP } } // CODE SNIP SKIP } CallLuaFunctionWithHr(hr, "OnXblMatchmakingGetMatchTicketDetails"); // CODE SNIP SKIP }; HRESULT hr = XblMatchmakingGetMatchTicketDetailsAsync( Data()->xboxLiveContext, scid.c_str(), hopperName.c_str(), ticketId.c_str(), asyncBlock.get() ); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } // CODE SNIPPET END LogToScreen("XblMatchmakingGetMatchTicketDetailsAsync: hr = %s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblMatchmakingGetHopperStatistics_Lua(lua_State* L) { // Params: // 1) matchmaking hopper name // 2) SCID std::string hopperName{ GetStringFromLua(L, 1, "PlayerSkillNoQoS") }; std::string scid = GetStringFromLua(L, 2, Data()->scid); CreateQueueIfNeeded(); // CODE SNIPPET START: XblMatchmakingGetHopperStatisticsAsync auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->context = nullptr; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* size_t bufferSize; HRESULT hr = XblMatchmakingGetHopperStatisticsResultSize(asyncBlock, &bufferSize); LogToScreen("XblMatchmakingGetHopperStatisticsResultSize: hr=%s", ConvertHR(hr).c_str()); // CODE SNIP SKIP if (SUCCEEDED(hr)) { std::vector buffer(bufferSize, 0); XblHopperStatisticsResponse* result{}; hr = XblMatchmakingGetHopperStatisticsResult(asyncBlock, bufferSize, buffer.data(), &result, nullptr); LogToScreen("XblMatchmakingGetHopperStatisticsResult: hr=%s", ConvertHR(hr).c_str()); // CODE SNIP SKIP if (SUCCEEDED(hr)) // CODE SNIP SKIP { // CODE SNIP SKIP LogToScreen("XblHopperStatisticsResponse->hopperName: %s", result->hopperName); // CODE SNIP SKIP LogToScreen("XblHopperStatisticsResponse->estimatedWaitTime: %d", result->estimatedWaitTime); // CODE SNIP SKIP LogToScreen("XblHopperStatisticsResponse->playersWaitingToMatch: %d", result->playersWaitingToMatch); // CODE SNIP SKIP } // CODE SNIP SKIP CallLuaFunctionWithHr(hr, "OnXblMatchmakingGetHopperStatistics"); // CODE SNIP SKIP } }; HRESULT hr = XblMatchmakingGetHopperStatisticsAsync( Data()->xboxLiveContext, scid.c_str(), hopperName.c_str(), asyncBlock.get() ); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } // CODE SNIPPET END LogToScreen("XblMatchmakingGetHopperStatisticsAsync: hr = %s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblMatchmakingDeleteMatchTicket_Lua(lua_State* L) { // Params: // 1) matchmaking hopper name // 2) SCID // 3) ticket ID std::string hopperName{ GetStringFromLua(L, 1, "PlayerSkillNoQoS") }; std::string scid = GetStringFromLua(L, 2, Data()->scid); std::string ticketId = GetStringFromLua(L, 3, Data()->matchTicketResponse->matchTicketId); CreateQueueIfNeeded(); // CODE SNIPPET START: XblMatchmakingDeleteMatchTicketAsync auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->context = nullptr; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* CallLuaFunctionWithHr(S_OK, "OnXblMatchmakingDeleteMatchTicket"); // CODE SNIP SKIP }; HRESULT hr = XblMatchmakingDeleteMatchTicketAsync( Data()->xboxLiveContext, scid.c_str(), hopperName.c_str(), ticketId.c_str(), asyncBlock.get() ); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } // CODE SNIPPET END LogToScreen("XblMatchmakingDeleteMatchTicketAsync: hr = %s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } void SetupAPIs_XblMultiplayer() { lua_register(Data()->L, "XblMultiplayerSessionReferenceCreate", XblMultiplayerSessionReferenceCreate_Lua); lua_register(Data()->L, "XblMultiplayerSessionReferenceParseFromUriPath", XblMultiplayerSessionReferenceParseFromUriPath_Lua); lua_register(Data()->L, "XblMultiplayerSessionReferenceIsValid", XblMultiplayerSessionReferenceIsValid_Lua); lua_register(Data()->L, "XblMultiplayerSessionCreateHandle", XblMultiplayerSessionCreateHandle_Lua); lua_register(Data()->L, "XblMultiplayerSessionDuplicateHandle", XblMultiplayerSessionDuplicateHandle_Lua); lua_register(Data()->L, "XblMultiplayerSessionCloseHandle", XblMultiplayerSessionCloseHandle_Lua); lua_register(Data()->L, "XblMultiplayerSessionTimeOfSession", XblMultiplayerSessionTimeOfSession_Lua); lua_register(Data()->L, "XblMultiplayerSessionGetInitializationInfo", XblMultiplayerSessionGetInitializationInfo_Lua); lua_register(Data()->L, "XblMultiplayerSessionSubscribedChangeTypes", XblMultiplayerSessionSubscribedChangeTypes_Lua); lua_register(Data()->L, "XblMultiplayerSessionHostCandidates", XblMultiplayerSessionHostCandidates_Lua); lua_register(Data()->L, "XblMultiplayerSessionSessionReference", XblMultiplayerSessionSessionReference_Lua); lua_register(Data()->L, "XblMultiplayerSessionSessionConstants", XblMultiplayerSessionSessionConstants_Lua); lua_register(Data()->L, "XblMultiplayerSessionConstantsSetMaxMembersInSession", XblMultiplayerSessionConstantsSetMaxMembersInSession_Lua); lua_register(Data()->L, "XblMultiplayerSessionConstantsSetVisibility", XblMultiplayerSessionConstantsSetVisibility_Lua); lua_register(Data()->L, "XblMultiplayerSessionConstantsSetTimeouts", XblMultiplayerSessionConstantsSetTimeouts_Lua); lua_register(Data()->L, "XblMultiplayerSessionConstantsSetQosConnectivityMetrics", XblMultiplayerSessionConstantsSetQosConnectivityMetrics_Lua); lua_register(Data()->L, "XblMultiplayerSessionConstantsSetMemberInitialization", XblMultiplayerSessionConstantsSetMemberInitialization_Lua); lua_register(Data()->L, "XblMultiplayerSessionConstantsSetPeerToPeerRequirements", XblMultiplayerSessionConstantsSetPeerToPeerRequirements_Lua); lua_register(Data()->L, "XblMultiplayerSessionConstantsSetPeerToHostRequirements", XblMultiplayerSessionConstantsSetPeerToHostRequirements_Lua); lua_register(Data()->L, "XblMultiplayerSessionConstantsSetMeasurementServerAddressesJson", XblMultiplayerSessionConstantsSetMeasurementServerAddressesJson_Lua); lua_register(Data()->L, "XblMultiplayerSessionConstantsSetCapabilities", XblMultiplayerSessionConstantsSetCapabilities_Lua); lua_register(Data()->L, "XblMultiplayerSessionConstantsSetCloudComputePackageJson", XblMultiplayerSessionConstantsSetCloudComputePackageJson_Lua); lua_register(Data()->L, "XblMultiplayerSessionSessionProperties", XblMultiplayerSessionSessionProperties_Lua); lua_register(Data()->L, "XblMultiplayerSessionPropertiesSetKeywords", XblMultiplayerSessionPropertiesSetKeywords_Lua); lua_register(Data()->L, "XblMultiplayerSessionPropertiesSetJoinRestriction", XblMultiplayerSessionPropertiesSetJoinRestriction_Lua); lua_register(Data()->L, "XblMultiplayerSessionPropertiesSetReadRestriction", XblMultiplayerSessionPropertiesSetReadRestriction_Lua); lua_register(Data()->L, "XblMultiplayerSessionPropertiesSetTurnCollection", XblMultiplayerSessionPropertiesSetTurnCollection_Lua); lua_register(Data()->L, "XblMultiplayerSessionRoleTypes", XblMultiplayerSessionRoleTypes_Lua); lua_register(Data()->L, "XblMultiplayerSessionGetRoleByName", XblMultiplayerSessionGetRoleByName_Lua); lua_register(Data()->L, "XblMultiplayerSessionSetMutableRoleSettings", XblMultiplayerSessionSetMutableRoleSettings_Lua); lua_register(Data()->L, "XblMultiplayerSessionMembers", XblMultiplayerSessionMembers_Lua); lua_register(Data()->L, "XblMultiplayerSessionGetMember", XblMultiplayerSessionGetMember_Lua); lua_register(Data()->L, "XblMultiplayerSessionMatchmakingServer", XblMultiplayerSessionMatchmakingServer_Lua); lua_register(Data()->L, "XblMultiplayerSessionMembersAccepted", XblMultiplayerSessionMembersAccepted_Lua); lua_register(Data()->L, "XblMultiplayerSessionRawServersJson", XblMultiplayerSessionRawServersJson_Lua); lua_register(Data()->L, "XblMultiplayerSessionSetRawServersJson", XblMultiplayerSessionSetRawServersJson_Lua); lua_register(Data()->L, "XblMultiplayerSessionEtag", XblMultiplayerSessionEtag_Lua); lua_register(Data()->L, "XblMultiplayerSessionCurrentUser", XblMultiplayerSessionCurrentUser_Lua); lua_register(Data()->L, "XblMultiplayerSessionGetInfo", XblMultiplayerSessionGetInfo_Lua); lua_register(Data()->L, "XblMultiplayerSessionWriteStatus", XblMultiplayerSessionWriteStatus_Lua); lua_register(Data()->L, "XblMultiplayerSessionAddMemberReservation", XblMultiplayerSessionAddMemberReservation_Lua); lua_register(Data()->L, "XblMultiplayerSessionJoin", XblMultiplayerSessionJoin_Lua); lua_register(Data()->L, "XblMultiplayerSessionSetInitializationSucceeded", XblMultiplayerSessionSetInitializationSucceeded_Lua); lua_register(Data()->L, "XblMultiplayerSessionSetHostDeviceToken", XblMultiplayerSessionSetHostDeviceToken_Lua); lua_register(Data()->L, "XblMultiplayerSessionSetMatchmakingServerConnectionPath", XblMultiplayerSessionSetMatchmakingServerConnectionPath_Lua); lua_register(Data()->L, "XblMultiplayerSessionSetClosed", XblMultiplayerSessionSetClosed_Lua); lua_register(Data()->L, "XblMultiplayerSessionSetLocked", XblMultiplayerSessionSetLocked_Lua); lua_register(Data()->L, "XblMultiplayerSessionSetAllocateCloudCompute", XblMultiplayerSessionSetAllocateCloudCompute_Lua); lua_register(Data()->L, "XblMultiplayerSessionSetMatchmakingResubmit", XblMultiplayerSessionSetMatchmakingResubmit_Lua); lua_register(Data()->L, "XblMultiplayerSessionSetServerConnectionStringCandidates", XblMultiplayerSessionSetServerConnectionStringCandidates_Lua); lua_register(Data()->L, "XblMultiplayerSessionSetSessionChangeSubscription", XblMultiplayerSessionSetSessionChangeSubscription_Lua); lua_register(Data()->L, "XblMultiplayerSessionLeave", XblMultiplayerSessionLeave_Lua); lua_register(Data()->L, "XblMultiplayerSessionCurrentUserSetStatus", XblMultiplayerSessionCurrentUserSetStatus_Lua); lua_register(Data()->L, "XblMultiplayerSessionCurrentUserSetSecureDeviceAddressBase64", XblMultiplayerSessionCurrentUserSetSecureDeviceAddressBase64_Lua); lua_register(Data()->L, "XblFormatSecureDeviceAddress", XblFormatSecureDeviceAddress_Lua); lua_register(Data()->L, "XblMultiplayerSessionCurrentUserSetRoles", XblMultiplayerSessionCurrentUserSetRoles_Lua); lua_register(Data()->L, "XblMultiplayerSessionCurrentUserSetMembersInGroup", XblMultiplayerSessionCurrentUserSetMembersInGroup_Lua); lua_register(Data()->L, "XblMultiplayerSessionCurrentUserSetGroups", XblMultiplayerSessionCurrentUserSetGroups_Lua); lua_register(Data()->L, "XblMultiplayerSessionCurrentUserSetEncounters", XblMultiplayerSessionCurrentUserSetEncounters_Lua); lua_register(Data()->L, "XblMultiplayerSessionCurrentUserSetQosMeasurements", XblMultiplayerSessionCurrentUserSetQosMeasurements_Lua); lua_register(Data()->L, "XblMultiplayerSessionCurrentUserSetServerQosMeasurements", XblMultiplayerSessionCurrentUserSetServerQosMeasurements_Lua); lua_register(Data()->L, "XblMultiplayerSessionCurrentUserSetCustomPropertyJson", XblMultiplayerSessionCurrentUserSetCustomPropertyJson_Lua); lua_register(Data()->L, "XblMultiplayerSessionCurrentUserDeleteCustomPropertyJson", XblMultiplayerSessionCurrentUserDeleteCustomPropertyJson_Lua); lua_register(Data()->L, "XblMultiplayerSessionSetMatchmakingTargetSessionConstantsJson", XblMultiplayerSessionSetMatchmakingTargetSessionConstantsJson_Lua); lua_register(Data()->L, "XblMultiplayerSessionSetCustomPropertyJson", XblMultiplayerSessionSetCustomPropertyJson_Lua); lua_register(Data()->L, "XblMultiplayerSessionDeleteCustomPropertyJson", XblMultiplayerSessionDeleteCustomPropertyJson_Lua); lua_register(Data()->L, "XblMultiplayerSessionCompare", XblMultiplayerSessionCompare_Lua); lua_register(Data()->L, "XblMultiplayerWriteSessionAsync", XblMultiplayerWriteSessionAsync_Lua); lua_register(Data()->L, "XblMultiplayerWriteSessionByHandleAsync", XblMultiplayerWriteSessionByHandleAsync_Lua); lua_register(Data()->L, "XblMultiplayerGetSessionAsync", XblMultiplayerGetSessionAsync_Lua); lua_register(Data()->L, "XblMultiplayerGetSessionByHandleAsync", XblMultiplayerGetSessionByHandleAsync_Lua); lua_register(Data()->L, "XblMultiplayerQuerySessionsAsync", XblMultiplayerQuerySessionsAsync_Lua); lua_register(Data()->L, "XblMultiplayerSetActivityAsync", XblMultiplayerSetActivityAsync_Lua); lua_register(Data()->L, "XblMultiplayerClearActivityAsync", XblMultiplayerClearActivityAsync_Lua); lua_register(Data()->L, "XblMultiplayerSendInvitesAsync", XblMultiplayerSendInvitesAsync_Lua); lua_register(Data()->L, "XblMultiplayerGetActivitiesForSocialGroupAsync", XblMultiplayerGetActivitiesForSocialGroupAsync_Lua); lua_register(Data()->L, "XblMultiplayerGetActivitiesForUsersAsync", XblMultiplayerGetActivitiesForUsersAsync_Lua); lua_register(Data()->L, "XblMultiplayerGetActivitiesWithPropertiesForSocialGroupAsync", XblMultiplayerGetActivitiesWithPropertiesForSocialGroupAsync_Lua); lua_register(Data()->L, "XblMultiplayerGetActivitiesWithPropertiesForUsersAsync", XblMultiplayerGetActivitiesWithPropertiesForUsersAsync_Lua); lua_register(Data()->L, "XblMultiplayerSetSubscriptionsEnabled", XblMultiplayerSetSubscriptionsEnabled_Lua); lua_register(Data()->L, "XblMultiplayerSubscriptionsEnabled", XblMultiplayerSubscriptionsEnabled_Lua); lua_register(Data()->L, "XblMultiplayerAddSessionChangedHandler", XblMultiplayerAddSessionChangedHandler_Lua); lua_register(Data()->L, "XblMultiplayerRemoveSessionChangedHandler", XblMultiplayerRemoveSessionChangedHandler_Lua); lua_register(Data()->L, "XblMultiplayerAddSubscriptionLostHandler", XblMultiplayerAddSubscriptionLostHandler_Lua); lua_register(Data()->L, "XblMultiplayerRemoveSubscriptionLostHandler", XblMultiplayerRemoveSubscriptionLostHandler_Lua); lua_register(Data()->L, "XblMultiplayerSearchHandleDuplicateHandle", XblMultiplayerSearchHandleDuplicateHandle_Lua); lua_register(Data()->L, "XblMultiplayerSearchHandleCloseHandle", XblMultiplayerSearchHandleCloseHandle_Lua); lua_register(Data()->L, "XblMultiplayerSearchHandleGetSessionReference", XblMultiplayerSearchHandleGetSessionReference_Lua); lua_register(Data()->L, "XblMultiplayerSearchHandleGetId", XblMultiplayerSearchHandleGetId_Lua); lua_register(Data()->L, "XblMultiplayerSearchHandleGetSessionOwnerXuids", XblMultiplayerSearchHandleGetSessionOwnerXuids_Lua); lua_register(Data()->L, "XblMultiplayerSearchHandleGetTags", XblMultiplayerSearchHandleGetTags_Lua); lua_register(Data()->L, "XblMultiplayerSearchHandleGetStringAttributes", XblMultiplayerSearchHandleGetStringAttributes_Lua); lua_register(Data()->L, "XblMultiplayerSearchHandleGetNumberAttributes", XblMultiplayerSearchHandleGetNumberAttributes_Lua); lua_register(Data()->L, "XblMultiplayerSearchHandleGetVisibility", XblMultiplayerSearchHandleGetVisibility_Lua); lua_register(Data()->L, "XblMultiplayerSearchHandleGetJoinRestriction", XblMultiplayerSearchHandleGetJoinRestriction_Lua); lua_register(Data()->L, "XblMultiplayerSearchHandleGetSessionClosed", XblMultiplayerSearchHandleGetSessionClosed_Lua); lua_register(Data()->L, "XblMultiplayerSearchHandleGetMemberCounts", XblMultiplayerSearchHandleGetMemberCounts_Lua); lua_register(Data()->L, "XblMultiplayerSearchHandleGetCreationTime", XblMultiplayerSearchHandleGetCreationTime_Lua); lua_register(Data()->L, "XblMultiplayerSearchHandleGetCustomSessionPropertiesJson", XblMultiplayerSearchHandleGetCustomSessionPropertiesJson_Lua); lua_register(Data()->L, "XblMultiplayerCreateSearchHandleAsync", XblMultiplayerCreateSearchHandleAsync_Lua); lua_register(Data()->L, "XblMultiplayerDeleteSearchHandleAsync", XblMultiplayerDeleteSearchHandleAsync_Lua); lua_register(Data()->L, "XblMultiplayerGetSearchHandlesAsync", XblMultiplayerGetSearchHandlesAsync_Lua); lua_register(Data()->L, "XblMultiplayerSetTransferHandleAsync", XblMultiplayerSetTransferHandleAsync_Lua); // XSAPI Matchmaking APIs lua_register(Data()->L, "XblMatchmakingCreateTicket", XblMatchmakingCreateTicket_Lua); lua_register(Data()->L, "XblMatchmakingGetMatchTicketDetails", XblMatchmakingGetMatchTicketDetails_Lua); lua_register(Data()->L, "XblMatchmakingGetHopperStatistics", XblMatchmakingGetHopperStatistics_Lua); lua_register(Data()->L, "XblMatchmakingDeleteMatchTicket", XblMatchmakingDeleteMatchTicket_Lua); } ================================================ FILE: Tests/ApiExplorer/APIs/apis_xblc_multiplayer_manager.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include #if HC_PLATFORM_IS_MICROSOFT #pragma warning( push ) #pragma warning( disable : 4365 ) #pragma warning( disable : 4061 ) #pragma warning( disable : 4996 ) #endif #include "rapidjson/document.h" static std::thread s_doWorkThread{}; static std::atomic s_doWork{ false }; void CheckMemberFound(XblMultiplayerSessionType sessionType, uint64_t xuid) { size_t count{}; std::vector members{}; if (sessionType == XblMultiplayerSessionType::LobbySession) { count = XblMultiplayerManagerLobbySessionMembersCount(); assert(count > 0); members = std::vector(count); XblMultiplayerManagerLobbySessionMembers(count, members.data()); } else { count = XblMultiplayerManagerGameSessionMembersCount(); assert(count > 0); members = std::vector(count); XblMultiplayerManagerGameSessionMembers(count, members.data()); } bool memberFound{ false }; for (auto member : members) { if (member.Xuid == xuid) { memberFound = true; break; } } assert(memberFound); } std::vector GetSessionMembers(XblMultiplayerSessionType sessionType) { size_t count{}; std::vector members{}; if (sessionType == XblMultiplayerSessionType::LobbySession) { count = XblMultiplayerManagerLobbySessionMembersCount(); members = std::vector(count); if (count > 0) { XblMultiplayerManagerLobbySessionMembers(count, members.data()); } } else { count = XblMultiplayerManagerGameSessionMembersCount(); members = std::vector(count); if (count > 0) { XblMultiplayerManagerGameSessionMembers(count, members.data()); } } return members; } HRESULT MultiplayerManagerDoWork() { // CODE SNIPPET START: XblMultiplayerManagerDoWork_C size_t eventCount{ 0 }; const XblMultiplayerEvent* events{ nullptr }; HRESULT hr = XblMultiplayerManagerDoWork(&events, &eventCount); if (FAILED(hr)) { // Handle failure return hr; // CODE SNIP SKIP } for (auto i = 0u; i < eventCount; ++i) { switch (events[i].EventType) { case XblMultiplayerEventType::MemberJoined: { // Handle MemberJoined size_t memberCount = 0; hr = XblMultiplayerEventArgsMembersCount(events[i].EventArgsHandle, &memberCount); assert(SUCCEEDED(hr)); std::vector eventMembers(memberCount); hr = XblMultiplayerEventArgsMembers(events[i].EventArgsHandle, memberCount, eventMembers.data()); assert(SUCCEEDED(hr)); // DOTS auto sessionMembers{ GetSessionMembers(events[i].SessionType) }; assert(memberCount <= sessionMembers.size()); for (auto eventMember : eventMembers) { bool memberFound{ false }; for (auto sessionMember : sessionMembers) { if (eventMember.Xuid == sessionMember.Xuid) { memberFound = true; break; } } assert(memberFound); } LogToScreen("XblMultiplayerManagerDoWork event XblMultiplayerEventType::MemberJoined"); // CODE SNIP SKIP for (auto& member : eventMembers) { LogToScreen(" member %llu", static_cast(member.Xuid)); } CallLuaFunctionWithHr(events[i].Result, "OnXblMultiplayerEventType_MemberJoined"); // CODE SNIP SKIP break; } case XblMultiplayerEventType::SessionPropertyChanged: { // Handle SessionPropertyChanged const char* changedProperty{ nullptr }; hr = XblMultiplayerEventArgsPropertiesJson(events[i].EventArgsHandle, &changedProperty); assert(SUCCEEDED(hr)); // DOTS rapidjson::Document changedDoc; changedDoc.Parse(changedProperty); rapidjson::Document doc; if (events[i].SessionType == XblMultiplayerSessionType::LobbySession) { doc.Parse(XblMultiplayerManagerLobbySessionPropertiesJson()); } else { doc.Parse(XblMultiplayerManagerGameSessionPropertiesJson()); } for (auto& member : changedDoc.GetObject()) { assert(doc.HasMember(member.name)); assert(doc[member.name] == member.value); UNREFERENCED_PARAMETER(member); } // CODE SKIP START LogToScreen("XblMultiplayerManagerDoWork event XblMultiplayerEventType::SessionPropertyChanged"); // CODE SNIP SKIP if (events[i].SessionType == XblMultiplayerSessionType::GameSession) { CallLuaFunctionWithHr(events[i].Result, "OnXblMultiplayerEventType_GameSessionPropertyChanged"); // CODE SNIP SKIP } else { CallLuaFunctionWithHr(events[i].Result, "OnXblMultiplayerEventType_LobbySessionPropertyChanged"); // CODE SNIP SKIP } // CODE SKIP END break; } // DOTS // CODE SKIP START case XblMultiplayerEventType::UserAdded: { auto h = events[i].Result; if (SUCCEEDED(h)) { // Handle UserAdded uint64_t xuid = 0; hr = XblMultiplayerEventArgsXuid(events[i].EventArgsHandle, &xuid); assert(SUCCEEDED(hr)); CheckMemberFound(events[i].SessionType, xuid); LogToScreen("XblMultiplayerManagerDoWork event XblMultiplayerEventType::UserAdded. XUID: %llu", static_cast(xuid)); // CODE SNIP SKIP } CallLuaFunctionWithHr(events[i].Result, "OnXblMultiplayerEventType_UserAdded"); // CODE SNIP SKIP break; } case XblMultiplayerEventType::UserRemoved: { // Handle UserRemoved hr = events[i].Result == __HRESULT_FROM_WIN32(ERROR_RESOURCE_DATA_NOT_FOUND) ? S_OK : events[i].Result; if (SUCCEEDED(hr)) { uint64_t xuid = 0; hr = XblMultiplayerEventArgsXuid(events[i].EventArgsHandle, &xuid); assert(SUCCEEDED(hr)); auto members{ GetSessionMembers(events[i].SessionType) }; for (auto member : members) { assert(xuid != member.Xuid); } } LogToScreen("XblMultiplayerManagerDoWork event XblMultiplayerEventType::UserRemoved"); // CODE SNIP SKIP CallLuaFunctionWithHr(hr, "OnXblMultiplayerEventType_UserRemoved"); // CODE SNIP SKIP break; } case XblMultiplayerEventType::JoinLobbyCompleted: { // Handle JoinLobbyCompleted uint64_t xuid = 0; hr = XblMultiplayerEventArgsXuid(events[i].EventArgsHandle, &xuid); assert(SUCCEEDED(hr)); if (SUCCEEDED(events[i].Result)) { CheckMemberFound(events[i].SessionType, xuid); hr = events[i].Result == __HRESULT_FROM_WIN32(ERROR_RESOURCE_DATA_NOT_FOUND) ? S_OK : events[i].Result; LogToScreen("XblMultiplayerManagerDoWork event XblMultiplayerEventType::JoinLobbyCompleted"); // CODE SNIP SKIP } CallLuaFunctionWithHr(hr, "OnXblMultiplayerEventType_JoinLobbyCompleted"); // CODE SNIP SKIP break; } case XblMultiplayerEventType::JoinGameCompleted: { // Handle JoinGameCompleted LogToScreen("XblMultiplayerManagerDoWork event XblMultiplayerEventType::JoinGameCompleted"); // CODE SNIP SKIP CallLuaFunctionWithHr(events[i].Result, "OnXblMultiplayerEventType_JoinGameCompleted"); // CODE SNIP SKIP break; } case XblMultiplayerEventType::LeaveGameCompleted: { // Handle LeaveGameCompleted LogToScreen("XblMultiplayerManagerDoWork event XblMultiplayerEventType::LeaveGameCompleted"); // CODE SNIP SKIP CallLuaFunctionWithHr(events[i].Result, "OnXblMultiplayerEventType_LeaveGameCompleted"); // CODE SNIP SKIP break; } case XblMultiplayerEventType::LocalMemberPropertyWriteCompleted: { // Handle LocalMemberPropertyWriteCompleted LogToScreen("XblMultiplayerManagerDoWork event XblMultiplayerEventType::LocalMemberPropertyWriteCompleted"); // CODE SNIP SKIP CallLuaFunctionWithHr(events[i].Result, "OnXblMultiplayerEventType_LocalMemberPropertyWriteCompleted"); // CODE SNIP SKIP break; } case XblMultiplayerEventType::InviteSent: { // Handle InviteSent LogToScreen("XblMultiplayerManagerDoWork event XblMultiplayerEventType::InviteSent"); // CODE SNIP SKIP CallLuaFunctionWithHr(events[i].Result, "OnXblMultiplayerEventType_InviteSent"); // CODE SNIP SKIP break; } case XblMultiplayerEventType::MemberLeft: { // Handle MemberLeft size_t memberCount = 0; hr = XblMultiplayerEventArgsMembersCount(events[i].EventArgsHandle, &memberCount); assert(SUCCEEDED(hr)); assert(memberCount > 0); std::vector members(memberCount); hr = XblMultiplayerEventArgsMembers(events[i].EventArgsHandle, memberCount, members.data()); assert(SUCCEEDED(hr)); auto sessionMembers{ GetSessionMembers(events[i].SessionType) }; for (auto remainingMember : sessionMembers) { for (auto member : members) { assert(member.Xuid != remainingMember.Xuid); } } LogToScreen("XblMultiplayerManagerDoWork event XblMultiplayerEventType::MemberLeft"); // CODE SNIP SKIP CallLuaFunctionWithHr(events[i].Result, "OnXblMultiplayerEventType_MemberLeft"); // CODE SNIP SKIP break; } case XblMultiplayerEventType::JoinabilityStateChanged: { // Handle JoinabilityStateChanged LogToScreen("XblMultiplayerManagerDoWork event XblMultiplayerEventType::JoinabilityStateChanged"); // CODE SNIP SKIP CallLuaFunctionWithHr(events[i].Result, "OnXblMultiplayerEventType_JoinabilityStateChanged"); // CODE SNIP SKIP break; } case XblMultiplayerEventType::HostChanged: { XblMultiplayerManagerMember member{}; hr = XblMultiplayerEventArgsMember(events[i].EventArgsHandle, &member); XblMultiplayerManagerMember host{}; HRESULT hr2 = XblMultiplayerManagerLobbySessionHost(&host); (void)(hr2); //suppress unused warning //If a host leaves and there is no new host, XblMultiplayerEventArgsMember returns 0x80070714 HRESULT_FROM_WIN32(ERROR_RESOURCE_DATA_NOT_FOUND) //Likewise, since there is no host, XblMultiplayerManagerLobbySessionHost returns 0x80070525 HRESULT_FROM_WIN32(ERROR_NO_SUCH_USER) assert((SUCCEEDED(hr) && SUCCEEDED(hr2)) || (hr == 0x80070714 && hr2 == 0x80070525)); assert(member.Xuid == host.Xuid); LogToScreen("XblMultiplayerManagerDoWork event XblMultiplayerEventType::HostChanged"); // CODE SNIP SKIP CallLuaFunctionWithHr(events[i].Result, "OnXblMultiplayerEventType_HostChanged"); // CODE SNIP SKIP break; } case XblMultiplayerEventType::MemberPropertyChanged: { XblMultiplayerManagerMember member; hr = XblMultiplayerEventArgsMember(events[i].EventArgsHandle, &member); assert(SUCCEEDED(hr)); const char* propertiesJson = nullptr; hr = XblMultiplayerEventArgsPropertiesJson(events[i].EventArgsHandle, &propertiesJson); assert(SUCCEEDED(hr)); auto sessionMembers{ GetSessionMembers(events[i].SessionType) }; XblMultiplayerManagerMember sessionMember{}; for (size_t j = 0; j < sessionMembers.size(); ++j) { if (member.Xuid == sessionMembers[j].Xuid) { sessionMember = sessionMembers[j]; break; } } assert(sessionMember.Xuid == member.Xuid); assert(strcmp(sessionMember.PropertiesJson, member.PropertiesJson) == 0); LogToScreen("XblMultiplayerManagerDoWork event XblMultiplayerEventType::MemberPropertyChanged"); // CODE SNIP SKIP CallLuaFunctionWithHr(events[i].Result, "OnXblMultiplayerEventType_MemberPropertyChanged"); // CODE SNIP SKIP break; } case XblMultiplayerEventType::FindMatchCompleted: { XblMultiplayerMatchStatus matchStatus; XblMultiplayerMeasurementFailure initializationFailureCause; hr = XblMultiplayerEventArgsFindMatchCompleted(events[i].EventArgsHandle, &matchStatus, &initializationFailureCause); assert(SUCCEEDED(hr)); assert(initializationFailureCause == XblMultiplayerMeasurementFailure::None); assert(matchStatus == XblMultiplayerMatchStatus::Completed); assert(XblMultiplayerManagerMatchStatus() == matchStatus); LogToScreen("XblMultiplayerManagerDoWork event XblMultiplayerEventType::FindMatchCompleted"); // CODE SNIP SKIP CallLuaFunctionWithHr(events[i].Result, "OnXblMultiplayerEventType_FindMatchCompleted"); // CODE SNIP SKIP break; } case XblMultiplayerEventType::PerformQosMeasurements: { XblMultiplayerPerformQoSMeasurementsArgs performQoSMeasurementsArgs; hr = XblMultiplayerEventArgsPerformQoSMeasurements(events[i].EventArgsHandle, &performQoSMeasurementsArgs); assert(SUCCEEDED(hr)); auto sessionMembers{ GetSessionMembers(events[i].SessionType) }; assert(performQoSMeasurementsArgs.remoteClientsSize == sessionMembers.size() - 1); for (size_t j = 0; j < performQoSMeasurementsArgs.remoteClientsSize; ++j) { bool clientFound{ false }; for (size_t k = 0; k < sessionMembers.size(); ++k) { if (strcmp(sessionMembers[k].ConnectionAddress, performQoSMeasurementsArgs.remoteClients[j].connectionAddress) == 0) { clientFound = true; assert(strcmp(sessionMembers[k].DeviceToken, performQoSMeasurementsArgs.remoteClients[j].deviceToken.Value) == 0); } } assert(clientFound); } LogToScreen("XblMultiplayerManagerDoWork event XblMultiplayerEventType::PerformQosMeasurements"); // CODE SNIP SKIP CallLuaFunctionWithHr(events[i].Result, "OnXblMultiplayerEventType_PerformQosMeasurements"); // CODE SNIP SKIP break; } case XblMultiplayerEventType::LocalMemberConnectionAddressWriteCompleted: { LogToScreen("XblMultiplayerManagerDoWork event XblMultiplayerEventType::LocalMemberConnectionAddressWriteCompleted"); // CODE SNIP SKIP CallLuaFunctionWithHr(events[i].Result, "OnXblMultiplayerEventType_LocalMemberConnectionAddressWriteCompleted"); // CODE SNIP SKIP break; } case XblMultiplayerEventType::SessionPropertyWriteCompleted: { LogToScreen("XblMultiplayerManagerDoWork event XblMultiplayerEventType::SessionPropertyWriteCompleted"); // CODE SNIP SKIP CallLuaFunctionWithHr(events[i].Result, "OnXblMultiplayerEventType_SessionPropertyWriteCompleted"); // CODE SNIP SKIP break; } case XblMultiplayerEventType::SessionSynchronizedPropertyWriteCompleted: { LogToScreen("XblMultiplayerManagerDoWork event XblMultiplayerEventType::SessionSynchronizedPropertyWriteCompleted"); // CODE SNIP SKIP if (events[i].Result == HTTP_E_STATUS_PRECOND_FAILED) { // Request rejected due to session conflict. Evaluate the need to write again and re-submit if needed. auto fnName = events[i].SessionType == XblMultiplayerSessionType::LobbySession ? "OnXblMultiplayerEventType_SessionSynchronizedPropertyWriteCompleted_412_LobbySession" // CODE SNIP SKIP : "OnXblMultiplayerEventType_SessionSynchronizedPropertyWriteCompleted_412_GameSession"; // CODE SNIP SKIP CallLuaFunctionWithHr(S_OK, fnName); // CODE SNIP SKIP } else { CallLuaFunctionWithHr(events[i].Result, "OnXblMultiplayerEventType_SessionSynchronizedPropertyWriteCompleted"); // CODE SNIP SKIP } break; } case XblMultiplayerEventType::SynchronizedHostWriteCompleted: { LogToScreen("XblMultiplayerManagerDoWork event XblMultiplayerEventType::SynchronizedHostWriteCompleted"); // CODE SNIP SKIP if (events[i].Result == HTTP_E_STATUS_PRECOND_FAILED) { // Request rejected due to session conflict. Evaluate the need to write again and re-submit if needed. auto fnName = events[i].SessionType == XblMultiplayerSessionType::LobbySession ? "OnXblMultiplayerEventType_SynchronizedHostWriteCompleted_412_LobbySession" // CODE SNIP SKIP : "OnXblMultiplayerEventType_SynchronizedHostWriteCompleted_412_GameSession"; // CODE SNIP SKIP CallLuaFunctionWithHr(S_OK, fnName); // CODE SNIP SKIP } else { CallLuaFunctionWithHr(events[i].Result, "OnXblMultiplayerEventType_SynchronizedHostWriteCompleted"); // CODE SNIP SKIP } break; } case XblMultiplayerEventType::ClientDisconnectedFromMultiplayerService: { LogToScreen("XblMultiplayerManagerDoWork event XblMultiplayerEventType::ClientDisconnectedFromMultiplayerService"); // CODE SNIP SKIP CallLuaFunctionWithHr(events[i].Result, "OnXblMultiplayerEventType_ClientDisconnectedFromMultiplayerService"); // CODE SNIP SKIP break; } case XblMultiplayerEventType::TournamentRegistrationStateChanged: case XblMultiplayerEventType::TournamentGameSessionReady: case XblMultiplayerEventType::ArbitrationComplete: default: { LogToScreen("Received MPM event of type %u, hr=%s", events[i].EventType, ConvertHR(events[i].Result).data()); // CODE SNIP SKIP LogToScreen("XblMultiplayerManagerDoWork event Other"); // CODE SNIP SKIP break; } // CODE SKIP END } } // CODE SNIPPET END return hr; } int StartDoWorkLoop_Lua(lua_State* L) { if (!s_doWork) { s_doWork = true; s_doWorkThread = std::thread([]() { Data()->m_mpmDoWorkDone = false; while (s_doWork && !Data()->m_quit) { MultiplayerManagerDoWork(); pal::Sleep(10); } Data()->m_mpmDoWorkDone = true; LogToScreen("Exiting MPM DoWork thread"); }); } return LuaReturnHR(L, S_OK); } void MPMStopDoWorkHelper() { if (s_doWork) { s_doWork = false; s_doWorkThread.join(); } } int StopDoWorkLoop_Lua(lua_State* L) { MPMStopDoWorkHelper(); return LuaReturnHR(L, S_OK); } int XblMultiplayerManagerInitialize_Lua(lua_State* L) { const char* lobbySessionTemplateName = "LobbySession"; auto queueUsedByMultiplayerManager = Data()->queue; // CODE SNIPPET START: XblMultiplayerManagerInitialize_C HRESULT hr = XblMultiplayerManagerInitialize(lobbySessionTemplateName, queueUsedByMultiplayerManager); // CODE SNIPPET END LogToScreen("XblMultiplayerManagerInitialize: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblMultiplayerManagerDoWork_Lua(lua_State* L) { HRESULT hr = MultiplayerManagerDoWork(); LogToScreen("XblMultiplayerManagerDoWork: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblMultiplayerManagerLobbySessionAddLocalUser_Lua(lua_State* L) { // CODE SNIPPET START: XblMultiplayerManagerLobbySessionAddLocalUser HRESULT hr = XblMultiplayerManagerLobbySessionAddLocalUser(Data()->xalUser); // CODE SNIPPET END LogToScreen("XblMultiplayerManagerLobbySessionAddLocalUser: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblMultiplayerManagerLobbySessionRemoveLocalUser_Lua(lua_State* L) { // CODE SNIPPET START: XblMultiplayerManagerLobbySessionAddLocalUser HRESULT hr = XblMultiplayerManagerLobbySessionRemoveLocalUser(Data()->xalUser); // CODE SNIPPET END LogToScreen("XblMultiplayerManagerLobbySessionRemoveLocalUser: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblMultiplayerManagerLobbySessionCorrelationId_Lua(lua_State* L) { // CODE SNIPPET START: XblMultiplayerManagerLobbySessionAddLocalUser XblGuid correlationId; HRESULT hr = XblMultiplayerManagerLobbySessionCorrelationId(&correlationId); // CODE SNIPPET END LogToScreen("XblMultiplayerManagerLobbySessionCorrelationId: id=%s", correlationId.value); return LuaReturnHR(L, hr); } int XblMultiplayerManagerLobbySessionSessionReference_Lua(lua_State* L) { // CODE SNIPPET START: XblMultiplayerManagerLobbySessionAddLocalUser XblMultiplayerSessionReference sessionReference{}; HRESULT hr = XblMultiplayerManagerLobbySessionSessionReference(&sessionReference); // CODE SNIPPET END LogToScreen("XblMultiplayerManagerLobbySessionSessionReference: scid = %s, sessionName = %s", sessionReference.Scid, sessionReference.SessionName); return LuaReturnHR(L, hr); } void LogMultiplayerSessionMember(const XblMultiplayerManagerMember& member) { LogToScreen("XblMultiplayerManagerMember: \n\tMemberId = %u\n\tXuid = %llu\n\tDebugGamertag = %s\n\tIsLocal = %u\n\tIsInLobby = %u\n\tIsInGame = %u\n\tStatus = %u", member.MemberId, static_cast(member.Xuid), member.DebugGamertag, member.IsLocal, member.IsInLobby, member.IsInGame, member.Status ); } int XblMultiplayerManagerLobbySessionLocalMembers_Lua(lua_State* L) { // CODE SNIPPET START: XblMultiplayerManagerLobbySessionLocalMembers size_t localMembersCount = XblMultiplayerManagerLobbySessionLocalMembersCount(); std::vector localMembers(localMembersCount, XblMultiplayerManagerMember{}); HRESULT hr = XblMultiplayerManagerLobbySessionLocalMembers(localMembersCount, localMembers.data()); // CODE SNIPPET END if (SUCCEEDED(hr)) { for (const auto& member : localMembers) { LogMultiplayerSessionMember(member); } } LogToScreen("XblMultiplayerManagerLobbySessionLocalMembers: hr = %s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblMultiplayerManagerLobbySessionSetLocalMemberProperties_Lua(lua_State* L) { // CODE SNIPPET START: XblMultiplayerManagerLobbySessionSetLocalMemberProperties HRESULT hr = XblMultiplayerManagerLobbySessionSetLocalMemberProperties(Data()->xalUser, "Health", "1", nullptr); // CODE SNIPPET END LogToScreen("XblMultiplayerManagerLobbySessionSetLocalMemberProperties: hr = %s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblMultiplayerManagerLobbySessionInviteFriends_Lua(lua_State* L) { #if HC_PLATFORM == HC_PLATFORM_WIN32 HRESULT hr = XblMultiplayerManagerLobbySessionInviteFriends(Data()->xalUser, nullptr, "//MPSD/custominvitestrings_JoinMyGame"); #else HRESULT hr = S_OK; #endif LogToScreen("XblMultiplayerManagerLobbySessionInviteFriends: hr = %s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblMultiplayerManagerJoinGameFromLobby_Lua(lua_State* L) { const char* gameSessionTemplateName = "GameSession"; // CODE SNIPPET START: XblMultiplayerManagerJoinGameFromLobby_C HRESULT hr = XblMultiplayerManagerJoinGameFromLobby(gameSessionTemplateName); if (!SUCCEEDED(hr)) { // Handle error } // CODE SNIPPET END LogToScreen("XblMultiplayerManagerJoinGameFromLobby: hr = %s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblMultiplayerManagerGameSessionActive_Lua(lua_State* L) { // CODE SNIPPET START: XblMultiplayerManagerGameSessionActive bool isGameSessionAcive = XblMultiplayerManagerGameSessionActive(); // CODE SNIPPET END LogToScreen("XblMultiplayerManagerGameSessionActive: active = %d", isGameSessionAcive); lua_pushboolean(L, isGameSessionAcive); return 1; } int XblMultiplayerManagerGameSessionMembers_Lua(lua_State* L) { HRESULT hr = S_OK; // CODE SNIPPET START: XblMultiplayerManagerGameSessionMembers size_t memberCount = XblMultiplayerManagerGameSessionMembersCount(); if (memberCount > 0) { std::vector gameSessionMembers(memberCount, XblMultiplayerManagerMember{}); hr = XblMultiplayerManagerGameSessionMembers(memberCount, gameSessionMembers.data()); if (SUCCEEDED(hr)) { for (const auto& member : gameSessionMembers) { LogMultiplayerSessionMember(member); } } } // CODE SNIPPET END LogToScreen("XblMultiplayerManagerGameSessionMembers: hr = %s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblMultiplayerManagerGameSessionSetProperties_Lua(lua_State* L) { // CODE SNIPPET START: XblMultiplayerManagerGameSessionSetProperties HRESULT hr = XblMultiplayerManagerGameSessionSetProperties("CustomProperty", "\"CustomPropertyValue\"", nullptr); // CODE SNIPPET END LogToScreen("XblMultiplayerManagerGameSessionSetProperties: hr = %s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblMultiplayerManagerLeaveGame_Lua(lua_State* L) { // CODE SNIPPET START: XblMultiplayerManagerLeaveGame HRESULT hr = XblMultiplayerManagerLeaveGame(); // CODE SNIPPET END LogToScreen("XblMultiplayerManagerLeaveGame: hr = %s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblMultiplayerManagerJoinLobbyViaActivity_Lua(lua_State* L) { uint64_t xuid1 = GetUint64FromLua(L, 1, Data()->m_multiDeviceManager->GetRemoteXuid()); auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->context = nullptr; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* HRESULT hr = XAsyncGetStatus(asyncBlock, false); if (SUCCEEDED(hr)) { size_t resultSize{ 0 }; hr = XblMultiplayerGetActivitiesWithPropertiesForUsersResultSize(asyncBlock, &resultSize); if (SUCCEEDED(hr)) { size_t count{ 0 }; std::vector buffer(resultSize, 0); XblMultiplayerActivityDetails* activityDetails{}; hr = XblMultiplayerGetActivitiesWithPropertiesForUsersResult(asyncBlock, resultSize, buffer.data(), &activityDetails, &count, nullptr); if (SUCCEEDED(hr)) { if (resultSize > 0) { std::string handleIdStr = activityDetails[0].HandleId; LogToScreen("Joining lobby via handle %s", handleIdStr.c_str()); auto handleId = handleIdStr.c_str(); auto xblUserHandle = Data()->xalUser; // CODE SNIPPET START: XblMultiplayerManagerJoinLobby_C hr = XblMultiplayerManagerJoinLobby(handleId, xblUserHandle); // CODE SNIPPET END } else { LogToScreen("No activity handle to join. Failing..."); hr = E_FAIL; } } } } CallLuaFunctionWithHr(hr, "OnXblMultiplayerGetActivitiesForUsersAsync"); }; uint64_t xuids[1] = {}; xuids[0] = xuid1; size_t xuidsCount = 1; HRESULT hr = XblMultiplayerGetActivitiesWithPropertiesForUsersAsync( Data()->xboxLiveContext, Data()->scid, xuids, xuidsCount, asyncBlock.get()); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } return LuaReturnHR(L, hr); } int XblMultiplayerManagerJoinLobby_lua(lua_State* L) { std::string handleId = { GetStringFromLua(L, 1, "GameSessionName") }; HRESULT hr = XblMultiplayerManagerJoinLobby(handleId.c_str(), Data()->xalUser); LogToScreen("XblMultiplayerManagerJoinLobby: hr = %s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblMultiplayerManagerJoinGame_Lua(lua_State* L) { // Params: // 1) Session name // 2) Session template name auto sessionName{ GetStringFromLua(L, 1, "GameSessionName") }; auto sessionTemplateName{ GetStringFromLua(L, 2, "GameSession") }; // CODE SNIPPET START: XblMultiplayerManagerJoinGame HRESULT hr = XblMultiplayerManagerJoinGame(sessionName.data(), sessionTemplateName.data(), &Data()->xboxUserId, 1); // CODE SNIPPET END return LuaReturnHR(L, hr); } int XblMultiplayerManagerSetJoinability_Lua(lua_State* L) { // CODE SNIPPET START: XblMultiplayerManagerSetJoinability HRESULT hr = XblMultiplayerManagerSetJoinability(XblMultiplayerJoinability::JoinableByFriends, nullptr); // CODE SNIPPET END LogToScreen("XblMultiplayerManagerSetJoinability: hr = %s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblMultiplayerManagerMemberAreMembersOnSameDevice_Lua(lua_State* L) { const XblMultiplayerManagerMember* first = nullptr; const XblMultiplayerManagerMember* second = nullptr; // CODE SNIPPET START: XblMultiplayerManagerMemberAreMembersOnSameDevice bool areOnSameDevice = XblMultiplayerManagerMemberAreMembersOnSameDevice(first, second); // CODE SNIPPET END LogToScreen("XblMultiplayerManagerMemberAreMembersOnSameDevice"); LogToScreen("areOnSameDevice: %d", areOnSameDevice); return LuaReturnHR(L, S_OK); } int XblMultiplayerManagerLobbySessionMembersCount_Lua(lua_State* L) { // CODE SNIPPET START: XblMultiplayerManagerLobbySessionMembersCount size_t count = XblMultiplayerManagerLobbySessionMembersCount(); // CODE SNIPPET END LogToScreen("XblMultiplayerManagerLobbySessionMembersCount"); LogToScreen("count: %d", count); return LuaReturnHR(L, S_OK); } int XblMultiplayerManagerLobbySessionMembers_Lua(lua_State* L) { // CODE SNIPPET START: XblMultiplayerManagerLobbySessionMembers size_t count = XblMultiplayerManagerLobbySessionMembersCount(); std::vector members(count); HRESULT hr = XblMultiplayerManagerLobbySessionMembers(count, members.data()); // CODE SNIPPET END LogToScreen("XblMultiplayerManagerLobbySessionMembers: hr = %s", ConvertHR(hr).c_str()); LogToScreen("count: %d", count); return LuaReturnHR(L, hr); } int XblMultiplayerManagerLobbySessionHost_Lua(lua_State* L) { // CODE SNIPPET START: XblMultiplayerManagerLobbySessionHost XblMultiplayerManagerMember hostMember; HRESULT hr = XblMultiplayerManagerLobbySessionHost(&hostMember); // CODE SNIPPET END LogToScreen("XblMultiplayerManagerLobbySessionHost: hr = %s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblMultiplayerManagerLobbySessionPropertiesJson_Lua(lua_State* L) { // CODE SNIPPET START: XblMultiplayerManagerLobbySessionPropertiesJson const char* json = XblMultiplayerManagerLobbySessionPropertiesJson(); // CODE SNIPPET END LogToScreen("XblMultiplayerManagerLobbySessionPropertiesJson"); LogToScreen("json: %s", json); return LuaReturnHR(L, S_OK); } int XblMultiplayerManagerLobbySessionConstants_Lua(lua_State* L) { // CODE SNIPPET START: XblMultiplayerManagerLobbySessionConstants const XblMultiplayerSessionConstants* consts = XblMultiplayerManagerLobbySessionConstants(); // CODE SNIPPET END LogToScreen("XblMultiplayerManagerLobbySessionConstants:\n" "ClientMatchmakingCapable = %d\n" "EnableMetricsBandwidthDown = %d\n" "EnableMetricsBandwidthUp = %d\n" "EnableMetricsCustom = %d\n" "EnableMetricsLatency = %d\n" "MaxMembersInSession = %d\n" "Visibility = %d\n" "InitiatorXuidsCount = %z\n" "MemberInactiveTimeout = %l\n" "MemberReadyTimeout = %l\n" "MemberReservedTimeout = %l\n" "SessionEmptyTimeout = %l\n" "MeasurementServerAddressesJson = %s\n" "SessionCloudComputePackageConstantsJson = %s\n" "CustomJson = %s", consts->ClientMatchmakingCapable, consts->EnableMetricsBandwidthDown, consts->EnableMetricsBandwidthUp, consts->EnableMetricsCustom, consts->EnableMetricsLatency, consts->MaxMembersInSession, consts->InitiatorXuidsCount, consts->MemberInactiveTimeout, consts->MemberReadyTimeout, consts->MemberReservedTimeout, consts->SessionEmptyTimeout, consts->Visibility, consts->MeasurementServerAddressesJson, consts->SessionCloudComputePackageConstantsJson, consts->CustomJson); return LuaReturnHR(L, S_OK); } int XblMultiplayerManagerLobbySessionDeleteLocalMemberProperties_Lua(lua_State* L) { std::string name = "name"; void* context = nullptr; #if HC_PLATFORM != HC_PLATFORM_UWP // CODE SNIPPET START: XblMultiplayerManagerLobbySessionDeleteLocalMemberProperties HRESULT hr = XblMultiplayerManagerLobbySessionDeleteLocalMemberProperties( Data()->xalUser, name.c_str(), context); // CODE SNIPPET END #else HRESULT hr = S_OK; #endif LogToScreen("XblMultiplayerManagerLobbySessionDeleteLocalMemberProperties: hr = %s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblMultiplayerManagerLobbySessionSetLocalMemberConnectionAddress_Lua(lua_State* L) { auto connectionAddressStr = GetStringFromLua(L, 1, "connectionAddress1"); auto connectionAddress = connectionAddressStr.c_str(); void* context = nullptr; auto xblUserHandle = Data()->xalUser; // CODE SNIPPET START: XblMultiplayerManagerLobbySessionSetLocalMemberConnectionAddress_C HRESULT hr = XblMultiplayerManagerLobbySessionSetLocalMemberConnectionAddress( xblUserHandle, connectionAddress, context); // CODE SNIPPET END LogToScreen("XblMultiplayerManagerLobbySessionSetLocalMemberConnectionAddress: hr = %s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblMultiplayerManagerLobbySessionIsHost_Lua(lua_State* L) { // CODE SNIPPET START: XblMultiplayerManagerLobbySessionIsHost bool isHost = XblMultiplayerManagerLobbySessionIsHost(Data()->xboxUserId); // CODE SNIPPET END LogToScreen("XblMultiplayerManagerLobbySessionIsHost: isHost = %d", isHost); return LuaReturnHR(L, S_OK); } int XblMultiplayerManagerLobbySessionSetProperties_Lua(lua_State* L) { std::string name = GetStringFromLua(L, 1, "role"); std::string valueJson = GetStringFromLua(L, 1, "{\"class\":\"fighter\"}"); void* context = nullptr; // CODE SNIPPET START: XblMultiplayerManagerLobbySessionSetProperties HRESULT hr = XblMultiplayerManagerLobbySessionSetProperties(name.c_str(), valueJson.c_str(), context); // CODE SNIPPET END LogToScreen("XblMultiplayerManagerLobbySessionSetProperties: hr = %s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblMultiplayerManagerLobbySessionSetSynchronizedProperties_Lua(lua_State* L) { std::string name = GetStringFromLua(L, 1, "name1"); std::string valueJson = GetStringFromLua(L, 1, "{}"); void* context = nullptr; // CODE SNIPPET START: XblMultiplayerManagerLobbySessionSetSynchronizedProperties HRESULT hr = XblMultiplayerManagerLobbySessionSetSynchronizedProperties(name.c_str(), valueJson.c_str(), context); // CODE SNIPPET END LogToScreen("XblMultiplayerManagerLobbySessionSetSynchronizedProperties: hr = %s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblMultiplayerManagerLobbySessionSetSynchronizedHost_Lua(lua_State* L) { size_t membersCount = XblMultiplayerManagerLobbySessionMembersCount(); std::vector members(membersCount); XblMultiplayerManagerLobbySessionMembers(membersCount, members.data()); std::string defaultDeviceToken = ""; for (auto member : members) { if (member.Xuid == Data()->xboxUserId) { defaultDeviceToken = member.DeviceToken; break; } } std::string deviceToken = GetStringFromLua(L, 1, defaultDeviceToken.empty() ? "deviceToken1" : defaultDeviceToken); void* context = nullptr; // CODE SNIPPET START: XblMultiplayerManagerLobbySessionSetSynchronizedHost HRESULT hr = XblMultiplayerManagerLobbySessionSetSynchronizedHost(deviceToken.c_str(), context); // CODE SNIPPET END LogToScreen("XblMultiplayerManagerLobbySessionSetSynchronizedHost: hr = %s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblMultiplayerManagerLobbySessionInviteUsers_Lua(lua_State* L) { //TODOs: Change XUID to whatever account is used in XblGameInviteAddNotificationHandler_Lua // to test sending and receiving invites auto xblUserHandle = Data()->xalUser; // CODE SNIPPET START: XblMultiplayerManagerLobbySessionInviteUsers_C size_t xuidsCount = 1; uint64_t xuids[1] = {}; xuids[0] = 1234567891234567; xuids[0] = 2814620188564745; // CODE SNIP SKIP HRESULT hr = XblMultiplayerManagerLobbySessionInviteUsers( xblUserHandle, xuids, xuidsCount, nullptr, // ContextStringId nullptr // CustomActivationContext ); // CODE SNIPPET END LogToScreen("XblMultiplayerManagerLobbySessionInviteUsers: hr = %s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblMultiplayerManagerGameSessionCorrelationId_Lua(lua_State* L) { // CODE SNIPPET START: XblMultiplayerManagerGameSessionCorrelationId const char* id = XblMultiplayerManagerGameSessionCorrelationId(); // CODE SNIPPET END LogToScreen("XblMultiplayerManagerGameSessionCorrelationId: id = %s", id); return LuaReturnHR(L, S_OK); } int XblMultiplayerManagerGameSessionSessionReference_Lua(lua_State* L) { // CODE SNIPPET START: XblMultiplayerManagerGameSessionSessionReference const XblMultiplayerSessionReference* session = XblMultiplayerManagerGameSessionSessionReference(); // CODE SNIPPET END LogToScreen("XblMultiplayerManagerGameSessionSessionReference: scid = %s", session->Scid); return LuaReturnHR(L, S_OK); } int XblMultiplayerManagerGameSessionHost_Lua(lua_State* L) { XblMultiplayerManagerMember hostMember; // CODE SNIPPET START: XblMultiplayerManagerGameSessionHost HRESULT hr = XblMultiplayerManagerGameSessionHost(&hostMember); // CODE SNIPPET END LogToScreen("XblMultiplayerManagerGameSessionHost: hr = %s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblMultiplayerManagerGameSessionPropertiesJson_Lua(lua_State* L) { // CODE SNIPPET START: XblMultiplayerManagerGameSessionPropertiesJson const char* json = XblMultiplayerManagerGameSessionPropertiesJson(); // CODE SNIPPET END LogToScreen("XblMultiplayerManagerGameSessionPropertiesJson: json = %s", json); return LuaReturnHR(L, S_OK); } int XblMultiplayerManagerGameSessionConstants_Lua(lua_State* L) { // CODE SNIPPET START: XblMultiplayerManagerGameSessionConstants const XblMultiplayerSessionConstants* consts = XblMultiplayerManagerGameSessionConstants(); // CODE SNIPPET END LogToScreen("XblMultiplayerManagerGameSessionConstants:\n" "ClientMatchmakingCapable = %d\n" "EnableMetricsBandwidthDown = %d\n" "EnableMetricsBandwidthUp = %d\n" "EnableMetricsCustom = %d\n" "EnableMetricsLatency = %d\n" "MaxMembersInSession = %d", consts->ClientMatchmakingCapable, consts->EnableMetricsBandwidthDown, consts->EnableMetricsBandwidthUp, consts->EnableMetricsCustom, consts->EnableMetricsLatency, consts->MaxMembersInSession ); LogToScreen( "InitiatorXuidsCount = %llu\n" "MemberInactiveTimeout = %llu\n" "MemberReadyTimeout = %llu\n" "MemberReservedTimeout = %llu\n" "SessionEmptyTimeout = %llu", static_cast(consts->InitiatorXuidsCount), static_cast(consts->MemberInactiveTimeout), static_cast(consts->MemberReadyTimeout), static_cast(consts->MemberReservedTimeout), static_cast(consts->SessionEmptyTimeout) ); LogToScreen("MeasurementServerAddressesJson = %s", consts->MeasurementServerAddressesJson); LogToScreen("SessionCloudComputePackageConstantsJson = %s", consts->SessionCloudComputePackageConstantsJson); LogToScreen("CustomJson = %s", consts->CustomJson); return LuaReturnHR(L, S_OK); } int XblMultiplayerManagerGameSessionIsHost_Lua(lua_State* L) { // CODE SNIPPET START: XblMultiplayerManagerGameSessionIsHost bool isHost = XblMultiplayerManagerGameSessionIsHost(Data()->xboxUserId); // CODE SNIPPET END LogToScreen("XblMultiplayerManagerGameSessionIsHost: isHost = %d", isHost); return LuaReturnHR(L, S_OK); } int XblMultiplayerManagerGameSessionSetSynchronizedProperties_Lua(lua_State* L) { // CODE SNIPPET START: XblMultiplayerManagerGameSessionSetSynchronizedProperties HRESULT hr = XblMultiplayerManagerGameSessionSetSynchronizedProperties("CustomSyncProperty", "\"CustomSyncPropertyValue\"", nullptr); // CODE SNIPPET END LogToScreen("XblMultiplayerManagerGameSessionSetSynchronizedProperties: hr = %s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblMultiplayerManagerGameSessionSetSynchronizedHost_Lua(lua_State* L) { std::string deviceToken = GetStringFromLua(L, 1, "deviceToken1"); void* context = nullptr; // CODE SNIPPET START: XblMultiplayerManagerGameSessionSetSynchronizedHost HRESULT hr = XblMultiplayerManagerGameSessionSetSynchronizedHost(deviceToken.c_str(), context); // CODE SNIPPET END LogToScreen("XblMultiplayerManagerGameSessionSetSynchronizedHost: hr = %s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblMultiplayerManagerFindMatch_Lua(lua_State* L) { // Params: // 1) matchmaking hopper name // 2) attributes json auto hopperNameStr{ GetStringFromLua(L, 1, "") }; auto attributesJsonStr{ GetStringFromLua(L, 2, "") }; const char* hopperName = hopperNameStr.c_str(); const char* attributesJson = attributesJsonStr.c_str(); // CODE SNIPPET START: XblMultiplayerManagerFindMatch_C uint32_t timeoutInSeconds = 30; HRESULT hr = XblMultiplayerManagerFindMatch(hopperName, attributesJson, timeoutInSeconds); if (!SUCCEEDED(hr)) { // Handle failure } // CODE SNIPPET END LogToScreen("XblMultiplayerManagerFindMatch: hr = %s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblMultiplayerManagerCancelMatch_Lua(lua_State* L) { // CODE SNIPPET START: XblMultiplayerManagerCancelMatch XblMultiplayerManagerCancelMatch(); // CODE SNIPPET END LogToScreen("XblMultiplayerManagerCancelMatch"); return LuaReturnHR(L, S_OK); } int XblMultiplayerManagerMatchStatus_Lua(lua_State* L) { // CODE SNIPPET START: XblMultiplayerManagerMatchStatus XblMultiplayerMatchStatus status = XblMultiplayerManagerMatchStatus(); // CODE SNIPPET END LogToScreen("XblMultiplayerManagerMatchStatus: status = %d", status); return LuaReturnHR(L, S_OK); } int XblMultiplayerManagerEstimatedMatchWaitTime_Lua(lua_State* L) { // CODE SNIPPET START: XblMultiplayerManagerEstimatedMatchWaitTime uint32_t waitTime = XblMultiplayerManagerEstimatedMatchWaitTime(); // CODE SNIPPET END LogToScreen("XblMultiplayerManagerEstimatedMatchWaitTime: waitTime = %d", waitTime); return LuaReturnHR(L, S_OK); } int XblMultiplayerManagerAutoFillMembersDuringMatchmaking_Lua(lua_State* L) { // CODE SNIPPET START: XblMultiplayerManagerAutoFillMembersDuringMatchmaking bool autoFill = XblMultiplayerManagerAutoFillMembersDuringMatchmaking(); // CODE SNIPPET END LogToScreen("XblMultiplayerManagerAutoFillMembersDuringMatchmaking: autoFill = %d", autoFill); return LuaReturnHR(L, S_OK); } int XblMultiplayerManagerSetAutoFillMembersDuringMatchmaking_Lua(lua_State* L) { // CODE SNIPPET START: XblMultiplayerManagerSetAutoFillMembersDuringMatchmaking XblMultiplayerManagerSetAutoFillMembersDuringMatchmaking(true); // CODE SNIPPET END LogToScreen("XblMultiplayerManagerSetAutoFillMembersDuringMatchmaking"); return LuaReturnHR(L, S_OK); } int XblMultiplayerManagerSetQosMeasurements_Lua(lua_State* L) { const char* qosJson = "{\"e69c43a8\": {" "\"latency\": 5953," "\"bandwidthDown\" : 19342," "\"bandwidthUp\" : 944," "\"customProperty\" : \"customVal\"}}"; // CODE SNIPPET START: XblMultiplayerManagerSetQosMeasurements HRESULT hr = XblMultiplayerManagerSetQosMeasurements(qosJson); // CODE SNIPPET END LogToScreen("XblMultiplayerManagerSetQosMeasurements: hr = %s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblMultiplayerManagerJoinability_Lua(lua_State* L) { // CODE SNIPPET START: XblMultiplayerManagerJoinability XblMultiplayerJoinability joinability = XblMultiplayerManagerJoinability(); // CODE SNIPPET END LogToScreen("XblMultiplayerManagerJoinability: joinability = %d", joinability); return LuaReturnHR(L, S_OK); } int VerifyMPMGameSessionProperites_Lua(lua_State* L) { bool isActive = XblMultiplayerManagerGameSessionActive(); assert(isActive); UNREFERENCED_PARAMETER(isActive); const XblMultiplayerSessionConstants* consts = XblMultiplayerManagerGameSessionConstants(); assert(consts != nullptr); assert(consts->MaxMembersInSession == 20); // defined by template UNREFERENCED_PARAMETER(consts); const char* corr = XblMultiplayerManagerGameSessionCorrelationId(); assert(corr != nullptr); assert(strlen(corr) > 0); UNREFERENCED_PARAMETER(corr); XblMultiplayerManagerMember hostMember; HRESULT hr = XblMultiplayerManagerGameSessionHost(&hostMember); assert(SUCCEEDED(hr) || hr == __HRESULT_FROM_WIN32(ERROR_NO_SUCH_USER)); UNREFERENCED_PARAMETER(hr); bool isHost = XblMultiplayerManagerGameSessionIsHost(Data()->xboxUserId); UNREFERENCED_PARAMETER(isHost); size_t membersCount = XblMultiplayerManagerGameSessionMembersCount(); std::vector members(membersCount); hr = XblMultiplayerManagerGameSessionMembers(membersCount, members.data()); assert(SUCCEEDED(hr)); assert(membersCount == 2); assert(strlen(members[0].DebugGamertag) > 0); assert(strlen(members[1].DebugGamertag) > 0); UNREFERENCED_PARAMETER(membersCount); UNREFERENCED_PARAMETER(members); bool sameDevice = XblMultiplayerManagerMemberAreMembersOnSameDevice(&members[0], &members[1]); assert(sameDevice == false); UNREFERENCED_PARAMETER(sameDevice); const char* props = XblMultiplayerManagerGameSessionPropertiesJson(); assert(props != nullptr); assert(strlen(props) > 0); UNREFERENCED_PARAMETER(props); const XblMultiplayerSessionReference* sessionRef = XblMultiplayerManagerGameSessionSessionReference(); assert(sessionRef != nullptr); assert(strlen(sessionRef->Scid) > 0); assert(strlen(sessionRef->SessionName) > 0); assert(strlen(sessionRef->SessionTemplateName) > 0); UNREFERENCED_PARAMETER(sessionRef); return LuaReturnHR(L, S_OK); } int VerifyMPMLobbySessionProperites_Lua(lua_State* L) { const XblMultiplayerSessionConstants* consts = XblMultiplayerManagerGameSessionConstants(); assert(consts != nullptr); assert(consts->MaxMembersInSession == 20); // defined by template UNREFERENCED_PARAMETER(consts); XblGuid correlationId; HRESULT hr = XblMultiplayerManagerLobbySessionCorrelationId(&correlationId); assert(SUCCEEDED(hr)); assert(correlationId.value[0] != 0); UNREFERENCED_PARAMETER(correlationId); XblMultiplayerManagerMember hostMember; hr = XblMultiplayerManagerLobbySessionHost(&hostMember); assert(SUCCEEDED(hr) || hr == __HRESULT_FROM_WIN32(ERROR_NO_SUCH_USER)); bool isHost = XblMultiplayerManagerLobbySessionIsHost(Data()->xboxUserId); UNREFERENCED_PARAMETER(isHost); size_t localMembersCount = XblMultiplayerManagerLobbySessionLocalMembersCount(); std::vector localmembers(localMembersCount); hr = XblMultiplayerManagerLobbySessionLocalMembers(localMembersCount, localmembers.data()); assert(SUCCEEDED(hr)); assert(localMembersCount == 1); assert(strlen(localmembers[0].DebugGamertag) > 0); UNREFERENCED_PARAMETER(localmembers); size_t membersCount = XblMultiplayerManagerLobbySessionMembersCount(); std::vector members(membersCount); hr = XblMultiplayerManagerLobbySessionMembers(membersCount, members.data()); assert(SUCCEEDED(hr)); assert(membersCount >= 1); assert(strlen(members[0].DebugGamertag) > 0); UNREFERENCED_PARAMETER(members); const char* props = XblMultiplayerManagerLobbySessionPropertiesJson(); assert(props != nullptr); assert(strlen(props) > 0); UNREFERENCED_PARAMETER(props); XblMultiplayerSessionReference sessionRef{}; hr = XblMultiplayerManagerLobbySessionSessionReference(&sessionRef); assert(SUCCEEDED(hr)); assert(strlen(sessionRef.Scid) > 0); assert(strlen(sessionRef.SessionName) > 0); assert(strlen(sessionRef.SessionTemplateName) > 0); UNREFERENCED_PARAMETER(sessionRef); return LuaReturnHR(L, S_OK); } void SetupAPIs_XblMultiplayerManager() { // Non XSAPI APIs lua_register(Data()->L, "StartDoWorkLoop", StartDoWorkLoop_Lua); lua_register(Data()->L, "StopDoWorkLoop", StopDoWorkLoop_Lua); // XSAPI MPM APIs lua_register(Data()->L, "XblMultiplayerManagerMemberAreMembersOnSameDevice", XblMultiplayerManagerMemberAreMembersOnSameDevice_Lua); lua_register(Data()->L, "XblMultiplayerManagerInitialize", XblMultiplayerManagerInitialize_Lua); lua_register(Data()->L, "XblMultiplayerManagerDoWork", XblMultiplayerManagerDoWork_Lua); lua_register(Data()->L, "XblMultiplayerManagerLobbySessionCorrelationId", XblMultiplayerManagerLobbySessionCorrelationId_Lua); lua_register(Data()->L, "XblMultiplayerManagerLobbySessionSessionReference", XblMultiplayerManagerLobbySessionSessionReference_Lua); lua_register(Data()->L, "XblMultiplayerManagerLobbySessionLocalMembers", XblMultiplayerManagerLobbySessionLocalMembers_Lua); lua_register(Data()->L, "XblMultiplayerManagerLobbySessionMembersCount", XblMultiplayerManagerLobbySessionMembersCount_Lua); lua_register(Data()->L, "XblMultiplayerManagerLobbySessionMembers", XblMultiplayerManagerLobbySessionMembers_Lua); lua_register(Data()->L, "XblMultiplayerManagerLobbySessionHost", XblMultiplayerManagerLobbySessionHost_Lua); lua_register(Data()->L, "XblMultiplayerManagerLobbySessionPropertiesJson", XblMultiplayerManagerLobbySessionPropertiesJson_Lua); lua_register(Data()->L, "XblMultiplayerManagerLobbySessionConstants", XblMultiplayerManagerLobbySessionConstants_Lua); lua_register(Data()->L, "XblMultiplayerManagerLobbySessionAddLocalUser", XblMultiplayerManagerLobbySessionAddLocalUser_Lua); lua_register(Data()->L, "XblMultiplayerManagerLobbySessionRemoveLocalUser", XblMultiplayerManagerLobbySessionRemoveLocalUser_Lua); lua_register(Data()->L, "XblMultiplayerManagerLobbySessionSetLocalMemberProperties", XblMultiplayerManagerLobbySessionSetLocalMemberProperties_Lua); lua_register(Data()->L, "XblMultiplayerManagerLobbySessionDeleteLocalMemberProperties", XblMultiplayerManagerLobbySessionDeleteLocalMemberProperties_Lua); lua_register(Data()->L, "XblMultiplayerManagerLobbySessionSetLocalMemberConnectionAddress", XblMultiplayerManagerLobbySessionSetLocalMemberConnectionAddress_Lua); lua_register(Data()->L, "XblMultiplayerManagerLobbySessionIsHost", XblMultiplayerManagerLobbySessionIsHost_Lua); lua_register(Data()->L, "XblMultiplayerManagerLobbySessionSetProperties", XblMultiplayerManagerLobbySessionSetProperties_Lua); lua_register(Data()->L, "XblMultiplayerManagerLobbySessionSetSynchronizedProperties", XblMultiplayerManagerLobbySessionSetSynchronizedProperties_Lua); lua_register(Data()->L, "XblMultiplayerManagerLobbySessionSetSynchronizedHost", XblMultiplayerManagerLobbySessionSetSynchronizedHost_Lua); lua_register(Data()->L, "XblMultiplayerManagerLobbySessionInviteFriends", XblMultiplayerManagerLobbySessionInviteFriends_Lua); lua_register(Data()->L, "XblMultiplayerManagerLobbySessionInviteUsers", XblMultiplayerManagerLobbySessionInviteUsers_Lua); lua_register(Data()->L, "XblMultiplayerManagerGameSessionActive", XblMultiplayerManagerGameSessionActive_Lua); lua_register(Data()->L, "XblMultiplayerManagerGameSessionCorrelationId", XblMultiplayerManagerGameSessionCorrelationId_Lua); lua_register(Data()->L, "XblMultiplayerManagerGameSessionSessionReference", XblMultiplayerManagerGameSessionSessionReference_Lua); lua_register(Data()->L, "XblMultiplayerManagerGameSessionMembers", XblMultiplayerManagerGameSessionMembers_Lua); lua_register(Data()->L, "XblMultiplayerManagerGameSessionHost", XblMultiplayerManagerGameSessionHost_Lua); lua_register(Data()->L, "XblMultiplayerManagerGameSessionPropertiesJson", XblMultiplayerManagerGameSessionPropertiesJson_Lua); lua_register(Data()->L, "XblMultiplayerManagerGameSessionConstants", XblMultiplayerManagerGameSessionConstants_Lua); lua_register(Data()->L, "XblMultiplayerManagerGameSessionIsHost", XblMultiplayerManagerGameSessionIsHost_Lua); lua_register(Data()->L, "XblMultiplayerManagerGameSessionSetProperties", XblMultiplayerManagerGameSessionSetProperties_Lua); lua_register(Data()->L, "XblMultiplayerManagerGameSessionSetSynchronizedProperties", XblMultiplayerManagerGameSessionSetSynchronizedProperties_Lua); lua_register(Data()->L, "XblMultiplayerManagerGameSessionSetSynchronizedHost", XblMultiplayerManagerGameSessionSetSynchronizedHost_Lua); lua_register(Data()->L, "XblMultiplayerManagerJoinLobby", XblMultiplayerManagerJoinLobby_lua); lua_register(Data()->L, "XblMultiplayerManagerJoinLobbyViaActivity", XblMultiplayerManagerJoinLobbyViaActivity_Lua); lua_register(Data()->L, "XblMultiplayerManagerJoinGameFromLobby", XblMultiplayerManagerJoinGameFromLobby_Lua); lua_register(Data()->L, "XblMultiplayerManagerJoinGame", XblMultiplayerManagerJoinGame_Lua); lua_register(Data()->L, "XblMultiplayerManagerLeaveGame", XblMultiplayerManagerLeaveGame_Lua); lua_register(Data()->L, "XblMultiplayerManagerFindMatch", XblMultiplayerManagerFindMatch_Lua); lua_register(Data()->L, "XblMultiplayerManagerCancelMatch", XblMultiplayerManagerCancelMatch_Lua); lua_register(Data()->L, "XblMultiplayerManagerMatchStatus", XblMultiplayerManagerMatchStatus_Lua); lua_register(Data()->L, "XblMultiplayerManagerEstimatedMatchWaitTime", XblMultiplayerManagerEstimatedMatchWaitTime_Lua); lua_register(Data()->L, "XblMultiplayerManagerAutoFillMembersDuringMatchmaking", XblMultiplayerManagerAutoFillMembersDuringMatchmaking_Lua); lua_register(Data()->L, "XblMultiplayerManagerSetAutoFillMembersDuringMatchmaking", XblMultiplayerManagerSetAutoFillMembersDuringMatchmaking_Lua); lua_register(Data()->L, "XblMultiplayerManagerSetQosMeasurements", XblMultiplayerManagerSetQosMeasurements_Lua); lua_register(Data()->L, "XblMultiplayerManagerJoinability", XblMultiplayerManagerJoinability_Lua); lua_register(Data()->L, "XblMultiplayerManagerSetJoinability", XblMultiplayerManagerSetJoinability_Lua); lua_register(Data()->L, "VerifyMPMGameSessionProperites", VerifyMPMGameSessionProperites_Lua); lua_register(Data()->L, "VerifyMPMLobbySessionProperites", VerifyMPMLobbySessionProperites_Lua); } #if HC_PLATFORM_IS_MICROSOFT #pragma warning( pop ) #endif ================================================ FILE: Tests/ApiExplorer/APIs/apis_xblc_presence.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" XBL_WARNING_DISABLE_DEPRECATED static struct PresenceState { PresenceState() = default; ~PresenceState() { assert(!presenceRecord); assert(!devicePresenceChangeSubscription); assert(!titlePresenceChangeSubscription); assert(!devicePresenceChangedHandlerToken); assert(!titlePresenceChangedHandlerToken); } XblPresenceRecordHandle presenceRecord{ nullptr }; XblRealTimeActivitySubscriptionHandle devicePresenceChangeSubscription{ nullptr }; XblRealTimeActivitySubscriptionHandle titlePresenceChangeSubscription{ nullptr }; XblFunctionContext devicePresenceChangedHandlerToken{ 0 }; XblFunctionContext titlePresenceChangedHandlerToken{ 0 }; } state; int XblPresenceRecordGetXuid_Lua(lua_State *L) { // CODE SNIPPET START: XblPresenceRecordGetXuid uint64_t xuid; HRESULT hr = XblPresenceRecordGetXuid(state.presenceRecord, &xuid); // CODE SNIPPET END LogToScreen("XblPresenceRecordGetXuid: hr=%s, xuid=%llu", ConvertHR(hr).c_str(), static_cast(xuid)); return LuaReturnHR(L, hr); } int XblPresenceRecordGetUserState_Lua(lua_State *L) { // CODE SNIPPET START: XblPresenceRecordGetXuid XblPresenceUserState userState{ XblPresenceUserState::Unknown }; HRESULT hr = XblPresenceRecordGetUserState(state.presenceRecord, &userState); // CODE SNIPPET END LogToScreen("XblPresenceRecordGetUserState: hr=%s, state=%u", ConvertHR(hr).c_str(), userState); return LuaReturnHR(L, hr); } int XblPresenceRecordGetDeviceRecords_Lua(lua_State *L) { // CODE SNIPPET START: XblPresenceRecordGetDeviceRecords_C const XblPresenceDeviceRecord* deviceRecords{ nullptr }; size_t deviceRecordsCount{ 0 }; HRESULT hr = XblPresenceRecordGetDeviceRecords(state.presenceRecord, &deviceRecords, &deviceRecordsCount); for (auto i = 0u; i < deviceRecordsCount; ++i) { auto& deviceRecord{ deviceRecords[i] }; LogToScreen("Got XblDeviceRecord with device type %u and %u title records", deviceRecord.deviceType, deviceRecord.titleRecordsCount); for (auto j = 0u; j < deviceRecord.titleRecordsCount; ++j) { auto& titleRecord{ deviceRecord.titleRecords[j] }; // Display rich presence string LogToScreen("Rich presence string for titleId %u: %s", titleRecord.titleId, titleRecord.richPresenceString); } } // CODE SNIPPET END LogToFile("XblPresenceRecordGetDeviceRecords: hr=%s", ConvertHR(hr).data()); return LuaReturnHR(L, hr); } int XblPresenceRecordCloseHandle_Lua(lua_State *L) { // CODE SNIPPET START: XblPresenceRecordCloseHandle_C XblPresenceRecordCloseHandle(state.presenceRecord); state.presenceRecord = nullptr; // CODE SNIPPET END return LuaReturnHR(L, S_OK); } int XblPresenceSetPresenceAsync_Lua(lua_State *L) { // CODE SNIPPET START: XblPresenceSetPresenceAsync_C auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->context = nullptr; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* HRESULT hr = XAsyncGetStatus(asyncBlock, false); // CODE SKIP START if (hr == HTTP_E_STATUS_429_TOO_MANY_REQUESTS) { LogToFile("XblPresenceSetPresence returned 429. Ignoring failure"); hr = S_OK; } CallLuaFunctionWithHr(hr, "OnXblPresenceSetPresenceAsync"); // CODE SKIP END }; std::vector tokens{ "0" }; XblPresenceRichPresenceIds ids { {}, "PuzzlesPresence", tokens.data(), tokens.size() }; pal::strcpy(ids.scid, sizeof(ids.scid), Data()->scid); HRESULT hr = XblPresenceSetPresenceAsync(Data()->xboxLiveContext, true, &ids, asyncBlock.get()); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } // CODE SNIPPET END LogToFile("XblPresenceSetPresenceAsync: hr=%s", ConvertHR(hr).data()); return LuaReturnHR(L, hr); } int XblPresenceGetPresenceAsync_Lua(lua_State *L) { // CODE SNIPPET START: XblPresenceGetPresenceAsync_C auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->context = nullptr; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* assert(state.presenceRecord == nullptr); // CODE SNIP SKIP HRESULT hr = XblPresenceGetPresenceResult(asyncBlock, &state.presenceRecord); // Be sure to call XblPresenceRecordCloseHandle when the presence record is no longer needed. LogToFile("XblPresenceGetPresenceResult hr=%s", ConvertHR(hr).data()); // CODE SNIP SKIP CallLuaFunctionWithHr(hr, "OnXblPresenceGetPresenceAsync"); // CODE SNIP SKIP }; uint64_t xuid = Data()->xboxUserId; if (Data()->m_multiDeviceManager->GetRemoteXuid() != 0) { xuid = Data()->m_multiDeviceManager->GetRemoteXuid(); } HRESULT hr = XblPresenceGetPresenceAsync(Data()->xboxLiveContext, xuid, asyncBlock.get()); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } // CODE SNIPPET END LogToFile("XblPresenceGetPresenceAsync: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblPresenceGetPresenceForMultipleUsersAsync_Lua(lua_State* L) { // CODE SNIPPET START: XblPresenceGetPresenceForMultipleUsersAsync_C auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->context = nullptr; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* size_t resultCount{ 0 }; HRESULT hr = XblPresenceGetPresenceForMultipleUsersResultCount(asyncBlock, &resultCount); if (SUCCEEDED(hr) && resultCount > 0) { std::vector handles(resultCount, nullptr); hr = XblPresenceGetPresenceForMultipleUsersResult(asyncBlock, handles.data(), resultCount); // Be sure to call XblPresenceRecordCloseHandle for each presence record when they are // no longer needed. // CODE SKIP START LogToFile("XblPresenceGetPresenceForMultipleUsersResult hr=%s", ConvertHR(hr).data()); if (SUCCEEDED(hr)) { for (auto i = 0u; i < resultCount; ++i) { XblPresenceRecordCloseHandle(handles[i]); } } // CODE SKIP END } CallLuaFunctionWithHr(hr, "OnXblPresenceGetPresenceForMultipleUsersAsync"); // CODE SNIP SKIP }; std::vector xuids{ Data()->xboxUserId }; // Filter results to only online users XblPresenceQueryFilters filters{}; filters.onlineOnly = true; filters.detailLevel = XblPresenceDetailLevel::All; HRESULT hr = XblPresenceGetPresenceForMultipleUsersAsync(Data()->xboxLiveContext, xuids.data(), xuids.size(), &filters, asyncBlock.get()); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } // CODE SNIPPET END LogToFile("XblPresenceGetPresenceForMultipleUsersAsync: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblPresenceGetPresenceForSocialGroupAsync_Lua(lua_State *L) { // CODE SNIPPET START: XblPresenceGetPresenceForSocialGroupAsync_C auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->context = nullptr; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* size_t resultCount{ 0 }; HRESULT hr = XblPresenceGetPresenceForSocialGroupResultCount(asyncBlock, &resultCount); if (SUCCEEDED(hr) && resultCount > 0) { std::vector handles(resultCount, nullptr); hr = XblPresenceGetPresenceForSocialGroupResult(asyncBlock, handles.data(), resultCount); // Be sure to call XblPresenceRecordCloseHandle for each presence record when they are // no longer needed. // CODE SKIP START LogToFile("XblPresenceGetPresenceForSocialGroupResult hr=%s", ConvertHR(hr).data()); if (SUCCEEDED(hr)) { for (auto i = 0u; i < resultCount; ++i) { XblPresenceRecordCloseHandle(handles[i]); } } // CODE SKIP END } CallLuaFunctionWithHr(hr, "OnXblPresenceGetPresenceForSocialGroupAsync"); // CODE SNIP SKIP }; HRESULT hr = XblPresenceGetPresenceForSocialGroupAsync(Data()->xboxLiveContext, "Favorites", nullptr, nullptr, asyncBlock.get()); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } // CODE SNIPPET END LogToFile("XblPresenceGetPresenceForSocialGroupAsync: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblPresenceSubscribeToDevicePresenceChange_Lua(lua_State* L) { // CODE SNIPPET START: XblPresenceSubscribeToDevicePresenceChange_C uint64_t xuid{ 2814639011617876 }; HRESULT hr = XblPresenceSubscribeToDevicePresenceChange( Data()->xboxLiveContext, xuid, &state.devicePresenceChangeSubscription ); // CODE SNIPPET END LogToFile("XblPresenceSubscribeToDevicePresenceChange: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblPresenceUnsubscribeFromDevicePresenceChange_Lua(lua_State* L) { // CODE SNIPPET START: XblPresenceUnsubscribeFromDevicePresenceChange_C HRESULT hr = XblPresenceUnsubscribeFromDevicePresenceChange( Data()->xboxLiveContext, state.devicePresenceChangeSubscription ); state.devicePresenceChangeSubscription = nullptr; // CODE SNIPPET END LogToFile("XblPresenceUnsubscribeFromDevicePresenceChange: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblPresenceSubscribeToTitlePresenceChange_Lua(lua_State* L) { // CODE SNIPPET START: XblPresenceSubscribeToTitlePresenceChange_C uint64_t xuid{ 2814639011617876 }; HRESULT hr = XblPresenceSubscribeToTitlePresenceChange( Data()->xboxLiveContext, xuid, Data()->titleId, &state.titlePresenceChangeSubscription ); // CODE SNIPPET END LogToFile("XblPresenceSubscribeToTitlePresenceChange: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblPresenceUnsubscribeFromTitlePresenceChange_Lua(lua_State* L) { // CODE SNIPPET START: XblPresenceUnsubscribeFromTitlePresenceChange_C HRESULT hr = XblPresenceUnsubscribeFromTitlePresenceChange( Data()->xboxLiveContext, state.titlePresenceChangeSubscription ); state.titlePresenceChangeSubscription = nullptr; // CODE SNIPPET END LogToFile("XblPresenceUnsubscribeFromTitlePresenceChange: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblPresenceTrackUsers_Lua(lua_State* L) { auto xuid{ GetUint64FromLua(L, 1, 2814639011617876) }; // CODE SNIPPET START: XblPresenceTrackUsers_C HRESULT hr = XblPresenceTrackUsers( Data()->xboxLiveContext, &xuid, 1 ); // CODE SNIPPET END LogToFile("XblPresenceTrackUsers: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblPresenceStopTrackingUsers_Lua(lua_State* L) { auto xuid{ GetUint64FromLua(L, 1, 2814639011617876) }; // CODE SNIPPET START: XblPresenceStopTrackingUsers_C HRESULT hr = XblPresenceStopTrackingUsers( Data()->xboxLiveContext, &xuid, 1 ); // CODE SNIPPET END LogToFile("XblPresenceStopTrackingUsers: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblPresenceTrackAdditionalTitles_Lua(lua_State* L) { auto titleId{ GetUint32FromLua(L, 1, Data()->titleId) }; // CODE SNIPPET START: XblPresenceTrackTitles_C HRESULT hr = XblPresenceTrackAdditionalTitles( Data()->xboxLiveContext, &titleId, 1 ); // CODE SNIPPET END LogToFile("XblPresenceTrackTitles: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblPresenceStopTrackingAdditionalTitles_Lua(lua_State* L) { auto titleId{ GetUint32FromLua(L, 1, Data()->titleId) }; // CODE SNIPPET START: XblPresenceStopTrackingTitles_C HRESULT hr = XblPresenceStopTrackingAdditionalTitles( Data()->xboxLiveContext, &titleId, 1 ); // CODE SNIPPET END LogToFile("XblPresenceStopTrackingTitles: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblPresenceAddDevicePresenceChangedHandler_Lua(lua_State* L) { // CODE SNIPPET START: XblPresenceAddDevicePresenceChangedHandler_C state.devicePresenceChangedHandlerToken = XblPresenceAddDevicePresenceChangedHandler( Data()->xboxLiveContext, [](void* context, uint64_t xuid, XblPresenceDeviceType deviceType, bool isUserLoggedOnDevice) { UNREFERENCED_PARAMETER(context); LogToFile("Device presence change notification received:"); LogToFile("Xuid = %u, deviceType = %u, isUserLoggedOnDevice = %u", xuid, deviceType, isUserLoggedOnDevice); CallLuaFunction("OnDevicePresenceChanged"); // CODE SNIP SKIP }, nullptr ); // CODE SNIPPET END LogToFile("XblPresenceAddDevicePresenceChangedHandler"); return LuaReturnHR(L, S_OK); } int XblPresenceRemoveDevicePresenceChangedHandler_Lua(lua_State* L) { // CODE SNIPPET START: XblPresenceRemoveDevicePresenceChangedHandler_C HRESULT hr = XblPresenceRemoveDevicePresenceChangedHandler( Data()->xboxLiveContext, state.devicePresenceChangedHandlerToken ); state.devicePresenceChangedHandlerToken = 0; // CODE SNIPPET END LogToFile("XblPresenceRemoveDevicePresenceChangedHandler: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblPresenceAddTitlePresenceChangedHandler_Lua(lua_State* L) { // CODE SNIPPET START: XblPresenceAddTitlePresenceChangedHandler_C state.titlePresenceChangedHandlerToken = XblPresenceAddTitlePresenceChangedHandler( Data()->xboxLiveContext, [](void* context, uint64_t xuid, uint32_t titleId, XblPresenceTitleState titleState) { UNREFERENCED_PARAMETER(context); LogToFile("Title presence change notification received:"); LogToFile("Xuid = %u, titleId = %u, titleState = %u", xuid, titleId, titleState); CallLuaFunction("OnTitlePresenceChanged"); // CODE SNIP SKIP }, nullptr ); // CODE SNIPPET END LogToFile("XblPresenceAddTitlePresenceChangedHandler"); return LuaReturnHR(L, S_OK); } int XblPresenceRemoveTitlePresenceChangedHandler_Lua(lua_State* L) { // CODE SNIPPET START: XblPresenceRemoveTitlePresenceChangedHandler_C HRESULT hr = XblPresenceRemoveTitlePresenceChangedHandler( Data()->xboxLiveContext, state.titlePresenceChangedHandlerToken ); state.titlePresenceChangedHandlerToken = 0; // CODE SNIPPET END LogToFile("XblPresenceRemoveTitlePresenceChangedHandler: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } void SetupAPIs_XblPresence() { lua_register(Data()->L, "XblPresenceRecordGetXuid", XblPresenceRecordGetXuid_Lua); lua_register(Data()->L, "XblPresenceRecordGetUserState", XblPresenceRecordGetUserState_Lua); lua_register(Data()->L, "XblPresenceRecordGetDeviceRecords", XblPresenceRecordGetDeviceRecords_Lua); lua_register(Data()->L, "XblPresenceRecordCloseHandle", XblPresenceRecordCloseHandle_Lua); lua_register(Data()->L, "XblPresenceSetPresenceAsync", XblPresenceSetPresenceAsync_Lua); lua_register(Data()->L, "XblPresenceGetPresenceAsync", XblPresenceGetPresenceAsync_Lua); lua_register(Data()->L, "XblPresenceGetPresenceForSocialGroupAsync", XblPresenceGetPresenceForSocialGroupAsync_Lua); lua_register(Data()->L, "XblPresenceGetPresenceForMultipleUsersAsync", XblPresenceGetPresenceForMultipleUsersAsync_Lua); lua_register(Data()->L, "XblPresenceSubscribeToDevicePresenceChange", XblPresenceSubscribeToDevicePresenceChange_Lua); lua_register(Data()->L, "XblPresenceUnsubscribeFromDevicePresenceChange", XblPresenceUnsubscribeFromDevicePresenceChange_Lua); lua_register(Data()->L, "XblPresenceSubscribeToTitlePresenceChange", XblPresenceSubscribeToTitlePresenceChange_Lua); lua_register(Data()->L, "XblPresenceUnsubscribeFromTitlePresenceChange", XblPresenceUnsubscribeFromTitlePresenceChange_Lua); lua_register(Data()->L, "XblPresenceTrackUsers", XblPresenceTrackUsers_Lua); lua_register(Data()->L, "XblPresenceStopTrackingUsers", XblPresenceStopTrackingUsers_Lua); lua_register(Data()->L, "XblPresenceTrackAdditionalTitles", XblPresenceTrackAdditionalTitles_Lua); lua_register(Data()->L, "XblPresenceStopTrackingAdditionalTitles", XblPresenceStopTrackingAdditionalTitles_Lua); lua_register(Data()->L, "XblPresenceAddDevicePresenceChangedHandler", XblPresenceAddDevicePresenceChangedHandler_Lua); lua_register(Data()->L, "XblPresenceRemoveDevicePresenceChangedHandler", XblPresenceRemoveDevicePresenceChangedHandler_Lua); lua_register(Data()->L, "XblPresenceAddTitlePresenceChangedHandler", XblPresenceAddTitlePresenceChangedHandler_Lua); lua_register(Data()->L, "XblPresenceRemoveTitlePresenceChangedHandler", XblPresenceRemoveTitlePresenceChangedHandler_Lua); } ================================================ FILE: Tests/ApiExplorer/APIs/apis_xblc_privacy.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #ifndef _countof #define _countof(array) (sizeof(array) / sizeof(array[0])) #endif int XblPrivacyGetAvoidListAsync_Lua(lua_State* L) { CreateQueueIfNeeded(); // CODE SNIPPET START: XblPrivacyGetAvoidListAsync auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* size_t resultCount{}; HRESULT hr = XblPrivacyGetAvoidListResultCount(asyncBlock, &resultCount); if (SUCCEEDED(hr)) { std::vector avoidedXuids(resultCount); hr = XblPrivacyGetAvoidListResult(asyncBlock, resultCount, avoidedXuids.data()); } LogToFile("XblPrivacyGetAvoidListResult: hr=%s avoided xuids count=%d", ConvertHR(hr).c_str(), resultCount); // CODE SNIP SKIP CallLuaFunctionWithHr(hr, "OnXblPrivacyGetAvoidListAsync"); // CODE SNIP SKIP }; HRESULT hr = XblPrivacyGetAvoidListAsync(Data()->xboxLiveContext, asyncBlock.get()); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } // CODE SNIPPET END LogToFile("XblPrivacyGetAvoidListAsync: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblPrivacyCheckPermissionAsync_Lua(lua_State* L) { CreateQueueIfNeeded(); XblPermission permissionToCheck{ static_cast(GetUint32FromLua(L, 1, (uint32_t)XblPermission::ViewTargetProfile)) }; uint64_t targetXuid{ GetUint64FromLua(L, 2, 2743710844428572) }; LogToFile("XblPrivacyCheckPermissionAsync: permissionToCheck = %d, targetXuid = %llu", permissionToCheck, static_cast(targetXuid)); // CODE SNIPPET START: XblPrivacyCheckPermissionAsync auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* size_t resultSize; HRESULT hr = XblPrivacyCheckPermissionResultSize(asyncBlock, &resultSize); if (SUCCEEDED(hr)) { std::vector buffer(resultSize, 0); XblPermissionCheckResult* result{}; hr = XblPrivacyCheckPermissionResult(asyncBlock, resultSize, buffer.data(), &result, nullptr); if (SUCCEEDED(hr)) // CODE SNIP SKIP { // CODE SNIP SKIP LogToFile("XblPrivacyCheckPermissionResult: hr=%s isAllowed=%d", ConvertHR(hr).c_str(), result->isAllowed); // CODE SNIP SKIP } // CODE SNIP SKIP } if (FAILED(hr)) // CODE SNIP SKIP { // CODE SNIP SKIP LogToFile("XblPrivacyCheckPermissionResult: hr=%s", ConvertHR(hr).c_str()); // CODE SNIP SKIP } // CODE SNIP SKIP CallLuaFunctionWithHr(hr, "OnXblPrivacyCheckPermissionAsync"); // CODE SNIP SKIP }; HRESULT hr = XblPrivacyCheckPermissionAsync(Data()->xboxLiveContext, permissionToCheck, targetXuid, asyncBlock.get()); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } // CODE SNIPPET END LogToFile("XblPrivacyGetAvoidListAsync: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblPrivacyCheckPermissionForAnonymousUserAsync_Lua(lua_State* L) { CreateQueueIfNeeded(); XblPermission permissionToCheck{ static_cast(GetUint32FromLua(L, 1, (uint32_t)XblPermission::CommunicateUsingText)) }; XblAnonymousUserType userType{ static_cast(GetUint32FromLua(L, 2, (uint32_t)XblAnonymousUserType::CrossNetworkUser)) }; LogToFile("XblPrivacyCheckPermissionForAnonymousUserAsync: permissionToCheck = %d, target = %d", permissionToCheck, userType); // CODE SNIPPET START: XblPrivacyCheckPermissionForAnonymousUserAsync auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* size_t resultSize; HRESULT hr = XblPrivacyCheckPermissionForAnonymousUserResultSize(asyncBlock, &resultSize); if (SUCCEEDED(hr)) { std::vector buffer(resultSize, 0); XblPermissionCheckResult* result{}; hr = XblPrivacyCheckPermissionForAnonymousUserResult(asyncBlock, resultSize, buffer.data(), &result, nullptr); if (SUCCEEDED(hr)) // CODE SNIP SKIP { // CODE SNIP SKIP LogToFile("XblPrivacyCheckPermissionResult: hr=%s isAllowed=%d", ConvertHR(hr).c_str(), result->isAllowed); // CODE SNIP SKIP } // CODE SNIP SKIP } if (FAILED(hr)) // CODE SNIP SKIP { // CODE SNIP SKIP LogToFile("XblPrivacyCheckPermissionResult: hr=%s", ConvertHR(hr).c_str()); // CODE SNIP SKIP } // CODE SNIP SKIP CallLuaFunctionWithHr(hr, "OnXblPrivacyCheckPermissionForAnonymousUserAsync"); // CODE SNIP SKIP }; HRESULT hr = XblPrivacyCheckPermissionForAnonymousUserAsync(Data()->xboxLiveContext, permissionToCheck, userType, asyncBlock.get()); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } // CODE SNIPPET END LogToFile("XblPrivacyGetAvoidListAsync: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblPrivacyBatchCheckPermissionAsync_Lua(lua_State* L) { CreateQueueIfNeeded(); // Only using default values for params, very difficult to parse variable arrays from lua XblPermission permissionsToCheck[] { XblPermission::CommunicateUsingText, XblPermission::CommunicateUsingVideo, XblPermission::CommunicateUsingVoice, XblPermission::ViewTargetProfile, XblPermission::ViewTargetGameHistory, XblPermission::ViewTargetVideoHistory, XblPermission::ViewTargetMusicHistory, XblPermission::ViewTargetExerciseInfo, XblPermission::ViewTargetPresence, XblPermission::ViewTargetVideoStatus, XblPermission::ViewTargetMusicStatus, XblPermission::PlayMultiplayer, XblPermission::ViewTargetUserCreatedContent, XblPermission::BroadcastWithTwitch, XblPermission::WriteComment, XblPermission::ShareItem, XblPermission::ShareTargetContentToExternalNetworks }; uint64_t targetXuids[] { 2743710844428572, 2533274819720636 }; XblAnonymousUserType targetUserTypes[]{ XblAnonymousUserType::CrossNetworkUser, XblAnonymousUserType::CrossNetworkFriend }; size_t expectedResultCount{ _countof(permissionsToCheck) * (_countof(targetXuids) + _countof(targetUserTypes)) }; // CODE SNIPPET START: XblPrivacyBatchCheckPermissionAsync auto asyncBlock = std::make_unique(); auto contextPtr = std::make_unique(expectedResultCount); asyncBlock->queue = Data()->queue; asyncBlock->context = contextPtr.get(); asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* std::unique_ptr expectedCount{ static_cast(asyncBlock->context) }; size_t resultSize; HRESULT hr = XblPrivacyBatchCheckPermissionResultSize(asyncBlock, &resultSize); if (SUCCEEDED(hr)) { std::vector buffer(resultSize, 0); size_t resultCount{}; XblPermissionCheckResult* results{}; hr = XblPrivacyBatchCheckPermissionResult(asyncBlock, resultSize, buffer.data(), &results, &resultCount, nullptr); if (SUCCEEDED(hr)) { assert(resultCount == *expectedCount); } } LogToFile("XblPrivacyBatchCheckPermissionResult: hr=%s", ConvertHR(hr).c_str()); // CODE SNIP SKIP CallLuaFunctionWithHr(hr, "OnXblPrivacyBatchCheckPermissionAsync"); // CODE SNIP SKIP }; HRESULT hr = XblPrivacyBatchCheckPermissionAsync( Data()->xboxLiveContext, permissionsToCheck, _countof(permissionsToCheck), targetXuids, _countof(targetXuids), targetUserTypes, _countof(targetUserTypes), asyncBlock.get() ); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); contextPtr.release(); } // CODE SNIPPET END LogToFile("XblPrivacyBatchCheckPermissionAsync: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } void SetupAPIs_XblPrivacy() { lua_register(Data()->L, "XblPrivacyGetAvoidListAsync", XblPrivacyGetAvoidListAsync_Lua); lua_register(Data()->L, "XblPrivacyCheckPermissionAsync", XblPrivacyCheckPermissionAsync_Lua); lua_register(Data()->L, "XblPrivacyCheckPermissionForAnonymousUserAsync", XblPrivacyCheckPermissionForAnonymousUserAsync_Lua); lua_register(Data()->L, "XblPrivacyBatchCheckPermissionAsync", XblPrivacyBatchCheckPermissionAsync_Lua); } ================================================ FILE: Tests/ApiExplorer/APIs/apis_xblc_profile.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" int XblProfileGetUserProfileAsync_Lua(lua_State *L) { CreateQueueIfNeeded(); // CODE SNIPPET START: XblProfileGetUserProfileAsync auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->context = nullptr; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* XblUserProfile profile = { 0 }; HRESULT hr = XblProfileGetUserProfileResult(asyncBlock, &profile); LogToFile("XblProfileGetUserProfileResult: hr=%s gamertag=%s", ConvertHR(hr).c_str(), profile.gamertag); // CODE SNIP SKIP CallLuaFunctionWithHr(hr, "OnXblProfileGetUserProfileAsync"); // CODE SNIP SKIP }; HRESULT hr = XblProfileGetUserProfileAsync(Data()->xboxLiveContext, Data()->xboxUserId, asyncBlock.get()); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } // CODE SNIPPET END LogToFile("XblProfileGetUserProfileAsync: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblProfileGetUserProfilesAsync_Lua(lua_State *L) { CreateQueueIfNeeded(); // CODE SNIPPET START: XblProfileGetUserProfilesAsync auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->context = nullptr; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* uint32_t profilesCount = 1; XblUserProfile profiles[1] = { 0 }; HRESULT hr = XblProfileGetUserProfilesResult(asyncBlock, profilesCount, profiles); LogToFile("XblProfileGetUserProfilesResult: hr=%s gamertag=%s", ConvertHR(hr).c_str(), profiles[0].gamertag); // CODE SNIP SKIP CallLuaFunctionWithHr(hr, "OnXblProfileGetUserProfilesAsync"); // CODE SNIP SKIP }; uint64_t xboxUserIds[1]; xboxUserIds[0] = Data()->xboxUserId; size_t xboxUserIdsCount = 1; HRESULT hr = XblProfileGetUserProfilesAsync(Data()->xboxLiveContext, xboxUserIds, xboxUserIdsCount, asyncBlock.get()); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } // CODE SNIPPET END LogToFile("XblProfileGetUserProfilesAsync: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblProfileGetUserProfilesForSocialGroupAsync_Lua(lua_State *L) { CreateQueueIfNeeded(); auto socialGroup = GetStringFromLua(L, 1, "People"); // CODE SNIPPET START: XblProfileGetUserProfilesForSocialGroupAsync auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->context = nullptr; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* size_t profilesCount = 0; HRESULT hr = XblProfileGetUserProfilesForSocialGroupResultCount(asyncBlock, &profilesCount); if (SUCCEEDED(hr) && profilesCount > 0) { std::vector profiles(profilesCount); hr = XblProfileGetUserProfilesForSocialGroupResult(asyncBlock, profilesCount, profiles.data()); LogToFile("XblProfileGetUserProfilesForSocialGroupResult: hr=%s profilesCount=%d gamertag=%s", ConvertHR(hr).c_str(), profilesCount, profiles[0].gamertag); // CODE SNIP SKIP } else if (hr == HTTP_E_STATUS_429_TOO_MANY_REQUESTS) { CallLuaFunctionWithHr(S_OK, "OnXblProfileGetUserProfilesForSocialGroupAsyncRetry"); return; } CallLuaFunctionWithHr(hr, "OnXblProfileGetUserProfilesForSocialGroupAsync"); // CODE SNIP SKIP }; HRESULT hr = XblProfileGetUserProfilesForSocialGroupAsync(Data()->xboxLiveContext, socialGroup.c_str(), asyncBlock.get()); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } // CODE SNIPPET END LogToFile("XblProfileGetUserProfilesForSocialGroupAsync: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } void SetupAPIs_XblProfile() { lua_register(Data()->L, "XblProfileGetUserProfileAsync", XblProfileGetUserProfileAsync_Lua); lua_register(Data()->L, "XblProfileGetUserProfilesAsync", XblProfileGetUserProfilesAsync_Lua); lua_register(Data()->L, "XblProfileGetUserProfilesForSocialGroupAsync", XblProfileGetUserProfilesForSocialGroupAsync_Lua); } ================================================ FILE: Tests/ApiExplorer/APIs/apis_xblc_real_time_activity.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" XBL_WARNING_DISABLE_DEPRECATED static XblFunctionContext s_connectionStateHandlerContext{ 0 }; static XblFunctionContext s_subscriptionErrorHandlerContext{ 0 }; static XblFunctionContext s_resyncHandlerContext{ 0 }; int XblRealTimeActivityActivate_Lua(lua_State *L) { // CODE SNIPPET START: XblRealTimeActivityActivate HRESULT hr = XblRealTimeActivityActivate(Data()->xboxLiveContext); // CODE SNIPPET END LogToFile("XblRealTimeActivityActivate: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblRealTimeActivityDeactivate_Lua(lua_State *L) { // CODE SNIPPET START: XblRealTimeActivityDeactivate HRESULT hr = XblRealTimeActivityDeactivate(Data()->xboxLiveContext); // CODE SNIPPET END LogToFile("XblRealTimeActivityDeactivate: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblRealTimeActivityAddConnectionStateChangeHandler_Lua(lua_State *L) { // CODE SNIPPET START: XblRealTimeActivityAddConnectionStateChangeHandler s_connectionStateHandlerContext = XblRealTimeActivityAddConnectionStateChangeHandler(Data()->xboxLiveContext, [](void *context, XblRealTimeActivityConnectionState connectionState) { UNREFERENCED_PARAMETER(context); // CODE SNIP SKIP LogToFile("XblRealTimeActivityConnectionState changed to %d", connectionState); // Handle connection state change switch (connectionState) { case XblRealTimeActivityConnectionState::Connected: // Handle connected state LogToFile("XblRealTimeActivityAddConnectionStateChangeHandler: Connected\n"); // CODE SNIP SKIP CallLuaFunction("OnXblRealTimeActivityAddConnectionStateChangeHandler_Connected"); // CODE SNIP SKIP break; case XblRealTimeActivityConnectionState::Connecting: // Handle connecting state LogToFile("XblRealTimeActivityAddConnectionStateChangeHandler: Connecting\n"); // CODE SNIP SKIP CallLuaFunction("OnXblRealTimeActivityAddConnectionStateChangeHandler_Connecting"); // CODE SNIP SKIP break; case XblRealTimeActivityConnectionState::Disconnected: // Handle disconnected state LogToFile("XblRealTimeActivityAddConnectionStateChangeHandler: Disconnected\n"); // CODE SNIP SKIP CallLuaFunction("OnXblRealTimeActivityAddConnectionStateChangeHandler_Disconnected"); // CODE SNIP SKIP break; } }, nullptr); // CODE SNIPPET END LogToFile("XblRealTimeActivityAddConnectionStateChangeHandler complete"); return LuaReturnHR(L, S_OK); } int XblRealTimeActivityRemoveConnectionStateChangeHandler_Lua(lua_State *L) { // CODE SNIPPET START: XblRealTimeActivityAddConnectionStateChangeHandler if (s_connectionStateHandlerContext != 0) { XblRealTimeActivityRemoveConnectionStateChangeHandler(Data()->xboxLiveContext, s_connectionStateHandlerContext); } // CODE SNIPPET END LogToFile("XblRealTimeActivityRemoveConnectionStateChangeHandler complete"); return LuaReturnHR(L, S_OK); } int XblRealTimeActivityAddSubscriptionErrorHandler_Lua(lua_State *L) { // CODE SNIPPET START: XblRealTimeActivityAddSubscriptionErrorHandler s_subscriptionErrorHandlerContext = XblRealTimeActivityAddSubscriptionErrorHandler(Data()->xboxLiveContext, [](void* context, _In_ XblRealTimeActivitySubscriptionHandle subscription, HRESULT subscriptionError) { UNREFERENCED_PARAMETER(context); // CODE SNIP SKIP UNREFERENCED_PARAMETER(subscription); // CODE SNIP SKIP // Handle subscription error LogToFile("Rta subscription error %s", ConvertHR(subscriptionError).c_str()); }, nullptr); // CODE SNIPPET END LogToFile("XblRealTimeActivityAddSubscriptionErrorHandler complete"); return LuaReturnHR(L, S_OK); } int XblRealTimeActivityRemoveSubscriptionErrorHandler_Lua(lua_State *L) { // CODE SNIPPET START: XblRealTimeActivityRemoveConnectionStateChangeHandler if (s_subscriptionErrorHandlerContext != 0) { XblRealTimeActivityRemoveConnectionStateChangeHandler(Data()->xboxLiveContext, s_subscriptionErrorHandlerContext); } // CODE SNIPPET END LogToFile("XblRealTimeActivityRemoveConnectionStateChangeHandler complete"); return LuaReturnHR(L, S_OK); } int XblRealTimeActivityAddResyncHandler_Lua(lua_State *L) { // CODE SNIPPET START: XblRealTimeActivityAddResyncHandler s_resyncHandlerContext = XblRealTimeActivityAddResyncHandler(Data()->xboxLiveContext, [](void* context) { UNREFERENCED_PARAMETER(context); // CODE SNIP SKIP // Handle resync LogToFile("XblResyncHandler called"); }, nullptr); // CODE SNIPPET END LogToFile("XblRealTimeActivityAddResyncHandler complete"); return LuaReturnHR(L, S_OK); } int XblRealTimeActivityRemoveResyncHandler_Lua(lua_State *L) { // CODE SNIPPET START: XblRealTimeActivityRemoveConnectionStateChangeHandler if (s_resyncHandlerContext != 0) { XblRealTimeActivityRemoveResyncHandler(Data()->xboxLiveContext, s_resyncHandlerContext); } // CODE SNIPPET END LogToFile("XblRealTimeActivityRemoveResyncHandler complete"); return LuaReturnHR(L, S_OK); } int XblRealTimeActivitySubscriptionGetState_Lua(lua_State *L) { XblRealTimeActivitySubscriptionHandle rtaSubscriptionHandle{ nullptr }; if (Data()->statisticChangeSubscriptionHandle) { rtaSubscriptionHandle = Data()->statisticChangeSubscriptionHandle; } if (rtaSubscriptionHandle) { // CODE SNIPPET START: XblRealTimeActivitySubscriptionGetState XblRealTimeActivitySubscriptionState state; XblRealTimeActivitySubscriptionGetState(Data()->statisticChangeSubscriptionHandle, &state); // CODE SNIPPET END } LogToFile("XblRealTimeActivitySubscriptionGetState complete"); return LuaReturnHR(L, S_OK); } int XblRealTimeActivitySubscriptionGetId_Lua(lua_State *L) { XblRealTimeActivitySubscriptionHandle rtaSubscriptionHandle{ nullptr }; if (Data()->statisticChangeSubscriptionHandle) { rtaSubscriptionHandle = Data()->statisticChangeSubscriptionHandle; } if (rtaSubscriptionHandle) { // CODE SNIPPET START: XblRealTimeActivitySubscriptionGetId uint32_t subscriptionId; XblRealTimeActivitySubscriptionGetId(rtaSubscriptionHandle, &subscriptionId); // CODE SNIPPET END } LogToFile("XblRealTimeActivitySubscriptionGetId complete"); return LuaReturnHR(L, S_OK); } // Declare test hook HRESULT XblTestHooksTriggerRTAResync(); int XblTestHooksTriggerRTAResync_Lua(lua_State *L) { HRESULT hr = S_OK; #if HC_PLATFORM != HC_PLATFORM_IOS // For some reason XCode complaining about link errors with this test hook. Disabling on iOS until that can be investigated hr = XblTestHooksTriggerRTAResync(); #endif return LuaReturnHR(L, hr); } #if !XSAPI_NO_PPL && HC_PLATFORM_IS_MICROSOFT && HC_PLATFORM != HC_PLATFORM_GDK #include "combaseapi.h" #include "xsapi-cpp/services.h" struct RealTimeActivityState { std::shared_ptr xblContext; std::shared_ptr session; }; std::unique_ptr g_multiplayerState; RealTimeActivityState* RTAState() { if (g_multiplayerState == nullptr) { g_multiplayerState = std::make_unique(); } return g_multiplayerState.get(); } int XblRtaMultiplayerInit_Lua(lua_State *L) { using namespace xbox::services; using namespace xbox::services::multiplayer; stringstream_t stream; stream << Data()->scid; string_t SCID = stream.str(); string_t SessionTemplate = L"GameSession"; GUID guid; CoCreateGuid(&guid); OLECHAR* guidString; StringFromCLSID(guid, &guidString); string_t SessionName = guidString; SessionName = SessionName.substr(1, SessionName.length() - 2); stringstream_t xuidStream; xuidStream << Data()->xboxUserId; auto context = RTAState(); std::shared_ptr xblContext = std::make_shared(Data()->xalUser); context->xblContext = xblContext; auto multiplayerSessionReference = std::make_shared(SCID, SessionTemplate, SessionName); context->session = std::make_shared(xuidStream.str(), *multiplayerSessionReference); auto session = context->session; xblContext->multiplayer_service().enable_multiplayer_subscriptions(); xblContext->multiplayer_service().add_multiplayer_connection_id_changed_handler([]() { LogToFile("add_multiplayer_connection_id_changed_handler"); auto context = RTAState(); auto xblContext = context->xblContext; auto session = context->session; LogToFile("updating mpsd connection id"); xblContext->multiplayer_service().get_current_session(session->session_reference()) .then([context](xbox_live_result> result) { if (result.err()) { LogToFile("you've been kicked"); } else if (auto session{ result.payload() }) { context->session = session; context->session->set_current_user_status(multiplayer_session_member_status::active); context->xblContext->multiplayer_service().write_session(context->session, multiplayer_session_write_mode::update_or_create_new) .then([context](xbox_live_result> result2) { if (result2.err()) { LogToFile("you've been kicked 2"); } else { context->session = result2.payload(); } }); } }); }); int count = 0; LogToFile("creating the session"); session->join(); session->set_current_user_member_custom_property_json(L"count", web::json::value(count)); xblContext->multiplayer_service().write_session(session, multiplayer_session_write_mode::update_or_create_new) .then([context](xbox_live_result> result) { if (result.err()) { LogToFile("get_current_session err"); } else { context->session = result.payload(); } }); return LuaReturnHR(L, S_OK); } int XblRtaMultiplayerOnConnected_Lua(lua_State *L) { return LuaReturnHR(L, S_OK); } #endif //!XSAPI_NO_PPL && HC_PLATFORM_IS_MICROSOFT void SetupAPIs_XblRta() { lua_register(Data()->L, "XblRealTimeActivityActivate", XblRealTimeActivityActivate_Lua); lua_register(Data()->L, "XblRealTimeActivityDeactivate", XblRealTimeActivityDeactivate_Lua); lua_register(Data()->L, "XblRealTimeActivityAddConnectionStateChangeHandler", XblRealTimeActivityAddConnectionStateChangeHandler_Lua); lua_register(Data()->L, "XblRealTimeActivityRemoveConnectionStateChangeHandler", XblRealTimeActivityRemoveConnectionStateChangeHandler_Lua); lua_register(Data()->L, "XblRealTimeActivityAddSubscriptionErrorHandler", XblRealTimeActivityAddSubscriptionErrorHandler_Lua); lua_register(Data()->L, "XblRealTimeActivityRemoveSubscriptionErrorHandler", XblRealTimeActivityRemoveSubscriptionErrorHandler_Lua); lua_register(Data()->L, "XblRealTimeActivityAddResyncHandler", XblRealTimeActivityAddResyncHandler_Lua); lua_register(Data()->L, "XblRealTimeActivityRemoveResyncHandler", XblRealTimeActivityRemoveResyncHandler_Lua); lua_register(Data()->L, "XblRealTimeActivitySubscriptionGetState", XblRealTimeActivitySubscriptionGetState_Lua); lua_register(Data()->L, "XblRealTimeActivitySubscriptionGetId", XblRealTimeActivitySubscriptionGetId_Lua); lua_register(Data()->L, "XblTestHooksTriggerRTAResync", XblTestHooksTriggerRTAResync_Lua); #if !XSAPI_NO_PPL && HC_PLATFORM_IS_MICROSOFT && HC_PLATFORM != HC_PLATFORM_GDK lua_register(Data()->L, "XblRtaMultiplayerInit", XblRtaMultiplayerInit_Lua); lua_register(Data()->L, "XblRtaMultiplayerOnConnected", XblRtaMultiplayerOnConnected_Lua); #endif } ================================================ FILE: Tests/ApiExplorer/APIs/apis_xblc_social.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" XBL_WARNING_DISABLE_DEPRECATED static struct SocialState { SocialState() = default; ~SocialState() { // Validate that our tests cleaned up properly assert(!socialResultHandle); assert(!socialSubscriptionHandle); assert(!socialRelationshipChangedHandlerToken); } XblSocialRelationshipResultHandle socialResultHandle{ nullptr }; XblRealTimeActivitySubscriptionHandle socialSubscriptionHandle{ nullptr }; XblFunctionContext socialRelationshipChangedHandlerToken{ 0 }; XblFunctionContext friendRequestCountChangedHandlerToken{ 0 }; } state; XblSocialRelationshipFilter ConvertStringToXblSocialRelationshipFilter(const char* str) { XblSocialRelationshipFilter filter = XblSocialRelationshipFilter::All; if (pal::stricmp(str, "XblSocialRelationshipFilter::All") == 0) filter = XblSocialRelationshipFilter::All; else if (pal::stricmp(str, "XblSocialRelationshipFilter::Favorite") == 0) filter = XblSocialRelationshipFilter::Favorite; else if (pal::stricmp(str, "XblSocialRelationshipFilter::LegacyXboxLiveFriends") == 0) filter = XblSocialRelationshipFilter::LegacyXboxLiveFriends; return filter; } int XblSocialGetSocialRelationshipsAsync_Lua(lua_State *L) { XblSocialRelationshipFilter socialRelationshipFilter = ConvertStringToXblSocialRelationshipFilter(GetStringFromLua(L, 1, "XblSocialRelationshipFilter::All").c_str()); // CODE SNIPPET START: XblSocialGetSocialRelationshipsAsync_C auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->context = nullptr; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* HRESULT hr = XblSocialGetSocialRelationshipsResult(asyncBlock, &state.socialResultHandle); // Be sure to call XblSocialRelationshipResultCloseHandle when the result object is no longer needed LogToFile("XblSocialGetSocialRelationshipsResult: hr=%s", ConvertHR(hr).c_str()); // CODE SNIP SKIP CallLuaFunctionWithHr(hr, "OnXblSocialGetSocialRelationshipsAsync"); // CODE SNIP SKIP }; HRESULT hr = XblSocialGetSocialRelationshipsAsync( Data()->xboxLiveContext, Data()->xboxUserId, socialRelationshipFilter, 0, 0, asyncBlock.get() ); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } // CODE SNIPPET END LogToFile("XblSocialGetSocialRelationshipsAsync: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblSocialRelationshipResultGetRelationships_Lua(lua_State *L) { // CODE SNIPPET START: XblSocialRelationshipResultGetRelationships_C const XblSocialRelationship* relationships = nullptr; size_t relationshipsCount = 0; HRESULT hr = XblSocialRelationshipResultGetRelationships(state.socialResultHandle, &relationships, &relationshipsCount); LogToFile("Got %u SocialRelationships:", relationshipsCount); for (size_t i = 0; i < relationshipsCount; ++i) { LogToFile("Xuid = %u, isFollowingCaller = %u", relationships[i].xboxUserId, relationships[i].isFollowingCaller); } // CODE SNIPPET END LogToFile("XblSocialRelationshipResultGetRelationships: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblSocialRelationshipResultHasNext_Lua(lua_State *L) { // CODE SNIPPET START: XblSocialRelationshipResultHasNext_C bool hasNext{ false }; HRESULT hr = XblSocialRelationshipResultHasNext(state.socialResultHandle, &hasNext); // CODE SNIPPET END LogToFile("XblSocialRelationshipResultHasNext: hr=%s hasNext=%s", ConvertHR(hr).c_str(), hasNext ? "true" : "false"); lua_pushnumber(L, (int)hasNext); return LuaReturnHR(L, hr, 1); } int XblSocialRelationshipResultGetNextAsync_Lua(lua_State *L) { // CODE SNIPPET START: XblSocialRelationshipResultGetNextAsync_C auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->context = nullptr; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* // Close handle to previous page of results if (state.socialResultHandle) { XblSocialRelationshipResultCloseHandle(state.socialResultHandle); } HRESULT hr = XblSocialRelationshipResultGetNextResult(asyncBlock, &state.socialResultHandle); LogToFile("XblSocialRelationshipResultGetNextResult: hr=%s", ConvertHR(hr).c_str()); // CODE SNIP SKIP CallLuaFunctionWithHr(hr, "OnXblSocialRelationshipResultGetNextAsync"); // CODE SNIP SKIP }; uint32_t maxItems = 100; HRESULT hr = XblSocialRelationshipResultGetNextAsync(Data()->xboxLiveContext, state.socialResultHandle, maxItems, asyncBlock.get()); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } // CODE SNIPPET END LogToFile("XblSocialRelationshipResultGetNextAsync: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblSocialRelationshipResultDuplicateHandle_Lua(lua_State *L) { // CODE SNIPPET START: XblSocialRelationshipResultDuplicateHandle_C XblSocialRelationshipResultHandle handle{}; XblSocialRelationshipResultDuplicateHandle(state.socialResultHandle, &handle); // CODE SNIPPET END XblSocialRelationshipResultCloseHandle(handle); LogToFile("XblSocialRelationshipResultDuplicateHandle"); return LuaReturnHR(L, S_OK); } int XblSocialRelationshipResultCloseHandle_Lua(lua_State *L) { // CODE SNIPPET START: XblSocialRelationshipResultCloseHandle_C XblSocialRelationshipResultCloseHandle(state.socialResultHandle); state.socialResultHandle = nullptr; // CODE SNIPPET END LogToFile("XblSocialRelationshipResultCloseHandle"); return LuaReturnHR(L, S_OK); } int XblSocialSubscribeToSocialRelationshipChange_Lua(lua_State *L) { // CODE SNIPPET START: XblSocialSubscribeToSocialRelationshipChange_C #if XSAPI_BUILT_FROM_SOURCE // CODE SNIP SKIP // TODO Remove when new GDK bins are generated HRESULT hr = XblSocialSubscribeToSocialRelationshipChange( Data()->xboxLiveContext, Data()->xboxUserId, &state.socialSubscriptionHandle ); // CODE SNIPPET END LogToFile("XblSocialSubscribeToSocialRelationshipChange: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); #else return LuaReturnHR(L, S_OK); #endif } int XblSocialUnsubscribeFromSocialRelationshipChange_Lua(lua_State *L) { // CODE SNIPPET START: XblSocialUnsubscribeFromSocialRelationshipChange_C #if XSAPI_BUILT_FROM_SOURCE // CODE SNIP SKIP // TODO Remove when new GDK bins are generated HRESULT hr = XblSocialUnsubscribeFromSocialRelationshipChange( Data()->xboxLiveContext, state.socialSubscriptionHandle ); state.socialSubscriptionHandle = nullptr; // CODE SNIPPET END LogToFile("XblSocialUnsubscribeFromSocialRelationshipChange: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); #else return LuaReturnHR(L, S_OK); #endif } int XblSocialAddSocialRelationshipChangedHandler_Lua(lua_State *L) { // CODE SNIPPET START: XblSocialAddSocialRelationshipChangedHandler_C state.socialRelationshipChangedHandlerToken = XblSocialAddSocialRelationshipChangedHandler( Data()->xboxLiveContext, [](const XblSocialRelationshipChangeEventArgs* args, void* context) { UNREFERENCED_PARAMETER(context); LogToFile("Social relationship changed:"); std::stringstream ss; for (size_t i = 0; i < args->xboxUserIdsCount; ++i) { if (i > 0) { ss << ", "; } ss << args->xboxUserIds[i]; } LogToFile("socialNotification = %u, affectedXuids = %s", args->socialNotification, ss.str().data()); CallLuaFunction("OnSocialRelationshipChanged"); // CODE SNIP SKIP }, nullptr ); // CODE SNIPPET END LogToFile("XblSocialAddSocialRelationshipChangedHandler"); return LuaReturnHR(L, S_OK); } int XblSocialRemoveSocialRelationshipChangedHandler_Lua(lua_State *L) { // CODE SNIPPET START: XblSocialRemoveSocialRelationshipChangedHandler_C HRESULT hr = XblSocialRemoveSocialRelationshipChangedHandler(Data()->xboxLiveContext, state.socialRelationshipChangedHandlerToken); state.socialRelationshipChangedHandlerToken = 0; // CODE SNIPPET END LogToFile("XblSocialRemoveSocialRelationshipChangedHandler: hr=%s", ConvertHR(hr).data()); return LuaReturnHR(L, hr); } int XblSocialAddFriendRequestCountChangedHandler_Lua(lua_State* L) { // CODE SNIPPET START: XblSocialAddSocialRelationshipChangedHandler_C HRESULT hr = XblSocialAddFriendRequestCountChangedHandler( Data()->xboxLiveContext, [](const XblSocialFriendRequestCountChangedEventArgs* args, void* context) { UNREFERENCED_PARAMETER(context); LogToFile("Incoming friend request count changed: %u", args->incomingFriendRequestCount); CallLuaFunction("OnFriendRequestCountChanged"); // CODE SNIP SKIP }, nullptr, &state.friendRequestCountChangedHandlerToken ); // CODE SNIPPET END LogToFile("XblSocialAddFriendRequestCountChangedHandler: hr=%s", ConvertHR(hr).data()); return LuaReturnHR(L, hr); } int XblSocialRemoveFriendRequestCountChangedHandler_Lua(lua_State* L) { // CODE SNIPPET START: XblSocialRemoveSocialRelationshipChangedHandler_C HRESULT hr = XblSocialRemoveFriendRequestCountChangedHandler(Data()->xboxLiveContext, state.friendRequestCountChangedHandlerToken); state.friendRequestCountChangedHandlerToken = 0; // CODE SNIPPET END LogToFile("XblSocialRemoveFriendRequestCountChangedHandler: hr=%s", ConvertHR(hr).data()); return LuaReturnHR(L, hr); } int XblSocialSubmitReputationFeedbackAsync_Lua(lua_State* L) { // CODE SNIPPET START: XblSocialSubmitReputationFeedbackAsync_C auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* HRESULT hr = XAsyncGetStatus(asyncBlock, false); LogToFile("XblSocialSubmitReputationFeedbackAsync: hr=%s", ConvertHR(hr).c_str()); // CODE SNIP SKIP CallLuaFunctionWithHr(hr, "OnXblSocialSubmitReputationFeedbackAsync"); // CODE SNIP SKIP }; uint64_t xuid{ 2814639011617876 }; HRESULT hr = XblSocialSubmitReputationFeedbackAsync( Data()->xboxLiveContext, xuid, XblReputationFeedbackType::PositiveHelpfulPlayer, nullptr, "Helpful player", nullptr, asyncBlock.get() ); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } // CODE SNIPPET END return LuaReturnHR(L, hr); } int XblSocialSubmitBatchReputationFeedbackAsync_Lua(lua_State *L) { // CODE SNIPPET START: XblSocialSubmitBatchReputationFeedbackAsync_C std::vector feedbackItems; feedbackItems.push_back(XblReputationFeedbackItem { 2814639011617876, XblReputationFeedbackType::PositiveHelpfulPlayer, nullptr, "Helpful player", nullptr }); // Add any additional feedback items here auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* HRESULT hr = XAsyncGetStatus(asyncBlock, false); LogToFile("XblSocialSubmitBatchReputationFeedbackAsync: hr=%s", ConvertHR(hr).c_str()); // CODE SNIP SKIP CallLuaFunctionWithHr(hr, "OnXblSocialSubmitBatchReputationFeedbackAsync"); // CODE SNIP SKIP }; HRESULT hr = XblSocialSubmitBatchReputationFeedbackAsync( Data()->xboxLiveContext, feedbackItems.data(), feedbackItems.size(), asyncBlock.get() ); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } // CODE SNIPPET END return LuaReturnHR(L, hr); } int profile_service_get_user_profile_Lua(lua_State *L) { HRESULT hr = S_OK; #if XSAPI_BUILT_FROM_SOURCE && HC_PLATFORM != HC_PLATFORM_UWP && HC_PLATFORM != HC_PLATFORM_GDK std::shared_ptr xblc = std::make_shared(Data()->xalUser); xblc->profile_service().get_user_profile(L"1234") .then([](xbox::services::xbox_live_result result) { HRESULT hr = ConvertXboxLiveErrorCodeToHresult(result.err()); LogToFile("get_user_profile: hr=%s", ConvertHR(hr).c_str()); CallLuaFunctionWithHr(S_OK, "On_profile_service_get_user_profile"); }); #endif return LuaReturnHR(L, hr); } int UnsubscribeToTitleAndDevicePresenceChangeForFriends_Lua(lua_State *L) { HRESULT hr = S_OK; for (XblRealTimeActivitySubscriptionHandle subscriptionHandleDevice : Data()->subscriptionHandleDeviceList) { hr = XblPresenceUnsubscribeFromDevicePresenceChange( Data()->xboxLiveContext, subscriptionHandleDevice ); LuaStopTestIfFailed(hr); assert(SUCCEEDED(hr)); } for (XblRealTimeActivitySubscriptionHandle subscriptionHandleTitle : Data()->subscriptionHandleTitleList) { hr = XblPresenceUnsubscribeFromTitlePresenceChange( Data()->xboxLiveContext, subscriptionHandleTitle ); } Data()->subscriptionHandleTitleList.clear(); Data()->subscriptionHandleDeviceList.clear(); LogToScreen("UnsubscribeToTitleAndDevicePresenceChangeForFriends: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int SubscribeToTitleAndDevicePresenceChangeForFriends_Lua(lua_State *L) { auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->context = nullptr; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* HRESULT hr = XblSocialGetSocialRelationshipsResult(asyncBlock, &state.socialResultHandle); const XblSocialRelationship* relationships = nullptr; size_t relationshipsCount = 0; hr = XblSocialRelationshipResultGetRelationships(state.socialResultHandle, &relationships, &relationshipsCount); Data()->subscriptionHandleTitleList.clear(); Data()->subscriptionHandleDeviceList.clear(); XblRealTimeActivitySubscriptionHandle subscriptionHandleDevice; XblRealTimeActivitySubscriptionHandle subscriptionHandleTitle; LogToScreen("Got %u SocialRelationships:", relationshipsCount); for (size_t i = 0; i < relationshipsCount; ++i) { if (i % 100 == 0) { LogToScreen("Sub'ing to friend %d", i); } hr = XblPresenceSubscribeToDevicePresenceChange( Data()->xboxLiveContext, relationships[i].xboxUserId, &subscriptionHandleDevice); LuaStopTestIfFailed(hr); assert(SUCCEEDED(hr)); hr = XblPresenceSubscribeToTitlePresenceChange( Data()->xboxLiveContext, relationships[i].xboxUserId, Data()->titleId, &subscriptionHandleTitle); LuaStopTestIfFailed(hr); assert(SUCCEEDED(hr)); Data()->subscriptionHandleDeviceList.push_back(subscriptionHandleDevice); Data()->subscriptionHandleTitleList.push_back(subscriptionHandleTitle); } LogToScreen("SubscribeToTitleAndDevicePresenceChangeForFriends: hr=%s", ConvertHR(hr).c_str()); CallLuaFunctionWithHr(hr, "OnSubscribeToTitleAndDevicePresenceChangeForFriends"); }; HRESULT hr = XblSocialGetSocialRelationshipsAsync( Data()->xboxLiveContext, Data()->xboxUserId, XblSocialRelationshipFilter::All, 0, 0, asyncBlock.get() ); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } LogToFile("SubscribeToTitleAndDevicePresenceChangeForFriends: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } void SetupAPIs_XblSocial() { lua_register(Data()->L, "XblSocialGetSocialRelationshipsAsync", XblSocialGetSocialRelationshipsAsync_Lua); lua_register(Data()->L, "XblSocialRelationshipResultGetRelationships", XblSocialRelationshipResultGetRelationships_Lua); lua_register(Data()->L, "XblSocialRelationshipResultHasNext", XblSocialRelationshipResultHasNext_Lua); lua_register(Data()->L, "XblSocialRelationshipResultGetNextAsync", XblSocialRelationshipResultGetNextAsync_Lua); lua_register(Data()->L, "XblSocialRelationshipResultDuplicateHandle", XblSocialRelationshipResultDuplicateHandle_Lua); lua_register(Data()->L, "XblSocialRelationshipResultCloseHandle", XblSocialRelationshipResultCloseHandle_Lua); lua_register(Data()->L, "XblSocialSubscribeToSocialRelationshipChange", XblSocialSubscribeToSocialRelationshipChange_Lua); lua_register(Data()->L, "XblSocialUnsubscribeFromSocialRelationshipChange", XblSocialUnsubscribeFromSocialRelationshipChange_Lua); lua_register(Data()->L, "XblSocialAddSocialRelationshipChangedHandler", XblSocialAddSocialRelationshipChangedHandler_Lua); lua_register(Data()->L, "XblSocialRemoveSocialRelationshipChangedHandler", XblSocialRemoveSocialRelationshipChangedHandler_Lua); lua_register(Data()->L, "XblSocialAddFriendRequestCountChangedHandler", XblSocialAddFriendRequestCountChangedHandler_Lua); lua_register(Data()->L, "XblSocialRemoveFriendRequestCountChangedHandler", XblSocialRemoveFriendRequestCountChangedHandler_Lua); lua_register(Data()->L, "XblSocialSubmitReputationFeedbackAsync", XblSocialSubmitReputationFeedbackAsync_Lua); lua_register(Data()->L, "XblSocialSubmitBatchReputationFeedbackAsync", XblSocialSubmitBatchReputationFeedbackAsync_Lua); lua_register(Data()->L, "SubscribeToTitleAndDevicePresenceChangeForFriends", SubscribeToTitleAndDevicePresenceChangeForFriends_Lua); lua_register(Data()->L, "UnsubscribeToTitleAndDevicePresenceChangeForFriends", UnsubscribeToTitleAndDevicePresenceChangeForFriends_Lua); lua_register(Data()->L, "profile_service_get_user_profile", profile_service_get_user_profile_Lua); } ================================================ FILE: Tests/ApiExplorer/APIs/apis_xblc_social_manager.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include static struct SocialManagerState { // TODO move doWork logic into this state class SocialManagerState() = default; ~SocialManagerState() { // Validate that our tests cleaned up correctly assert(!doWork); assert(groups.empty()); } std::set groups; std::thread doWorkThread{}; std::atomic doWork{ false }; std::atomic doWorkJoinWhenDone{ false }; } state; HRESULT SocialManagerDoWork() { // CODE SNIPPET START: XblSocialManagerDoWork_C const XblSocialManagerEvent* events{ nullptr }; size_t eventCount{ 0 }; HRESULT hr = XblSocialManagerDoWork(&events, &eventCount); if (SUCCEEDED(hr)) { for (size_t i = 0; i < eventCount; i++) { // Act on the event auto& socialEvent = events[i]; // CODE SKIP START static std::unordered_map eventTypesMap { { XblSocialManagerEventType::UsersAddedToSocialGraph, "UsersAddedToSocialGraph" }, { XblSocialManagerEventType::UsersRemovedFromSocialGraph, "UsersRemovedFromSocialGraph" }, { XblSocialManagerEventType::PresenceChanged, "PresenceChanged" }, { XblSocialManagerEventType::ProfilesChanged, "ProfilesChanged" }, { XblSocialManagerEventType::SocialRelationshipsChanged, "SocialRelationshipsChanged" }, { XblSocialManagerEventType::LocalUserAdded, "LocalUserAdded" }, { XblSocialManagerEventType::SocialUserGroupLoaded, "SocialUserGroupLoaded" }, { XblSocialManagerEventType::SocialUserGroupUpdated, "SocialUserGroupUpdated" }, { XblSocialManagerEventType::UnknownEvent, "UnknownEvent" } }; // CODE SKIP END std::stringstream ss; ss << "XblSocialManagerDoWork: Event of type " << eventTypesMap[socialEvent.eventType] << std::endl; for (size_t j = 0; j < XBL_SOCIAL_MANAGER_MAX_AFFECTED_USERS_PER_EVENT; j++) { if (socialEvent.usersAffected[j] != nullptr) { if (j == 0) { ss << "Users affected: " << std::endl; } ss << "\t" << socialEvent.usersAffected[j]->gamertag << std::endl; } } LogToFile(ss.str().c_str()); // CODE SKIP START switch (socialEvent.eventType) { case XblSocialManagerEventType::UsersAddedToSocialGraph: LogToFile("XblSocialManagerDoWork: UsersAddedToSocialGraph event"); CallLuaFunctionWithHr(hr, "OnXblSocialManagerDoWork_UsersAddedToSocialGraphEvent"); break; case XblSocialManagerEventType::UsersRemovedFromSocialGraph: LogToFile("XblSocialManagerDoWork: UsersRemovedFromSocialGraph event"); CallLuaFunctionWithHr(hr, "OnXblSocialManagerDoWork_UsersRemovedFromSocialGraphEvent"); break; case XblSocialManagerEventType::PresenceChanged: CallLuaFunctionWithHr(hr, "OnXblSocialManagerDoWork_PresenceChangedEvent"); break; case XblSocialManagerEventType::ProfilesChanged: LogToFile("XblSocialManagerDoWork: ProfilesChanged event"); CallLuaFunctionWithHr(hr, "OnXblSocialManagerDoWork_ProfilesChangedEvent"); break; case XblSocialManagerEventType::SocialRelationshipsChanged: LogToFile("XblSocialManagerDoWork: SocialRelationshipsChanged event"); CallLuaFunctionWithHr(hr, "OnXblSocialManagerDoWork_SocialRelationshipsChangedEvent"); break; case XblSocialManagerEventType::LocalUserAdded: LogToFile("XblSocialManagerDoWork: LocalUserAdded event"); CallLuaFunctionWithHr(hr, "OnXblSocialManagerDoWork_LocalUserAddedEvent"); break; case XblSocialManagerEventType::SocialUserGroupLoaded: LogToFile("XblSocialManagerDoWork: SocialUserGroupLoaded event"); CallLuaFunctionWithHr(hr, "OnXblSocialManagerDoWork_SocialUserGroupLoadedEvent"); break; case XblSocialManagerEventType::SocialUserGroupUpdated: LogToFile("XblSocialManagerDoWork: SocialUserGroupUpdated event"); CallLuaFunctionWithHr(hr, "OnXblSocialManagerDoWork_SocialUserGroupUpdatedEvent"); break; case XblSocialManagerEventType::UnknownEvent: default: LogToFile("XblSocialManagerDoWork: Unknown event"); CallLuaFunctionWithHr(hr, "OnXblSocialManagerDoWork_UnknownEvent"); break; } // CODE SKIP END } } // CODE SNIPPET END return hr; } int StartSocialManagerDoWorkLoop_Lua(lua_State* L) { state.doWork = true; state.doWorkJoinWhenDone = true; std::lock_guard lock(Data()->m_luaLock); state.doWorkThread = std::thread([]() { Data()->m_socialDoWorkDone = false; while (state.doWork && !Data()->m_quit) { { std::lock_guard lock(Data()->m_luaLock); SocialManagerDoWork(); } pal::Sleep(10); } Data()->m_socialDoWorkDone = true; LogToFile("Exiting do work thread"); }); return LuaReturnHR(L, S_OK); } int StopSocialManagerDoWorkLoop_Lua(lua_State* L) { LogToFile("StopSocialManagerDoWorkLoop_Lua"); state.doWorkJoinWhenDone = true; state.doWork = false; #if ENABLE_PERF_PROFILING LogToFile(xbox::services::detail::PerfTester::Instance().FormatStats().data()); #endif return LuaReturnHR(L, S_OK); } void StopSocialManagerDoWorkHelper() { if (state.doWorkJoinWhenDone) { state.doWork = false; state.doWorkJoinWhenDone = false; state.doWorkThread.join(); } } XblSocialManagerExtraDetailLevel ConvertStringToXblSocialManagerExtraDetailLevel(const char* str) { XblSocialManagerExtraDetailLevel detailLevel = XblSocialManagerExtraDetailLevel::NoExtraDetail; if (pal::stricmp(str, "XblSocialManagerExtraDetailLevel::TitleHistoryLevel") == 0) detailLevel = XblSocialManagerExtraDetailLevel::TitleHistoryLevel; else if (pal::stricmp(str, "XblSocialManagerExtraDetailLevel::PreferredColorLevel") == 0) detailLevel = XblSocialManagerExtraDetailLevel::PreferredColorLevel; else if (pal::stricmp(str, "XblSocialManagerExtraDetailLevel::All") == 0) detailLevel = XblSocialManagerExtraDetailLevel::All; return detailLevel; } XblPresenceFilter ConvertStringToXblPresenceFilter(const std::string& filterString) { XblPresenceFilter filter = XblPresenceFilter::Unknown; auto str{ filterString.data() }; if (pal::stricmp(str, "XblPresenceFilter::TitleOnline") == 0) filter = XblPresenceFilter::TitleOnline; else if (pal::stricmp(str, "XblPresenceFilter::TitleOffline") == 0) filter = XblPresenceFilter::TitleOffline; #if XSAPI_BUILT_FROM_SOURCE else if (pal::stricmp(str, "XblPresenceFilter::TitleOnlineOutsideTitle") == 0) filter = XblPresenceFilter::TitleOnlineOutsideTitle; #endif else if (pal::stricmp(str, "XblPresenceFilter::AllOnline") == 0) filter = XblPresenceFilter::AllOnline; else if (pal::stricmp(str, "XblPresenceFilter::AllOffline") == 0) filter = XblPresenceFilter::AllOffline; else if (pal::stricmp(str, "XblPresenceFilter::AllTitle") == 0) filter = XblPresenceFilter::AllTitle; else if (pal::stricmp(str, "XblPresenceFilter::All") == 0) filter = XblPresenceFilter::All; return filter; } XblRelationshipFilter ConvertStringToXblRelationshipFilter(const std::string& filterString) { XblRelationshipFilter filter = XblRelationshipFilter::Friends; auto str{ filterString.data() }; if (pal::stricmp(str, "XblRelationshipFilter::Friends") == 0) filter = XblRelationshipFilter::Friends; else if (pal::stricmp(str, "XblRelationshipFilter::Favorite") == 0) filter = XblRelationshipFilter::Favorite; return filter; } // commands int XblSocialManagerPresenceRecordIsUserPlayingTitle_Lua(lua_State *L) { auto group{ reinterpret_cast(GetUint64FromLua(L, 1, reinterpret_cast(*state.groups.begin()))) }; uint32_t titleId = GetUint32FromLua(L, 1, 174925616); if (group == nullptr) { LogToFile("XblSocialManagerPresenceRecordIsUserPlayingTitle: No XblSocialManagerUserGroup Loaded"); return S_OK; } XblSocialManagerUserPtrArray users{ nullptr }; size_t count{ 0 }; HRESULT result = XblSocialManagerUserGroupGetUsers(group, &users, &count); bool playingTitle{ false }; if (SUCCEEDED(result) && count > 0) { XblSocialManagerPresenceRecord presenceRecord = users[0]->presenceRecord; // CODE SNIPPET START: XblSocialManagerPresenceRecordIsUserPlayingTitle playingTitle = XblSocialManagerPresenceRecordIsUserPlayingTitle(&presenceRecord, Data()->titleId); // CODE SNIPPET END LogToFile("XblSocialManagerPresenceRecordIsUserPlayingTitle: TitleId: %d, playing: %u", titleId, playingTitle); } LogToFile("XblSocialManagerPresenceRecordIsUserPlayingTitle: hr=%s", ConvertHR(result).c_str()); return LuaReturnHR(L, result); } int XblSocialManagerUserGroupGetType_Lua(lua_State* L) { auto group{ reinterpret_cast(GetUint64FromLua(L, 1, reinterpret_cast(*state.groups.begin()))) }; if (group == nullptr) { LogToFile("XblSocialManagerUserGroupGetUsers: No XblSocialManagerUserGroup Loaded"); return S_OK; } // CODE SNIPPET START: XblSocialManagerUserGroupGetType XblSocialUserGroupType type{ XblSocialUserGroupType::FilterType }; HRESULT hr = XblSocialManagerUserGroupGetType(group, &type); // CODE SNIPPET END LogToFile("XblSocialManagerUserGroupGetType: type=%u, hr=%s", type, ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblSocialManagerUserGroupGetLocalUser_Lua(lua_State* L) { auto group{ reinterpret_cast(GetUint64FromLua(L, 1, reinterpret_cast(*state.groups.begin()))) }; // CODE SNIPPET START: XblSocialManagerUserGroupGetLocalUser XblUserHandle user{ nullptr }; HRESULT hr = XblSocialManagerUserGroupGetLocalUser(group, &user); // CODE SNIPPET END LogToFile("XblSocialManagerUserGroupGetLocalUser: user=%llu, hr=%s", user, ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblSocialManagerUserGroupGetFilters_Lua(lua_State* L) { auto group{ reinterpret_cast(GetUint64FromLua(L, 1, reinterpret_cast(*state.groups.begin()))) }; // CODE SNIPPET START: XblSocialManagerUserGroupGetFilters XblPresenceFilter presenceFilter{ XblPresenceFilter::Unknown }; XblRelationshipFilter relationshipFilter{ XblRelationshipFilter::Unknown }; HRESULT hr = XblSocialManagerUserGroupGetFilters(group, &presenceFilter, &relationshipFilter); // CODE SNIPPET END LogToFile("XblSocialManagerUserGroupGetFilters: presenceFilter=%u, relationshipFilter=%u, hr=%s", presenceFilter, relationshipFilter, ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblSocialManagerUserGroupGetUsers_Lua(lua_State *L) { auto group{ reinterpret_cast(GetUint64FromLua(L, 1, reinterpret_cast(*state.groups.begin()))) }; if (group == nullptr) { LogToFile("XblSocialManagerUserGroupGetUsers: No XblSocialManagerUserGroup Loaded"); return S_OK; } // CODE SNIPPET START: XblSocialManagerUserGroupGetUsers_C XblSocialManagerUserPtrArray users{ nullptr }; size_t userCount{ 0 }; HRESULT hr = XblSocialManagerUserGroupGetUsers(group, &users, &userCount); LogToFile("XblSocialManagerUserGroupGetUsers: %s usersCount: %d", ConvertHR(hr).c_str(), userCount); // CODE SNIP SKIP for (size_t i = 0; i < userCount; ++i) { // Display user info etc. // CODE SKIP START LogToFile("\t%s", users[i]->gamertag); // CODE SKIP END } // CODE SNIPPET END return LuaReturnHR(L, hr); } int XblSocialManagerUserGroupGetUsersTrackedByGroup_Lua(lua_State *L) { auto group{ reinterpret_cast(GetUint64FromLua(L, 1, reinterpret_cast(*state.groups.begin()))) }; if (group == nullptr) { LogToFile("XblSocialManagerUserGroupGetUsersTrackedByGroup: No XblSocialManagerUserGroup Loaded"); return S_OK; } // CODE SNIPPET START: XblSocialManagerUserGroupGetUsersTrackedByGroup const uint64_t* xuids{ nullptr }; size_t count{ 0 }; HRESULT hr = XblSocialManagerUserGroupGetUsersTrackedByGroup( group, &xuids, &count ); // CODE SNIPPET END LogToFile("XblSocialManagerUserGroupGetUsersTrackedByGroup: %s trackedUsersCount: %d", ConvertHR(hr).c_str(), count); for (size_t i = 0; i < count; ++i) { LogToFile("\t%llu", static_cast(xuids[i])); } LogToFile("XblSocialManagerUserGroupGetUsersTrackedByGroup: %s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblSocialManagerAddLocalUser_Lua(lua_State *L) { XblSocialManagerExtraDetailLevel extraLevelDetail = ConvertStringToXblSocialManagerExtraDetailLevel( GetStringFromLua(L, 1, "XblSocialManagerExtraDetailLevel::NoExtraDetail").c_str()); LogToFile("XblSocialManagerAddLocalUser: ExtraLevelDetail: %d", extraLevelDetail); XalUserHandle user = Data()->xalUser; // CODE SNIPPET START: XblSocialManagerAddLocalUser HRESULT hr = XblSocialManagerAddLocalUser(user, extraLevelDetail, nullptr); // CODE SNIPPET END LogToFile("XblSocialManagerAddLocalUser: %s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblSocialManagerRemoveLocalUser_Lua(lua_State *L) { XalUserHandle user = Data()->xalUser; // CODE SNIPPET START: XblSocialManagerRemoveLocalUser_C HRESULT hr = XblSocialManagerRemoveLocalUser(user); // CODE SNIPPET END LogToFile("XblSocialManagerRemoveLocalUser: %s", ConvertHR(hr).c_str()); return LuaReturnHR(L, S_OK); } int XblSocialManagerCreateSocialUserGroupFromFilters_Lua(lua_State *L) { XalUserHandle user = Data()->xalUser; // CODE SNIPPET START: XblSocialManagerCreateSocialUserGroupFromFilters_C XblPresenceFilter presenceFilter{ XblPresenceFilter::All }; XblRelationshipFilter relationshipFilter{ XblRelationshipFilter::Friends }; // CODE SKIP START XblPresenceFilter presenceFilterArg = ConvertStringToXblPresenceFilter(GetStringFromLua(L, 1, std::string{})); if (presenceFilter != XblPresenceFilter::Unknown) { presenceFilter = presenceFilterArg; } XblRelationshipFilter relationshipFilterArg = ConvertStringToXblRelationshipFilter(GetStringFromLua(L, 2, std::string{})); if (relationshipFilterArg != XblRelationshipFilter::Unknown) { relationshipFilter = relationshipFilterArg; } LogToFile("XblSocialManagerCreateSocialUserGroupFromFilters: PresenceFilter: %d", presenceFilter); LogToFile("XblSocialManagerCreateSocialUserGroupFromFilters: RelationshipFilter: %d", relationshipFilter); // CODE SKIP END XblSocialManagerUserGroupHandle groupHandle{ nullptr }; HRESULT hr = XblSocialManagerCreateSocialUserGroupFromFilters(user, presenceFilter, relationshipFilter, &groupHandle); if (SUCCEEDED(hr)) { state.groups.insert(groupHandle); } // CODE SNIPPET END lua_pushinteger(L, reinterpret_cast(groupHandle)); LogToFile("XblSocialManagerCreateSocialUserGroupFromFilters: %s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr, 1); } int XblSocialManagerDestroySocialUserGroup_Lua(lua_State* L) { auto groupHandle{ reinterpret_cast(GetUint64FromLua(L, 1, reinterpret_cast(*state.groups.begin()))) }; // CODE SNIPPET START: XblSocialManagerDestroySocialUserGroup_C HRESULT hr = XblSocialManagerDestroySocialUserGroup(groupHandle); if (SUCCEEDED(hr)) { state.groups.erase(groupHandle); } // CODE SNIPPET END LogToFile("XblSocialManagerDestroySocialUserGroup: %s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } // Pool of XDKS.1 xuids to create social groups from std::vector listXuids { 2814639011617876,2814641789541994,2814644008675844,2814644210052185,2814645164579523,2814646075485729,2814649783195402,2814650260879943, 2814652370182940,2814652714045777,2814654391560620,2814654975417728,2814656000993855,2814660006763195,2814666715930430,2814667316080600, 2814669550092398,2814669684179632,2814669733667211,2814671180786692,2814679901432274,2814613501048225,2814614352529204,2814615856126401, 2814616641363830,2814617883586813,2814618053453081,2814629752527080,2814631255161151,2814632477267887,2814633284389038,2814635732495522, 2814635779785472,2814635974475208,2814636979708499,2814618092438397,2814618260480530,2814618319551907,2814619559360314,2814620368929739, 2814620769042115,2814621007349381,2814623088399025,2814623825448960,2814624220291971,2814624961587858,2814626394212372,2814626639518570, 2814628203722867,2814629143923154,2814614382301082,2814614959737919,2814615558140392,2814618401629514,2814618701087902,2814619300882392, 2814623785189962,2814623956387698,2814625066090704,2814625471782204,2814626946705530,2814627006318591,2814628046127456,2814631487749991, 2814631517599783,2814632798310691,2814633582140204,2814634204785789,2814634895412664,2814635439049207,2814638609354868,2814639589885754, 2814641670947751,2814643512602566,2814646137630843,2814648499394446,2814651465227139,2814652150012664,2814653926747608,2814655098938516, 2814655264861214,2814655417678099,2814655883565306,2814656031821923,2814656159501072,2814656780954834,2814660657970845,2814661604435490, 2814663444319727,2814663818015575,2814665274839967,2814667273133504,2814670761542037,2814672762886609,2814673772488023,2814674096344056, 2814674229538758,2814678943953289,2814680898042782 }; int XblSocialManagerCreateSocialUserGroupFromList_Lua(lua_State *L) { // Params: // 1) number of xuids to include in list // 2) offset in the above vector of first xuid auto count{ GetUint64FromLua(L, 1, 10) }; auto offset{ GetUint64FromLua(L, 2, 0) }; assert(offset + count <= listXuids.size()); XalUserHandle user = Data()->xalUser; // CODE SNIPPET START: XblSocialManagerCreateSocialUserGroupFromList_C std::vector xuids { listXuids.begin() + static_cast(offset), listXuids.begin() + static_cast(offset + count) }; XblSocialManagerUserGroupHandle groupHandle{ nullptr }; HRESULT hr = XblSocialManagerCreateSocialUserGroupFromList(user, xuids.data(), xuids.size(), &groupHandle); if (SUCCEEDED(hr)) { state.groups.insert(groupHandle); } // CODE SNIPPET END lua_pushinteger(L, reinterpret_cast(groupHandle)); LogToFile("XblSocialManagerCreateSocialUserGroupFromList: %s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr, 1); } int XblSocialManagerGetLocalUsers_Lua(lua_State *L) { // CODE SNIPPET START: XblSocialManagerGetLocalUsers size_t localUsersCount = XblSocialManagerGetLocalUserCount(); std::vector localUsers(localUsersCount, nullptr); HRESULT hr = XblSocialManagerGetLocalUsers(localUsersCount, &localUsers[0]); // CODE SNIPPET END LogToFile("XblSocialManagerGetLocalUsers: %s localUsersCount: %d", ConvertHR(hr).c_str(), localUsersCount); for (uint32_t i = 0; i < localUsersCount; i++) { size_t gamerTagSize = XalUserGetGamertagSize(localUsers[i], XalGamertagComponent_Classic); std::vector gamerTag(gamerTagSize, '\0'); size_t bufferUsed; hr = XalUserGetGamertag(localUsers[i], XalGamertagComponent_Classic, gamerTagSize, gamerTag.data(), &bufferUsed); if (SUCCEEDED(hr)) { LogToFile("\t%s", gamerTag.data()); } } LogToFile("XblSocialManagerGetLocalUsers: %s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblSocialManagerUpdateSocialUserGroup_Lua(lua_State *L) { // Params: // 1) group pointer // 1) number of xuids to include in list // 2) offset in the above vector of first xuid auto group{ reinterpret_cast(GetUint64FromLua(L, 1, reinterpret_cast(*state.groups.begin()))) }; auto count{ GetUint64FromLua(L, 2, 15) }; auto offset{ GetUint64FromLua(L, 3, 0) }; // CODE SNIPPET START: XblSocialManagerUpdateSocialUserGroup_C std::vector xuids { listXuids.begin() + static_cast(offset), listXuids.begin() + static_cast(offset + count) }; HRESULT hr = XblSocialManagerUpdateSocialUserGroup(group, xuids.data(), xuids.size()); // CODE SNIPPET END LogToFile("XblSocialManagerUpdateSocialUserGroup: %s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblSocialManagerSetRichPresencePollingStatus_Lua(lua_State *L) { bool shouldEnablePolling = GetBoolFromLua(L, 1, false); LogToFile("XblSocialManagerSetRichPresencePollingStatus: ShouldEnablePolling: %s", shouldEnablePolling ? "true" : "false"); XalUserHandle user = Data()->xalUser; // CODE SNIPPET START: XblSocialManagerSetRichPresencePollingStatus HRESULT hr = XblSocialManagerSetRichPresencePollingStatus(user, shouldEnablePolling); // CODE SNIPPET END LogToFile("XblSocialManagerSetRichPresencePollingStatus: %s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblSocialManagerDoWork_Lua(lua_State *L) { HRESULT hr = SocialManagerDoWork(); LogToFile("XblSocialManagerDoWork: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } void SetupAPIs_XblSocialManager() { // Non XSAPI APIs lua_register(Data()->L, "StartSocialManagerDoWorkLoop", StartSocialManagerDoWorkLoop_Lua); lua_register(Data()->L, "StopSocialManagerDoWorkLoop", StopSocialManagerDoWorkLoop_Lua); // XSAPI APIs lua_register(Data()->L, "XblSocialManagerPresenceRecordIsUserPlayingTitle", XblSocialManagerPresenceRecordIsUserPlayingTitle_Lua); lua_register(Data()->L, "XblSocialManagerUserGroupGetType", XblSocialManagerUserGroupGetType_Lua); lua_register(Data()->L, "XblSocialManagerUserGroupGetLocalUser", XblSocialManagerUserGroupGetLocalUser_Lua); lua_register(Data()->L, "XblSocialManagerUserGroupGetFilters", XblSocialManagerUserGroupGetFilters_Lua); lua_register(Data()->L, "XblSocialManagerUserGroupGetUsers", XblSocialManagerUserGroupGetUsers_Lua); lua_register(Data()->L, "XblSocialManagerUserGroupGetUsersTrackedByGroup", XblSocialManagerUserGroupGetUsersTrackedByGroup_Lua); lua_register(Data()->L, "XblSocialManagerAddLocalUser", XblSocialManagerAddLocalUser_Lua); lua_register(Data()->L, "XblSocialManagerRemoveLocalUser", XblSocialManagerRemoveLocalUser_Lua); lua_register(Data()->L, "XblSocialManagerCreateSocialUserGroupFromFilters", XblSocialManagerCreateSocialUserGroupFromFilters_Lua); lua_register(Data()->L, "XblSocialManagerCreateSocialUserGroupFromList", XblSocialManagerCreateSocialUserGroupFromList_Lua); lua_register(Data()->L, "XblSocialManagerGetLocalUsers", XblSocialManagerGetLocalUsers_Lua); lua_register(Data()->L, "XblSocialManagerUpdateSocialUserGroup", XblSocialManagerUpdateSocialUserGroup_Lua); lua_register(Data()->L, "XblSocialManagerSetRichPresencePollingStatus", XblSocialManagerSetRichPresencePollingStatus_Lua); lua_register(Data()->L, "XblSocialManagerDoWork", XblSocialManagerDoWork_Lua); lua_register(Data()->L, "XblSocialManagerDestroySocialUserGroup", XblSocialManagerDestroySocialUserGroup_Lua); } ================================================ FILE: Tests/ApiExplorer/APIs/apis_xblc_statistics.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" XBL_WARNING_DISABLE_DEPRECATED int GetLastStat_Lua(lua_State *L) { long long n = Data()->lastUserStat; lua_pushinteger(L, static_cast(n)); return 1; } int XblUserStatisticsGetSingleUserStatisticAsync_Lua(lua_State *L) { CreateQueueIfNeeded(); if (Data()->xboxUserId == 0) { return LuaReturnHR(L, E_FAIL); } auto statisticName = GetStringFromLua(L, 1, "TotalPuzzlesSolved"); LogToFile("XblUserStatisticsGetSingleUserStatisticAsync: statisticName: %s", statisticName.c_str()); // CODE SNIPPET START: XblUserStatisticsGetSingleUserStatisticAsync auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* size_t resultSize; HRESULT hr = XblUserStatisticsGetSingleUserStatisticResultSize(asyncBlock, &resultSize); if (SUCCEEDED(hr)) { std::vector buffer(resultSize, 0); XblUserStatisticsResult* result{}; hr = XblUserStatisticsGetSingleUserStatisticResult(asyncBlock, resultSize, buffer.data(), &result, nullptr); VERIFY_IS_TRUE(result != nullptr, "null");// CODE SNIP SKIP VERIFY_IS_TRUE(result->xboxUserId != 0, "xuid");// CODE SNIP SKIP VERIFY_IS_TRUE(result->serviceConfigStatisticsCount == 1, "serviceConfigStatisticsCount");// CODE SNIP SKIP VERIFY_IS_TRUE(result->serviceConfigStatistics[0].statisticsCount == 1, "statisticsCount");// CODE SNIP SKIP VERIFY_IS_TRUE(strcmp(result->serviceConfigStatistics[0].statistics[0].statisticType, "Integer")==0, "statisticType");// CODE SNIP SKIP if (SUCCEEDED(hr) && result->serviceConfigStatisticsCount > 0 && result->serviceConfigStatistics->statisticsCount > 0) { int64_t userStatValue = atoll(result->serviceConfigStatistics[0].statistics[0].value); // Now you can show or store userStatValue Data()->lastUserStat = userStatValue; // CODE SNIP SKIP LogToScreen("%s's stat %s is %lld. Note: With GDK, ensure fiddler isn't running for stat upload to work", // CODE SNIP SKIP Data()->gamertag.c_str(), // CODE SNIP SKIP result->serviceConfigStatistics[0].statistics[0].statisticName, // CODE SNIP SKIP Data()->lastUserStat); // CODE SNIP SKIP } } CallLuaFunctionWithHr(hr, "OnXblUserStatisticsGetSingleUserStatisticAsync"); // CODE SNIP SKIP }; HRESULT hr = XblUserStatisticsGetSingleUserStatisticAsync( Data()->xboxLiveContext, Data()->xboxUserId, Data()->scid, statisticName.c_str(), asyncBlock.get() ); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } // CODE SNIPPET END LogToFile("XblUserStatisticsGetSingleUserStatisticAsync: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblUserStatisticsGetSingleUserStatisticsAsync_Lua(lua_State *L) { CreateQueueIfNeeded(); auto statisticName1 = GetStringFromLua(L, 1, "TotalPuzzlesSolved"); auto statisticName2 = GetStringFromLua(L, 2, "TotalRoundsStarted"); LogToFile("XblUserStatisticsGetSingleUserStatisticsAsync: statisticName1: %s", statisticName1.c_str()); LogToFile("XblUserStatisticsGetSingleUserStatisticsAsync: statisticName2: %s", statisticName2.c_str()); // CODE SNIPPET START: XblUserStatisticsGetSingleUserStatisticsAsync auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* size_t resultSize; HRESULT hr = XblUserStatisticsGetSingleUserStatisticsResultSize(asyncBlock, &resultSize); if (SUCCEEDED(hr)) { std::vector buffer(resultSize, 0); XblUserStatisticsResult* results{}; hr = XblUserStatisticsGetSingleUserStatisticsResult(asyncBlock, resultSize, buffer.data(), &results, nullptr); if (SUCCEEDED(hr)) { // Now you can use the XblUserStatisticsResult array in results } } CallLuaFunctionWithHr(hr, "OnXblUserStatisticsGetSingleUserStatisticsAsync"); // CODE SNIP SKIP }; const char* statisticNames[2] = {}; statisticNames[0] = statisticName1.c_str(); statisticNames[1] = statisticName2.c_str(); HRESULT hr = XblUserStatisticsGetSingleUserStatisticsAsync( Data()->xboxLiveContext, Data()->xboxUserId, Data()->scid, statisticNames, 2, asyncBlock.get() ); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } // CODE SNIPPET END LogToFile("XblUserStatisticsGetSingleUserStatisticsAsync: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblUserStatisticsGetMultipleUserStatisticsAsync_Lua(lua_State *L) { CreateQueueIfNeeded(); auto statisticName1 = GetStringFromLua(L, 1, "TotalPuzzlesSolved"); auto statisticName2 = GetStringFromLua(L, 2, "TotalRoundsStarted"); LogToFile("XblUserStatisticsGetMultipleUserStatisticsAsync: statisticName1: %s", statisticName1.c_str()); LogToFile("XblUserStatisticsGetMultipleUserStatisticsAsync: statisticName2: %s", statisticName2.c_str()); auto xuid1 = GetUint64FromLua(L, 3, Data()->xboxUserId); auto xuid2 = GetUint64FromLua(L, 4, 2814634367189975); LogToFile("XblUserStatisticsGetMultipleUserStatisticsAsync: xuid1: %ul", xuid1); LogToFile("XblUserStatisticsGetMultipleUserStatisticsAsync: xuid2: %ul", xuid2); // CODE SNIPPET START: XblUserStatisticsGetMultipleUserStatisticsAsync auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* size_t resultSize; HRESULT hr = XblUserStatisticsGetMultipleUserStatisticsResultSize(asyncBlock, &resultSize); if (SUCCEEDED(hr)) { std::vector buffer(resultSize, 0); XblUserStatisticsResult* results{}; size_t resultsCount = 0; hr = XblUserStatisticsGetMultipleUserStatisticsResult(asyncBlock, resultSize, buffer.data(), &results, &resultsCount, nullptr); // Process results array to read the user stats data for (size_t iResult = 0; iResult < resultsCount; iResult++) { LogToFile("%d", results[iResult].xboxUserId); for (size_t iScid = 0; iScid < results[iResult].serviceConfigStatisticsCount; iScid++) { LogToFile("SCID: %s", results[iResult].serviceConfigStatistics[iScid].serviceConfigurationId); for (size_t iStat = 0; iStat < results[iResult].serviceConfigStatistics[iScid].statisticsCount; iStat++) { LogToFile("Stat %d: name:%s value:%s type:%s", iResult, results[iResult].serviceConfigStatistics[iScid].statistics[iStat].statisticName, results[iResult].serviceConfigStatistics[iScid].statistics[iStat].value, results[iResult].serviceConfigStatistics[iScid].statistics[iStat].statisticType ); } } } } CallLuaFunctionWithHr(hr, "OnXblUserStatisticsGetMultipleUserStatisticsAsync"); // CODE SNIP SKIP }; const char* statisticNames[2] = {}; statisticNames[0] = statisticName1.c_str(); statisticNames[1] = statisticName2.c_str(); uint64_t xuids[2] = {}; xuids[0] = xuid1; xuids[1] = xuid2; HRESULT hr = XblUserStatisticsGetMultipleUserStatisticsAsync( Data()->xboxLiveContext, xuids, 2, Data()->scid, statisticNames, 2, asyncBlock.get() ); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } // CODE SNIPPET END LogToFile("XblUserStatisticsGetMultipleUserStatisticsAsync: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblUserStatisticsGetMultipleUserStatisticsForMultipleServiceConfigurationsAsync_Lua(lua_State *L) { CreateQueueIfNeeded(); auto statisticName1 = GetStringFromLua(L, 1, "TotalPuzzlesSolved"); auto statisticName2 = GetStringFromLua(L, 2, "TotalRoundsStarted"); LogToFile("XblUserStatisticsGetMultipleUserStatisticsForMultipleServiceConfigurationsAsync: statisticName1: %s", statisticName1.c_str()); LogToFile("XblUserStatisticsGetMultipleUserStatisticsForMultipleServiceConfigurationsAsync: statisticName2: %s", statisticName2.c_str()); auto xuid1 = GetUint64FromLua(L, 3, Data()->xboxUserId); auto xuid2 = GetUint64FromLua(L, 4, 2814634367189975); LogToFile("XblUserStatisticsGetMultipleUserStatisticsForMultipleServiceConfigurationsAsync: xuid1: %ul", xuid1); LogToFile("XblUserStatisticsGetMultipleUserStatisticsForMultipleServiceConfigurationsAsync: xuid2: %ul", xuid2); // CODE SNIPPET START: XblUserStatisticsGetMultipleUserStatisticsForMultipleServiceConfigurationsAsync auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* size_t resultSize; HRESULT hr = XblUserStatisticsGetMultipleUserStatisticsForMultipleServiceConfigurationsResultSize(asyncBlock, &resultSize); if (SUCCEEDED(hr)) { std::vector buffer(resultSize, 0); XblUserStatisticsResult* results{}; size_t resultsCount = 0; hr = XblUserStatisticsGetMultipleUserStatisticsForMultipleServiceConfigurationsResult(asyncBlock, resultSize, buffer.data(), &results, &resultsCount, nullptr); LogToFile("XblUserStatisticsGetMultipleUserStatisticsForMultipleServiceConfigurationsResult: hr=%s count=%d", ConvertHR(hr).c_str(), resultsCount); // CODE SNIP SKIP if (SUCCEEDED(hr)) { for (size_t i = 0; i < resultsCount; i++) { // Log results std::stringstream stream; stream << "XUID: " << results[i].xboxUserId << std::endl;; for (size_t j = 0; j < results[i].serviceConfigStatisticsCount; j++) { stream << " " << results[i].serviceConfigStatistics[j].serviceConfigurationId << ": " << std::endl; for (size_t k = 0; k < results[i].serviceConfigStatistics[j].statisticsCount; k++) { stream << " " << results[i].serviceConfigStatistics[j].statistics[k].statisticName << "=" << results[i].serviceConfigStatistics[j].statistics[k].value << std::endl; } } LogToScreen(stream.str().c_str()); // CODE SNIP SKIP } } } CallLuaFunctionWithHr(hr, "OnXblUserStatisticsGetMultipleUserStatisticsForMultipleServiceConfigurationsAsync"); // CODE SNIP SKIP }; const char* requestedStatsNames1[2] = {}; requestedStatsNames1[0] = statisticName1.c_str(); requestedStatsNames1[1] = statisticName2.c_str(); XblRequestedStatistics requestedStats[1] = {}; pal::strcpy(requestedStats[0].serviceConfigurationId, sizeof(XblRequestedStatistics::serviceConfigurationId), Data()->scid); requestedStats[0].statistics = requestedStatsNames1; requestedStats[0].statisticsCount = 2; uint64_t xuids[2] = {}; xuids[0] = xuid1; xuids[1] = xuid2; HRESULT hr = XblUserStatisticsGetMultipleUserStatisticsForMultipleServiceConfigurationsAsync( Data()->xboxLiveContext, xuids, 2, requestedStats, 1, asyncBlock.get() ); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } // CODE SNIPPET END LogToFile("XblUserStatisticsGetMultipleUserStatisticsForMultipleServiceConfigurationsAsync: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblUserStatisticsTrackStatistics_Lua(lua_State *L) { CreateQueueIfNeeded(); if (Data()->xboxUserId == 0) { return LuaReturnHR(L, E_FAIL); } auto statisticName = GetStringFromLua(L, 1, "TotalPuzzlesSolved"); LogToFile("XblUserStatisticsTrackStatistics: statisticName: %s", statisticName.c_str()); std::vector statisticNames{ statisticName.data() }; // CODE SNIPPET START: XblUserStatisticsTrackStatistics HRESULT hr = XblUserStatisticsTrackStatistics( Data()->xboxLiveContext, &Data()->xboxUserId, 1, Data()->scid, statisticNames.data(), statisticNames.size() ); // CODE SNIPPET END LogToFile("XblUserStatisticsTrackStatistics: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblUserStatisticsStopTrackingStatistics_Lua(lua_State *L) { CreateQueueIfNeeded(); if (Data()->xboxUserId == 0) { return LuaReturnHR(L, E_FAIL); } auto statisticName = GetStringFromLua(L, 1, "TotalPuzzlesSolved"); LogToFile("XblUserStatisticsTrackStatistics: statisticName: %s", statisticName.c_str()); std::vector statisticNames{ statisticName.data() }; // CODE SNIPPET START: XblUserStatisticsStopTrackingStatistics HRESULT hr = XblUserStatisticsStopTrackingStatistics( Data()->xboxLiveContext, &Data()->xboxUserId, 1, Data()->scid, statisticNames.data(), statisticNames.size() ); // CODE SNIPPET END LogToFile("XblUserStatisticsStopTrackingStatistics: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblUserStatisticsStopTrackingUsers_Lua(lua_State* L) { CreateQueueIfNeeded(); if (Data()->xboxUserId == 0) { return LuaReturnHR(L, E_FAIL); } // CODE SNIPPET START: XblUserStatisticsStopTrackingStatistics HRESULT hr = XblUserStatisticsStopTrackingUsers( Data()->xboxLiveContext, &Data()->xboxUserId, 1 ); // CODE SNIPPET END LogToFile("XblUserStatisticsStopTrackingUsers: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblUserStatisticsSubscribeToStatisticChange_Lua(lua_State *L) { CreateQueueIfNeeded(); if (Data()->xboxUserId == 0) { return LuaReturnHR(L, E_FAIL); } if (Data()->statisticChangeSubscriptionHandle != nullptr) { return LuaReturnHR(L, E_FAIL); } auto statisticName = GetStringFromLua(L, 1, "TotalPuzzlesSolved"); LogToFile("XblUserStatisticsSubscribeToStatisticChange: statisticName: %s", statisticName.c_str()); // CODE SNIPPET START: XblUserStatisticsSubscribeToStatisticChange XblRealTimeActivitySubscriptionHandle subscriptionHandle{ nullptr }; HRESULT hr = XblUserStatisticsSubscribeToStatisticChange( Data()->xboxLiveContext, Data()->xboxUserId, Data()->scid, statisticName.c_str(), &subscriptionHandle ); // CODE SNIPPET END Data()->statisticChangeSubscriptionHandle = subscriptionHandle; LogToFile("XblUserStatisticsSubscribeToStatisticChange: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblUserStatisticsUnsubscribeFromStatisticChange_Lua(lua_State *L) { CreateQueueIfNeeded(); if (Data()->xboxUserId == 0) { return LuaReturnHR(L, E_FAIL); } HRESULT hr = S_OK; if (Data()->statisticChangeSubscriptionHandle != nullptr) { // CODE SNIPPET START: XblUserStatisticsUnsubscribeFromStatisticChange hr = XblUserStatisticsUnsubscribeFromStatisticChange( Data()->xboxLiveContext, Data()->statisticChangeSubscriptionHandle ); // CODE SNIPPET END Data()->statisticChangeSubscriptionHandle = nullptr; } LogToFile("XblUserStatisticsUnsubscribeFromStatisticChange: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblUserStatisticsAddStatisticChangedHandler_Lua(lua_State *L) { CreateQueueIfNeeded(); if (Data()->xboxUserId == 0) { return LuaReturnHR(L, E_FAIL); } // CODE SNIPPET START: XblUserStatisticsAddStatisticChangedHandler void* context{ nullptr }; XblFunctionContext statisticChangedFunctionContext = XblUserStatisticsAddStatisticChangedHandler( Data()->xboxLiveContext, [](XblStatisticChangeEventArgs eventArgs, void* context) { // Handle stat change LogToScreen("Statistic changed callback: stat changed (%s = %s)", eventArgs.latestStatistic.statisticName, eventArgs.latestStatistic.value); CallLuaFunctionWithHr(S_OK, "OnStatisticChangedHandler"); // CODE SNIP SKIP UNREFERENCED_PARAMETER(context); // CODE SNIP SKIP }, context ); // CODE SNIPPET END Data()->statisticChangedFunctionContext = statisticChangedFunctionContext; LogToFile("XblUserStatisticsAddStatisticChangedHandler: done"); return LuaReturnHR(L, S_OK); } int XblUserStatisticsRemoveStatisticChangedHandler_Lua(lua_State *L) { CreateQueueIfNeeded(); if (Data()->xboxUserId == 0) { return LuaReturnHR(L, E_FAIL); } // CODE SNIPPET START: XblUserStatisticsRemoveStatisticChangedHandler XblUserStatisticsRemoveStatisticChangedHandler( Data()->xboxLiveContext, Data()->statisticChangedFunctionContext ); // CODE SNIPPET END Data()->statisticChangedFunctionContext = 0; LogToFile("XblUserStatisticsRemoveStatisticChangedHandler: done"); return LuaReturnHR(L, S_OK); } void SetupAPIs_XblStatistics() { lua_register(Data()->L, "GetLastStat", GetLastStat_Lua); lua_register(Data()->L, "XblUserStatisticsGetSingleUserStatisticAsync", XblUserStatisticsGetSingleUserStatisticAsync_Lua); lua_register(Data()->L, "XblUserStatisticsGetSingleUserStatisticsAsync", XblUserStatisticsGetSingleUserStatisticsAsync_Lua); lua_register(Data()->L, "XblUserStatisticsGetMultipleUserStatisticsAsync", XblUserStatisticsGetMultipleUserStatisticsAsync_Lua); lua_register(Data()->L, "XblUserStatisticsGetMultipleUserStatisticsForMultipleServiceConfigurationsAsync", XblUserStatisticsGetMultipleUserStatisticsForMultipleServiceConfigurationsAsync_Lua); lua_register(Data()->L, "XblUserStatisticsTrackStatistics", XblUserStatisticsTrackStatistics_Lua); lua_register(Data()->L, "XblUserStatisticsStopTrackingStatistics", XblUserStatisticsStopTrackingStatistics_Lua); lua_register(Data()->L, "XblUserStatisticsStopTrackingUsers", XblUserStatisticsStopTrackingUsers_Lua); lua_register(Data()->L, "XblUserStatisticsSubscribeToStatisticChange", XblUserStatisticsSubscribeToStatisticChange_Lua); lua_register(Data()->L, "XblUserStatisticsUnsubscribeFromStatisticChange", XblUserStatisticsUnsubscribeFromStatisticChange_Lua); lua_register(Data()->L, "XblUserStatisticsAddStatisticChangedHandler", XblUserStatisticsAddStatisticChangedHandler_Lua); lua_register(Data()->L, "XblUserStatisticsRemoveStatisticChangedHandler", XblUserStatisticsRemoveStatisticChangedHandler_Lua); } ================================================ FILE: Tests/ApiExplorer/APIs/apis_xblc_stats2017.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #if HC_PLATFORM_IS_MICROSOFT #pragma warning( push ) #pragma warning( disable : 4365 ) #pragma warning( disable : 4061 ) #pragma warning( disable : 4996 ) #endif #include "rapidjson/writer.h" #include "rapidjson/stringbuffer.h" #include "rapidjson/document.h" #if HC_PLATFORM_IS_MICROSOFT #pragma warning( pop ) #endif using namespace utility; struct StatsValueDocument { public: StatsValueDocument(rapidjson::Document&& svd) noexcept : m_svd{ std::move(svd) } { assert(m_svd.HasMember("stats")); assert(m_svd["stats"].HasMember("title")); } StatsValueDocument(const StatsValueDocument& other) noexcept { m_svd.CopyFrom(other.m_svd, m_svd.GetAllocator()); } StatsValueDocument(StatsValueDocument&& other) noexcept : m_svd{ std::move(other.m_svd) } { } StatsValueDocument& operator=(const StatsValueDocument& other) noexcept { m_svd.CopyFrom(other.m_svd, m_svd.GetAllocator()); return *this; } StatsValueDocument() noexcept = default; ~StatsValueDocument() noexcept = default; bool operator==(const StatsValueDocument& other) const noexcept { return m_svd["stats"]["title"] == other.m_svd["stats"]["title"]; } std::vector Stats() const noexcept { std::vector stats; auto& titleJson{ m_svd["stats"]["title"] }; for (auto it = titleJson.MemberBegin(); it != titleJson.MemberEnd(); ++it) { auto& value{ it->value["value"] }; stats.push_back(XblTitleManagedStatistic { it->name.GetString(), value.IsString() ? XblTitleManagedStatType::String : XblTitleManagedStatType::Number, value.IsNumber() ? value.GetDouble() : 0, value.IsString() ? value.GetString() : nullptr }); } return stats; } std::string Serialize() const noexcept { rapidjson::StringBuffer buffer; rapidjson::Writer writer(buffer); m_svd["stats"]["title"].Accept(writer); return buffer.GetString(); } void SetStat(const std::string& name, const std::string& value) noexcept { SetStat(name, rapidjson::Value{ value.data(), m_svd.GetAllocator() }.Move()); } void SetStat(const std::string& name, double value) noexcept { SetStat(name, rapidjson::Value{ value }.Move()); } void DeleteStat(const std::string& name) noexcept { m_svd["stats"]["title"].EraseMember(name.data()); } void ClearStats() noexcept { m_svd["stats"]["title"] = rapidjson::Value{ rapidjson::kObjectType }; } private: void SetStat(const std::string& name, rapidjson::Value& value) noexcept { auto& titleJson{ m_svd["stats"]["title"] }; if (titleJson.HasMember(name.data())) { titleJson[name.data()]["value"] = value.Move(); } else { auto& a{ m_svd.GetAllocator() }; rapidjson::Value statJson{ rapidjson::kObjectType }; statJson.AddMember("value", value.Move(), a); titleJson.AddMember(rapidjson::Value{ name.data(), a }, statJson.Move(), a); } } rapidjson::Document m_svd{}; }; static std::unique_ptr s_svd{ nullptr }; StatsValueDocument GetStatsValueDocument(uint64_t xuid, HRESULT* resultHr) { XblHttpCallHandle call{}; std::stringstream uri; uri << "https://statsread.xboxlive.com/stats/users/" << xuid << "/scids/" << Data()->scid; HRESULT hr = XblHttpCallCreate(Data()->xboxLiveContext, "GET", uri.str().data(), &call); assert(SUCCEEDED(hr)); XAsyncBlock async{}; hr = XblHttpCallPerformAsync(call, XblHttpCallResponseBodyType::String, &async); assert(SUCCEEDED(hr)); hr = XAsyncGetStatus(&async, true); if (SUCCEEDED(hr)) { assert(SUCCEEDED(hr)); const char* responseString{ nullptr }; hr = XblHttpCallGetResponseString(call, &responseString); assert(SUCCEEDED(hr)); rapidjson::Document svd{}; svd.Parse(responseString); assert(!svd.HasParseError()); *resultHr = S_OK; return StatsValueDocument{ std::move(svd) }; } else { *resultHr = E_FAIL; return StatsValueDocument(); } } int XblTitleManagedStatsWriteAsyncWithSVD_Lua(lua_State *L) { if (!s_svd) { HRESULT hr = S_OK; s_svd = std::make_unique(GetStatsValueDocument(Data()->xboxUserId, &hr)); if (FAILED(hr)) { LogToFile("XblTitleManagedStatsWriteAsyncWithSVD: hr = %s", ConvertHR(hr).data()); s_svd = nullptr; CallLuaFunction("OnXblTitleManagedStatsUnableToGetTokenAndSignature"); return LuaReturnHR(L, S_OK); } } auto now = datetime::utc_now(); auto timeString = conversions::to_utf8string(now.to_string(datetime::date_format::ISO_8601)); s_svd->SetStat("TimeString", timeString.data()); s_svd->SetStat("NumericStat", 100.0); std::vector statList{ s_svd->Stats() }; auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->context = nullptr; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* HRESULT hr = XAsyncGetStatus(asyncBlock, false); if (hr == E_FAIL) { CallLuaFunction("OnXblTitleManagedStatsUnableToGetTokenAndSignature"); return; } CallLuaFunctionWithHr(hr, "OnXblTitleManagedStatsWriteAsyncWithSVD"); // CODE SNIP SKIP }; HRESULT hr = XblTitleManagedStatsWriteAsync( Data()->xboxLiveContext, Data()->xboxUserId, statList.data(), statList.size(), asyncBlock.get() ); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } LogToFile("XblTitleManagedStatsWriteAsyncWithSVD: hr = %s", ConvertHR(hr).data()); return LuaReturnHR(L, hr); } int XblTitleManagedStatsWriteAsync_Lua(lua_State *L) { static double s_stat1Value = 0.0; s_stat1Value++; // CODE SNIPPET START: XblTitleManagedStatsWriteAsync XblTitleManagedStatistic stat1{}; std::string stat1Name = "TestStat1"; stat1.statisticName = stat1Name.c_str(); stat1.statisticType = XblTitleManagedStatType::Number; stat1.numberValue = s_stat1Value; XblTitleManagedStatistic stat2{}; std::string stat2Name = "TestStat2"; std::string stat2Value = "TestValue1234"; stat2.statisticName = stat2Name.c_str(); stat2.statisticType = XblTitleManagedStatType::String; stat2.stringValue = stat2Value.c_str(); std::vector statList{ stat1, stat2 }; auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->context = nullptr; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* HRESULT hr = XAsyncGetStatus(asyncBlock, false); if (hr != HTTP_E_STATUS_429_TOO_MANY_REQUESTS) { LogToScreen("XblTitleManagedStatsWriteAsync: 0x%0.8x", hr); } if (hr == E_FAIL) { CallLuaFunction("OnXblTitleManagedStatsUnableToGetTokenAndSignature"); return; } CallLuaFunctionWithHr(hr, "OnXblTitleManagedStatsWriteAsync"); // CODE SNIP SKIP }; HRESULT hr = XblTitleManagedStatsWriteAsync( Data()->xboxLiveContext, Data()->xboxUserId, statList.data(), statList.size(), asyncBlock.get() ); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } // CODE SNIPPET END LogToFile("XblTitleManagedStatsWriteAsync: hr = %s", ConvertHR(hr).data()); return LuaReturnHR(L, hr); } int XblTitleManagedStatsUpdateStatsAsync_Lua(lua_State* L) { if (!s_svd) { HRESULT hr = S_OK; s_svd = std::make_unique(GetStatsValueDocument(Data()->xboxUserId, &hr)); if (FAILED(hr)) { LogToFile("XblTitleManagedStatsWriteAsyncWithSVD: hr = %s", ConvertHR(hr).data()); s_svd = nullptr; CallLuaFunction("OnXblTitleManagedStatsUnableToGetTokenAndSignature"); return LuaReturnHR(L, S_OK); } } auto timeString = conversions::to_utf8string(datetime::utc_now().to_string(datetime::date_format::ISO_8601)); s_svd->SetStat("AddedStat",timeString.data()); s_svd->SetStat("NumericStat", 85.0); // CODE SNIPPET START: XblTitleManagedStatsUpdateStatsAsync std::vector stats{ s_svd->Stats() }; std::vector updatedStats; for (const XblTitleManagedStatistic& stat : stats) { if (std::string{ "AddedStat" } == stat.statisticName || std::string{ "NumericStat" } == stat.statisticName) { updatedStats.push_back(stat); } } auto stat1 = updatedStats.begin(); auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* HRESULT hr = XAsyncGetStatus(asyncBlock, false); if (hr == E_FAIL) { CallLuaFunction("OnXblTitleManagedStatsUnableToGetTokenAndSignature"); return; } CallLuaFunctionWithHr(hr, "OnXblTitleManagedStatsUpdateStatsAsync"); // CODE SNIP SKIP }; HRESULT hr = XblTitleManagedStatsUpdateStatsAsync( Data()->xboxLiveContext, &(*stat1), updatedStats.size(), asyncBlock.get() ); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } // CODE SNIPPET END LogToFile("XblTitleManagedStatsUpdateStatsAsync: hr = %s", ConvertHR(hr).data()); return LuaReturnHR(L, hr); } int XblTitleManagedStatsDeleteStatsAsync_Lua(lua_State* L) { if (!s_svd) { HRESULT hr = S_OK; s_svd = std::make_unique(GetStatsValueDocument(Data()->xboxUserId, &hr)); if (FAILED(hr)) { LogToFile("XblTitleManagedStatsWriteAsyncWithSVD: hr = %s", ConvertHR(hr).data()); s_svd = nullptr; CallLuaFunction("OnXblTitleManagedStatsUnableToGetTokenAndSignature"); return LuaReturnHR(L, S_OK); } } // CODE SNIPPET START: XblTitleManagedStatsDeleteStats const char* statName{ "TimeString" }; s_svd->DeleteStat(statName); // CODE SNIP SKIP auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* HRESULT hr = XAsyncGetStatus(asyncBlock, false); if (hr == E_FAIL) { CallLuaFunction("OnXblTitleManagedStatsUnableToGetTokenAndSignature"); return; } CallLuaFunctionWithHr(hr, "OnXblTitleManagedStatsDeleteStatsAsync"); // CODE SNIP SKIP }; HRESULT hr = XblTitleManagedStatsDeleteStatsAsync( Data()->xboxLiveContext, &statName, 1, asyncBlock.get() ); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } // CODE SNIPPET END LogToFile("XblTitleManagedStatsDeleteStatsAsync: hr = %s", ConvertHR(hr).data()); return LuaReturnHR(L, hr); } int ValidateSVD_Lua(lua_State* L) { UNREFERENCED_PARAMETER(L); // statsread enpoint has very aggressive throttling, so we shouldn't read the SVD after each // update. Just make sure we are in sync once at the end of the test. if (s_svd) { HRESULT hr = S_OK; auto updatedSVD{ GetStatsValueDocument(Data()->xboxUserId, &hr) }; if (FAILED(hr)) { return 0; } LogToFile("Service SVD: %s", updatedSVD.Serialize().data()); LogToFile("Local SVD: %s", s_svd->Serialize().data()); assert(*s_svd == updatedSVD); } return 0; } int ClearSVD_Lua(lua_State* L) { UNREFERENCED_PARAMETER(L); if (!s_svd) { HRESULT hr = S_OK; s_svd = std::make_unique(GetStatsValueDocument(Data()->xboxUserId, &hr)); if (FAILED(hr)) { LogToFile("XblTitleManagedStatsWriteAsyncWithSVD: hr = %s", ConvertHR(hr).data()); s_svd = nullptr; lua_pushnumber(L, 1); return 1; } } s_svd->ClearStats(); return 0; } void SetupAPIs_XblTitleManagedStats() { lua_register(Data()->L, "XblTitleManagedStatsWriteAsyncWithSVD", XblTitleManagedStatsWriteAsyncWithSVD_Lua); lua_register(Data()->L, "XblTitleManagedStatsWriteAsync", XblTitleManagedStatsWriteAsync_Lua); lua_register(Data()->L, "XblTitleManagedStatsUpdateStatsAsync", XblTitleManagedStatsUpdateStatsAsync_Lua); lua_register(Data()->L, "XblTitleManagedStatsDeleteStatsAsync", XblTitleManagedStatsDeleteStatsAsync_Lua); // Test helpers lua_register(Data()->L, "ValidateSVD", ValidateSVD_Lua); lua_register(Data()->L, "ClearSVD", ClearSVD_Lua); } ================================================ FILE: Tests/ApiExplorer/APIs/apis_xblc_stringVerify.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" int XblStringVerifyStringAsync_Lua(lua_State *L) { CreateQueueIfNeeded(); char testString[] = { 'F', 'u', 'c', 'k', 's', 't', 'r', 'i', 'n', 'g', '\0' }; // CODE SNIPPET START: XblStringVerifyStringAsync auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->context = nullptr; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* size_t resultSize; HRESULT hr = XblStringVerifyStringResultSize(asyncBlock, &resultSize); if (SUCCEEDED(hr)) { std::vector buffer(resultSize, 0); XblVerifyStringResult* result{}; hr = XblStringVerifyStringResult(asyncBlock, resultSize, buffer.data(), &result, nullptr); LogToFile( "Result: Result Code: %d - First Offending String: %s", result->resultCode, (result->resultCode == XblVerifyStringResultCode::Offensive) ? result->firstOffendingSubstring : ""); // CODE SNIP SKIP } CallLuaFunctionWithHr(hr, "OnTestStringVerify"); // CODE SNIP SKIP }; HRESULT hr = XblStringVerifyStringAsync( Data()->xboxLiveContext, testString, asyncBlock.get() ); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } // CODE SNIPPET END return LuaReturnHR(L, hr); } int XblStringVerifyStringsAsync_Lua(lua_State *L) { CreateQueueIfNeeded(); char testString1[] = { 'S', 'h', 'i', 't', 's', 't', 'r', 'i', 'n', 'g', '\0' }; // CODE SNIP SKIP char testString2[] = { 'G', 'o', 'o', 'd', 's', 't', 'r', 'i', 'n', 'g', '\0' }; // CODE SNIP SKIP char testString3[] = { 'S', 'h', 'i', 't', 's', 't', 'r', 'i', 'n', 'g', '2', '\0' }; // CODE SNIP SKIP const char* testStrings[] = { testString1, testString2, testString3 }; // CODE SNIPPET START: XblStringVerifyStringsAsync auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->context = nullptr; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* size_t resultSize; HRESULT hr = XblStringVerifyStringsResultSize(asyncBlock, &resultSize); if (SUCCEEDED(hr)) { std::vector buffer(resultSize, 0); XblVerifyStringResult* result{}; size_t resultCount = 0; hr = XblStringVerifyStringsResult(asyncBlock, resultSize, buffer.data(), &result, &resultCount, nullptr); LogToScreen("XblStringVerifyStringsResult: %s count=%d", ConvertHR(hr).c_str(), resultCount); // CODE SNIP SKIP if (SUCCEEDED(hr)) // CODE SNIP SKIP { // CODE SNIP SKIP assert(resultCount == 3); // CODE SNIP SKIP } for (uint64_t i = 0; i < resultCount; i++) // CODE SNIP SKIP { // CODE SNIP SKIP LogToFile( "Result [%d]: Result Code: %d - First Offending String: %s", i, result->resultCode, (result->resultCode == XblVerifyStringResultCode::Offensive)? result->firstOffendingSubstring: "" ); // CODE SNIP SKIP result++; // CODE SNIP SKIP } // CODE SNIP SKIP } }; HRESULT hr = XblStringVerifyStringsAsync( Data()->xboxLiveContext, testStrings, 3, asyncBlock.get() ); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } // CODE SNIPPET END return LuaReturnHR(L, hr); } void SetupAPIs_XblStringVerify() { lua_register(Data()->L, "XblStringVerifyStringAsync", XblStringVerifyStringAsync_Lua); lua_register(Data()->L, "XblStringVerifyStringsAsync", XblStringVerifyStringsAsync_Lua); } ================================================ FILE: Tests/ApiExplorer/APIs/apis_xblc_title_storage.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "rapidjson/document.h" #define CHECKHR(hr) if (FAILED(hr)) goto Cleanup; // /// Metadata about a blob. /// typedef struct JsonStorageBlobMetadata { /// /// Blob path is a unique string that conforms to a SubPath\file format (example: "foo\bar\blob.txt"). /// std::string blobPath; /// /// [optional] Friendly display name to show in app UI. /// std::string displayName; /// /// Gets the number of bytes of the blob data. /// size_t length; /// /// Gets the position in the list of blob metadata /// size_t positionInList; } JsonStorageBlobMetadata; struct JsonBlobMetadataResult : public std::enable_shared_from_this { std::string m_scid; uint64_t m_xuid; std::string m_blobPath; std::vector m_items; std::string m_continuationToken; }; JsonBlobMetadataResult DeserializeResult(std::string blobPathRoot, std::string json) { rapidjson::Document document; document.Parse(json.c_str()); JsonBlobMetadataResult result; // Deserialize blobs std::vector metadataList; size_t index = 0; for (const auto& blobJson : document["blobs"].GetArray()) { JsonStorageBlobMetadata metadata; metadata.blobPath = blobPathRoot + blobJson["fileName"].GetString(); if (blobJson.HasMember("displayName")) { metadata.displayName = blobJson["displayName"].GetString(); } metadata.length = static_cast(blobJson["size"].GetInt()); metadata.positionInList = index; index++; result.m_items.push_back(metadata); } return result; } XblTitleStorageType ConvertStringToXblTitleStorageType(const char* str) { XblTitleStorageType type = XblTitleStorageType::TrustedPlatformStorage; if (pal::stricmp(str, "XblTitleStorageType::TrustedPlatformStorage") == 0) type = XblTitleStorageType::TrustedPlatformStorage; else if (pal::stricmp(str, "XblTitleStorageType::GlobalStorage") == 0) type = XblTitleStorageType::GlobalStorage; else if (pal::stricmp(str, "XblTitleStorageType::Universal") == 0) type = XblTitleStorageType::Universal; return type; } XblTitleStorageBlobType ConvertStringToXblTitleStorageBlobType(const char* str) { XblTitleStorageBlobType type = XblTitleStorageBlobType::Unknown; if (pal::stricmp(str, "XblTitleStorageBlobType::Binary") == 0) type = XblTitleStorageBlobType::Binary; else if (pal::stricmp(str, "XblTitleStorageBlobType::Json") == 0) type = XblTitleStorageBlobType::Json; else if (pal::stricmp(str, "XblTitleStorageBlobType::Config") == 0) type = XblTitleStorageBlobType::Config; return type; } XblTitleStorageETagMatchCondition ConvertStringToXblTitleStorageETagMatchCondition(const char* str) { XblTitleStorageETagMatchCondition matchCondition = XblTitleStorageETagMatchCondition::NotUsed; if (pal::stricmp(str, "XblTitleStorageETagMatchCondition::IfMatch") == 0) matchCondition = XblTitleStorageETagMatchCondition::IfMatch; else if (pal::stricmp(str, "XblTitleStorageETagMatchCondition::IfNotMatch") == 0) matchCondition = XblTitleStorageETagMatchCondition::IfNotMatch; return matchCondition; } int XblTitleStorageBlobMetadataResultGetItems_Lua(lua_State *L) { CreateQueueIfNeeded(); // CODE SNIPPET START: XblTitleStorageBlobMetadataResultGetItems XblTitleStorageBlobMetadataResultHandle handle = Data()->blobMetadataResultHandle; const XblTitleStorageBlobMetadata* items; size_t itemsSize; HRESULT hr = XblTitleStorageBlobMetadataResultGetItems(handle, &items, &itemsSize); // CODE SNIPPET END LogToScreen("XblTitleStorageBlobMetadataResultGetItems: hr=%s", ConvertHR(hr).c_str()); if (itemsSize > 0) { Data()->binaryBlobMetadata = items[0]; Data()->jsonBlobMetadata = items[1]; } return LuaReturnHR(L, hr); } int XblTitleStorageBlobMetadataResultHasNext_Lua(lua_State *L) { CreateQueueIfNeeded(); // CODE SNIPPET START: XblTitleStorageBlobMetadataResultHasNext XblTitleStorageBlobMetadataResultHandle handle = Data()->blobMetadataResultHandle; bool hasNext; HRESULT hr = XblTitleStorageBlobMetadataResultHasNext(handle, &hasNext); // CODE SNIPPET END LogToScreen("XblTitleStorageBlobMetadataResultHasNext: hr=%s hasNext=%s", ConvertHR(hr).c_str(), hasNext ? "true" : "false"); return LuaReturnHR(L, hr); } int XblTitleStorageBlobMetadataResultGetNextAsync_Lua(lua_State *L) { CreateQueueIfNeeded(); uint32_t maxItems = GetUint32FromLua(L, 1, 0); // CODE SNIPPET START: XblTitleStorageBlobMetadataResultGetNextAsync XblTitleStorageBlobMetadataResultHandle handle = Data()->blobMetadataResultHandle; auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->context = nullptr; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* XblTitleStorageBlobMetadataResultHandle handle{ nullptr }; HRESULT hr = XblTitleStorageBlobMetadataResultGetNextResult(asyncBlock, &handle); if (Data()->blobMetadataResultHandle != nullptr) // CODE SNIP SKIP { // CODE SNIP SKIP XblTitleStorageBlobMetadataResultCloseHandle(Data()->blobMetadataResultHandle); // CODE SNIP SKIP } // CODE SNIP SKIP Data()->blobMetadataResultHandle = handle; // CODE SNIP SKIP LogToScreen("XblTitleStorageBlobMetadataResultGetNextResult: hr=%s", ConvertHR(hr).c_str()); // CODE SNIP SKIP CallLuaFunctionWithHr(hr, "OnXblTitleStorageBlobMetadataResultGetNextAsync"); // CODE SNIP SKIP }; HRESULT hr = XblTitleStorageBlobMetadataResultGetNextAsync(handle, maxItems, asyncBlock.get()); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } // CODE SNIPPET END LogToScreen("XblTitleStorageBlobMetadataResultGetNextAsync: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblTitleStorageBlobMetadataResultDuplicateHandle_Lua(lua_State *L) { CreateQueueIfNeeded(); // CODE SNIPPET START: XblTitleStorageBlobMetadataResultDuplicateHandle XblTitleStorageBlobMetadataResultHandle handle = Data()->blobMetadataResultHandle; XblTitleStorageBlobMetadataResultHandle duplicatedHandle; HRESULT hr = XblTitleStorageBlobMetadataResultDuplicateHandle(handle, &duplicatedHandle); // CODE SNIPPET END LogToScreen("XblTitleStorageBlobMetadataResultDuplicateHandle: hr=%s", ConvertHR(hr).c_str()); // Cleanup XblTitleStorageBlobMetadataResultCloseHandle(duplicatedHandle); return LuaReturnHR(L, hr); } int XblTitleStorageBlobMetadataResultCloseHandle_Lua(lua_State *L) { CreateQueueIfNeeded(); // CODE SNIPPET START: XblTitleStorageBlobMetadataResultCloseHandle XblTitleStorageBlobMetadataResultHandle handle = Data()->blobMetadataResultHandle; XblTitleStorageBlobMetadataResultCloseHandle(handle); Data()->blobMetadataResultHandle = nullptr; // CODE SNIPPET END LogToScreen("XblTitleStorageBlobMetadataResultCloseHandle done"); return LuaReturnHR(L, S_OK); } int XblTitleStorageGetQuotaAsync_Lua(lua_State *L) { CreateQueueIfNeeded(); XblTitleStorageType storageType = ConvertStringToXblTitleStorageType(GetStringFromLua(L, 1, "XblTitleStorageType::Universal").c_str()); // CODE SNIPPET START: XblTitleStorageGetQuotaAsync auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->context = nullptr; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* size_t usedBytes; size_t quotaBytes; HRESULT hr = XblTitleStorageGetQuotaResult(asyncBlock, &usedBytes, "aBytes); LogToScreen("XblTitleStorageGetQuotaResult: usedBytes=%u quotaBytes=%u", usedBytes, quotaBytes); // CODE SNIP SKIP CallLuaFunctionWithHr(hr, "OnXblTitleStorageGetQuotaAsync"); // CODE SNIP SKIP }; HRESULT hr = XblTitleStorageGetQuotaAsync( Data()->xboxLiveContext, Data()->scid, storageType, asyncBlock.get() ); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } // CODE SNIPPET END LogToScreen("XblTitleStorageGetQuotaAsync: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblTitleStorageGetBlobMetadataAsync_Lua(lua_State *L) { CreateQueueIfNeeded(); XblTitleStorageType storageType = ConvertStringToXblTitleStorageType(GetStringFromLua(L, 1, "XblTitleStorageType::Universal").c_str()); std::string blobPath = GetStringFromLua(L, 2, ""); uint64_t xboxUserId = GetUint64FromLua(L, 3, 0); uint32_t skipItems = GetUint32FromLua(L, 4, 0); uint32_t maxItems = GetUint32FromLua(L, 5, 0); // CODE SNIPPET START: XblTitleStorageGetBlobMetadataAsync auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->context = nullptr; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* XblTitleStorageBlobMetadataResultHandle handle; HRESULT hr = XblTitleStorageGetBlobMetadataResult(asyncBlock, &handle); LogToScreen("XblTitleStorageGetBlobMetadataResult: hr=%s", ConvertHR(hr).c_str()); // CODE SNIP SKIP if (SUCCEEDED(hr)) { if (Data()->blobMetadataResultHandle != nullptr) { XblTitleStorageBlobMetadataResultCloseHandle(Data()->blobMetadataResultHandle); } Data()->blobMetadataResultHandle = handle; } CallLuaFunctionWithHr(hr, "OnXblTitleStorageGetBlobMetadataAsync"); // CODE SNIP SKIP }; HRESULT hr = XblTitleStorageGetBlobMetadataAsync( Data()->xboxLiveContext, Data()->scid, storageType, blobPath.c_str(), xboxUserId, skipItems, maxItems, asyncBlock.get() ); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } // CODE SNIPPET END LogToScreen("XblTitleStorageGetBlobMetadataAsync: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblTitleStorageDeleteBinaryBlobAsync_Lua(lua_State *L) { CreateQueueIfNeeded(); bool deleteOnlyIfEtagMatches = GetBoolFromLua(L, 1, false); // CODE SNIPPET START: XblTitleStorageDeleteBlobAsync auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->context = nullptr; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* HRESULT hr = XAsyncGetStatus(asyncBlock, false); LogToScreen("XblTitleStorageDeleteBlobAsync result (BINARY): hr=%s", ConvertHR(hr).c_str()); // CODE SNIP SKIP CallLuaFunctionWithHr(hr, "OnXblTitleStorageDeleteBinaryBlobAsync"); // CODE SNIP SKIP }; HRESULT hr = XblTitleStorageDeleteBlobAsync( Data()->xboxLiveContext, Data()->binaryBlobMetadata, deleteOnlyIfEtagMatches, asyncBlock.get() ); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } // CODE SNIPPET END LogToScreen("XblTitleStorageDeleteBlobAsync (BINARY): hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblTitleStorageDeleteJsonBlobAsync_Lua(lua_State *L) { CreateQueueIfNeeded(); bool deleteOnlyIfEtagMatches = GetBoolFromLua(L, 1, false); // CODE SNIPPET START: XblTitleStorageDeleteBlobAsync auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->context = nullptr; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* HRESULT hr = XAsyncGetStatus(asyncBlock, false); LogToScreen("XblTitleStorageDeleteBlobAsync result (JSON): hr=%s", ConvertHR(hr).c_str()); // CODE SNIP SKIP CallLuaFunctionWithHr(hr, "OnXblTitleStorageDeleteJsonBlobAsync"); // CODE SNIP SKIP }; HRESULT hr = XblTitleStorageDeleteBlobAsync( Data()->xboxLiveContext, Data()->jsonBlobMetadata, deleteOnlyIfEtagMatches, asyncBlock.get() ); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } // CODE SNIPPET END LogToScreen("XblTitleStorageDeleteBlobAsync (JSON): hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblTitleStorageDownloadBinaryBlobAsync_Lua(lua_State *L) { CreateQueueIfNeeded(); std::string selectQuery = GetStringFromLua(L, 1, ""); uint32_t preferredDownloadBlockSize = GetUint32FromLua(L, 1, 1024*256); // CODE SNIPPET START: XblTitleStorageDownloadBlobAsync std::unique_ptr> downloadBlobBuffer = std::make_unique>(Data()->binaryBlobMetadata.length); auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->context = downloadBlobBuffer.get(); asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* std::unique_ptr> downloadBlobBuffer{ static_cast*>(asyncBlock->context) }; HRESULT hr = XblTitleStorageDownloadBlobResult(asyncBlock, &Data()->binaryBlobMetadata); LogToScreen("XblTitleStorageDownloadBlobResult (BINARY): hr=%s", ConvertHR(hr).c_str()); // CODE SNIP SKIP CallLuaFunctionWithHr(hr, "OnXblTitleStorageDownloadBinaryBlobAsync"); // CODE SNIP SKIP }; HRESULT hr = XblTitleStorageDownloadBlobAsync( Data()->xboxLiveContext, Data()->binaryBlobMetadata, downloadBlobBuffer->data(), Data()->binaryBlobMetadata.length, XblTitleStorageETagMatchCondition::NotUsed, selectQuery.c_str(), // optional preferredDownloadBlockSize, // optional asyncBlock.get() ); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); downloadBlobBuffer.release(); } // CODE SNIPPET END LogToScreen("XblTitleStorageDownloadBlobAsync (BINARY): hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblTitleStorageDownloadJsonBlobAsync_Lua(lua_State *L) { CreateQueueIfNeeded(); std::string selectQuery = GetStringFromLua(L, 1, ""); uint32_t preferredDownloadBlockSize = GetUint32FromLua(L, 1, 1024); size_t length = Data()->jsonBlobMetadata.length; std::unique_ptr> downloadBlobBuffer = std::make_unique>(length); auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->context = downloadBlobBuffer.get(); asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; std::unique_ptr> downloadBlobBuffer{ static_cast*>(asyncBlock->context) }; HRESULT hr = XblTitleStorageDownloadBlobResult(asyncBlock, &Data()->jsonBlobMetadata); LogToScreen("XblTitleStorageDownloadBlobResult (JSON): hr=%s", ConvertHR(hr).c_str()); // CODE SNIP SKIP CallLuaFunctionWithHr(hr, "OnXblTitleStorageDownloadJsonBlobAsync"); // CODE SNIP SKIP }; HRESULT hr = XblTitleStorageDownloadBlobAsync( Data()->xboxLiveContext, Data()->jsonBlobMetadata, downloadBlobBuffer->data(), Data()->jsonBlobMetadata.length, XblTitleStorageETagMatchCondition::NotUsed, selectQuery.c_str(), // optional preferredDownloadBlockSize, // optional asyncBlock.get() ); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); downloadBlobBuffer.release(); } LogToScreen("XblTitleStorageDownloadBlobAsync (JSON): hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblTitleStorageUploadJsonBlobAsync_Lua(lua_State *L) { CreateQueueIfNeeded(); std::string displayName = GetStringFromLua(L, 1, "Test Name 4"); std::string blobPath = GetStringFromLua(L, 2, "apirunner/test/path/json.txt"); std::string blobBufferParam = GetStringFromLua(L, 3, "{}"); XblTitleStorageType storageType = ConvertStringToXblTitleStorageType(GetStringFromLua(L, 4, "XblTitleStorageType::Universal").c_str()); XblTitleStorageBlobType blobType = ConvertStringToXblTitleStorageBlobType(GetStringFromLua(L, 5, "XblTitleStorageBlobType::Json").c_str()); uint32_t preferredUploadBlockSize = GetUint32FromLua(L, 6, 1024); XblTitleStorageETagMatchCondition eTagMatchCondition = ConvertStringToXblTitleStorageETagMatchCondition(GetStringFromLua(L, 7, "XblTitleStorageETagMatchCondition::NotUsed").c_str()); std::unique_ptr> blobBuffer = std::make_unique>(); size_t blobBufferSize; blobBufferSize = blobBufferParam.size() + 1; blobBuffer->resize(blobBufferSize); pal::strcpy(blobBuffer->data(), blobBufferSize, blobBufferParam.c_str()); auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->context = blobBuffer.get(); asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; HRESULT hr = XblTitleStorageUploadBlobResult(asyncBlock, &Data()->jsonBlobMetadata); LogToScreen("XblTitleStorageUploadBlobResult (JSON): hr=%s", ConvertHR(hr).c_str()); // CODE SNIP SKIP CallLuaFunctionWithHr(hr, "OnXblTitleStorageUploadJsonBlobAsync"); // CODE SNIP SKIP }; XblTitleStorageBlobMetadata blobMetadata{}; pal::strcpy(blobMetadata.displayName, displayName.size() + 1, displayName.c_str()); pal::strcpy(blobMetadata.serviceConfigurationId, XBL_SCID_LENGTH, Data()->scid); pal::strcpy(blobMetadata.blobPath, blobPath.size() + 1, blobPath.c_str()); blobMetadata.storageType = storageType; blobMetadata.blobType = blobType; time(&blobMetadata.clientTimestamp); HRESULT hr = XblTitleStorageUploadBlobAsync( Data()->xboxLiveContext, blobMetadata, reinterpret_cast(blobBuffer->data()), blobBufferSize, eTagMatchCondition, preferredUploadBlockSize, // optional asyncBlock.get() ); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); blobBuffer.release(); } LogToScreen("XblTitleStorageUploadBlobAsync (JSON): hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblTitleStorageUploadBinaryBlobAsync_Lua(lua_State *L) { CreateQueueIfNeeded(); std::string displayName = GetStringFromLua(L, 1, "Test Binary Data"); std::string blobPath = GetStringFromLua(L, 2, "apirunner/test/path.txt"); XblTitleStorageType storageType = ConvertStringToXblTitleStorageType(GetStringFromLua(L, 3, "XblTitleStorageType::Universal").c_str()); XblTitleStorageBlobType blobType = ConvertStringToXblTitleStorageBlobType(GetStringFromLua(L, 4, "XblTitleStorageBlobType::Binary").c_str()); XblTitleStorageETagMatchCondition eTagMatchCondition = ConvertStringToXblTitleStorageETagMatchCondition(GetStringFromLua(L, 5, "XblTitleStorageETagMatchCondition::NotUsed").c_str()); std::unique_ptr> blobBuffer = std::make_unique>(); size_t preferredUploadBlockSize = 1024 * 256; size_t blobBufferSize = 1024 * 600; // requires 3 chunk uploads blobBuffer->resize(blobBufferSize); char zero = '0'; for (size_t i = 0; i < blobBufferSize; i++) { (*blobBuffer)[i] = (char)(zero + i % 10); } // CODE SNIPPET START: XblTitleStorageUploadBlobAsync auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->context = blobBuffer.get(); asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* HRESULT hr = XblTitleStorageUploadBlobResult(asyncBlock, &Data()->binaryBlobMetadata); LogToScreen("XblTitleStorageUploadBlobResult (Binary): hr=%s", ConvertHR(hr).c_str()); // CODE SNIP SKIP CallLuaFunctionWithHr(hr, "OnXblTitleStorageUploadBinaryBlobAsync"); // CODE SNIP SKIP }; XblTitleStorageBlobMetadata blobMetadata{}; pal::strcpy(blobMetadata.displayName, displayName.size() + 1, displayName.c_str()); pal::strcpy(blobMetadata.serviceConfigurationId, XBL_SCID_LENGTH, Data()->scid); pal::strcpy(blobMetadata.blobPath, blobPath.size() + 1, blobPath.c_str()); blobMetadata.storageType = storageType; blobMetadata.blobType = blobType; time(&blobMetadata.clientTimestamp); HRESULT hr = XblTitleStorageUploadBlobAsync( Data()->xboxLiveContext, blobMetadata, reinterpret_cast(blobBuffer->data()), blobBufferSize, eTagMatchCondition, preferredUploadBlockSize, // optional asyncBlock.get() ); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); blobBuffer.release(); } // CODE SNIPPET END LogToScreen("XblTitleStorageUploadBlobAsync: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int RestCallToDownloadJsonBlob_Lua(lua_State *L) { CreateQueueIfNeeded(); auto response = Data()->metadataResponseString; HRESULT hr = S_OK; std::string methodName = GetStringFromLua(L, 1, "GET"); char url[300]; sprintf_s(url, "https://titlestorage.xboxlive.com/json/users/xuid(%" PRIu64 ")/scids/00000000-0000-0000-0000-000076029b4d/data/", Data()->xboxUserId); auto result = DeserializeResult(url, response); for (const auto& blobMetadata : result.m_items) { // Download the blob XblHttpCallHandle httpCallHandle; hr = XblHttpCallCreate(Data()->xboxLiveContext, "GET", blobMetadata.blobPath.c_str(), &httpCallHandle); XblHttpCallRequestSetHeader(httpCallHandle, "Content-Type", "application/json; charset=utf-8", true); XblHttpCallRequestSetHeader(httpCallHandle, "Accept-Language", "en-US,en", true); XblHttpCallRequestSetHeader(httpCallHandle, "x-xbl-contract-version", "2", true); Data()->titleStorageHttpCalls.push_back(httpCallHandle); auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->context = Data()->titleStorageHttpCalls[blobMetadata.positionInList]; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* HRESULT hr = XAsyncGetStatus(asyncBlock, false); if (SUCCEEDED(hr)) { auto httpCall = static_cast(asyncBlock->context); const char* responseString; hr = XblHttpCallGetResponseString(httpCall, &responseString); LogToFile("Response String: length %d, hr=%s", strlen(responseString), ConvertHR(hr).c_str()); LogToFile("Response: %s", responseString); CHECKHR(hr); Data()->blobResponseStrings.push_back(responseString); uint32_t statusCode; hr = XblHttpCallGetStatusCode(httpCall, &statusCode); LogToScreen("Status Code: %d, hr=%s", statusCode, ConvertHR(hr).c_str()); CHECKHR(hr); } Cleanup: Data()->titleStorageCompletedHttpCalls++; if (Data()->titleStorageCompletedHttpCalls == Data()->titleStorageHttpCalls.size()) { CallLuaFunctionWithHr(hr, "OnDownloadBlobs"); } LogToScreen("XblHttpCallPerformAsync Completion: hr=%s", ConvertHR(hr).c_str()); }; LogToScreen("Downloading %s", blobMetadata.blobPath.c_str()); hr = XblHttpCallPerformAsync(httpCallHandle, XblHttpCallResponseBodyType::String, asyncBlock.get()); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } } return LuaReturnHR(L, hr); } int RestCallToUploadJsonBlob_Lua(lua_State *L) { CreateQueueIfNeeded(); auto response = Data()->metadataResponseString; HRESULT hr = S_OK; std::string methodName = GetStringFromLua(L, 1, "GET"); char url[300]; sprintf_s(url, "https://titlestorage.xboxlive.com/json/users/xuid(%" PRIu64 ")/scids/00000000-0000-0000-0000-000076029b4d/data/apirunner/test/json/file.json,json", Data()->xboxUserId); std::string blobContent = GetStringFromLua(L, 1, "{}"); // Upload the blob XblHttpCallHandle httpCallHandle; hr = XblHttpCallCreate(Data()->xboxLiveContext, "PUT", url, &httpCallHandle); XblHttpCallRequestSetHeader(httpCallHandle, "Content-Type", "application/json; charset=utf-8", true); XblHttpCallRequestSetHeader(httpCallHandle, "Accept-Language", "en-US,en", true); XblHttpCallRequestSetHeader(httpCallHandle, "x-xbl-contract-version", "2", true); XblHttpCallRequestSetRequestBodyString(httpCallHandle, blobContent.c_str()); Data()->xblHttpCall = httpCallHandle; auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->context = Data()->xblHttpCall; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* HRESULT hr = XAsyncGetStatus(asyncBlock, false); if (SUCCEEDED(hr)) { auto httpCall = static_cast(asyncBlock->context); uint32_t statusCode; hr = XblHttpCallGetStatusCode(httpCall, &statusCode); LogToScreen("Status Code: %d, hr=%s", statusCode, ConvertHR(hr).c_str()); CHECKHR(hr); } Cleanup: CallLuaFunctionWithHr(hr, "OnXblTitleStorageRestUpload"); LogToScreen("XblHttpCallPerformAsync Completion: hr=%s", ConvertHR(hr).c_str()); }; LogToScreen("Uploading %s", url); hr = XblHttpCallPerformAsync(httpCallHandle, XblHttpCallResponseBodyType::String, asyncBlock.get()); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } return LuaReturnHR(L, hr); } int RestCallForJsonMetadata_Lua(lua_State *L) { CreateQueueIfNeeded(); std::string methodName = GetStringFromLua(L, 1, "GET"); char url[300]; sprintf_s(url, "https://titlestorage.xboxlive.com/json/users/xuid(%" PRIu64 ")/scids/00000000-0000-0000-0000-000076029b4d/data/apirunner/test/json?maxItems=100", Data()->xboxUserId); XblHttpCallHandle httpCallHandle; HRESULT hr = XblHttpCallCreate(Data()->xboxLiveContext, methodName.c_str(), url, &httpCallHandle); XblHttpCallRequestSetHeader(httpCallHandle, "Content-Type", "application/json; charset=utf-8", true); XblHttpCallRequestSetHeader(httpCallHandle, "Accept-Language", "en-US,en", true); XblHttpCallRequestSetHeader(httpCallHandle, "x-xbl-contract-version", "2", true); Data()->xblHttpCall = httpCallHandle; auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->context = Data()->xblHttpCall; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* HRESULT hr = XAsyncGetStatus(asyncBlock, false); LogToScreen("XblHttpCallPerformAsync result: hr=%s", ConvertHR(hr).c_str()); if (SUCCEEDED(hr)) { const char* responseString; hr = XblHttpCallGetResponseString(Data()->xblHttpCall, &responseString); Data()->metadataResponseString = responseString; LogToScreen("XblHttpCallResponseGetResponseString: length %d, hr=%s", strlen(responseString), ConvertHR(hr).c_str()); CHECKHR(hr); uint32_t statusCode; hr = XblHttpCallGetStatusCode(Data()->xblHttpCall, &statusCode); LogToScreen("XblHttpCallResponseGetStatusCode: %d, hr=%s", statusCode, ConvertHR(hr).c_str()); CHECKHR(hr); } Cleanup: LogToScreen("XblHttpCallPerformAsync Completion: hr=%s", ConvertHR(hr).c_str()); CallLuaFunctionWithHr(hr, "OnDownloadMetadataBlobs"); }; hr = XblHttpCallPerformAsync(Data()->xblHttpCall, XblHttpCallResponseBodyType::String, asyncBlock.get()); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } return LuaReturnHR(L, hr); } void SetupAPIs_XblTitleStorage() { lua_register(Data()->L, "RestCallForJsonMetadata", RestCallForJsonMetadata_Lua); lua_register(Data()->L, "RestCallToDownloadJsonBlob", RestCallToDownloadJsonBlob_Lua); lua_register(Data()->L, "RestCallToUploadJsonBlob", RestCallToUploadJsonBlob_Lua); lua_register(Data()->L, "XblTitleStorageBlobMetadataResultGetItems", XblTitleStorageBlobMetadataResultGetItems_Lua); lua_register(Data()->L, "XblTitleStorageBlobMetadataResultHasNext", XblTitleStorageBlobMetadataResultHasNext_Lua); lua_register(Data()->L, "XblTitleStorageBlobMetadataResultGetNextAsync", XblTitleStorageBlobMetadataResultGetNextAsync_Lua); lua_register(Data()->L, "XblTitleStorageBlobMetadataResultDuplicateHandle", XblTitleStorageBlobMetadataResultDuplicateHandle_Lua); lua_register(Data()->L, "XblTitleStorageBlobMetadataResultCloseHandle", XblTitleStorageBlobMetadataResultCloseHandle_Lua); lua_register(Data()->L, "XblTitleStorageUploadJsonBlobAsync", XblTitleStorageUploadJsonBlobAsync_Lua); lua_register(Data()->L, "XblTitleStorageGetQuotaAsync", XblTitleStorageGetQuotaAsync_Lua); lua_register(Data()->L, "XblTitleStorageGetBlobMetadataAsync", XblTitleStorageGetBlobMetadataAsync_Lua); lua_register(Data()->L, "XblTitleStorageDeleteBinaryBlobAsync", XblTitleStorageDeleteBinaryBlobAsync_Lua); lua_register(Data()->L, "XblTitleStorageDeleteJsonBlobAsync", XblTitleStorageDeleteJsonBlobAsync_Lua); lua_register(Data()->L, "XblTitleStorageDownloadBinaryBlobAsync", XblTitleStorageDownloadBinaryBlobAsync_Lua); lua_register(Data()->L, "XblTitleStorageDownloadJsonBlobAsync", XblTitleStorageDownloadJsonBlobAsync_Lua); lua_register(Data()->L, "XblTitleStorageUploadBinaryBlobAsync", XblTitleStorageUploadBinaryBlobAsync_Lua); } ================================================ FILE: Tests/ApiExplorer/APIs/apis_xblc_xblhttp.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include #define CHECKHR(hr) if (FAILED(hr)) goto Cleanup; int XblHttpCallCreate_Lua(lua_State *L) { std::string methodName = GetStringFromLua(L, 1, "GET"); char url[300]; sprintf_s(url, "https://achievements.xboxlive.com/users/xuid(%" PRIu64 ")/achievements?titleId=1979882317&maxItems=100", Data()->xboxUserId); XblHttpCallHandle output; HRESULT hr = XblHttpCallCreate(Data()->xboxLiveContext, methodName.c_str(), url, &output); XblHttpCallRequestSetHeader(output, "Content-Type", "application/json; charset=utf-8", true); XblHttpCallRequestSetHeader(output, "Accept-Language", "en-US,en", true); XblHttpCallRequestSetHeader(output, "x-xbl-contract-version", "2", true); Data()->xblHttpCall = output; LogToFile("XblHttpCallCreate: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblHttpCallPerform_Lua(lua_State *L) { auto asyncBlock = std::make_unique(); asyncBlock->queue = Data()->queue; asyncBlock->context = Data()->xblHttpCall; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* HRESULT hr = XAsyncGetStatus(asyncBlock, false); LogToFile("XblHttpCallPerformAsync result: hr=%s", ConvertHR(hr).c_str()); if (SUCCEEDED(hr)) { const char* responseString; hr = XblHttpCallGetResponseString(Data()->xblHttpCall, &responseString); LogToFile("XblHttpCallResponseGetResponseString: length %d, hr=%s", strlen(responseString), ConvertHR(hr).c_str()); CHECKHR(hr); uint32_t statusCode; hr = XblHttpCallGetStatusCode(Data()->xblHttpCall, &statusCode); LogToFile("XblHttpCallResponseGetStatusCode: %d, hr=%s", statusCode, ConvertHR(hr).c_str()); CHECKHR(hr); HRESULT networkErrorCode; uint32_t platformNetworkErrorCode; hr = XblHttpCallGetNetworkErrorCode(Data()->xblHttpCall, &networkErrorCode, &platformNetworkErrorCode); LogToFile("XblHttpCallResponseGetNetworkErrorCode: codehr=%s, rawcode=%d, hr=%s", ConvertHR(networkErrorCode).c_str(), platformNetworkErrorCode, ConvertHR(hr).c_str()); CHECKHR(hr); const char* errorMsg; hr = XblHttpCallGetPlatformNetworkErrorMessage(Data()->xblHttpCall, &errorMsg); LogToFile("XblHttpCallResponseGetPlatformNetworkErrorMessage: hr=%s, msg=%s", ConvertHR(hr).c_str(), errorMsg); CHECKHR(hr); const char* cv; hr = XblHttpCallGetHeader(Data()->xblHttpCall, "MS-CV", &cv); LogToFile("XblHttpCallResponseGetHeader: cv=%s, hr=%s", cv, ConvertHR(hr).c_str()); CHECKHR(hr); uint32_t numHeaders; hr = XblHttpCallGetNumHeaders(Data()->xblHttpCall, &numHeaders); LogToFile("XblHttpCallResponseGetNumHeaders: num=%d, hr=%s", numHeaders, ConvertHR(hr).c_str()); CHECKHR(hr); if (numHeaders > 0) { const char* headerName; const char* headerValue; hr = XblHttpCallGetHeaderAtIndex(Data()->xblHttpCall, 0, &headerName, &headerValue); LogToFile("XblHttpCallResponseGetHeaderAtIndex: name: %s, val: %s, hr: %s", headerName, headerValue, ConvertHR(hr).c_str()); CHECKHR(hr); } } Cleanup: LogToFile("XblHttpCallPerformAsync Completion: hr=%s", ConvertHR(hr).c_str()); CallLuaFunctionWithHr(hr, "XblHttpCallPerformCompleted"); }; HRESULT hr = XblHttpCallPerformAsync(Data()->xblHttpCall, XblHttpCallResponseBodyType::String, asyncBlock.get()); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); } LogToFile("XblHttpCallPerformAsync: hr=%s", ConvertHR(hr).c_str()); return LuaReturnHR(L, hr); } int XblHttpCallDuplicate_Lua(lua_State *L) { LogToFile("Starting duplicate testing..."); XblHttpCallHandle duplicatedHandle; HRESULT hr = XblHttpCallDuplicateHandle(Data()->xblHttpCall, &duplicatedHandle); LogToFile("XblHttpCallDuplicateHandle: hr=%s", ConvertHR(hr).c_str()); LogToFile(" orig: %p dup: %p", Data()->xblHttpCall, duplicatedHandle); XblHttpCallCloseHandle(duplicatedHandle); LogToFile("Closed duplicate handle", ConvertHR(hr).c_str()); LogToFile(" orig: %p dup: %p", Data()->xblHttpCall, duplicatedHandle); return LuaReturnHR(L, hr); } int XblHttpCallValidateSetters_Lua(lua_State *L) { LogToFile("Starting setters testing..."); uint8_t bodyBytes[] = { 0xD, 0xE, 0xA, 0xD, 0xB, 0xE, 0xE, 0xF }; HRESULT hr = XblHttpCallSetTracing(Data()->xblHttpCall, false); LogToFile("XblHttpCallSetTracing: hr=%s", ConvertHR(hr).c_str()); CHECKHR(hr); hr = XblHttpCallRequestSetRequestBodyBytes( Data()->xblHttpCall, bodyBytes, sizeof(bodyBytes) ); LogToFile("XblHttpCallRequestSetRequestBodyBytes: hr=%s", ConvertHR(hr).c_str()); CHECKHR(hr); hr = XblHttpCallRequestSetRequestBodyString(Data()->xblHttpCall, "requestBodyStringContent"); LogToFile("XblHttpCallRequestSetRequestBodyString: hr=%s", ConvertHR(hr).c_str()); CHECKHR(hr); hr = XblHttpCallRequestSetRetryAllowed(Data()->xblHttpCall, false); LogToFile("XblHttpCallRequestSetRetryAllowed: hr=%s", ConvertHR(hr).c_str()); CHECKHR(hr); hr = XblHttpCallRequestSetRetryCacheId(Data()->xblHttpCall, 12345); LogToFile("XblHttpCallRequestSetRetryCacheId: hr=%s", ConvertHR(hr).c_str()); CHECKHR(hr); hr = XblHttpCallRequestSetLongHttpCall(Data()->xblHttpCall, true); LogToFile("XblHttpCallRequestSetLongHttpCall: hr=%s", ConvertHR(hr).c_str()); CHECKHR(hr); Cleanup: return LuaReturnHR(L, hr); } int XblHttpCallValidateGetters_Lua(lua_State *L) { LogToFile("Starting getters testing..."); const char* url; HRESULT hr = XblHttpCallGetRequestUrl(Data()->xblHttpCall, &url); LogToFile("XblHttpCallGetRequestUrl: hr=%s", ConvertHR(hr).c_str()); LogToFile(" url length: %d", strlen(url)); return LuaReturnHR(L, hr); } void SetupAPIs_XblHttp() { lua_register(Data()->L, "XblHttpCallCreate", XblHttpCallCreate_Lua); lua_register(Data()->L, "XblHttpCallPerform", XblHttpCallPerform_Lua); lua_register(Data()->L, "XblHttpCallDuplicate", XblHttpCallDuplicate_Lua); lua_register(Data()->L, "XblHttpCallValidateGetters", XblHttpCallValidateGetters_Lua); lua_register(Data()->L, "XblHttpCallValidateSetters", XblHttpCallValidateSetters_Lua); } ================================================ FILE: Tests/ApiExplorer/Android/pch.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "pch_common.h" #define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "AndroidAPIExplorerJni", __VA_ARGS__)) #define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, "AndroidAPIExplorerJni", __VA_ARGS__)) #define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "AndroidAPIExplorerJni", __VA_ARGS__)) #define UNREFERENCED_PARAMETER(P) (P) #define RETURN_STRING_FROM_ENUM(e) case(e): return #e; ================================================ FILE: Tests/ApiExplorer/Include/api_explorer.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #if HC_PLATFORM == HC_PLATFORM_ANDROID #include "multidevice.h" #endif #include "lua.h" #include "lualib.h" #include "lauxlib.h" // C++ is not currently supported in IOS Framework. In the future, this should be updated to allow C++ tests in iOS API Explorer #define IOS_CPP_ENABLED 0 #define CPP_TESTS_ENABLED (HC_PLATFORM != HC_PLATFORM_IOS || IOS_CPP_ENABLED) struct ApiCommand; typedef void (*CommandCallbackType)(const std::vector& cmdLineTokens); typedef HRESULT (*ApiCallbackType)(ApiCommand*); struct Command { char name[256]; CommandCallbackType cmdCallback; bool hideFromHelp; }; struct SubCommand { char name[256]; char subname[256]; CommandCallbackType cmdCallback; bool hideFromHelp; }; struct ApiExplorerData { ~ApiExplorerData() { if (L != nullptr) { lua_close(L); // cleanup Lua } if (queue) { XTaskQueueCloseHandle(queue); } } std::recursive_mutex m_luaLock; lua_State* L{ nullptr }; // the Lua interpreter bool m_quit{ false }; bool m_socialDoWorkDone{ true }; bool m_mpmDoWorkDone{ true }; bool m_achievementsDoWorkDone{ true }; std::vector m_commands; std::vector m_cmdLineLog; std::vector m_tutorial; std::string m_tutorialWaitFor; std::vector m_ignoreHRs; std::string m_testsPath; HRESULT m_testHR{ S_OK }; bool m_stopTest{ false }; bool m_callUpdate{ false }; HRESULT m_lastError{ S_OK }; bool m_tutorialWait{ false }; bool m_runningTests{ false }; int m_onlyFileNumber{ 0 }; int m_minFileNumber{ 0 }; int m_maxFileNumber{ 0 }; bool m_checkHR{ true }; HRESULT m_runTestsHR{ S_OK }; bool m_isXalInitialized{ false }; std::string m_onXalTryAddFirstUserSilentlyAsync; std::string m_onTaskQueueTerminateWithAsyncWait; bool m_repeatJsonCmds{ false }; bool m_wasTestSkipped{ false }; bool m_trackUnhookedMemory{ false }; std::mutex m_catMessageLock; std::string m_catMessage; // ApiRunnerState XalUserHandle xalUser{ nullptr }; bool gotXalUser{ false }; XTaskQueueHandle queue{ nullptr }; XblContextHandle xboxLiveContext{ nullptr }; std::vector subscriptionHandleDeviceList; std::vector subscriptionHandleTitleList; std::string gamertag; uint64_t xboxUserId{ 0 }; uint32_t titleId{ 0 }; const char* scid{ nullptr }; XblFunctionContext fnAddServiceCallRoutedHandler{ 0 }; HCMockCallHandle nsalMockCall{ nullptr }; bool libHttpClientInit{ false }; // Android Specific Data #if HC_PLATFORM == HC_PLATFORM_ANDROID JavaVM *javaVM; jobject applicationContext; HCInitArgs initArgs; /// The Java Application Context. jclass m_mainActivityClass; jobject m_mainActivityClassInstance; jmethodID m_getApplicationContext; #endif // Achievements Data XblAchievementsResultHandle achievementsResult{ nullptr }; #if CPP_TESTS_ENABLED xbox::services::achievements::achievements_result achievementsResultCpp{}; #endif // libHttpClient Data HCCallHandle httpCall{ nullptr }; HCMockCallHandle mockHttpCall{ nullptr }; HCWebsocketHandle websocket{ nullptr }; // XblHttpCall Data XblHttpCallHandle xblHttpCall{ nullptr }; // Leaderboard Data std::vector leaderboardBuffer; XblLeaderboardResult* leaderboardResult{ nullptr }; #if CPP_TESTS_ENABLED xbox::services::leaderboard::leaderboard_result leaderboardResultCpp{}; #endif //Social Data #if CPP_TESTS_ENABLED xbox::services::social::xbox_social_relationship_result socialRelationshipResult; std::shared_ptr socialRelationshipChangeSubscription; function_context socialRelationshipChangedHandlerContext; #endif // Matchmaking Data XblCreateMatchTicketResponse* matchTicketResponse; #if CPP_TESTS_ENABLED xbox::services::matchmaking::create_match_ticket_response matchTicketResponseCpp; #endif // User Statistic Data XblRealTimeActivitySubscriptionHandle statisticChangeSubscriptionHandle{ nullptr }; XblFunctionContext statisticChangedFunctionContext{ 0 }; long long lastUserStat = 0; #if CPP_TESTS_ENABLED std::shared_ptr statisticChangeSubscriptionCpp{ nullptr }; function_context statisticChangedFunctionContextCpp{ nullptr }; #endif // Title Storage Data XblTitleStorageBlobMetadataResultHandle blobMetadataResultHandle{ nullptr }; XblTitleStorageBlobMetadata binaryBlobMetadata{}; XblTitleStorageBlobMetadata jsonBlobMetadata{}; #if CPP_TESTS_ENABLED xbox::services::title_storage::title_storage_blob_metadata_result blobMetadataResultCpp{}; xbox::services::title_storage::title_storage_blob_metadata blobMetadataCpp{}; #endif // Title Storage Rest API Calls Data std::vector blobResponseStrings; std::string metadataResponseString; std::vector titleStorageHttpCalls; size_t titleStorageCompletedHttpCalls = 0; size_t filesToDownload = 0; // MP std::string inviteHandle; #if HC_PLATFORM == HC_PLATFORM_IOS std::string apnsToken; #endif #if HC_PLATFORM == HC_PLATFORM_UWP Windows::UI::Core::CoreDispatcher^ m_dispatcher; #endif #if HC_PLATFORM == HC_PLATFORM_WIN32 XblRealTimeActivitySubscriptionHandle gameInviteNotificationSubscriptionHandle{ nullptr }; XblFunctionContext gameInviteNotificationFunctionContext{ 0 }; #endif std::shared_ptr m_multiDeviceManager; XblFunctionContext m_achievementProgressNotificationFunctionContext{ 0 }; }; enum TypeType { TypeType_None, TypeType_Command, TypeType_Event, TypeType_API }; std::vector TokenizeString(const std::string& input); void LogCat(bool logToFile, _Printf_format_string_ char const* format, ...); ApiExplorerData* Data(); ================================================ FILE: Tests/ApiExplorer/Include/multidevice.h ================================================ #pragma once #if HC_PLATFORM_IS_MICROSOFT #pragma warning( push ) #pragma warning( disable : 4365 ) #pragma warning( disable : 4266 ) #endif #include "cpprest/http_msg.h" #if HC_PLATFORM_IS_MICROSOFT #pragma warning( pop ) #endif struct ServerState { std::string client1xuid; std::string client1gt; std::string client2xuid; std::string client2gt; std::string id; std::map propmap; }; class ApiRunnerMultiDeviceManager { private: std::mutex m_mutex; ServerState m_state; std::string m_sessionId; std::string m_sessionName; bool m_hosted{ false }; bool m_isInitialized{ false }; bool m_isHCInitialized{ false }; bool m_isJoinable{ false }; std::string m_sessionState; std::function m_onStateChangedHandler; std::atomic m_doPoll; std::atomic m_isJoined; std::atomic m_isAbort; utility::datetime m_nextCallTime; bool m_callGetJoinable; public: ~ApiRunnerMultiDeviceManager(); HRESULT Init(_In_ std::function onStateChangedHandler); bool IsInitialized() { return m_isInitialized; } bool IsJoinable() { return m_isJoinable; } bool Joined() { return m_isJoined; } bool Abort() { return m_isAbort; } std::string SessionId() { return m_sessionId; } const ServerState& GetSessionState() { return m_state; } bool IsHost() { return m_hosted; } uint64_t GetLocalXuid(); uint64_t GetRemoteXuid(); HRESULT HostSession( _In_ const std::string& name, _In_ const std::string& xuid, _In_ const std::string& gamertag, _In_ std::function onResponse); HRESULT WaitTillPeerConnects(); HRESULT JoinOpenSession( _In_ const std::string& name, _In_ const std::string& xuid, _In_ const std::string& gamertag, _In_ std::function handler); HRESULT GetJoinable( _In_ const std::string& name, _In_ std::function handler ); HRESULT JoinSession( _In_ const std::string& sessionId, _In_ const std::string& xuid, _In_ const std::string& gamertag, _In_ std::function handler); HRESULT SetSessionState( _In_ const std::string& key, _In_ const std::string& value, _In_ std::function handler); HRESULT StartSessionStateChangePolling(); HRESULT StopSessionStateChangePolling(); HRESULT RefreshSessionState(); std::string GetSessionStateString() { return m_sessionState; } std::string GetStateValueFromKey(std::string key); bool IsDoPoll() { return m_doPoll; } void ParseState(const std::string& responseString); HRESULT MakeCall( _In_ const std::string& method, _In_ const std::string& url, _In_ const std::vector>& headers, _In_ std::function handler); }; ================================================ FILE: Tests/ApiExplorer/Include/runner.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once enum TestSet { SingleDevice, MultiDevice, SingleDeviceBVTs }; // Call these by runner void ApiRunnerSetupApiExplorer(); void ApiRunnerSetupApiExplorerTestsPath(std::string testsPath); HRESULT ApiRunnerRunTest(std::string testName); HRESULT ApiRunnerRunTestWithSetup(std::string testName, bool repeat); void ApiRunnerSetRunTestsParams(int onlyFileNumber, int minFileNumber, int maxFileNumber); HRESULT ApiRunnerRunTests(TestSet set); void ApiRunnerProcessCmdLine(const std::string& cmdLine); std::string ApiRunnerReadFile(std::string fileName); HRESULT ApiRunnerProcessJsonCmds(std::string json); bool ApiRunnerIsRunningTests(); HRESULT ApiRunnerGetTestResult(); // These functions need to be implemented by each runner void LogToFile(_Printf_format_string_ char const* format, ...); void LogToScreen(_Printf_format_string_ char const* format, ...); // These functions are specific to APIExplorer on iOS #if HC_PLATFORM == HC_PLATFORM_IOS void SetupAPNSRegistrationToken(std::string registrationToken); #endif // These functions are specific to APIExplorer on Android #if HC_PLATFORM == HC_PLATFORM_ANDROID void SetupAndroidContext( JavaVM *javaVM, jobject context, jclass mainActivityClass, jobject mainActivityInstance, jmethodID getApplicationContext); #endif ================================================ FILE: Tests/ApiExplorer/Shared/Win/pal.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #ifndef max #define max(a,b) (((a) > (b)) ? (a) : (b)) #endif #ifndef min #define min(a,b) (((a) < (b)) ? (a) : (b)) #endif namespace pal { void strcpy(char * dst, size_t size, const char* src) { strcpy_s(dst, size, src); } int stricmp(const char* left, const char* right) { return _stricmp(left, right); } int vsprintf(char* message, size_t size, char const* format, va_list varArgs) { int result = vsnprintf(message, size, format, varArgs); result = max(result, 0); // result is negative on error result = min(static_cast(size) - 1, result); // don't let it go past size-1 message[result] = 0; return result; } char* strtok(char* str, char const* delimiter, char** context) { return strtok_s(str, delimiter, context); } void Sleep(unsigned long duration) { ::Sleep(duration); } bool FileExists(std::string fileName) { struct stat fileInfo; return stat(fileName.c_str(), &fileInfo) == 0; } std::vector EnumFolders(std::string folder) { std::vector folders; std::string searchFolder = folder + "\\*"; folders.push_back(folder); WIN32_FIND_DATAA fd; HANDLE hFind = ::FindFirstFileA(searchFolder.c_str(), &fd); if (hFind != INVALID_HANDLE_VALUE) { do { if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { std::string subFolder = fd.cFileName; if (subFolder != "." && subFolder != ".." && subFolder != "shared") { subFolder = folder; subFolder += "\\"; subFolder += fd.cFileName; folders.push_back(subFolder); } } } while (::FindNextFileA(hFind, &fd)); ::FindClose(hFind); } return folders; } std::vector EnumFilesInFolderHelper(std::string folder, std::string spec) { std::vector files; std::string searchFolder = folder + "\\" + spec; WIN32_FIND_DATAA fd; HANDLE hFind = ::FindFirstFileA(searchFolder.c_str(), &fd); if (hFind != INVALID_HANDLE_VALUE) { do { if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { std::string filePath = folder; filePath += "\\"; filePath += fd.cFileName; files.push_back(filePath); } } while (::FindNextFileA(hFind, &fd)); ::FindClose(hFind); } return files; } std::vector EnumFilesInFolder(std::string folder, std::string spec) { std::vector folders = EnumFolders(folder); std::vector files; for (std::string iFolder : folders) { std::vector filesHelper; filesHelper = EnumFilesInFolderHelper(iFolder, spec); for (std::string f : filesHelper) { files.push_back(f); } } return files; } std::string FindFile(std::string fileName) { if (FileExists(fileName)) { return fileName; } char strBasePath[MAX_PATH] = { 0 }; GetModuleFileNameA(NULL, strBasePath, MAX_PATH); char* lastSlash = strrchr(strBasePath, '\\'); if (lastSlash != nullptr) { *lastSlash = 0; } std::string testFilePath = Data()->m_testsPath + "\\" + fileName; if (FileExists(testFilePath)) { return testFilePath; } testFilePath = Data()->m_testsPath + "\\..\\" + fileName; if (FileExists(testFilePath)) { return testFilePath; } std::string fullPath; std::string newPath = strBasePath; newPath += "\\"; for (int i = 0; i < 5; i++) { newPath += "..\\"; testFilePath = newPath + fileName; if (FileExists(testFilePath)) { return testFilePath; } testFilePath = newPath + "Tests\\APIExplorer\\" + fileName; if (FileExists(testFilePath)) { return testFilePath; } testFilePath = newPath + "..\\..\\..\\..\\Tests\\APIExplorer\\" + fileName; if (FileExists(testFilePath)) { return testFilePath; } } return fullPath; } std::string GetLuaPath() { std::string path{}; auto utestPath = FindFile("_luasetup_\\u-test\\u-test.lua"); path += utestPath.substr(0, utestPath.find_last_of('\\')) + "\\?.lua"; auto commonPath = FindFile("_luasetup_\\xal\\common.lua"); path += ";" + commonPath.substr(0, commonPath.find_last_of('\\')) + "\\?.lua"; return path; } } ================================================ FILE: Tests/ApiExplorer/Shared/commands.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "utils.h" #if HC_PLATFORM == HC_PLATFORM_ANDROID #include "api_explorer.h" #include "pal.h" #include "runner.h" #endif #include "cpprest/json.h" #include "mem_hook.h" #include #include #if HC_PLATFORM == HC_PLATFORM_IOS || HC_PLATFORM == HC_PLATFORM_ANDROID #define E_NOT_VALID_STATE E_FAIL #endif std::shared_ptr GetApiFromCmdLine(const std::vector& cmdLineTokens, std::string apiName, bool log); void SetupLua(); void CleanupLua(); void RegisterLuaAPIs(); void SetupCommands(); void ReplayCommands(const std::vector& lineLogs); void ApiRunnerProcessCmdLine(const std::string& cmdLine); void Log(_Printf_format_string_ char const* format, ...); void LogInit(); HRESULT CallLuaFunctionWithStringArgs(std::string fnName, std::vector strs); HRESULT RunSetupScript(); #if API_EXPLORER_EDITOR void StoreCmdLineInLog(std::string cmdLine, const std::vector& tokens); void ClearScreen(); #endif void MPMStopDoWorkHelper(); std::unique_ptr g_data; void InitApiExplorerData() { g_data = std::make_unique(); g_data->m_runTestsHR = S_OK; g_data->m_runningTests = false; g_data->m_checkHR = true; g_data->m_lastError = S_OK; g_data->m_callUpdate = false; if (g_data->m_testsPath.empty()) { #if HC_PLATFORM != HC_PLATFORM_ANDROID auto rootFile = pal::FindFile("tests/tests.root"); #else auto rootFile = pal::FindFile("tests.root"); #endif assert(!rootFile.empty()); g_data->m_testsPath = rootFile.substr(0, rootFile.find_last_of("/")); } } ApiExplorerData* Data() { return g_data.get(); } void ReplayCommands(const std::vector& lineLogs) { for (auto& cmd : lineLogs) { Log(">> %s", cmd.c_str()); ApiRunnerProcessCmdLine(cmd); } } void ApiRunnerProcessCmdLine(const std::string& cmdLine) { std::vector tokens = TokenizeString(cmdLine); if (tokens.size() == 0) { return; } #if API_EXPLORER_EDITOR StoreCmdLineInLog(cmdLine, tokens); #endif for (auto& cmd : Data()->m_commands) { if (cmd.name == tokens[0]) { cmd.cmdCallback(tokens); return; } } } #if !API_EXPLORER_EDITOR void Log(_Printf_format_string_ char const* format, ...) { char message[4096] = {}; va_list varArgs{}; va_start(varArgs, format); pal::vsprintf(message, 4096, format, varArgs); va_end(varArgs); LogToScreen(message); LogToFile(message); } #endif static inline void rtrim(std::string &s) { s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) { return !std::isspace(ch); }).base(), s.end()); } std::vector TokenizeString(const std::string& input) { std::vector tokens; const char seperators[32] = " \r\n"; char* tok; char *next_token = NULL; char szInput[1024] = { 0 }; pal::strcpy(szInput, 1024, input.c_str()); if (strchr(szInput, '\"') != nullptr) { tok = pal::strtok(szInput, "\"", &next_token); while (tok != nullptr) { std::string strTok = tok; rtrim(strTok); tokens.push_back(strTok); tok = pal::strtok(0, "\"", &next_token); } } else { tok = pal::strtok(szInput, seperators, &next_token); while (tok != nullptr) { tokens.push_back(tok); tok = pal::strtok(0, seperators, &next_token); } } return tokens; } #if API_EXPLORER_EDITOR void OnCmdHelp(const std::vector& cmdLineTokens) { UNREFERENCED_PARAMETER(cmdLineTokens); for (auto& cmd : Data()->m_commands) { if (!cmd.hideFromHelp) Log(" %s", cmd.name); } } #endif void OnCmdQuit(const std::vector&) { Data()->m_quit = true; } void MultiDeviceInit() { Data()->m_multiDeviceManager->Init( [](const std::string& key, const std::string& value) // onStateChangedHandler { UNREFERENCED_PARAMETER(key); UNREFERENCED_PARAMETER(value); // note: don't call into LUA as we're not in LUA file processing thread }); } void OnCmdHost(const std::vector& cmdLineTokens) { if (cmdLineTokens.size() < 2) { LogToScreen("Try: host name. eg host youralias"); return; } SetupLua(); RunSetupScript(); CleanupLua(); uint64_t xuid = Data()->xboxUserId; char xuidStr[128]; sprintf_s(xuidStr, "%llu", static_cast(xuid)); std::string gamertag = Data()->gamertag; LogToScreen("Local XUID: %s GamerTag: %s", xuidStr, gamertag.c_str()); MultiDeviceInit(); bool hostedSession = false; std::string name = cmdLineTokens[1]; Data()->m_multiDeviceManager->HostSession( name, xuidStr, gamertag, [&hostedSession](HRESULT hr) { if (SUCCEEDED(hr)) { hr = Data()->m_multiDeviceManager->WaitTillPeerConnects(); if (SUCCEEDED(hr)) { hostedSession = true; } } }); while (!hostedSession) { pal::Sleep(100); } } void OnCmdMemTrack(const std::vector& cmdLineTokens) { if (cmdLineTokens.size() < 2) { LogToScreen("Try: memtrack bool. eg memtrack true"); return; } std::string enabledStr = cmdLineTokens[1]; bool enabled = (enabledStr == "true"); Data()->m_trackUnhookedMemory = enabled; LogToScreen("TrackUnhookedMemory: %d", enabled); } void OnCmdJoin(const std::vector& cmdLineTokens) { if (cmdLineTokens.size() < 2) { LogToScreen("Try: join name. eg join youralias"); return; } SetupLua(); RunSetupScript(); CleanupLua(); uint64_t xuid = Data()->xboxUserId; char xuidStr[128]; sprintf_s(xuidStr, "%llu", static_cast(xuid)); std::string gamertag = Data()->gamertag; LogToScreen("Local XUID: %s GamerTag: %s", xuidStr, gamertag.c_str()); MultiDeviceInit(); bool joinedSession = false; std::string name = cmdLineTokens[1]; Data()->m_multiDeviceManager->JoinOpenSession( name, xuidStr, gamertag, [&joinedSession](HRESULT hr) { if (SUCCEEDED(hr)) { joinedSession = true; } }); while (!joinedSession) { pal::Sleep(100); } } bool LoadFile(const std::string& fileNameInput) { std::string strFilePath = pal::FindFile(fileNameInput); if (strFilePath.empty()) { std::string testPath = "Tests\\" + fileNameInput; strFilePath = pal::FindFile(testPath); } if (!strFilePath.empty()) { std::lock_guard lock(Data()->m_luaLock); if (Data()->L == nullptr) { return false; } int result = luaL_dofile(Data()->L, strFilePath.c_str()); // load the script if (result == LUA_OK) { return true; } else { const char * error = lua_tostring(Data()->L, -1); LogToScreen("Couldn't load %s, error = %s", fileNameInput.c_str(), error); } } else { LogToFile("Couldn't find %s", fileNameInput.c_str()); } return false; } HRESULT WaitForTestResult() { while (!Data()->m_stopTest) { if(Data()->m_callUpdate) { CallLuaFunction("update"); } pal::Sleep(10); } Data()->m_callUpdate = false; return Data()->m_testHR; } void WaitForXalCleanup() { while (Data()->m_isXalInitialized) { pal::Sleep(10); } } void APIRunner_CleanupLeakCheck() { auto memHook = GetApiRunnerMemHook(); memHook->LogStats("CleanupLeakCheck"); memHook->LogLeaks(); } HRESULT RunTestWithoutCleanup(const std::string& scriptName) { Data()->m_stopTest = false; assert(!scriptName.empty()); bool testLoaded = LoadFile(scriptName); if (!testLoaded) { std::string scriptFailure = "Failed to load " + scriptName; Log(scriptFailure.c_str()); return E_FAIL; } return WaitForTestResult(); } HRESULT RunTestInternal(std::string testName, bool overrideSkip) { if (!testName.empty()) { Data()->m_stopTest = false; Data()->m_testHR = S_OK; Data()->m_ignoreHRs.clear(); Data()->m_onXalTryAddFirstUserSilentlyAsync = ""; Data()->m_onTaskQueueTerminateWithAsyncWait = ""; if (overrideSkip) { // Tell test harness not to skip test if because it was directly run CallLuaString("api = require 'u-test'; api.skipOverride = true;"); } else { CallLuaString("api = require 'u-test'; api.skipOverride = false;"); } RunTestWithoutCleanup(testName); } std::this_thread::sleep_for(std::chrono::milliseconds{ 500 }); CallLuaString("common = require 'common'; common.cleanup()"); WaitForXalCleanup(); return S_OK; } HRESULT ApiRunnerRunTest(std::string testName) { return RunTestInternal(testName, true); } HRESULT RunSetupScript() { char message[4096] = {}; #if HC_PLATFORM != HC_PLATFORM_ANDROID std::string sharedFolder = "_luasetup_\\xal"; sprintf_s(message, "tests\\%s\\setup.lua", sharedFolder.c_str()); #else std::string sharedFolder = "xal"; sprintf_s(message, "%s/setup.lua", sharedFolder.c_str()); #endif std::string strFilePath = pal::FindFile(message); if (strFilePath.empty() || strFilePath.length() < 5) { Log("Can't find setup.lua"); return E_NOINTERFACE; } return RunTestInternal(message, true); } void OnCmdRepeatRunTest(const std::vector& cmdLineTokens) { if (cmdLineTokens.size() < 2) { std::string jsonFileContents = ApiRunnerReadFile("cmds.json"); ApiRunnerProcessJsonCmds(jsonFileContents); } else { ApiRunnerRunTestWithSetup(cmdLineTokens[1], true); } } void OnCmdRunTest(const std::vector& cmdLineTokens) { if (cmdLineTokens.size() < 2) { LogToScreen("Try: runtest testName"); } else { ApiRunnerRunTestWithSetup(cmdLineTokens[1], false); } } void EnableTestSet(TestSet set) { if (set == MultiDevice) { CallLuaString("api = require 'u-test'; api.enablebvts = false; api.enablemultidevice = true;"); } else if (set == SingleDeviceBVTs) { CallLuaString("api = require 'u-test'; api.enablebvts = true; api.enablemultidevice = false;"); } else { CallLuaString("api = require 'u-test'; api.enablebvts = false; api.enablemultidevice = false;"); } } void OnCmdFaultInjection(const std::vector& cmdLineTokens) { assert(cmdLineTokens.size() >= 2); if (pal::stricmp(cmdLineTokens[1].c_str(), "user") == 0) { XblEnableFaultInjection(INJECTION_FEATURE_USER); } if (pal::stricmp(cmdLineTokens[1].c_str(), "http") == 0) { XblEnableFaultInjection(INJECTION_FEATURE_HTTP); } if (pal::stricmp(cmdLineTokens[1].c_str(), "options") == 0) { assert(cmdLineTokens.size() == 5); int64_t failFreq = static_cast(atoi(cmdLineTokens[2].c_str())); uint64_t freqChangeSpeed = static_cast(atoi(cmdLineTokens[3].c_str())); int64_t freqChangeAmount = static_cast(atoi(cmdLineTokens[4].c_str())); XblSetFaultInjectOptions(failFreq, freqChangeSpeed, freqChangeAmount); } } void OnCmdRunScript(const std::vector& cmdLineTokens) { assert(cmdLineTokens.size() == 2); SetupLua(); RunTestWithoutCleanup(cmdLineTokens[1]); CleanupLua(); } bool DoesTestContainsSetMaker(std::string test, TestSet set) { std::string strFilePath = pal::FindFile(test); if (strFilePath.empty()) { std::string testPath = "Tests\\" + test; strFilePath = pal::FindFile(testPath); } std::string fileContents = ApiRunnerReadFile(strFilePath); std::string marker; bool avoid = false; switch (set) { case MultiDevice: marker = "test.ismultidevice"; break; case SingleDevice: marker = "test.ismultidevice"; avoid = true; break; case SingleDeviceBVTs: marker = "test.isbvt"; break; } bool found = false; if (fileContents.find(marker) != std::string::npos) { found = true; } if (avoid) found = !found; return found; } HRESULT RunTestsHelper(TestSet set) { SetupLua(); LogToScreen("Signing in silent if possible"); if (Data()->m_trackUnhookedMemory) { auto memHook = GetApiRunnerMemHook(); memHook->StartMemTracking(); } HRESULT hr = RunSetupScript(); if (FAILED(hr)) { LogToScreen("RunSetupScript() failed with HR = %s", ConvertHR(hr).c_str()); return hr; } if (!Data()->gotXalUser) { LogToScreen("Data()->GotXalUser was invalid"); return E_NOT_VALID_STATE; } Data()->m_runTestsHR = S_OK; Data()->m_runningTests = true; LogToScreen("Running tests in %s", Data()->m_testsPath.c_str()); LogToScreen("See debug output & log file for detail"); LogToScreen(" "); EnableTestSet(set); std::vector tests = pal::EnumFilesInFolder(Data()->m_testsPath, "*.lua"); int iFileNumber = 0; for (auto& test : tests) { if (Data()->m_quit) return S_OK; if (test.find("u-test.lua") != std::string::npos) continue; iFileNumber++; if (Data()->m_onlyFileNumber > 0) { // Skip every file except the one at a specific index if (iFileNumber != Data()->m_onlyFileNumber) continue; } if (Data()->m_minFileNumber > 0 || Data()->m_maxFileNumber > 0) { // Skip files outside range if (iFileNumber < Data()->m_minFileNumber || iFileNumber > Data()->m_maxFileNumber) { continue; } } std::string testName = test; std::size_t found = testName.find_last_of('\\'); if (found == std::string::npos) { found = testName.find_last_of('/'); } if (found != std::string::npos) { testName = testName.substr(found + 1); } if (!DoesTestContainsSetMaker(test, set)) { continue; } LogToFile("***************************************"); LogToScreen("[FILE] Processing %s. File #%d", testName.c_str(), iFileNumber); LogToFile("***************************************"); hr = RunTestInternal(test, false); if (FAILED(hr)) { LogToScreen("FAILED: hr=%s (0x%0.8x)", ConvertHR(hr).c_str(), hr); return hr; } #if HC_PLATFORM != HC_PLATFORM_GDK if (!Data()->m_wasTestSkipped) { pal::Sleep(6000); // For in-proc SDK (Win32/Mobile, etc.), need to delay between each test otherwise you'll get 429 from https://title.mgt.xboxlive.com/titles/current/endpoints which only allows 50 requests per 300 seconds } #endif Data()->m_wasTestSkipped = false; } if (Data()->m_trackUnhookedMemory) { auto memHook = GetApiRunnerMemHook(); memHook->LogUnhookedStats(); APIRunner_CleanupLeakCheck(); } CallLuaString("test.summary()"); CleanupLua(); Data()->m_runningTests = false; return S_OK; } HRESULT WaitTillDone() { Data()->m_quit = true; while ( Data()->m_runningTests || !Data()->m_socialDoWorkDone || !Data()->m_mpmDoWorkDone) { pal::Sleep(50); } return S_OK; } #if HC_PLATFORM_IS_MICROSOFT typedef wchar_t char_t; typedef std::wstring string_t; typedef std::wstringstream stringstream_t; #else typedef char char_t; typedef std::string string_t; typedef std::stringstream stringstream_t; #endif static int CharTFromUft8( _In_z_ const char* inArray, _Out_writes_z_(cchOutArray) char_t* outArray, _In_ int cchOutArray ) { #if HC_PLATFORM_IS_MICROSOFT // query for the buffer size auto queryResult = MultiByteToWideChar( CP_UTF8, MB_ERR_INVALID_CHARS, inArray, -1, nullptr, 0 ); if (queryResult > cchOutArray && cchOutArray == 0) { return queryResult; } else if (queryResult == 0 || queryResult > cchOutArray) { throw std::exception("char_t_from_utf8 failed"); } auto conversionResult = MultiByteToWideChar( CP_UTF8, MB_ERR_INVALID_CHARS, inArray, -1, outArray, cchOutArray ); if (conversionResult == 0) { throw std::exception("char_t_from_utf8 failed"); } return conversionResult; #else int len = (int)strlen(inArray); if (len < cchOutArray && outArray != nullptr) { strlcpy(outArray, inArray, len + 1); } else if (cchOutArray > 0) { return 0; } return len + 1; #endif } static string_t StringTFromUtf8(_In_z_ const char* utf8) { #if HC_PLATFORM_IS_MICROSOFT uint64_t cchOutString = static_cast(CharTFromUft8(utf8, nullptr, 0)); string_t out(static_cast(cchOutString - 1), '\0'); CharTFromUft8(utf8, &out[0], static_cast(cchOutString)); return out; #else return string_t(utf8); #endif } static int Utf8FromCharT( _In_z_ const char_t* inArray, _Out_writes_z_(cchOutArray) char* outArray, _In_ int cchOutArray ) { #if HC_PLATFORM_IS_MICROSOFT // query for the buffer size auto queryResult = WideCharToMultiByte( CP_UTF8, WC_ERR_INVALID_CHARS, inArray, -1, nullptr, 0, nullptr, nullptr ); if (queryResult > cchOutArray && cchOutArray == 0) { return queryResult; } else if (queryResult == 0 || queryResult > cchOutArray) { throw std::exception("utf8_from_char_t failed"); } auto conversionResult = WideCharToMultiByte( CP_UTF8, WC_ERR_INVALID_CHARS, inArray, -1, outArray, cchOutArray, nullptr, nullptr ); if (conversionResult == 0) { throw std::exception("utf8_from_char_t failed"); } return conversionResult; #else int len = (int)strlen(inArray); if (len < cchOutArray && outArray != nullptr) { strlcpy(outArray, inArray, len + 1); } else if (cchOutArray > 0) { return 0; } return len + 1; #endif } #if HC_PLATFORM != HC_PLATFORM_IOS web::json::value extract_json_field( _In_ const web::json::value& json, _In_ const string_t& name) { if (json.is_object()) { auto& jsonObj = json.as_object(); auto it = jsonObj.find(name); if (it != jsonObj.end()) { return it->second; } } return web::json::value::null(); } web::json::array extract_json_array( _In_ const web::json::value& jsonValue, _In_ const string_t& arrayName ) { web::json::value field(extract_json_field(jsonValue, arrayName)); if ((!field.is_array()) || field.is_null()) { return web::json::value::array().as_array(); } return field.as_array(); } #endif std::string ApiRunnerReadFile(std::string fileName) { assert(Data() != nullptr); // call ApiRunnerSetupApiExplorer() first #if HC_PLATFORM == HC_PLATFORM_ANDROID // In Android, the test files are considered assets. // These files are copied from the assets folder into the external storage // when the app is installed and launched on a device. // Searching for the file is then done using the filename and doesn't use paths like other platforms std::string fileNameEdited = fileName.substr(0, fileName.find_last_of("\\")); std::string strFilePath = pal::FindFile(fileNameEdited); #else std::string strFilePath = pal::FindFile(fileName); #endif if (strFilePath.empty()) { std::string testPath = "Tests\\" + fileName; strFilePath = pal::FindFile(testPath); } if (strFilePath.empty()) { return ""; } std::ifstream fileStream(strFilePath); std::string str; fileStream.seekg(0, std::ios::end); str.reserve(static_cast(fileStream.tellg())); fileStream.seekg(0, std::ios::beg); str.assign((std::istreambuf_iterator(fileStream)), std::istreambuf_iterator()); return str; } HRESULT ApiRunnerProcessJsonCmds(std::string json) { #if HC_PLATFORM == HC_PLATFORM_IOS ApiRunnerProcessCmdLine("runtests"); #else assert(Data() != nullptr); // call ApiRunnerSetupApiExplorer() first web::json::value jsonCmds = web::json::value::parse(StringTFromUtf8(json.c_str())); auto commands = extract_json_array(jsonCmds, _T("commands")); do { for (const auto& command : commands) { char cmdStr[1024] = { 0 }; Utf8FromCharT(command.as_string().data(), cmdStr, sizeof(cmdStr)); ApiRunnerProcessCmdLine(std::string(cmdStr)); } } while (Data()->m_repeatJsonCmds); #endif return S_OK; } void ApiRunnerSetRunTestsParams(int onlyFileNumber, int minFileNumber, int maxFileNumber) { Data()->m_onlyFileNumber = onlyFileNumber; Data()->m_minFileNumber = minFileNumber; Data()->m_maxFileNumber = maxFileNumber; } HRESULT ApiRunnerRunTests(TestSet set) { assert(Data() != nullptr); // call ApiRunnerSetupApiExplorer() first if (Data()->m_runningTests) { LogToFile("Tests in progress"); return S_OK; } HRESULT hr = RunTestsHelper(set); Data()->m_runTestsHR = hr; Data()->m_runningTests = false; return hr; } void OnCmdMultiDeviceTests(const std::vector&) { ApiRunnerRunTests(TestSet::MultiDevice); } void OnCmdRunBvts(const std::vector&) { ApiRunnerRunTests(TestSet::SingleDeviceBVTs); } void OnCmdRunTests(const std::vector&) { ApiRunnerRunTests(TestSet::SingleDevice); } HRESULT ApiRunnerRunTestWithSetup(std::string testName, bool repeat) { if (Data()->m_runningTests) { LogToFile("Tests in progress"); return E_UNEXPECTED; } Data()->m_runningTests = true; SetupLua(); if (Data()->m_trackUnhookedMemory) { auto memHook = GetApiRunnerMemHook(); memHook->StartMemTracking(); } HRESULT hr = RunSetupScript(); if (SUCCEEDED(hr)) { auto testFullPath = pal::FindFile(testName); if (testFullPath.empty()) { LogToScreen("Can't find %s", testName.c_str()); } if (repeat) { while (true) { hr = RunTestInternal(testName, true); if (FAILED(hr)) { break; } } } else { hr = RunTestInternal(testName, true); } } if (Data()->m_trackUnhookedMemory) { auto memHook = GetApiRunnerMemHook(); memHook->LogUnhookedStats(); APIRunner_CleanupLeakCheck(); } CleanupLua(); Data()->m_runningTests = false; return hr; } void OnCmdClear(const std::vector&) { #if API_EXPLORER_EDITOR ClearScreen(); #endif } void OnCmdLoop(const std::vector&) { Data()->m_repeatJsonCmds = true; } bool IsFailHr(HRESULT hr) { if (SUCCEEDED(hr)) { return false; } for (auto hrIgnore : Data()->m_ignoreHRs) { if (hrIgnore == hr) { return false; } } return true; } void OutputDebugStackTrace(std::vector stackTrace) { LogToScreen("----------- BEGIN STACK TRACE -----------"); for (std::string stack_string : stackTrace) { LogToScreen(stack_string.c_str()); } LogToScreen("----------- END STACK TRACE -----------"); } void LuaStopTestIfFailed(HRESULT hr) { Data()->m_lastError = hr; if (FAILED(hr) && Data()->m_checkHR) { char text[1024]; auto memHook = GetApiRunnerMemHook(); std::vector stackTrace = memHook->GetStackLogLine(); OutputDebugStackTrace(stackTrace); sprintf_s(text, "local hr = 0x%0.8x; test.equal_no_log(hr, 0); test.stopTest();", hr); std::lock_guard lock(Data()->m_luaLock); if (Data()->L != nullptr) { luaL_dostring(Data()->L, text); StopTestFile_Lua(Data()->L); } } } int LuaReturnHR(lua_State *L, HRESULT hr, int extraParams) { LuaStopTestIfFailed(hr); lua_pushnumber(L, hr); return 1 + extraParams; } HRESULT CallLuaStringWithDefault(std::string customFn, std::string defaultFn) { Log(customFn.c_str()); if (!customFn.empty()) { return CallLuaString(customFn); } else { return CallLuaString(defaultFn); } } HRESULT CallLuaString(std::string str) { std::lock_guard lock(Data()->m_luaLock); if (Data()->L != nullptr) { luaL_dostring(Data()->L, str.c_str()); } return S_OK; } HRESULT CallLuaFunctionWithHr(HRESULT hr, std::string fnName) { LuaStopTestIfFailed(hr); if (FAILED(hr) && Data()->m_checkHR) { return hr; } return CallLuaFunction(fnName); } HRESULT CallLuaFunction(std::string fnName) { std::lock_guard lock(Data()->m_luaLock); if (Data()->L == nullptr) { return E_NOINTERFACE; } lua_getglobal(Data()->L, fnName.c_str()); // get function if (lua_isfunction(Data()->L, lua_gettop(Data()->L))) { lua_call(Data()->L, 0, 0); // call the function with 0 arguments, return 0 results } else { return E_FAIL; } return S_OK; } HRESULT CallLuaFunctionWithStringArgs(std::string fnName, std::vector strs) { std::lock_guard lock(Data()->m_luaLock); if (Data()->L == nullptr) { return E_NOINTERFACE; } lua_getglobal(Data()->L, fnName.c_str()); // get function if (lua_isfunction(Data()->L, lua_gettop(Data()->L))) { for (const auto& s : strs) { lua_pushstring(Data()->L, s.c_str()); } lua_call(Data()->L, static_cast(strs.size()), 0); } else { return E_FAIL; } return S_OK; } void ApiRunnerSetupApiExplorerTestsPath(std::string testsPath) { Data()->m_testsPath = testsPath; } void CleanupLua() { MPMStopDoWorkHelper(); if (Data()->m_multiDeviceManager) { Data()->m_multiDeviceManager->StopSessionStateChangePolling(); } { std::lock_guard lock(Data()->m_luaLock); lua_close(Data()->L); // cleanup Lua Data()->L = nullptr; } } void SetupLua() { std::lock_guard lock(Data()->m_luaLock); Data()->L = luaL_newstate(); assert(Data()->L != nullptr); luaL_openlibs(Data()->L); // load Lua base libraries // Setup paths std::string luaPath = pal::GetLuaPath(); lua_getglobal(Data()->L, "package"); lua_getfield(Data()->L, -1, "path"); // get field "path" from table at top of stack (-1) std::string cur_path = lua_tostring(Data()->L, -1); // grab path string from top of stack cur_path.append(";"); cur_path.append(luaPath); lua_pop(Data()->L, 1); // get rid of the string on the stack we just pushed on line 5 lua_pushstring(Data()->L, cur_path.c_str()); // push the new one lua_setfield(Data()->L, -2, "path"); // set the field "path" in table at -2 with value at top of stack lua_pop(Data()->L, 1); // get rid of package table from top of stack RegisterLuaAPIs(); } void ApiRunnerSetupApiExplorer() { LogInit(); InitApiExplorerData(); SetupCommands(); Data()->m_multiDeviceManager = std::make_shared(); } void SetupCommands() { #if API_EXPLORER_EDITOR Data()->m_commands.push_back({ "help", OnCmdHelp, true }); Data()->m_commands.push_back({ "?", OnCmdHelp, true }); #endif Data()->m_commands.push_back({ "exit", OnCmdQuit, false }); Data()->m_commands.push_back({ "quit", OnCmdQuit, true }); Data()->m_commands.push_back({ "q", OnCmdQuit, true }); Data()->m_commands.push_back({ "cls", OnCmdClear, true }); Data()->m_commands.push_back({ "clear", OnCmdClear, false }); Data()->m_commands.push_back({ "loop", OnCmdLoop, false }); Data()->m_commands.push_back({ "runtest", OnCmdRunTest, true }); Data()->m_commands.push_back({ "rt", OnCmdRunTest, false }); Data()->m_commands.push_back({ "repeat", OnCmdRepeatRunTest, true }); Data()->m_commands.push_back({ "runtests", OnCmdRunTests, false }); Data()->m_commands.push_back({ "rts", OnCmdRunTests, true }); Data()->m_commands.push_back({ "run", OnCmdRunTests, true }); Data()->m_commands.push_back({ "runbvts", OnCmdRunBvts, false }); Data()->m_commands.push_back({ "runbarescript", OnCmdRunScript, true }); Data()->m_commands.push_back({ "faultinjection", OnCmdFaultInjection, true }); Data()->m_commands.push_back({ "host", OnCmdHost, true }); Data()->m_commands.push_back({ "join", OnCmdJoin, true }); Data()->m_commands.push_back({ "runmultidevicetests", OnCmdMultiDeviceTests, true }); Data()->m_commands.push_back({ "memtrack", OnCmdMemTrack, true }); } bool ApiRunnerIsRunningTests() { return !Data()->m_runningTests; } HRESULT ApiRunnerGetTestResult() { return Data()->m_runTestsHR; } uint64_t GetUint64FromLua(lua_State *L, int paramNum, uint64_t defaultArg) { uint64_t t = defaultArg; if (lua_gettop(L) >= paramNum) { t = (uint64_t)lua_tointeger(L, paramNum); } return t; } uint32_t GetUint32FromLua(lua_State *L, int paramNum, uint32_t defaultArg) { return (uint32_t)GetUint64FromLua(L, paramNum, defaultArg); } bool GetBoolFromLua(lua_State *L, int paramNum, bool defaultArg) { bool t = defaultArg; if (lua_gettop(L) >= paramNum) { t = (bool)lua_tonumber(L, paramNum); } return t; } std::string GetStringFromLua(lua_State *L, int paramNum, std::string defaultArg) { std::string t = defaultArg; if (lua_gettop(L) >= paramNum) { t = lua_tostring(L, paramNum); } return t; } void LogCat(bool logToFile, _Printf_format_string_ char const* format, ...) { char message[8000] = {}; va_list varArgs{}; va_start(varArgs, format); pal::vsprintf(message, 8000, format, varArgs); va_end(varArgs); std::string catMessage = message; { std::lock_guard lock(Data()->m_catMessageLock); Data()->m_catMessage += catMessage; if (Data()->m_catMessage.find_first_of('\n') != std::string::npos) { std::vector tokens; const char seperators[32] = "\n"; char* tok; char *next_token = NULL; char szInput[16000] = { 0 }; pal::strcpy(szInput, 16000, Data()->m_catMessage.c_str()); tok = pal::strtok(szInput, seperators, &next_token); while (tok != nullptr) { tokens.push_back(tok); tok = pal::strtok(0, seperators, &next_token); } for (size_t iToken = 0; iToken < tokens.size(); iToken++) { if (logToFile) { LogToFile(tokens[iToken].c_str()); } else { LogToScreen(tokens[iToken].c_str()); } } Data()->m_catMessage = ""; } } } #if HC_PLATFORM == HC_PLATFORM_IOS void SetupAPNSRegistrationToken(std::string registrationToken) { Data()->apnsToken = registrationToken; } #endif #if HC_PLATFORM == HC_PLATFORM_ANDROID void SetupAndroidContext( JavaVM *javaVM, jobject context, jclass mainActivityClass, jobject mainActivityInstance, jmethodID getApplicationContext) { Data()->javaVM = javaVM; Data()->m_mainActivityClass = mainActivityClass; Data()->m_getApplicationContext = getApplicationContext; Data()->m_mainActivityClassInstance = mainActivityInstance; Data()->applicationContext = context; Data()->initArgs = {}; Data()->initArgs.applicationContext = context; Data()->initArgs.javaVM = javaVM; } #endif ================================================ FILE: Tests/ApiExplorer/Shared/log.cpp ================================================ #include "pch.h" #if HC_PLATFORM == HC_PLATFORM_WIN32 #include #endif #if HC_PLATFORM == HC_PLATFORM_ANDROID #include "pal.h" #endif #include std::string g_logFilePath; void LogInit() { #if HC_PLATFORM == HC_PLATFORM_WIN32 char path[MAX_PATH + 1] = {0}; SHGetFolderPathA(NULL, CSIDL_DESKTOP, NULL, 0, path); g_logFilePath = path; g_logFilePath += "\\"; #endif #if HC_PLATFORM == HC_PLATFORM_UWP Platform::String^ localfolder = Windows::Storage::ApplicationData::Current->LocalFolder->Path; //for local saving for future //convert folder name from wchar to ascii std::wstring folderNameW(localfolder->Begin()); std::string folderNameA(folderNameW.begin(), folderNameW.end()); g_logFilePath = folderNameA; g_logFilePath += "\\"; #endif g_logFilePath += "apirunner-log.txt"; if (!g_logFilePath.empty()) { std::remove(g_logFilePath.c_str()); // erase existing file } } void LogToFile(_Printf_format_string_ char const* format, ...) { char message[4096] = {}; va_list varArgs{}; va_start(varArgs, format); pal::vsprintf(message, 4096, format, varArgs); va_end(varArgs); if (!g_logFilePath.empty()) { std::ofstream file{ g_logFilePath.c_str(), std::ios::app }; if (file.is_open()) { file << message << std::endl; file.close(); } } #if HC_PLATFORM_IS_MICROSOFT OutputDebugStringA(message); OutputDebugStringA("\r\n"); #endif } #if HC_PLATFORM != HC_PLATFORM_UWP && HC_PLATFORM != HC_PLATFORM_WIN32 && HC_PLATFORM != HC_PLATFORM_GDK && HC_PLATFORM != HC_PLATFORM_XDK && HC_PLATFORM != HC_PLATFORM_IOS && HC_PLATFORM != HC_PLATFORM_ANDROID void LogToScreen(_Printf_format_string_ char const* format, ...) { // Stock impl that just logs to file // Hook up UI logs for each platform based on platform specific UI display calls char message[8000] = {}; va_list varArgs{}; va_start(varArgs, format); pal::vsprintf(message, 4096, format, varArgs); va_end(varArgs); LogToFile(message); } #endif ================================================ FILE: Tests/ApiExplorer/Shared/lua-include.cpp ================================================ #define _CRT_SECURE_NO_WARNINGS #include "pch.h" #if HC_PLATFORM != HC_PLATFORM_ANDROID #pragma warning (push) #pragma warning(disable : 4334) #pragma warning(disable : 4365) #pragma warning(disable : 4242) #pragma warning(disable : 4061) #pragma warning(disable : 4191) #pragma warning(disable : 4310) #endif #include "lapi.c" #include "lauxlib.c" #include "lbaselib.c" #if HC_PLATFORM != HC_PLATFORM_ANDROID #include "lbitlib.c" #endif #include "lcode.c" #include "lcorolib.c" #include "lctype.c" #include "ldblib.c" #include "ldebug.c" #include "ldo.c" #include "ldump.c" #include "lfunc.c" #include "lgc.c" #include "linit.c" #include "liolib.c" #include "llex.c" #include "lmathlib.c" #include "lmem.c" #include "loadlib.c" #include "lobject.c" #include "lopcodes.c" #include "loslib.c" #include "lparser.c" #include "lstate.c" #include "lstring.c" #include "lstrlib.c" #include "ltable.c" #include "ltablib.c" #include "ltm.c" #include "lundump.c" #include "lutf8lib.c" #include "lvm.c" #include "lzio.c" #if HC_PLATFORM != HC_PLATFORM_ANDROID #pragma warning (pop) #endif ================================================ FILE: Tests/ApiExplorer/Shared/mem_hook.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "mem_hook.h" #define TRACK_UNHOOKED_ALLOCS 1 #if HC_PLATFORM == HC_PLATFORM_WIN32 || (HC_PLATFORM == HC_PLATFORM_GDK && !_GAMING_XBOX) #include #include #include #include #define TRACE_MAX_STACK_FRAMES 1024 #define TRACE_MAX_FUNCTION_NAME_LENGTH 1024 #endif #if HC_PLATFORM == HC_PLATFORM_ANDROID #include "runner.h" #endif ApiRunerMemHook* g_mem = new ApiRunerMemHook(); bool g_rawMemHookInitTracking{ false }; ApiRunerMemHook* GetApiRunnerMemHook() { return g_mem; } _Ret_maybenull_ _Post_writable_byte_size_(size) void* STDAPIVCALLTYPE ApiRunnerMemManagerMemAlloc(_In_ size_t size, _In_ HCMemoryType) { auto mem = GetApiRunnerMemHook(); return mem->AllocMem(size); } void STDAPIVCALLTYPE ApiRunnerMemManagerMemFree(_In_ _Post_invalid_ void* pointer, _In_ HCMemoryType) { auto mem = GetApiRunnerMemHook(); mem->DeleteMem(pointer); } _Ret_maybenull_ _Post_writable_byte_size_(size) void* ApiRunnerMemManagerXalMemAlloc(size_t size, uint32_t) { auto mem = GetApiRunnerMemHook(); return mem->AllocMem(size); } void ApiRunnerMemManagerXalMemFree(_In_ _Post_invalid_ void* pointer, uint32_t) { auto mem = GetApiRunnerMemHook(); mem->DeleteMem(pointer); } bool ApiRunerMemHook::IsMemTrackingStarted() { return m_startMemTracking && m_refCountInsideAlloc == 0; } ApiRunerMemHook::ApiRunerMemHook() { m_refCountInsideAlloc = 0; #if HC_PLATFORM == HC_PLATFORM_WIN32 || HC_PLATFORM == HC_PLATFORM_GDK m_process = GetCurrentProcess(); SymSetOptions(SYMOPT_LOAD_LINES | SYMOPT_DEBUG | SYMOPT_DEFERRED_LOADS | SYMOPT_CASE_INSENSITIVE); SymInitialize(m_process, NULL, TRUE); #endif } HRESULT ApiRunerMemHook::StartMemTracking() { if (m_startMemTracking) return S_OK; // libHttpClient hooks does not need to be called if XBL or XAL is hooked but for extra insurance during tests HRESULT hr = HCMemSetFunctions(&ApiRunnerMemManagerXalMemAlloc, &ApiRunnerMemManagerXalMemFree); assert(SUCCEEDED(hr)); if (FAILED(hr)) return hr; hr = XblMemSetFunctions(&ApiRunnerMemManagerMemAlloc, &ApiRunnerMemManagerMemFree); assert(SUCCEEDED(hr)); if (FAILED(hr)) return hr; ResetStats(); m_startMemTracking = true; g_rawMemHookInitTracking = true; return S_OK; } HRESULT ApiRunerMemHook::StopMemTracking() { if (!m_startMemTracking) return S_OK; HRESULT hr = HCMemSetFunctions(nullptr, nullptr); assert(SUCCEEDED(hr)); if (FAILED(hr)) return hr; hr = XblMemSetFunctions(nullptr, nullptr); assert(SUCCEEDED(hr)); if (FAILED(hr)) return hr; m_startMemTracking = false; g_rawMemHookInitTracking = false; return S_OK; } std::vector ApiRunerMemHook::GetStackLogLine() { #if HC_PLATFORM == HC_PLATFORM_WIN32 || HC_PLATFORM == HC_PLATFORM_GDK StackInfo stackInfo{ 0 }; GetStackTrace(stackInfo); std::vector logs; logs.reserve(static_cast(stackInfo.stackSize)); for (int i = 0; i < stackInfo.stackSize; i++) { char sz[1024]; if (stackInfo.szStack[i][0] != 0) { sprintf_s(sz, "%.32s in %s: line: %lu", stackInfo.szStack[i], stackInfo.szFileNames[i], stackInfo.lineNumber[i]); } logs.push_back(sz); } return logs; #else return std::vector(); #endif } void ApiRunerMemHook::AssertOnAllocOfId(uint64_t id) { m_assertId = id; } void* ApiRunerMemHook::AllocMem(_In_ size_t size) { #if TRACK_UNHOOKED_ALLOCS == 1 m_refCountInsideAlloc++; std::lock_guard guard(m_lock); #endif void* ptr = malloc(size); #if TRACK_UNHOOKED_ALLOCS == 1 m_allocSizeMap[ptr] = size; m_allocated += size; m_allocId++; m_allocIdMap[ptr] = m_allocId; if (m_allocId == m_assertId) { LogToFile("Breakpoint here"); } m_mapStackLog[ptr] = GetStackLogLine(); m_refCountInsideAlloc--; #endif return ptr; } bool ApiRunerMemHook::IsMemHooked(void* p) { #if TRACK_UNHOOKED_ALLOCS == 1 std::lock_guard guard(m_lock); auto it = m_allocSizeMap.find(p); return (it != m_allocSizeMap.end()); #else return false; #endif } void ApiRunerMemHook::DeleteMem(_In_ _Post_invalid_ void* pointer) { #if TRACK_UNHOOKED_ALLOCS == 1 std::lock_guard guard(m_lock); uint64_t size = 0; auto it = m_allocSizeMap.find(pointer); if (it != m_allocSizeMap.end()) { size = it->second; m_allocSizeMap.erase(it); } else { // assert(false); // Add the following ifdef above line 53 in internal_mem.h // if this assertion is reached. This will help track down what process // is being freed incorrectly. #if 0 static int s_id = 0; std::wstringstream msg; msg << L"s_id:" << s_id << L" p: 0x" << std::hex << p << "\n"; OutputDebugString(msg.str().c_str()); s_id++; #endif } m_allocDeleted += size; #endif free(pointer); } void ApiRunerMemHook::ResetStats() { std::lock_guard guard(m_lock); m_allocated = 0; m_allocDeleted = 0; m_allocId = 0; m_allocSizeMap.clear(); m_mapStackLog.clear(); } void MemHookLog(_Printf_format_string_ char const* format, ...) { // Stock impl that just logs to file // Hook up UI logs for each platform based on platform specific UI display calls #if HC_PLATFORM_IS_MICROSOFT char message[8000] = {}; va_list varArgs{}; va_start(varArgs, format); pal::vsprintf(message, 4096, format, varArgs); va_end(varArgs); OutputDebugStringA(message); OutputDebugStringA("\n"); #else UNREFERENCED_PARAMETER(format); #endif } void ApiRunerMemHook::LogLeaks() { std::lock_guard guard(m_lock); LogToScreen("Leaks: %d mem leaks found", m_allocSizeMap.size()); // Using MemHookLog since long LogToScreen lines can causes mem allocations and thus doesn't work well in this module MemHookLog("Leaks: -- START --"); for (auto& it : m_allocSizeMap) { void* ptr = it.first; uint64_t size = it.second; auto& stackLog = m_mapStackLog[ptr]; auto& id = m_allocIdMap[ptr]; if (stackLog.size() >= 4) MemHookLog("[%d] %0.8x: %d from %s", id, ptr, size, stackLog[4].c_str()); else MemHookLog("[%d] %0.8x: %d", id, ptr, size); } MemHookLog("Leaks: -- END --"); MemHookLog("== Leak CSV Start =="); for (auto& it : m_allocSizeMap) { void* ptr = it.first; uint64_t size = it.second; auto& stackLog = m_mapStackLog[ptr]; auto& id = m_allocIdMap[ptr]; int stackLineId{ 0 }; for (auto& stackLine : stackLog) { stackLineId++; MemHookLog("%d,%0.8x,%d,%d,%s", id, ptr, size, stackLineId, stackLine.c_str()); } } MemHookLog("== Leak CSV End =="); } void ApiRunerMemHook::LogStats(const std::string& name) { std::lock_guard guard(m_lock); MemHookLog("%s mem: %u outstanding alloc, (%u total / %u deleted)", name.c_str(), m_allocated - m_allocDeleted, m_allocated, m_allocDeleted); } // IsStackInsideCallback() needs non-allocating case insenstive string compare so using manual impl char* stristr(const char* str1, const char* str2) { const char* p1 = str1; const char* p2 = str2; const char* r = *p2 == 0 ? str1 : 0; while (*p1 != 0 && *p2 != 0) { if (tolower((unsigned char)*p1) == tolower((unsigned char)*p2)) { if (r == 0) { r = p1; } p2++; } else { p2 = str2; if (r != 0) { p1 = r + 1; } if (tolower((unsigned char)*p1) == tolower((unsigned char)*p2)) { r = p1; p2++; } else { r = 0; } } p1++; } return *p2 == 0 ? (char*)r : 0; } bool ApiRunerMemHook::IsStackInsideCallback(const char* stackSymbolName, const char* filePath) { const char* found; found = stristr(stackSymbolName, "ExampleWebsocketMessageReceived"); if (found != nullptr) return true; found = stristr(stackSymbolName, "ExampleWebsocketClosed"); if (found != nullptr) return true; found = stristr(stackSymbolName, "WaitForTestResult"); if (found != nullptr) return true; found = stristr(filePath, "\\Include\\xsapi-cpp\\"); if (found != nullptr) return true; found = stristr(stackSymbolName, " list; for (int i = 0; i < m_rawAllocMemorySpots; i++) { std::stringstream msg; msg << m_allocTrace[i] << "(" << m_allocTraceLine[i] << ")"; list.push_back(msg.str()); } std::sort(list.begin(), list.end()); list.erase(std::unique(list.begin(), list.end()), list.end()); for(std::string s : list) { LogToScreen(s.c_str()); } g_rawMemHookInitTracking = true; } bool ApiRunerMemHook::IsStackInXSAPI(StackInfo& stackInfo) { #if HC_PLATFORM == HC_PLATFORM_WIN32 || (HC_PLATFORM == HC_PLATFORM_GDK && !_GAMING_XBOX) GetStackTrace(stackInfo); bool foundInXsapi = false; for (int i = 0; i < stackInfo.stackSize; i++) { stackInfo.isInXsapi[i] = IsFilePathInXSAPI(stackInfo.szFileNames[i]); if (stackInfo.isInXsapi[i]) { foundInXsapi = true; } } if (foundInXsapi) { bool foundCallback = IsStackFramesInsideCallback(&stackInfo); if (foundCallback) { return false; } bool foundTestCodeFirst = IsStackFramesInsideTestCode(&stackInfo); if (foundTestCodeFirst) { return false; } return true; } #else UNREFERENCED_PARAMETER(stackInfo); #endif return false; } void ApiRunerMemHook::GetStackTrace(StackInfo &stackInfo) { #if HC_PLATFORM == HC_PLATFORM_WIN32 || (HC_PLATFORM == HC_PLATFORM_GDK && !_GAMING_XBOX) std::lock_guard guard(m_lock); void* stack[TRACE_MAX_STACK_FRAMES] = { 0 }; WORD numberOfFrames = CaptureStackBackTrace(0, TRACE_MAX_STACK_FRAMES, stack, NULL); stackInfo.stackSize = numberOfFrames < 64 ? numberOfFrames : 64; for (int i = 0; i < stackInfo.stackSize; i++) { stackInfo.szFileNames[i][0] = 0; stackInfo.szStack[i][0] = 0; stackInfo.lineNumber[i] = 0; DWORD64 address = (DWORD64)(stack[i]); char buffer[sizeof(SYMBOL_INFO) + TRACE_MAX_FUNCTION_NAME_LENGTH * sizeof(CHAR)] = { 0 }; PSYMBOL_INFO symbolInfo = (PSYMBOL_INFO)buffer; symbolInfo->MaxNameLen = TRACE_MAX_FUNCTION_NAME_LENGTH - 1; symbolInfo->SizeOfStruct = sizeof(SYMBOL_INFO); SymFromAddr(m_process, address, NULL, symbolInfo); DWORD displacement; char line64Buffer[sizeof(IMAGEHLP_LINE64)] = { 0 }; IMAGEHLP_LINE64 *line = (IMAGEHLP_LINE64*)line64Buffer; line->SizeOfStruct = sizeof(IMAGEHLP_LINE64); if (SymGetLineFromAddr64(m_process, address, &displacement, line)) { #if HC_PLATFORM_IS_MICROSOFT strcpy_s(stackInfo.szFileNames[i], line->FileName); strcpy_s(stackInfo.szStack[i], symbolInfo->Name); #else strcpy(stackInfo.szFileNames[i], line->FileName); strcpy(stackInfo.szStack[i], symbolInfo->Name); #endif stackInfo.lineNumber[i] = line->LineNumber; } } #else memset(&stackInfo, 0, sizeof(StackInfo)); #endif } #if HC_PLATFORM == HC_PLATFORM_WIN32 || (HC_PLATFORM == HC_PLATFORM_GDK && !_GAMING_XBOX) bool ApiRunerMemHook::IsStackFramesInsideTestCode(StackInfo* pStackInfo) { for (int i = 0; i < pStackInfo->stackSize; i++) { if (pStackInfo->isInXsapi[i]) return false; const char* found = stristr(pStackInfo->szFileNames[i], "\\apiexplorer\\"); if (found != nullptr) return true; } return false; } bool ApiRunerMemHook::IsStackFramesInsideCallback(StackInfo* pStackInfo) { for (int i = 0; i < pStackInfo->stackSize; i++) { if (IsStackInsideCallback(pStackInfo->szStack[i], pStackInfo->szFileNames[i])) { return true; } } return false; } #endif #if TRACK_UNHOOKED_ALLOCS == 1 using namespace std; void* operator new(size_t size) { if (g_rawMemHookInitTracking) { auto mem = GetApiRunnerMemHook(); if (mem->IsMemTrackingStarted()) { StackInfo stackInfo{ 0 }; bool isInXSAPI = mem->IsStackInXSAPI(stackInfo); if (isInXSAPI) { for (int i = 0; i < stackInfo.stackSize; i++) { if (stackInfo.isInXsapi[i]) { #if HC_PLATFORM_IS_MICROSOFT strcpy_s(mem->m_allocTrace[mem->m_rawAllocMemorySpots], stackInfo.szFileNames[i]); #else strcpy(mem->m_allocTrace[mem->m_rawAllocMemorySpots], stackInfo.szFileNames[i]); #endif mem->m_allocTraceLine[mem->m_rawAllocMemorySpots] = stackInfo.lineNumber[i]; break; } } mem->m_rawAllocMemory += size; mem->m_rawAllocMemorySpots++; if (mem->m_rawAllocMemorySpots >= APIRUNNER_MAX_UNHOOKED_MEM_TRACKED) { mem->m_rawAllocMemorySpots = APIRUNNER_MAX_UNHOOKED_MEM_TRACKED - 1; } } } } void* p = malloc(size); return p; } void operator delete(void * p) { if (g_rawMemHookInitTracking) { auto mem = GetApiRunnerMemHook(); if (mem->IsMemHooked(p)) { // This shouldn't be mem hooked assert(false); } } free(p); } #endif ================================================ FILE: Tests/ApiExplorer/Shared/mem_hook.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "pch.h" #if HC_PLATFORM == HC_PLATFORM_WIN32 || HC_PLATFORM == HC_PLATFORM_GDK #include #endif struct StackInfo { int stackSize; bool isInXsapi[64]; DWORD lineNumber[64]; char szFileNames[64][1024]; char szStack[64][1024]; }; #define APIRUNNER_MAX_UNHOOKED_MEM_TRACKED 1024 class ApiRunerMemHook { public: ApiRunerMemHook(); HRESULT StartMemTracking(); HRESULT StopMemTracking(); bool IsMemTrackingStarted(); void ResetStats(); void LogStats(const std::string& name); void LogLeaks(); void AssertOnAllocOfId(uint64_t id); _Ret_maybenull_ _Post_writable_byte_size_(size) void* AllocMem(_In_ size_t size); void DeleteMem(_In_ _Post_invalid_ void* pointer); std::vector GetStackLogLine(); bool IsStackInXSAPI(StackInfo& stackInfo); bool IsMemHooked(void* p); void GetStackTrace(StackInfo &stackInfo); #if HC_PLATFORM == HC_PLATFORM_WIN32 || HC_PLATFORM == HC_PLATFORM_GDK bool IsStackFramesInsideTestCode(StackInfo* pStackInfo); bool IsStackFramesInsideCallback(StackInfo* pStackInfo); #endif bool IsFilePathInXSAPI(const char* filePath); bool IsStackInsideCallback(const char* stackSymbolName, const char* filePath); void LogUnhookedStats(); uint64_t m_rawAllocMemory{ 0 }; uint64_t m_rawAllocMemorySpots{ 0 }; char m_allocTrace[APIRUNNER_MAX_UNHOOKED_MEM_TRACKED][1024] { 0 }; unsigned long m_allocTraceLine[APIRUNNER_MAX_UNHOOKED_MEM_TRACKED] { 0 }; private: #if HC_PLATFORM == HC_PLATFORM_WIN32 || HC_PLATFORM == HC_PLATFORM_GDK HANDLE m_process{ nullptr }; #endif bool m_startMemTracking{ false }; uint64_t m_allocated{ 0 }; uint64_t m_allocDeleted{ 0 }; uint64_t m_unhookedAllocated{ 0 }; uint64_t m_unhookedDeleted{ 0 }; uint64_t m_allocId{ 0 }; uint64_t m_assertId{ 0 }; std::recursive_mutex m_lock; std::map< void*, uint64_t > m_allocSizeMap; std::map< void*, uint64_t > m_allocIdMap; std::map< void*, std::vector > m_mapStackLog; std::atomic m_refCountInsideAlloc; }; ApiRunerMemHook* GetApiRunnerMemHook(); ================================================ FILE: Tests/ApiExplorer/Shared/multidevice.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #if HC_PLATFORM_IS_MICROSOFT #pragma warning( push ) #pragma warning( disable : 4365 ) #pragma warning( disable : 4061 ) #pragma warning( disable : 4996 ) #endif #include "rapidjson/document.h" #if HC_PLATFORM_IS_MICROSOFT #pragma warning( pop ) #endif #if HC_PLATFORM == HC_PLATFORM_ANDROID #include #include "multidevice.h" #include "api_explorer.h" #include "runner.h" #include "pal.h" #endif #if __has_include("apirunnercloudfns.h") #include "apirunnercloudfns.h" #else #define APIRUNNER_GET_JOINABLE "" #define APIRUNNER_HOST_SESSION "" #define APIRUNNER_JOIN_SESSION "" #define APIRUNNER_SET_STATE "" #define APIRUNNER_GET_STATE "" #endif #define RETURN_HR_IF_FAILED(expr) { HRESULT exprResult{ expr }; if (FAILED(exprResult)) { return exprResult; } } #if HC_PLATFORM_IS_MICROSOFT #define SPRINTF_XPLAT sprintf_s #else #define SPRINTF_XPLAT sprintf #endif using namespace utility; static std::thread s_watchThread{}; ApiRunnerMultiDeviceManager::~ApiRunnerMultiDeviceManager() { // TODO: add back when HC init and cleanup is ref counted //if (m_isHCInitialized) //{ // HCCleanup(); //} } std::string ExtractJsonString( _In_ const rapidjson::Document& jsonDoc, _In_ const std::string& stringName) { if (jsonDoc.IsObject() && jsonDoc.HasMember(stringName.c_str())) { const rapidjson::Value& field = jsonDoc[stringName.c_str()]; if (field.IsString()) { return field.GetString(); } } return ""; } HRESULT ApiRunnerMultiDeviceManager::JoinOpenSession( _In_ const std::string& name, _In_ const std::string& xuid, _In_ const std::string& gamertag, _In_ std::function handler) { auto manager = Data()->m_multiDeviceManager; manager->m_nextCallTime = datetime::utc_now() - datetime::from_seconds(1); manager->m_callGetJoinable = true; LogToScreen("MultiDevice: Looking for test session to join..."); while (!manager->Joined()) { int64_t delta = static_cast(manager->m_nextCallTime.to_interval()) - static_cast(datetime::utc_now().to_interval()); if (delta < 0 && manager->m_callGetJoinable) { manager->m_callGetJoinable = false; manager->GetJoinable( name, [xuid, gamertag, handler](HRESULT, uint32_t, const std::string& joinableSessionId, const std::string& client1xuid, const std::string& client1gt) { auto manager = Data()->m_multiDeviceManager; if (!joinableSessionId.empty() && !xuid.empty() && !client1gt.empty()) { LogToScreen("MultiDevice: Found test session: %s", joinableSessionId.c_str()); LogToScreen("MultiDevice: Remote XUID: %s GamerTag: %s", client1xuid.c_str(), client1gt.c_str()); manager->JoinSession(joinableSessionId, xuid, gamertag, [handler](HRESULT hr) { if (SUCCEEDED(hr)) { handler(hr); } else { auto manager = Data()->m_multiDeviceManager; manager->m_nextCallTime = datetime::utc_now() + datetime::from_seconds(1); manager->m_callGetJoinable = true; } }); } else { manager->m_nextCallTime = datetime::utc_now() + datetime::from_seconds(1); manager->m_callGetJoinable = true; } }); } pal::Sleep(100); } return S_OK; } HRESULT ApiRunnerMultiDeviceManager::GetJoinable( _In_ const std::string& name, _In_ std::function handler ) { std::vector> headers; headers.push_back(std::vector{"name", name}); HRESULT hr = MakeCall("GET", APIRUNNER_GET_JOINABLE, headers, [handler](HRESULT hr, uint32_t statusCode, const std::string& responseString) { std::string sessionId; std::string xuid; std::string gt; if (SUCCEEDED(hr) && statusCode == 200 && responseString.length() > 0) { rapidjson::Document jsonDoc; jsonDoc.Parse(responseString.c_str()); if (!jsonDoc.HasParseError()) { // Example response: //{ // "id": "637123016212526708", // "client1xuid" : "123", // "client1gt" : "ninja", // "client2xuid" : null, // "client2gt" : null, // "props" : null //} sessionId = ExtractJsonString(jsonDoc, "id"); xuid = ExtractJsonString(jsonDoc, "client1xuid"); gt = ExtractJsonString(jsonDoc, "client1gt"); } } auto manager = Data()->m_multiDeviceManager; manager->m_isInitialized = true; if (handler) { handler(hr, statusCode, sessionId, xuid, gt); } }); if (FAILED(hr)) { LogToScreen("MultiDevice: Get joinable test session failed %0.8x", hr); } return hr; } HRESULT ApiRunnerMultiDeviceManager::Init( _In_ std::function onStateChangedHandler ) { m_doPoll = false; m_onStateChangedHandler = std::move(onStateChangedHandler); RETURN_HR_IF_FAILED(HCInitialize(nullptr)); m_isHCInitialized = true; return S_OK; } HRESULT ApiRunnerMultiDeviceManager::WaitTillPeerConnects() { auto manager = Data()->m_multiDeviceManager; manager->m_nextCallTime = datetime::utc_now() - datetime::from_seconds(1); manager->m_callGetJoinable = true; std::string curSessionId = manager->m_sessionId; std::string name = manager->m_sessionName; LogToScreen("MultiDevice: Waiting for peer to connect to test session..."); while (!manager->Joined()) { int64_t delta = static_cast(manager->m_nextCallTime.to_interval()) - static_cast(datetime::utc_now().to_interval()); if (delta < 0) { manager->m_nextCallTime = datetime::utc_now() + datetime::from_seconds(1); std::string state = manager->GetSessionStateString(); if (state.length() > 0) { rapidjson::Document jsonDoc; jsonDoc.Parse(state.c_str()); if (!jsonDoc.HasParseError()) { std::string client2xuid = ExtractJsonString(jsonDoc, "client2xuid"); if (!client2xuid.empty()) { std::string client2gt = ExtractJsonString(jsonDoc, "client2gt"); LogToScreen("MultiDevice: Connected to peer XUID: %s GamerTag: %s...", client2xuid.c_str(), client2gt.c_str()); manager->m_isJoined = true; } } } } pal::Sleep(100); } if (manager->Abort()) { return E_ABORT; } return S_OK; } uint64_t ApiRunnerMultiDeviceManager::GetLocalXuid() { std::string xuid = IsHost() ? m_state.client1xuid : m_state.client2xuid; return strtoull(xuid.c_str(), nullptr, 0); } uint64_t ApiRunnerMultiDeviceManager::GetRemoteXuid() { std::string xuid = IsHost() ? m_state.client2xuid : m_state.client1xuid; return strtoull(xuid.c_str(), nullptr, 0); } HRESULT ApiRunnerMultiDeviceManager::HostSession( _In_ const std::string& name, _In_ const std::string& xuid, _In_ const std::string& gamertag, _In_ std::function onResponse) { LogToScreen("MultiDevice: Hosting test session as XUID: %s GamerTag: %s...", xuid.c_str(), gamertag.c_str()); std::vector> headers; headers.push_back(std::vector{"name", name}); headers.push_back(std::vector{"xuid", xuid}); headers.push_back(std::vector{"gt", gamertag}); HRESULT hr = MakeCall("POST", APIRUNNER_HOST_SESSION, headers, [onResponse, name](HRESULT hr, uint32_t statusCode, const std::string& responseString) { auto manager = Data()->m_multiDeviceManager; if (SUCCEEDED(hr) && statusCode == 200 && responseString.length() > 0) { rapidjson::Document jsonDoc; jsonDoc.Parse(responseString.c_str()); if (!jsonDoc.HasParseError()) { // Example response: //{ // "id": "637123065073534386", // "client1xuid" : "123", // "client1gt" : "ninja", // "log" : "Deleted: 1" //} { std::lock_guard lock(manager->m_mutex); manager->m_sessionId = ExtractJsonString(jsonDoc, "id"); manager->m_sessionState = responseString; manager->m_sessionName = name; manager->m_hosted = true; LogToScreen("MultiDevice: Hosted test session %s", manager->m_sessionId.c_str()); } manager->StartSessionStateChangePolling(); if (onResponse) { onResponse(S_OK); } } } else { LogToScreen("MultiDevice: Hosting test session failed. 0x%0.8x. HTTP status: %d", hr, statusCode); if (onResponse) { onResponse(E_FAIL); } } }); if (FAILED(hr)) { LogToScreen("MultiDevice: Hosting test session failed %0.8x", hr); if (onResponse) { onResponse(hr); } } return hr; } HRESULT ApiRunnerMultiDeviceManager::JoinSession( _In_ const std::string& sessionId, _In_ const std::string& xuid, _In_ const std::string& gamertag, _In_ std::function handler) { std::vector> headers; headers.push_back(std::vector{"id", sessionId}); headers.push_back(std::vector{"xuid", xuid}); headers.push_back(std::vector{"gt", gamertag}); HRESULT hr = MakeCall("POST", APIRUNNER_JOIN_SESSION, headers, [handler](HRESULT hr, uint32_t statusCode, const std::string& responseString) { // Could return 409 if there's another client joined auto manager = Data()->m_multiDeviceManager; if (SUCCEEDED(hr) && statusCode == 200 && responseString.length() > 0) { rapidjson::Document jsonDoc; jsonDoc.Parse(responseString.c_str()); if (!jsonDoc.HasParseError()) { // Example: //{ // "id": "637123065073534386", // "client1xuid" : "123", // "client1gt" : "ninja", // "client2xuid" : "456", // "client2gt" : "karate", // "props" : null //} { std::lock_guard lock(manager->m_mutex); manager->m_sessionId = ExtractJsonString(jsonDoc, "id"); manager->m_sessionState = responseString; manager->m_sessionName = ExtractJsonString(jsonDoc, "name"); LogToScreen("MultiDevice: Joining test session %s", manager->m_sessionId.c_str()); manager->StartSessionStateChangePolling(); manager->m_isJoined = true; manager->m_hosted = false; } if (handler) { handler(S_OK); } } } if (manager->m_sessionId.empty()) { LogToScreen("MultiDevice: Joining test session failed. 0x%0.8x. HTTP status: %d", hr, statusCode); if (handler) { handler(E_FAIL); } } }); if (FAILED(hr)) { LogToScreen("MultiDevice: Joining test session failed %0.8x", hr); if (handler) { handler(hr); } } return hr; } HRESULT ApiRunnerMultiDeviceManager::SetSessionState( _In_ const std::string& key, _In_ const std::string& value, _In_ std::function /*handler*/) { std::vector> headers; if (value.length() == 0) { LogToScreen("MultiDevice: Deleting %s key in cloud DB", key.c_str()); } else { LogToScreen("MultiDevice: Setting %s key to %s in cloud DB", key.c_str(), value.c_str()); } headers.push_back(std::vector{"id", m_sessionId}); headers.push_back(std::vector{"key", key}); headers.push_back(std::vector{"value", value}); HRESULT hr = MakeCall("POST", APIRUNNER_SET_STATE, headers, [](HRESULT hr, uint32_t statusCode, const std::string& responseString) { auto manager = Data()->m_multiDeviceManager; if (SUCCEEDED(hr) && statusCode == 200 && responseString.length() > 0) { rapidjson::Document jsonDoc; jsonDoc.Parse(responseString.c_str()); if (!jsonDoc.HasParseError()) { std::lock_guard lock(manager->m_mutex); manager->m_sessionState = responseString; } } else { LogToScreen("Set test session state failed %0.8x %d", hr, statusCode); } }); if (FAILED(hr)) { LogToScreen("Set test session state failed %0.8x", hr); } return hr; } HRESULT ApiRunnerMultiDeviceManager::StartSessionStateChangePolling() { if (!m_doPoll) { LogToScreen("Watching test session for changes"); m_doPoll = true; s_watchThread = std::thread([]() { auto manager = Data()->m_multiDeviceManager; while (manager->IsDoPoll()) { manager->RefreshSessionState(); pal::Sleep(1000); } LogToScreen("Stopping watch thread"); }); } return S_OK; } HRESULT ApiRunnerMultiDeviceManager::StopSessionStateChangePolling() { if (m_doPoll) { LogToScreen("Stopped watching for test session changes"); m_doPoll = false; s_watchThread.join(); } return S_OK; } void ApiRunnerMultiDeviceManager::ParseState(const std::string& responseString) { rapidjson::Document jsonDoc; jsonDoc.Parse(responseString.c_str()); ServerState oldState; { std::lock_guard lock(m_mutex); oldState = std::move(m_state); m_state.client1xuid = ExtractJsonString(jsonDoc, "client1xuid"); m_state.client1gt = ExtractJsonString(jsonDoc, "client1gt"); m_state.client2xuid = ExtractJsonString(jsonDoc, "client2xuid"); m_state.client2gt = ExtractJsonString(jsonDoc, "client2gt"); m_state.id = ExtractJsonString(jsonDoc, "id"); } std::string props = ExtractJsonString(jsonDoc, "props"); rapidjson::Document jsonDocProps; jsonDocProps.Parse(props.c_str()); if (!jsonDocProps.HasParseError()) { for (auto& m : jsonDocProps.GetObject()) { std::string key = m.name.GetString(); std::string value = m.value.GetString(); { std::lock_guard lock(m_mutex); m_state.propmap[key] = value; } if (value != oldState.propmap[key]) { m_onStateChangedHandler(key, value); } } } } std::string ApiRunnerMultiDeviceManager::GetStateValueFromKey(std::string key) { std::lock_guard lock(m_mutex); return m_state.propmap[key]; } HRESULT ApiRunnerMultiDeviceManager::RefreshSessionState() { std::vector> headers; headers.push_back(std::vector{"id", m_sessionId}); HRESULT hr = MakeCall("GET", APIRUNNER_GET_STATE, headers, [](HRESULT hr, uint32_t statusCode, const std::string& responseString) { auto manager = Data()->m_multiDeviceManager; if (SUCCEEDED(hr) && statusCode == 200 && responseString.length() > 0) { rapidjson::Document jsonDoc; jsonDoc.Parse(responseString.c_str()); if (!jsonDoc.HasParseError()) { std::string oldState; { std::lock_guard lock(manager->m_mutex); oldState = std::move(manager->m_sessionState); manager->m_sessionState = responseString; } if(oldState != responseString) { manager->ParseState(responseString); } } } else { LogToScreen("Get test session state failed %0.8x %d", hr, statusCode); } }); if (FAILED(hr)) { LogToScreen("Get test session state failed %0.8x", hr); } return hr; } class HCCallHandleRAII { public: ~HCCallHandleRAII() { if (handle != nullptr) { HCHttpCallCloseHandle(handle); } } HCCallHandle handle{ nullptr }; }; struct ApiRunnerMultiDeviceManagerCallContext { HCCallHandleRAII call; std::function handler; }; HRESULT ApiRunnerMultiDeviceManager::MakeCall( _In_ const std::string& method, _In_ const std::string& url, _In_ const std::vector>& headers, _In_ std::function handler) { auto contextPtr = std::unique_ptr( new ApiRunnerMultiDeviceManagerCallContext{ nullptr, handler } ); HRESULT hr = HCHttpCallCreate(&contextPtr->call.handle); if (hr == E_HC_NOT_INITIALISED) { // TODO: remove when HC init/cleanup is ref counted RETURN_HR_IF_FAILED(HCInitialize(nullptr)); RETURN_HR_IF_FAILED(HCHttpCallCreate(&contextPtr->call.handle)); } RETURN_HR_IF_FAILED(HCHttpCallRequestSetUrl(contextPtr->call.handle, method.c_str(), url.c_str())); for (auto& header : headers) { std::string headerName = header[0]; std::string headerValue = header[1]; RETURN_HR_IF_FAILED(HCHttpCallRequestSetHeader(contextPtr->call.handle, headerName.c_str(), headerValue.c_str(), true)); } auto asyncBlock = std::make_unique(); asyncBlock->context = contextPtr.get(); XTaskQueueHandle queue = nullptr; hr = XTaskQueueCreate( XTaskQueueDispatchMode::ThreadPool, XTaskQueueDispatchMode::ThreadPool, &queue); RETURN_HR_IF_FAILED(hr); asyncBlock->queue = queue; asyncBlock->callback = [](XAsyncBlock* asyncBlock) { std::string responseString; uint32_t statusCode{ 0 }; HRESULT networkErrorCode{ S_OK }; uint32_t platErrCode{ 0 }; std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock* std::unique_ptr contextPtr{ static_cast(asyncBlock->context) }; HRESULT hr = XAsyncGetStatus(asyncBlock, false); if (SUCCEEDED(hr)) { hr = HCHttpCallResponseGetNetworkErrorCode(contextPtr->call.handle, &networkErrorCode, &platErrCode); if (SUCCEEDED(hr)) { hr = HCHttpCallResponseGetStatusCode(contextPtr->call.handle, &statusCode); if (SUCCEEDED(hr)) { const char* response{ nullptr }; hr = HCHttpCallResponseGetResponseString(contextPtr->call.handle, &response); if (response != nullptr && SUCCEEDED(hr)) { responseString = response; // ptr memory is only alive while call handle isn't closed, so copy to std::string } } } } if (FAILED(hr)) { networkErrorCode = hr; } if (contextPtr->handler) { contextPtr->handler(networkErrorCode, statusCode, responseString); } // HCHttpCallCloseHandle will get called will context gets freed since it'll dtor HCCallHandleRAII }; hr = HCHttpCallPerformAsync(contextPtr->call.handle, asyncBlock.get()); if (SUCCEEDED(hr)) { // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership. // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock* asyncBlock.release(); contextPtr.release(); } return hr; } ================================================ FILE: Tests/ApiExplorer/Shared/pal.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once typedef unsigned char byte; #if !HC_PLATFORM_IS_MICROSOFT typedef char CHAR; typedef unsigned long DWORD; constexpr auto sprintf_s = sprintf; #define MAX_PATH 260 #define WINAPI #define LPVOID void* #ifndef UNREFERENCED_PARAMETER #define UNREFERENCED_PARAMETER(x) #endif #else // Windows Platforms #endif #if HC_PLATFORM == HC_PLATFORM_ANDROID typedef unsigned long DWORD, *PDWORD, *LPDWORD; #endif namespace pal { void strcpy(char * dst, size_t size, const char* src); int stricmp(const char* left, const char* right); int vsprintf(char* message, size_t size,char const* format, va_list varArgs); char* strtok(char* str, char const* delimiter, char** context); // Due to java not supporting unsigned numeric values, we have to do regular long on Android #if HC_PLATFORM != HC_PLATFORM_ANDROID void Sleep(unsigned long duration); #else void Sleep(long duration); #endif bool FileExists(std::string fileName); // TODO might only be used on win32 std::vector EnumFilesInFolder(std::string folder, std::string spec); std::string FindFile(std::string fileName); std::string GetLuaPath(); #if HC_PLATFORM == HC_PLATFORM_ANDROID std::string OpenFile(std::string filePath); #endif } ================================================ FILE: Tests/ApiExplorer/Shared/pch_apicommon.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch_common.h" #if HC_PLATFORM != HC_PLATFORM_IOS #include #endif ================================================ FILE: Tests/ApiExplorer/Shared/pch_common.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "pch_common.h" // APIRunner uses cpprestsdk and XSAPI lib doesn't on non-GDK platforms #if HC_PLATFORM == HC_PLATFORM_GDK #include "cpprestsdk_impl.h" #endif ================================================ FILE: Tests/ApiExplorer/Shared/pch_common.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #ifdef _WIN32 #ifndef _WIN32_WINNT_WIN10 #define _WIN32_WINNT_WIN10 0x0A00 #endif #endif //#ifdef _WIN32 #include #include #include #include #if HC_PLATFORM != HC_PLATFORM_UWP #include #if HC_PLATFORM != HC_PLATFORM_GDK #include #endif #endif #include #include #include #include #include #include #include #include #include #include #include #include #if HC_PLATFORM != HC_PLATFORM_IOS #include #endif #include #include "pal.h" #include "utils.h" #include "mem_hook.h" #include "lua.h" #include "lualib.h" #include "lauxlib.h" #if XSAPI_BUILT_FROM_SOURCE #undef RAPIDJSON_NAMESPACE #undef RAPIDJSON_NAMESPACE_BEGIN #undef RAPIDJSON_NAMESPACE_END #endif #pragma warning( push ) #pragma warning( disable : 4365 ) #include "../../../External/rapidjson/include/rapidjson/document.h" #pragma warning( pop ) #include "../Include/multidevice.h" #include "../Include/api_explorer.h" #include "../Include/runner.h" #include "../APIs/apis.h" #if HC_PLATFORM_IS_MICROSOFT #define SPRINTF(buffer, size, format, ...) sprintf_s(buffer, size, format, __VA_ARGS__) #else #define SPRINTF(buffer, size, format, ...) snprintf(buffer, size, format, __VA_ARGS__) #endif ================================================ FILE: Tests/ApiExplorer/Shared/utils.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "utils.h" #if HC_PLATFORM == HC_PLATFORM_ANDROID #include "runner.h" #include "pal.h" #include "api_explorer.h" #endif #if HC_PLATFORM == HC_PLATFORM_GDK #include #endif HRESULT VerifyIsTrue(bool condition, const char* pszCondition) { if (!condition) { LogToFile("Failure: "); LogToFile(pszCondition); return E_FAIL; } return S_OK; } #define RETURN_STRING_FROM_HR(e) case (e): return #e #pragma warning (push) #pragma warning (disable: 4061) std::string ConvertHRtoString(HRESULT hr) { switch (hr) { RETURN_STRING_FROM_HR(S_OK); RETURN_STRING_FROM_HR(E_FAIL); RETURN_STRING_FROM_HR(E_POINTER); RETURN_STRING_FROM_HR(E_INVALIDARG); RETURN_STRING_FROM_HR(E_OUTOFMEMORY); RETURN_STRING_FROM_HR(E_NOT_SUFFICIENT_BUFFER); RETURN_STRING_FROM_HR(E_NOT_SUPPORTED); RETURN_STRING_FROM_HR(E_ABORT); RETURN_STRING_FROM_HR(E_NOTIMPL); RETURN_STRING_FROM_HR(E_ACCESSDENIED); RETURN_STRING_FROM_HR(E_PENDING); RETURN_STRING_FROM_HR(E_UNEXPECTED); RETURN_STRING_FROM_HR(E_TIME_CRITICAL_THREAD); RETURN_STRING_FROM_HR(E_NOINTERFACE); RETURN_STRING_FROM_HR(E_BOUNDS); RETURN_STRING_FROM_HR(E_NO_TASK_QUEUE); RETURN_STRING_FROM_HR(HTTP_E_STATUS_AMBIGUOUS); RETURN_STRING_FROM_HR(HTTP_E_STATUS_BAD_GATEWAY); RETURN_STRING_FROM_HR(HTTP_E_STATUS_BAD_METHOD); RETURN_STRING_FROM_HR(HTTP_E_STATUS_BAD_REQUEST); RETURN_STRING_FROM_HR(HTTP_E_STATUS_CONFLICT); RETURN_STRING_FROM_HR(HTTP_E_STATUS_DENIED); RETURN_STRING_FROM_HR(HTTP_E_STATUS_EXPECTATION_FAILED); RETURN_STRING_FROM_HR(HTTP_E_STATUS_FORBIDDEN); RETURN_STRING_FROM_HR(HTTP_E_STATUS_GATEWAY_TIMEOUT); RETURN_STRING_FROM_HR(HTTP_E_STATUS_GONE); RETURN_STRING_FROM_HR(HTTP_E_STATUS_LENGTH_REQUIRED); RETURN_STRING_FROM_HR(HTTP_E_STATUS_MOVED); RETURN_STRING_FROM_HR(HTTP_E_STATUS_NONE_ACCEPTABLE); RETURN_STRING_FROM_HR(HTTP_E_STATUS_NOT_FOUND); RETURN_STRING_FROM_HR(HTTP_E_STATUS_NOT_MODIFIED); RETURN_STRING_FROM_HR(HTTP_E_STATUS_NOT_SUPPORTED); RETURN_STRING_FROM_HR(HTTP_E_STATUS_PAYMENT_REQ); RETURN_STRING_FROM_HR(HTTP_E_STATUS_PRECOND_FAILED); RETURN_STRING_FROM_HR(HTTP_E_STATUS_PROXY_AUTH_REQ); RETURN_STRING_FROM_HR(HTTP_E_STATUS_RANGE_NOT_SATISFIABLE); RETURN_STRING_FROM_HR(HTTP_E_STATUS_REDIRECT); RETURN_STRING_FROM_HR(HTTP_E_STATUS_REDIRECT_KEEP_VERB); RETURN_STRING_FROM_HR(HTTP_E_STATUS_REDIRECT_METHOD); RETURN_STRING_FROM_HR(HTTP_E_STATUS_REQUEST_TIMEOUT); RETURN_STRING_FROM_HR(HTTP_E_STATUS_REQUEST_TOO_LARGE); RETURN_STRING_FROM_HR(HTTP_E_STATUS_SERVER_ERROR); RETURN_STRING_FROM_HR(HTTP_E_STATUS_SERVICE_UNAVAIL); RETURN_STRING_FROM_HR(HTTP_E_STATUS_UNEXPECTED); RETURN_STRING_FROM_HR(HTTP_E_STATUS_UNEXPECTED_SERVER_ERROR); RETURN_STRING_FROM_HR(HTTP_E_STATUS_UNSUPPORTED_MEDIA); RETURN_STRING_FROM_HR(HTTP_E_STATUS_URI_TOO_LONG); RETURN_STRING_FROM_HR(HTTP_E_STATUS_USE_PROXY); RETURN_STRING_FROM_HR(HTTP_E_STATUS_VERSION_NOT_SUP); RETURN_STRING_FROM_HR(ONL_E_ACTION_REQUIRED); RETURN_STRING_FROM_HR(WEB_E_INVALID_JSON_STRING); RETURN_STRING_FROM_HR(WEB_E_UNEXPECTED_CONTENT); #if HC_PLATFORM != HC_PLATFORM_UWP RETURN_STRING_FROM_HR(E_XAL_NOTINITIALIZED); RETURN_STRING_FROM_HR(E_XAL_ALREADYINITIALIZED); RETURN_STRING_FROM_HR(E_XAL_USERSETNOTEMPTY); RETURN_STRING_FROM_HR(E_XAL_USERSETFULL); RETURN_STRING_FROM_HR(E_XAL_USERSIGNEDOUT); RETURN_STRING_FROM_HR(E_XAL_DUPLICATEDUSER); RETURN_STRING_FROM_HR(E_XAL_NETWORK); RETURN_STRING_FROM_HR(E_XAL_CLIENTERROR); RETURN_STRING_FROM_HR(E_XAL_UIREQUIRED); RETURN_STRING_FROM_HR(E_XAL_HANDLERALREADYREGISTERED); RETURN_STRING_FROM_HR(E_XAL_UNEXPECTEDUSERSIGNEDIN); RETURN_STRING_FROM_HR(E_XAL_NOTATTACHEDTOJVM); RETURN_STRING_FROM_HR(E_XAL_DEVICEUSER); RETURN_STRING_FROM_HR(E_XAL_DEFERRALNOTAVAILABLE); #if HC_PLATFORM != HC_PLATFORM_GDK RETURN_STRING_FROM_HR(E_XAL_MISSINGPLATFORMEVENTHANDLER); #endif RETURN_STRING_FROM_HR(E_XAL_USERNOTFOUND); RETURN_STRING_FROM_HR(E_XAL_NOTOKENREQUIRED); RETURN_STRING_FROM_HR(E_XAL_NODEFAULTUSER); RETURN_STRING_FROM_HR(E_XAL_FAILEDTORESOLVE); #endif RETURN_STRING_FROM_HR(E_XBL_RUNTIME_ERROR); RETURN_STRING_FROM_HR(E_XBL_RTA_GENERIC_ERROR); RETURN_STRING_FROM_HR(E_XBL_RTA_SUBSCRIPTION_LIMIT_REACHED); RETURN_STRING_FROM_HR(E_XBL_RTA_ACCESS_DENIED); RETURN_STRING_FROM_HR(E_XBL_AUTH_UNKNOWN_ERROR); RETURN_STRING_FROM_HR(E_XBL_AUTH_RUNTIME_ERROR); RETURN_STRING_FROM_HR(E_XBL_AUTH_NO_TOKEN); RETURN_STRING_FROM_HR(E_XBL_ALREADY_INITIALIZED); RETURN_STRING_FROM_HR(__HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW)); RETURN_STRING_FROM_HR(__HRESULT_FROM_WIN32(ERROR_BAD_CONFIGURATION)); RETURN_STRING_FROM_HR(__HRESULT_FROM_WIN32(ERROR_BAD_LENGTH)); RETURN_STRING_FROM_HR(__HRESULT_FROM_WIN32(ERROR_CANCELLED)); RETURN_STRING_FROM_HR(__HRESULT_FROM_WIN32(ERROR_NO_SUCH_USER)); RETURN_STRING_FROM_HR(__HRESULT_FROM_WIN32(ERROR_RESOURCE_DATA_NOT_FOUND)); RETURN_STRING_FROM_HR(E_HC_PERFORM_ALREADY_CALLED); RETURN_STRING_FROM_HR(E_HC_ALREADY_INITIALISED); RETURN_STRING_FROM_HR(E_HC_CONNECT_ALREADY_CALLED); RETURN_STRING_FROM_HR(E_HC_NO_NETWORK); #if HC_PLATFORM == HC_PLATFORM_GDK RETURN_STRING_FROM_HR(E_GAMERUNTIME_NOT_INITIALIZED); RETURN_STRING_FROM_HR(E_GAMERUNTIME_DLL_NOT_FOUND); RETURN_STRING_FROM_HR(E_GAMERUNTIME_VERSION_MISMATCH); //RETURN_STRING_FROM_HR(E_GAMEUSER_MAX_USERS_ADDED); //RETURN_STRING_FROM_HR(E_GAMEUSER_SIGNED_OUT); //RETURN_STRING_FROM_HR(E_GAMEUSER_RESOLVE_USER_ISSUE_REQUIRED); //RETURN_STRING_FROM_HR(E_GAMEUSER_DEFERRAL_NOT_AVAILABLE); //RETURN_STRING_FROM_HR(E_GAMEUSER_USER_NOT_FOUND); //RETURN_STRING_FROM_HR(E_GAMEUSER_NO_TOKEN_REQUIRED); //RETURN_STRING_FROM_HR(E_GAMEUSER_NO_DEFAULT_USER); //RETURN_STRING_FROM_HR(E_GAMEUSER_FAILED_TO_RESOLVE); RETURN_STRING_FROM_HR(E_GAMEUSER_NO_TITLE_ID); RETURN_STRING_FROM_HR(E_GAMEUSER_UNKNOWN_GAME_IDENTITY); RETURN_STRING_FROM_HR(E_GAMEUSER_NO_PACKAGE_IDENTITY); RETURN_STRING_FROM_HR(E_GAMEUSER_FAILED_TO_GET_TOKEN); RETURN_STRING_FROM_HR(E_GAMEPACKAGE_APP_NOT_PACKAGED); RETURN_STRING_FROM_HR(E_GAMEPACKAGE_NO_INSTALLED_LANGUAGES); RETURN_STRING_FROM_HR(E_GAMESTORE_LICENSE_ACTION_NOT_APPLICABLE_TO_PRODUCT); RETURN_STRING_FROM_HR(E_GAMESTORE_NETWORK_ERROR); RETURN_STRING_FROM_HR(E_GAMESTORE_SERVER_ERROR); RETURN_STRING_FROM_HR(E_GAMESTORE_INSUFFICIENT_QUANTITY); RETURN_STRING_FROM_HR(E_GAMESTORE_ALREADY_PURCHASED); #endif default: return "Unknown error"; } } std::string ConvertHR(HRESULT hr) { CHAR sz[256]; #if HC_PLATFORM_IS_MICROSOFT sprintf_s(sz, "%s (0x%0.8x)", ConvertHRtoString(hr).c_str(), hr); #else sprintf(sz, "%s (0x%0.8x)", ConvertHRtoString(hr).c_str(), hr); #endif return sz; } std::string ReadFile(std::string fileName) { std::string filePath = pal::FindFile(fileName); if (filePath.empty()) { std::string testPath = "Tests\\" + fileName; filePath = pal::FindFile(testPath); } if (filePath.empty()) { return std::string(); } std::ifstream inputStream(filePath.c_str()); return std::string(std::istreambuf_iterator(inputStream), std::istreambuf_iterator()); } uint64_t ConvertStringToUInt64(std::string str) { return std::stoull(str); } HRESULT CreateQueueIfNeeded() { HRESULT hr = S_OK; if (Data()->queue == nullptr) { hr = XTaskQueueCreate( XTaskQueueDispatchMode::ThreadPool, XTaskQueueDispatchMode::ThreadPool, &Data()->queue); assert(SUCCEEDED(hr)); } return hr; } #if CPP_TESTS_ENABLED using namespace xbox::services; HRESULT ConvertHttpStatusToHresult(_In_ uint32_t httpStatusCode) { xbox::services::xbox_live_error_code errCode = static_cast(httpStatusCode); HRESULT hr = HTTP_E_STATUS_UNEXPECTED; // 2xx are http success codes if ((httpStatusCode >= 200) && (httpStatusCode < 300)) { hr = S_OK; } // MSXML XHR bug: get_status() returns HTTP/1223 for HTTP/204: // http://blogs.msdn.com/b/ieinternals/archive/2009/07/23/the-ie8-native-xmlhttprequest-object.aspx // treat it as success code as well else if (httpStatusCode == 1223) { hr = S_OK; } else { switch (errCode) { case xbox_live_error_code::http_status_300_multiple_choices: hr = HTTP_E_STATUS_AMBIGUOUS; break; case xbox_live_error_code::http_status_301_moved_permanently: hr = HTTP_E_STATUS_MOVED; break; case xbox_live_error_code::http_status_302_found: hr = HTTP_E_STATUS_REDIRECT; break; case xbox_live_error_code::http_status_303_see_other: hr = HTTP_E_STATUS_REDIRECT_METHOD; break; case xbox_live_error_code::http_status_304_not_modified: hr = HTTP_E_STATUS_NOT_MODIFIED; break; case xbox_live_error_code::http_status_305_use_proxy: hr = HTTP_E_STATUS_USE_PROXY; break; case xbox_live_error_code::http_status_307_temporary_redirect: hr = HTTP_E_STATUS_REDIRECT_KEEP_VERB; break; case xbox_live_error_code::http_status_400_bad_request: hr = HTTP_E_STATUS_BAD_REQUEST; break; case xbox_live_error_code::http_status_401_unauthorized: hr = HTTP_E_STATUS_DENIED; break; case xbox_live_error_code::http_status_402_payment_required: hr = HTTP_E_STATUS_PAYMENT_REQ; break; case xbox_live_error_code::http_status_403_forbidden: hr = HTTP_E_STATUS_FORBIDDEN; break; case xbox_live_error_code::http_status_404_not_found: hr = HTTP_E_STATUS_NOT_FOUND; break; case xbox_live_error_code::http_status_405_method_not_allowed: hr = HTTP_E_STATUS_BAD_METHOD; break; case xbox_live_error_code::http_status_406_not_acceptable: hr = HTTP_E_STATUS_NONE_ACCEPTABLE; break; case xbox_live_error_code::http_status_407_proxy_authentication_required: hr = HTTP_E_STATUS_PROXY_AUTH_REQ; break; case xbox_live_error_code::http_status_408_request_timeout: hr = HTTP_E_STATUS_REQUEST_TIMEOUT; break; case xbox_live_error_code::http_status_409_conflict: hr = HTTP_E_STATUS_CONFLICT; break; case xbox_live_error_code::http_status_410_gone: hr = HTTP_E_STATUS_GONE; break; case xbox_live_error_code::http_status_411_length_required: hr = HTTP_E_STATUS_LENGTH_REQUIRED; break; case xbox_live_error_code::http_status_412_precondition_failed: hr = HTTP_E_STATUS_PRECOND_FAILED; break; case xbox_live_error_code::http_status_413_request_entity_too_large: hr = HTTP_E_STATUS_REQUEST_TOO_LARGE; break; case xbox_live_error_code::http_status_414_request_uri_too_long: hr = HTTP_E_STATUS_URI_TOO_LONG; break; case xbox_live_error_code::http_status_415_unsupported_media_type: hr = HTTP_E_STATUS_UNSUPPORTED_MEDIA; break; case xbox_live_error_code::http_status_416_requested_range_not_satisfiable: hr = HTTP_E_STATUS_RANGE_NOT_SATISFIABLE; break; case xbox_live_error_code::http_status_417_expectation_failed: hr = HTTP_E_STATUS_EXPECTATION_FAILED; break; case xbox_live_error_code::http_status_421_misdirected_request: hr = MAKE_HTTP_HRESULT(421); break; case xbox_live_error_code::http_status_422_unprocessable_entity: hr = MAKE_HTTP_HRESULT(422); break; case xbox_live_error_code::http_status_423_locked: hr = MAKE_HTTP_HRESULT(423); break; case xbox_live_error_code::http_status_424_failed_dependency: hr = MAKE_HTTP_HRESULT(424); break; case xbox_live_error_code::http_status_426_upgrade_required: hr = MAKE_HTTP_HRESULT(426); break; case xbox_live_error_code::http_status_428_precondition_required: hr = MAKE_HTTP_HRESULT(428); break; case xbox_live_error_code::http_status_429_too_many_requests: hr = MAKE_HTTP_HRESULT(429); break; case xbox_live_error_code::http_status_431_request_header_fields_too_large: hr = MAKE_HTTP_HRESULT(431); break; case xbox_live_error_code::http_status_449_retry_with:hr = MAKE_HTTP_HRESULT(449); break; case xbox_live_error_code::http_status_451_unavailable_for_legal_reasons: hr = MAKE_HTTP_HRESULT(451); break; case xbox_live_error_code::http_status_500_internal_server_error: hr = HTTP_E_STATUS_SERVER_ERROR; break; case xbox_live_error_code::http_status_501_not_implemented: hr = HTTP_E_STATUS_NOT_SUPPORTED; break; case xbox_live_error_code::http_status_502_bad_gateway: hr = HTTP_E_STATUS_BAD_GATEWAY; break; case xbox_live_error_code::http_status_503_service_unavailable: hr = HTTP_E_STATUS_SERVICE_UNAVAIL; break; case xbox_live_error_code::http_status_504_gateway_timeout: hr = HTTP_E_STATUS_GATEWAY_TIMEOUT; break; case xbox_live_error_code::http_status_505_http_version_not_supported: hr = HTTP_E_STATUS_VERSION_NOT_SUP; break; case xbox_live_error_code::http_status_506_variant_also_negotiates: hr = MAKE_HTTP_HRESULT(506); break; case xbox_live_error_code::http_status_507_insufficient_storage: hr = MAKE_HTTP_HRESULT(507); break; case xbox_live_error_code::http_status_508_loop_detected: hr = MAKE_HTTP_HRESULT(508); break; case xbox_live_error_code::http_status_510_not_extended: hr = MAKE_HTTP_HRESULT(510); break; case xbox_live_error_code::http_status_511_network_authentication_required: hr = MAKE_HTTP_HRESULT(511); break; default: hr = HTTP_E_STATUS_UNEXPECTED; break; } } return hr; } HRESULT ConvertXboxLiveErrorCodeToHresult(_In_ const std::error_code& errCode) { int err = static_cast(errCode.value()); xbox_live_error_code xblErr = static_cast(err); if (err == 204) { return __HRESULT_FROM_WIN32(ERROR_RESOURCE_DATA_NOT_FOUND); } else if (err >= 300 && err <= 505) { return (HRESULT)ConvertHttpStatusToHresult(static_cast(err)); } else if (err >= 1000 && err <= 9999) { switch (xblErr) { case xbox_live_error_code::bad_alloc: return E_OUTOFMEMORY; case xbox_live_error_code::invalid_argument: return E_INVALIDARG; case xbox_live_error_code::runtime_error: return E_XBL_RUNTIME_ERROR; case xbox_live_error_code::length_error: return __HRESULT_FROM_WIN32(ERROR_BAD_LENGTH); case xbox_live_error_code::out_of_range: return E_BOUNDS; case xbox_live_error_code::range_error: return E_BOUNDS; case xbox_live_error_code::bad_cast: return E_NOINTERFACE; case xbox_live_error_code::logic_error: return E_UNEXPECTED; case xbox_live_error_code::json_error: return WEB_E_INVALID_JSON_STRING; case xbox_live_error_code::uri_error: return WEB_E_UNEXPECTED_CONTENT; case xbox_live_error_code::websocket_error: return WEB_E_UNEXPECTED_CONTENT; case xbox_live_error_code::auth_user_interaction_required: return ONL_E_ACTION_REQUIRED; case xbox_live_error_code::rta_generic_error: return E_XBL_RTA_GENERIC_ERROR; case xbox_live_error_code::rta_subscription_limit_reached: return E_XBL_RTA_SUBSCRIPTION_LIMIT_REACHED; case xbox_live_error_code::rta_access_denied: return E_XBL_RTA_ACCESS_DENIED; case xbox_live_error_code::auth_unknown_error: return E_XBL_AUTH_UNKNOWN_ERROR; case xbox_live_error_code::auth_runtime_error: return E_XBL_AUTH_RUNTIME_ERROR; case xbox_live_error_code::auth_no_token_error: return E_XBL_AUTH_NO_TOKEN; case xbox_live_error_code::auth_user_not_signed_in: return __HRESULT_FROM_WIN32(ERROR_NO_SUCH_USER); case xbox_live_error_code::auth_user_cancel: return __HRESULT_FROM_WIN32(ERROR_CANCELLED); case xbox_live_error_code::auth_user_switched: return __HRESULT_FROM_WIN32(ERROR_NO_SUCH_USER); case xbox_live_error_code::invalid_config: return __HRESULT_FROM_WIN32(ERROR_BAD_CONFIGURATION); case xbox_live_error_code::unsupported: return E_NOTIMPL; default: return E_FAIL; } } else if ((err & 0x87DD0000) == 0x87D8000) { return HTTP_E_STATUS_UNEXPECTED_SERVER_ERROR; } else return err; //return the original error code if can't be translated. } #endif #pragma warning (pop) ================================================ FILE: Tests/ApiExplorer/Shared/utils.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #if HC_PLATFORM != HC_PLATFORM_UWP #include #endif // internal test-only APIs #define NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN namespace xbox { namespace services { #define NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END }} #include "../../../Source/Shared/fault_injection.h" #define VERIFY_IS_TRUE(x, y) if (SUCCEEDED(hr)) { hr = VerifyIsTrue(x, y); } #define ENSURE_IS_TRUE(x, y) if (FAILED(VerifyIsTrue(x, y))) return LuaReturnHR(L, E_ABORT); std::string ConvertHRtoString(HRESULT hr); std::string ConvertHR(HRESULT hr); std::string ReadFile(std::string fileName); uint64_t ConvertStringToUInt64(std::string str); HRESULT CreateQueueIfNeeded(); HRESULT VerifyIsTrue(bool condition, const char* pszCondition); HRESULT ConvertXboxLiveErrorCodeToHresult(_In_ const std::error_code& errCode); ================================================ FILE: Tests/ApiExplorer/Shared/xboxservices.config ================================================ { "TitleId" : 1979882317, "PrimaryServiceConfigId" : "00000000-0000-0000-0000-000076029b4d" } ================================================ FILE: Tests/ApiExplorer/Tests/MultiplayerActivity/receive_invite_gsdk.lua ================================================ test = require 'u-test' common = require 'common' function TestReceiveInvite_Handler() XGameInviteRegisterForEvent(); MultiDeviceSyncAndWait("SendInvite"); end function OnXGameInviteRegisterForEvent() MultiDeviceSyncAndWait("Complete"); test.stopTest(); end test.ismultidevice = true test.TestReceiveInvite = function() common.init(TestReceiveInvite_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/MultiplayerActivity/receive_invite_win32.lua ================================================ test = require 'u-test' common = require 'common' function TestReceiveInvite_Handler() XblGameInviteRegisterForEventAsync() end function OnXblGameInviteRegisterForEventAsync() XblMultiplayerActivityAddInviteHandler(); MultiDeviceSyncAndWait("SendInvite"); end function OnMultiplayerActivityGameInvite() XblMultiplayerActivityRemoveInviteHandler() XblGameInviteUnregisterForEventAsync() MultiDeviceSyncAndWait("Complete"); test.stopTest(); end test.ismultidevice = true; test.TestReceiveInvite = function() common.init(TestReceiveInvite_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/MultiplayerActivity/send_invite.lua ================================================ test = require 'u-test' common = require 'common' function TestSendInvite_Handler() --MultiDeviceSyncAndWait("SendInvite"); XblMultiplayerActivitySendInvitesAsync(MultiDeviceGetRemoteXuid()) end function OnXblMultiplayerActivitySendInvitesAsync() --MultiDeviceSyncAndWait("Complete") test.stopTest() end test.ismultidevice = true; test.TestSendInvite = function() common.init(TestSendInvite_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/MultiplayerActivity/set_activity.lua ================================================ test = require 'u-test' common = require 'common' function SetActivity_Handler() XblMultiplayerActivitySetActivityAsync() end function OnXblMultiplayerActivitySetActivityAsync() print("SetActivity call complete") end test.skip = true test.SetActivity = function() common.init(SetActivity_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/MultiplayerActivity/update_activity.lua ================================================ test = require 'u-test' common = require 'common' function UpdateActivity_Handler() print("UpdateActivity_Handler") XblMultiplayerActivitySetActivityAsync() end function OnXblMultiplayerActivitySetActivityAsync() print("OnXblMultiplayerActivitySetActivityAsync") XblMultiplayerActivityGetActivityAsync() end function OnXblMultiplayerActivityGetActivityAsync() print("OnXblMultiplayerActivityGetActivityAsync") XblMultiplayerActivityDeleteActivityAsync() end function OnXblMultiplayerActivityDeleteActivityAsync() print("OnXblMultiplayerActivityDeleteActivityAsync") test.stopTest() end test.UpdateActivity = function() common.init(UpdateActivity_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/MultiplayerActivity/update_recent_players.lua ================================================ test = require 'u-test' common = require 'common' function UpdateRecentPlayers_Handler() print("UpdateRecentPlayers_Handler") XblMultiplayerActivityUpdateRecentPlayers(0) XblMultiplayerActivityUpdateRecentPlayers(1) XblMultiplayerActivityUpdateRecentPlayers(2) XblMultiplayerActivityFlushRecentPlayersAsync() end function OnXblMultiplayerActivityFlushRecentPlayersAsync() print("OnXblMultiplayerActivityFlushRecentPlayersAsync") test.stopTest(); end test.UpdateRecentPlayers = function() common.init(UpdateRecentPlayers_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/_luasetup_/u-test/LICENSE ================================================ MIT License Copyright (c) 2017-2018 Ilia Udalov Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: Tests/ApiExplorer/Tests/_luasetup_/u-test/README.md ================================================ # u-test [![License](http://img.shields.io/badge/License-MIT-green.svg)](LICENSE.txt) **u-test** is a sane and simple unit testing framework for Lua. It has all essential unit test framework features: defining test cases, test suites, set of build-in assertions, configurable tests output, protected calls and etc. ### Top features that are not present in other lua test frameworks 1. Nice command line interface (like gtest). 1. Backtrace in failed assertions. 1. Ordered test execution (as written in source file). 1. Support 5.1/5.2/5.3 and LuaJIT. 1. Select particular tests with regexp. ### How to install #### GitHub Just copy `u-test.lua` to your projct or add this repo as submodule. ``` $ git clone git://github.com/iudalov/u-test ``` #### [LuaRocks](https://luarocks.org) Just run: ``` $ luarocks install u-test ``` ### How to use ```lua local test = require 'u-test' -- You can use 'assert' to check invariants. test.hello_world = function () test.assert(true) test.assert(1 ~= 2) end -- This is how you can crete your first test case test.addition = function () test.equal(1 + 1, 2) test.not_equal("1 + 1", "2") test.almost_equal(1 + 1, 2.1, 0.2) end -- You can enable custom start_up and tear_down actions -- Thse actions will be invoked: -- start_up - before test case -- tear_down - after test case local global_state = 0 test.start_up = function () global_state = 1 end test.tear_down = function () global_state = 0 end test.dummy1 = function() test.equal(global_state, 1) test.is_number(global_state) end -- You can separate tests by test suites test.string.format = function () test.equal(string.format("%d + %d", 1, 1), "1 + 1") test.not_equal(string.format("Sparky %s", "bark"), "Fluffy bark") end test.string.find = function () test.is_nil(string.find("u-test", "banana")) test.is_not_nil(string.find("u-test", "u")) end -- You can declare test case with parameters test.string.starts_with = function (str, prefix) test.equal(string.find(str, prefix), 1) end -- Then, run it with multiple parameters test.string.starts_with("Lua rocks", "Lua") test.string.starts_with("Wow", "Wow") local global_table = {} -- Each test suite can be customized by start_up and tear_down test.table.start_up = function () global_table = { 1, 2, "three", 4, "five" } end test.table.tear_down = function () global_table = {} end test.table.concat = function () test.equal(table.concat(global_table, ", "), "1, 2, three, 4, five") end -- you can disabe broken test case like this test.broken.skip = true test.broken.bad_case = function () test.equal(1, 2) there_is_no_such_function() end -- obtain total number of tests and numer of failed tests local ntests, nfailed = test.result() -- this code prints tests summary and invokes os.exit with 0 or 1 test.summary() ``` ![Output](https://raw.githubusercontent.com/iudalov/u-test/master/res/ui.png) ### List of all assertions ```lua test.assert(true) test.equal(1, 1) test.not_equal(1, 2) test.is_false(false) test.is_true(true) test.is_not_nil("Something") test.is_nil(nil) test.is_boolean(true) test.is_boolean(false) test.is_string("I am string! look at me!") test.is_number(3) test.is_table({"I am table now"}) test.is_function(function () end) test.is_userdata(userdata_value) ``` ================================================ FILE: Tests/ApiExplorer/Tests/_luasetup_/u-test/u-test.lua ================================================ -- COMMAND LINE ---------------------------------------------------------------- print = function(...) LogToScreen(...) end local quiet, grey, test_regex -- UTILS ----------------------------------------------------------------------- local function red(str) return grey and str or "" .. str .. " " end local function blue(str) return grey and str or "" .. str .. " " end local function green(str) return grey and str or "" .. str .. " " end local function yellow(str) return grey and str or "" .. str .. " " end local tab_tag = blue "[----------]" local done_tag = blue "[==========]" local run_tag = blue "[ RUN ]" local ok_tag = green "[ OK ]" local fail_tag = red "[ FAIL]" local disabled_tag = yellow "[ DISABLED ]" local passed_tag = green "[ PASSED ]" local failed_tag = red "[ FAILED ]" local ntests = 0 local failed = false local failed_list = {} local function log(msg) if not quiet then LogToScreen(msg) end end local function trace(start_frame) log("Trace:") local frame = start_frame while true do local info = debug.getinfo(frame, "Sl") if not info then break end if info.what == "C" then -- log("??????") else log(info.short_src .. ":" .. info.currentline .. " ") end frame = frame + 1 end end local function fail(msg, start_frame) failed = true log("Fail: " .. msg) trace(start_frame or 4) end local function fail_no_log(msg, start_frame) failed = true end local function stringize_var_arg(varg, ...) if varg then local rest = stringize_var_arg(...) if rest ~= "" then return tostring(varg) .. ", ".. rest else return tostring(varg) end else return "" end end local function test_pretty_name(suite_name, test_name) if suite_name == "__root" then return test_name else return suite_name .. "." .. test_name end end -- PUBLIC API ----------------------------------------------------------------- local api = { test_suite_name = "__root", enablebvts = false, enablemultidevice = false, skipOverride = false, skip = false, isbvt = false, ismultidevice = false } api.assert = function (cond) if not cond then fail("assertion " .. tostring(cond) .. " failed") end end api.equal_no_log = function (l, r) if l ~= r then fail_no_log(tostring(l) .. " ~= " .. tostring(r)) end end api.equal = function (l, r) if l ~= r then fail(tostring(l) .. " ~= " .. tostring(r)) end end api.not_equal = function (l, r) if l == r then fail(tostring(l) .. " == " .. tostring(r)) end end api.almost_equal = function (l, r, diff) if require("math").abs(l - r) > diff then fail("|" .. tostring(l) .. " - " .. tostring(r) .."| > " .. tostring(diff)) end end api.is_false = function (maybe_false) if maybe_false or type(maybe_false) ~= "boolean" then fail("got " .. tostring(maybe_false) .. " instead of false") end end api.is_true = function (maybe_true) if not maybe_true or type(maybe_true) ~= "boolean" then fail("got " .. tostring(maybe_true) .. " instead of true") end end api.is_not_nil = function (maybe_not_nil) if type(maybe_not_nil) == "nil" then fail("got nil") end end local function skip_test(test_suite) failed = false -- return back to default for future tests test_suite.ismultidevice = false test_suite.isbvt = false test_suite.skip = false SetTestWasSkipped() api.stopTest() end local last_test_suite local function run_test(test_suite, test_name, test_function, ...) local suite_name = test_suite.test_suite_name full_test_name = test_pretty_name(suite_name, test_name) if test_regex and not string.match(full_test_name, test_regex) then return end start = os.time() -- uncomment for extra debug logs -- if api.skipOverride then log("skipOverride true") else log("skipOverride false") end -- if api.enablemultidevice then log("enablemultidevice true") else log("enablemultidevice false") end -- if api.enablebvts then log("enablebvts true") else log("enablebvts false") end -- if test_suite.skip then log("skip true") else log("skip false") end -- if test_suite.ismultidevice then log("ismultidevice true") else log("ismultidevice false") end -- if test_suite.isbvt then log("isbvt true") else log("isbvt false") end if test_suite.skip and not api.skipOverride then log(disabled_tag .. " " .. full_test_name) skip_test(test_suite) -- doesnt match filter, so stop test early return end if api.enablemultidevice then if not test_suite.ismultidevice then skip_test(test_suite) -- doesnt match filter, so stop test early return end else if test_suite.ismultidevice and not api.skipOverride then log("spot3") skip_test(test_suite) -- doesnt match filter, so stop test early return end end if api.enablebvts then if not test_suite.isbvt then skip_test(test_suite) -- doesnt match filter, so stop test early return end end -- return back to default for future tests test_suite.ismultidevice = false test_suite.isbvt = false if suite_name ~= last_test_suite then log(tab_tag) last_test_suite = suite_name end ntests = ntests + 1 failed = false log(run_tag .. " " .. full_test_name) status = true for _, f in ipairs({test_suite.start_up, test_function, test_suite.tear_down}) do status, err = pcall(f, ...) if status == nil then status = true end if not status then failed = true log(tostring(err)) end end end api.stopTest = function() local stop = os.time() local is_test_failed = not status or failed log(string.format("%s %s %d sec", is_test_failed and fail_tag or ok_tag, full_test_name, os.difftime(stop, start))) if is_test_failed then table.insert(failed_list, full_test_name) end StopTestFile(); end api.summary = function () log(done_tag) local nfailed = #failed_list if nfailed == 0 then log(passed_tag .. " " .. ntests .. " test(s)") else log(failed_tag .. " " .. nfailed .. " out of " .. ntests .. ":") for _, test_name in ipairs(failed_list) do log(failed_tag .. "\t" .. test_name) end end end api.result = function ( ... ) return ntests, #failed_list end local default_start_up = function () end local default_tear_down = function () collectgarbage() end api.start_up = default_start_up api.tear_down = default_tear_down local all_test_cases = { __root = {} } local function handle_new_test(suite, test_name, test_function) local suite_name = suite.test_suite_name if not all_test_cases[suite_name] then all_test_cases[suite_name] = {} end all_test_cases[suite_name][test_name] = test_function local info = debug.getinfo(test_function) if info.nparams == nil and string.sub(test_name, #test_name - 1, #test_name) ~= "_p" or info.nparams == 0 then run_test(suite, test_name, test_function) end end local function lookup_test_with_params(suite, test_name) local suite_name = suite.test_suite_name if all_test_cases[suite_name] and all_test_cases[suite_name][test_name] then return function (...) run_test(suite , test_name .. "(" .. stringize_var_arg(...) .. ")" , all_test_cases[suite_name][test_name], ...) end else local full_test_name = test_pretty_name(suite_name, test_name) table.insert(failed_list, full_test_name) ntests = ntests + 1 log(fail_tag .. " No " .. full_test_name .. " parametrized test case!") end end local function new_test_suite(_, name) local test_suite = { test_suite_name = name, start_up = default_start_up, tear_down = default_tear_down, skip = false, isbvt = false, ismultidevice = false } setmetatable(test_suite, { __newindex = handle_new_test, __index = lookup_test_with_params }) return test_suite end local test_suites = {} setmetatable(api, { __index = function (tbl, name) if all_test_cases.__root[name] then return lookup_test_with_params(tbl, name) end if not test_suites[name] then test_suites[name] = new_test_suite(tbl, name) end return test_suites[name] end, __newindex = handle_new_test }) return api ================================================ FILE: Tests/ApiExplorer/Tests/_luasetup_/uwp/setup.lua ================================================ test = require 'u-test' common = require 'common' function LuaSetupHandler() local hr = GetLastError() print("LuaSetupHandler: hr=" .. hr) SetCheckHR(1) if hr ~= 0 then print("LuaSetupHandler: hr != 0. Calling UwpXblSigninAsync") common.doCallbackOnError = false UwpXblSigninAsync() else print("LuaSetupHandler: stopping test") common.doCallbackOnError = false test.stopTest(); end end test.StartSetupTest = function() SetCheckHR(0) common.doCallbackOnError = true common.init(LuaSetupHandler) end function OnUwpXblSigninAsync() local hr = GetLastError() print("OnUwpXblSigninAsync: hr " .. hr ) if hr == 0 then UwpXblContextCreateHandle() end test.stopTest(); end ================================================ FILE: Tests/ApiExplorer/Tests/_luasetup_/xal/common.lua ================================================ local common = {} function common.init(fn) common.functionAddFirstAsync = fn XalPlatformWebSetEventHandler() XalPlatformStorageSetEventHandlers() XalInitialize() XblInitialize() XblGetScid() XalTryAddFirstUserSilentlyAsync() end function common.cleanup() XblContextCloseHandle(); XalUserCloseHandle() XblCleanupAsync() XalCleanupAsync() XTaskQueueCloseHandle() end function common.OnXalTryAddFirstUserSilentlyAsync() local hr = GetLastError() local handle = 0 if hr == 0 then print("SignInSilently Succeeded. Creating XblContext") handle, hr = XblContextCreateHandle() print("hr " .. hr) print("handle " .. handle) if hr == 0 then XalUserGetGamertag() XalUserGetId() else if GetCheckHR() == true then test.stopTest(); else SetCheckHR(1) end end common.functionAddFirstAsync() else print("SignInSilently Failed") if GetCheckHR() == true then test.stopTest(); else SetCheckHR(1) common.functionAddFirstAsync() end end end return common ================================================ FILE: Tests/ApiExplorer/Tests/_luasetup_/xal/setup.lua ================================================ test = require 'u-test' common = require 'common' function SetupTest_Handler() print("SetupTest_Handler") local hr = GetLastError() SetCheckHR(1) if hr ~= 0 then print("Calling XalAddUserWithUiAsync") XalAddUserWithUiAsync() else test.stopTest(); end end test.StartSetupTest = function() print("Running shared test.StartSetupTest") SetCheckHR(0) common.init(SetupTest_Handler) end function OnXalAddUserWithUiAsync() print("In OnXalAddUserWithUiAsync") XblContextCreateHandle() XalUserGetGamertag() XalUserGetId() test.stopTest(); end ================================================ FILE: Tests/ApiExplorer/Tests/achievements/PerformanceTestMockResponse.json ================================================ { "achievements": [ { "id": "1", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 1", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789001", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 1", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 1", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 1", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 1", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "2", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 2", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789002", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 2", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 2", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 2", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 2", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "3", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 3", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789003", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 3", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 3", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 3", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 3", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "4", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 4", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789004", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 4", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 4", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 4", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 4", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "5", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 5", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789005", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 5", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 5", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 5", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 5", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "6", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 6", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789006", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 6", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 6", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 6", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 6", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "7", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 7", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789007", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 7", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 7", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 7", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 7", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "8", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 8", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789008", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 8", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 8", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 8", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 8", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "9", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 9", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789009", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 9", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 9", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 9", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 9", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "10", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 10", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789010", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 10", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 10", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 10", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 10", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "11", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 11", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789011", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 11", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 11", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 11", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 11", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "12", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 12", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789012", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 12", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 12", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 12", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 12", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "13", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 13", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789013", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 13", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 13", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 13", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 13", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "14", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 14", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789014", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 14", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 14", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 14", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 14", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "15", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 15", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789015", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 15", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 15", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 15", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 15", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "16", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 16", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789016", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 16", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 16", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 16", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 16", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "17", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 17", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789017", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 17", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 17", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 17", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 17", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "18", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 18", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789018", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 18", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 18", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 18", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 18", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "19", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 19", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789019", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 19", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 19", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 19", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 19", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "20", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 20", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789020", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 20", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 20", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 20", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 20", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "21", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 21", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789021", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 21", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 21", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 21", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 21", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "22", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 22", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789022", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 22", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 22", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 22", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 22", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "23", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 23", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789023", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 23", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 23", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 23", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 23", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "24", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 24", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789024", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 24", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 24", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 24", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 24", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "25", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 25", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789025", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 25", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 25", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 25", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 25", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "26", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 26", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789026", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 26", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 26", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 26", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 26", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "27", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 27", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789027", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 27", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 27", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 27", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 27", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "28", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 28", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789028", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 28", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 28", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 28", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 28", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "29", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 29", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789029", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 29", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 29", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 29", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 29", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "30", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 30", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789030", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 30", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 30", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 30", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 30", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "31", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 31", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789031", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 31", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 31", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 31", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 31", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "32", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 32", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789032", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 32", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 32", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 32", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 32", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "33", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 33", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789033", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 33", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 33", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 33", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 33", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "34", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 34", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789034", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 34", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 34", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 34", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 34", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "35", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 35", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789035", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 35", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 35", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 35", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 35", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "36", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 36", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789036", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 36", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 36", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 36", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 36", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "37", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 37", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789037", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 37", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 37", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 37", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 37", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "38", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 38", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789038", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 38", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 38", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 38", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 38", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "39", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 39", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789039", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 39", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 39", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 39", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 39", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "40", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 40", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789040", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 40", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 40", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 40", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 40", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "41", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 41", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789041", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 41", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 41", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 41", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 41", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "42", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 42", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789042", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 42", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 42", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 42", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 42", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "43", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 43", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789043", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 43", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 43", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 43", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 43", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "44", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 44", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789044", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 44", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 44", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 44", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 44", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "45", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 45", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789045", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 45", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 45", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 45", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 45", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "46", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 46", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789046", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 46", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 46", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 46", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 46", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "47", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 47", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789047", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 47", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 47", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 47", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 47", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "48", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 48", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789048", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 48", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 48", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 48", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 48", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "49", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 49", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789049", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 49", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 49", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 49", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 49", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "50", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 50", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789050", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 50", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 50", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 50", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 50", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "51", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 51", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789051", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 51", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 51", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 51", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 51", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "52", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 52", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789052", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 52", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 52", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 52", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 52", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "53", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 53", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789053", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 53", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 53", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 53", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 53", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "54", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 54", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789054", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 54", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 54", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 54", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 54", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "55", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 55", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789055", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 55", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 55", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 55", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 55", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "56", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 56", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789056", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 56", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 56", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 56", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 56", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "57", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 57", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789057", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 57", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 57", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 57", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 57", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "58", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 58", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789058", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 58", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 58", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 58", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 58", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "59", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 59", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789059", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 59", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 59", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 59", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 59", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "60", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 60", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789060", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 60", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 60", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 60", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 60", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "61", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 61", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789061", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 61", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 61", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 61", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 61", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "62", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 62", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789062", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 62", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 62", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 62", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 62", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "63", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 63", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789063", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 63", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 63", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 63", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 63", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "64", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 64", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789064", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 64", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 64", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 64", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 64", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "65", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 65", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789065", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 65", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 65", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 65", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 65", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "66", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 66", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789066", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 66", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 66", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 66", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 66", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "67", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 67", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789067", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 67", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 67", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 67", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 67", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "68", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 68", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789068", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 68", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 68", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 68", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 68", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "69", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 69", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789069", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 69", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 69", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 69", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 69", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "70", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 70", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789070", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 70", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 70", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 70", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 70", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "71", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 71", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789071", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 71", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 71", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 71", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 71", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "72", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 72", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789072", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 72", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 72", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 72", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 72", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "73", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 73", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789073", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 73", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 73", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 73", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 73", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "74", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 74", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789074", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 74", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 74", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 74", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 74", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "75", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 75", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789075", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 75", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 75", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 75", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 75", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "76", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 76", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789076", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 76", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 76", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 76", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 76", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "77", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 77", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789077", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 77", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 77", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 77", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 77", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "78", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 78", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789078", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 78", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 78", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 78", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 78", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "79", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 79", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789079", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 79", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 79", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 79", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 79", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "80", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 80", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789080", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 80", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 80", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 80", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 80", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "81", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 81", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789081", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 81", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 81", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 81", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 81", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "82", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 82", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789082", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 82", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 82", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 82", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 82", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "83", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 83", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789083", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 83", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 83", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 83", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 83", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "84", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 84", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789084", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 84", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 84", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 84", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 84", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "85", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 85", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789085", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 85", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 85", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 85", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 85", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "86", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 86", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789086", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 86", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 86", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 86", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 86", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "87", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 87", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789087", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 87", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 87", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 87", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 87", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "88", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 88", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789088", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 88", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 88", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 88", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 88", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "89", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 89", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789089", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 89", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 89", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 89", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 89", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "90", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 90", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789090", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 90", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 90", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 90", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 90", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "91", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 91", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789091", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 91", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 91", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 91", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 91", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "92", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 92", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789092", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 92", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 92", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 92", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 92", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "93", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 93", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789093", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 93", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 93", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 93", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 93", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "94", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 94", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789094", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 94", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 94", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 94", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 94", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "95", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 95", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789095", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 95", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 95", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 95", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 95", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "96", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 96", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789096", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 96", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 96", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 96", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 96", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "97", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 97", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789097", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 97", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 97", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 97", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 97", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "98", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 98", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789098", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 98", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 98", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 98", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 98", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "99", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 99", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789099", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 99", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 99", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 99", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 99", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false }, { "id": "100", "serviceConfigId": "b5dd9daf-0000-0000-0000-000000000000", "name": "Default NameString for Microsoft Achievements Sample Achievement 100", "titleAssociations": [ { "name": "Microsoft Achievements Sample", "id": 3051199919, "version": "abc" } ], "progressState": "NotStarted", "progression": { "requirements": [ { "id": "12345678-1234-1234-1234-123456789100", "current": null, "target": "100", "operationType": "sum", "ruleParticipationType": "Individual" } ], "timeUnlocked": "0001-01-01T00:00:00Z" }, "mediaAssets": [ { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com/" } ], "platforms": [ "Durango", "Xbox360" ], "isSecret": true, "description": "Default DescriptionString for Microsoft Achievements Sample Achievement 100", "lockedDescription": "Default UnachievedString for Microsoft Achievements Sample Achievement 100", "productId": "12345678-1234-1234-1234-123456789012", "achievementType": "Challenge", "participationType": "Individual", "timeWindow": { "startDate": "2013-02-01T00:00:00Z", "endDate": "2100-07-01T00:00:00Z" }, "rewards": [ { "name": null, "description": null, "value": "10", "type": "Gamerscore", "valueType": "Int", "mediaAsset": null }, { "name": "Default Name for InAppReward for Microsoft Achievements Sample Achievement 100", "description": "Default Description for InAppReward for Microsoft Achievements Sample Achievement 100", "value": "1", "type": "InApp", "valueType": "String", "mediaAsset": { "name": "Icon Name", "type": "Icon", "url": "http://www.xbox.com" } } ], "estimatedTime": "06:12:14", "deeplink": "aWFtYWRlZXBsaW5r", "isRevoked": false } ] } ================================================ FILE: Tests/ApiExplorer/Tests/achievements/achievements-cpp.lua ================================================ test = require 'u-test' common = require 'common' function GetAchievementsForTitleIdCpp_Handler() print("GetAchievementsForTitleIdCpp_Handler") AchievementsServiceGetAchievementsForTitleId() end function OnAchievementsServiceGetAchievementsForTitleId() hr, hasNext = AchievementsResultHasNextCpp() print("hasNext " .. hr) print("hr " .. hr) if hasNext ~= 0 then AchievementsResultGetNextCpp() else test.stopTest(); end end function OnAchievementsResultGetNextCpp() print("OnAchievementsResultGetNextCpp") OnAchievementsServiceGetAchievementsForTitleId() end test.TestGetAchievementsForTitleCpp = function() common.init(GetAchievementsForTitleIdCpp_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/achievements/achievements.lua ================================================ test = require 'u-test' common = require 'common' function GetAchievementsForTitleId_Handler() print("GetAchievementsForTitleId_Handler") XblAchievementsGetAchievementsForTitleIdAsync() end function OnXblAchievementsGetAchievementsForTitleIdAsync() XblAchievementsResultGetAchievements() hr, hasNext = XblAchievementsResultHasNext() print("hasNext " .. hr) print("hr " .. hr) if hasNext ~= 0 then XblAchievementsResultGetNextAsync() else XblAchievementsResultCloseHandle(); test.stopTest(); end end function OnXblAchievementsResultGetNextAsync() print("OnXblAchievementsResultGetNextAsync") XblAchievementsGetAchievementsForTitleIdAsync() end test.isbvt = true test.TestGetAchievementsForTitle = function() common.init(GetAchievementsForTitleId_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/achievements/achievements_manager.lua ================================================ test = require 'u-test' common = require 'common' local function VerifySuccess(hr) SetCheckHR(1) if hr ~= 0 then test.equal(hr, 0) print("Failure. hr=" .. hr) test.stopTest() end return true end function AchievementsManager_Handler() print("AchievementsManager_Handler") StartAchievementsManagerDoWorkLoop() XblAchievementsManagerAddLocalUser() end function OnXblAchievementsManagerDoWork_LocalUserAddedEvent() print("OnXblAchievementsManagerDoWork_LocalUserAddedEvent") handle, hr = XblAchievementsManagerGetAchievement("1") VerifySuccess(hr) count, hr = XblAchievementsManagerResultGetAchievements(handle) VerifySuccess(hr) XblAchievementsManagerResultCloseHandle(handle) handle, hr = XblAchievementsManagerGetAchievements() VerifySuccess(hr) count, hr = XblAchievementsManagerResultGetAchievements(handle) if count ~= 2 then test.equal(hr, 0); print("Failure. Number of achievements found (" .. count .. ") different from expected (2). hr=" .. hr) test.stopTest() end VerifySuccess(hr) XblAchievementsManagerResultCloseHandle(handle) handle, hr = XblAchievementsManagerGetAchievements() VerifySuccess(hr) count, hr = XblAchievementsManagerResultGetAchievements() VerifySuccess(hr) XblAchievementsManagerResultCloseHandle(handle) handle, hr = XblAchievementsManagerGetAchievementsByState() VerifySuccess(hr) count, hr = XblAchievementsManagerResultGetAchievements() VerifySuccess(hr) XblAchievementsManagerResultCloseHandle(handle) XblAchievementsManagerRemoveLocalUser() StopAchievementsManagerDoWorkLoop() test.stopTest() end function OnXblAchievementsManagerDoWork_AchievementProgressUpdatedEvent() print("OnXblAchievementsManagerDoWork_AchievementProgressUpdatedEvent") end function OnXblAchievementsManagerDoWork_AchievementUnlockedEvent() print("OnXblAchievementsManagerDoWork_AchievementUnlockedEvent") end test.isbvt = true test.TestAchievementsManager = function() common.init(AchievementsManager_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/achievements/achievements_manager_performance_test.lua ================================================ test = require 'u-test' common = require 'common' local function VerifySuccess(hr) SetCheckHR(1) if hr ~= 0 then test.equal(hr, 0) print("Failure. hr=" .. hr) test.stopTest() end return true end function AchievementsManagerPerformance_Handler( ... ) print("AchievementsManagerPerformance_Handler") -- start test SetupAchievementsManagerPerformanceTestMock() StartAchievementsManagerDoWorkLoop() XblAchievementsManagerAddLocalUser() end function OnXblAchievementsManagerDoWork_LocalUserAddedEvent() print("OnXblAchievementsManagerDoWork_LocalUserAddedEvent") -- call get achievements performance test handle, hr = XblAchievementsManagerGetAchievementsPerfTest() VerifySuccess(hr) count, hr = XblAchievementsManagerResultGetAchievements(handle) if count ~= 100 then test.equal(hr, 0); print("Failure. Number of achievements found (" .. count .. ") different from expected (100). hr=" .. hr) test.stopTest() end VerifySuccess(hr) XblAchievementsManagerResultCloseHandle(handle) XblAchievementsManagerRemoveLocalUser() StopAchievementsManagerDoWorkLoop() test.stopTest() end test.isbvt = true test.TestAchievementsManager = function() common.init(AchievementsManagerPerformance_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/achievements/achievements_manager_update_achievements.lua ================================================ test = require 'u-test' common = require 'common' -- Note: This test requires an account with achievement id 2 locked with no progress, and can only be run -- one time on that account as we currently have no way to programmatically reset the unlock -- status of an achievement from API Explorer. To successfully run the test again you will have -- to run the XblPlayerDataReset tool to reset the user or sign in with a different account. local function VerifySuccess(hr) SetCheckHR(1) if hr ~= 0 then test.equal(hr, 0) print("Failure. hr=" .. hr) test.stopTest() end return true end local unlockeReceived = false local updateReceived = 0 local originalUnlockCount = 0 local testUpdate = true -- if false, then Unlock is being tested local function IsUpdateTestCompleted() if updateReceived >= 2 and testUpdate then updateReceived = 0 return true end return false end local function IsUnlockTestCompleted() if unlockReceived and updateReceived >= 1 and not testUpdate then unlockReceived = false updateReceived = 0 return true end return false end -- Order of unlock and update events getting processed isn't guaranteed, so moving -- the test completion logic to a separate function. local function EndTestIfUnlockComplete() if IsUnlockTestCompleted() then XblAchievementsManagerRemoveLocalUser() StopAchievementsManagerDoWorkLoop() test.stopTest() end end function AchievementsManagerUpdateAchievements_Handler() print("AchievementsManagerUpdateAchievements_Handler") StartAchievementsManagerDoWorkLoop() XblAchievementsManagerAddLocalUser() end function OnXblAchievementsManagerDoWork_LocalUserAddedEvent() print("OnXblAchievementsManagerDoWork_LocalUserAddedEvent") handle, hr = XblAchievementsManagerGetAchievementsByState() VerifySuccess(hr) count, hr = XblAchievementsManagerResultGetAchievements() VerifySuccess(hr) XblAchievementsManagerResultCloseHandle(handle) originalUnlockCount = count testUpdate = true XblAchievementsManagerUpdateAchievement("2", 90) XblAchievementsManagerUpdateAchievement("1", 50) end function OnXblAchievementsManagerDoWork_AchievementProgressUpdatedEvent() print("OnXblAchievementsManagerDoWork_AchievementProgressUpdatedEvent") handle, hr = XblAchievementsManagerGetAchievement("2") VerifySuccess(hr) count, hr = XblAchievementsManagerResultGetAchievements() VerifySuccess(hr) XblAchievementsManagerResultCloseHandle(handle) updateReceived = updateReceived + 1 if IsUpdateTestCompleted() then updateReceived = 0 testUpdate = false XblAchievementsManagerUpdateAchievement("2", 100) end EndTestIfUnlockComplete() end function OnXblAchievementsManagerDoWork_AchievementUnlockedEvent() print("OnXblAchievementsManagerDoWork_AchievementUnlockedEvent") handle, hr = XblAchievementsManagerGetAchievementsByState() VerifySuccess(hr) count, hr = XblAchievementsManagerResultGetAchievements() VerifySuccess(hr) XblAchievementsManagerResultCloseHandle(handle) unlockReceived = true if count ~= originalUnlockCount + 1 then test.equal(count, originalUnlockCount + 1) print("Failure. Number of unlocked achievements (" .. count .. ") differs from expected (" .. originalUnlockCount + 1 .. ").") test.stopTest() end EndTestIfUnlockComplete() end test.skip = true test.TestAchievementsManager = function() common.init(AchievementsManagerUpdateAchievements_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/achievements/achievements_progress_notification.lua ================================================ test = require 'u-test' common = require 'common' function AchievementProgressNotifications_Handler() print ('AchievementProgressNotifications_Handler') XblAchievementsAddAchievementProgressChangeHandler() SetCheckHR(0) Sleep(5000) XblAchievementsUpdateAchievementAsync("2", 20) end function OnXblAchievementsAddAchievementProgressHandler() print ('OnXblAchievementsAddAchievementProgressHandler') XblAchievementsRemoveAchievementProgressChangeHandler() test.stopTest(); end function OnXblAchievementsUpdateAchievementAsync() print ("OnXblAchievementsUpdateAchievementAsync") local hr = GetLastError() SetCheckHR(1) if hr == -2145844944 then -- -2145844944 == 0x80190130 == HTTP_E_STATUS_NOT_MODIFIED print("Achievement not modified.") XblAchievementsRemoveAchievementProgressChangeHandler() test.stopTest() elseif hr ~= 0 then test.equal(hr, 0); print("Failure. hr=" .. hr) test.stopTest() else print ("Waiting to receive notification.") end end test.skip = true; test.TestAchievementProgressNotification = function() common.init(AchievementProgressNotifications_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/achievements/update-achievements-cpp.lua ================================================ test = require 'u-test' common = require 'common' function AchievementsServiceUpdateAchievement_Handler() print("AchievementsServiceUpdateAchievement_Handler") SetCheckHR(0) AchievementsServiceUpdateAchievement() end function OnAchievementsServiceUpdateAchievement() local hr = GetLastError() SetCheckHR(1) if hr ~= 0 and hr ~= -2145844944 then -- -2145844944 == 0x80190130 == HTTP_E_STATUS_NOT_MODIFIED test.equal(hr, 0); print("Failure. hr=" .. hr) end test.stopTest(); end test.TestAchievementsServiceUpdateAchievementCpp = function() common.init(AchievementsServiceUpdateAchievement_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/achievements/update-achievements.lua ================================================ test = require 'u-test' common = require 'common' function XblAchievementsUpdateAchievementAsync_Handler() print("XblAchievementsUpdateAchievementAsync_Handler") SetCheckHR(0) XblAchievementsUpdateAchievementAsync() end function OnXblAchievementsUpdateAchievementAsync() local hr = GetLastError() SetCheckHR(1) if hr ~= 0 and hr ~= -2145844944 then -- -2145844944 == 0x80190130 == HTTP_E_STATUS_NOT_MODIFIED test.equal(hr, 0); print("Failure. hr=" .. hr) end test.stopTest(); end test.isbvt = true test.TestXblAchievementsUpdateAchievementAsync = function() common.init(XblAchievementsUpdateAchievementAsync_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/cmds.json ================================================ { "commands": [ // remove "rem" from lines below to uncomment // multi-device examples // one device should host, and one device should join "rem host changetoyouralias", "rem join changetoyouralias", "rem runmultidevicetests", "rem rt mp\\MP_JoinLobbyViaActivity.lua", "rem rt multiplayerManager\\MPM_JoinLobbyViaActivity.lua", "rem rt multiplayerManager\\MPM_JoinFixedGameSession.lua", "rem rt multiplayerManager\\MPM_Match.lua", "rem rt multiplayerManager\\MPM_Invite.lua", "rem rt multiplayerManager\\MPM_InviteUI.lua", // fault injection "rem faultinjection options 1 7 1", // faultinjection options failFreq freqChangeSpeed freqChangeAmount "rem faultinjection user", // "faultinjection" enables specific fault injection on specific features "rem faultinjection http", // "faultinjection" enables specific fault injection on specific features // mem hook tracking "rem memtrack true", // result logged after runtests or rt command ends // single-device command examples "rem runbarescript misc\\global_state.lua", // "runbarescript" will run the script without any XBL/XAL initializtion for special test needs "rem rt achievements\\achievements_progress_notification.lua", "rem rt achievements\\achievements_manager_performance_test.lua", "rem rt achievements\\achievements_manager_update_achievements.lua", "rem rt gdk-gameinvite\\game-invite.lua", // rt will run a single test "rem repeat social\\social_manager_2.lua", // "repeat" will repeat this single test without cleanup forever "rem loop", // "loop" will loop the entire cmds.json file including cleanup, forever "rt multiplayerActivity\\send_invite.lua" ] } ================================================ FILE: Tests/ApiExplorer/Tests/events/events-cpp.lua ================================================ test = require 'u-test' common = require 'common' function TestWriteEventCpp_Handler() print("TestWriteEventCpp_Handler") EventsServiceWriteInGameEvent() test.stopTest(); end test.TestWriteEventCpp = function() common.init(TestWriteEventCpp_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/events/events.lua ================================================ test = require 'u-test' common = require 'common' function TestWriteEvent_Handler() print("TestWriteEvent_Handler") XblEventsWriteInGameEvent() test.stopTest(); end test.TestWriteEvent = function() common.init(TestWriteEvent_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/events/offline-cpp.lua ================================================ -- Writes some events while offline, regains connection and uploads the events test = require 'u-test' common = require 'common' function TestOfflineEventsCpp_Handler() print("TestOfflineEventsCpp_Handler") LeaderboardServiceGetLeaderboard() end local count = 0; function OnLeaderboardServiceGetLeaderboard() print("OnLeaderboardServiceGetLeaderboard") count = count + 1 if count == 1 then UploadEventsAndWait(1) LeaderboardServiceGetLeaderboard() elseif count == 2 then SetupEventUploadMock() UploadEventsAndWait(9) HCMockClearMocks() --Clear mocks and events should upload automatically Sleep(5000) LeaderboardServiceGetLeaderboard() elseif count == 3 then test.stopTest() end end function SetupEventUploadMock() --Force event uploads to fail HCMockCallCreate() HCMockResponseSetNetworkErrorCode() HCMockAddMock("POST", "https://vortex.data.microsoft.com/collect/v1") end function UploadEventsAndWait(count) while count > 0 do EventsServiceWriteInGameEvent() count = count - 1 end Sleep(5000) end test.TestOfflineEventsCpp = function() common.init(TestOfflineEventsCpp_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/events/offline.lua ================================================ -- Writes some events while offline, regains connection and uploads the events test = require 'u-test' common = require 'common' function TestOfflineEvents_Handler() print("TestOfflineEvents_Handler") XblLeaderboardGetLeaderboardAsync() end local count = 0; function OnXblLeaderboardGetLeaderboardAsync() print("OnXblLeaderboardGetLeaderboardAsync") count = count + 1 if count == 1 then UploadEventsAndWait(1) XblLeaderboardGetLeaderboardAsync() elseif count == 2 then SetupEventUploadMock() UploadEventsAndWait(9) HCMockClearMocks() --Clear mocks and events should upload automatically Sleep(5000) XblLeaderboardGetLeaderboardAsync() elseif count == 3 then test.stopTest() end end function SetupEventUploadMock() --Force event uploads to fail HCMockCallCreate() HCMockResponseSetNetworkErrorCode() HCMockAddMock("POST", "https://vortex.data.microsoft.com/collect/v1") end function UploadEventsAndWait(count) while count > 0 do XblEventsWriteInGameEvent() count = count - 1 end Sleep(5000) end test.TestOfflineEvents = function() common.init(TestOfflineEvents_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/events/offline2-cpp.lua ================================================ -- Write 10 events while offline. An offline events file with 10 events should be written during this test, and then deleted at the end of this test. test = require 'u-test' common = require 'common' function TestOfflineEventsCpp_Handler() print("TestOfflineEventsCpp_Handler") SetupEventUploadMock() EventsServiceWriteInGameEvent() EventsServiceWriteInGameEvent() EventsServiceWriteInGameEvent() EventsServiceWriteInGameEvent() EventsServiceWriteInGameEvent() EventsServiceWriteInGameEvent() EventsServiceWriteInGameEvent() EventsServiceWriteInGameEvent() EventsServiceWriteInGameEvent() EventsServiceWriteInGameEvent() XblContextCloseHandle() Sleep(2000) ValidateOfflineEventsDirectoryFileExistsAndDelete() test.stopTest() end function SetupEventUploadMock() --Force event uploads to fail HCMockCallCreate() HCMockResponseSetNetworkErrorCode() HCMockAddMock("POST", "https://vortex.data.microsoft.com/collect/v1") end test.TestOfflineEvents2Cpp = function() common.init(TestOfflineEventsCpp_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/events/offline2.lua ================================================ -- Write 10 events while offline. An offline events file with 10 events should be leftover after this test. test = require 'u-test' common = require 'common' function TestOfflineEvents_Handler() print("TestOfflineEvents_Handler") SetupEventUploadMock() XblEventsWriteInGameEvent() XblEventsWriteInGameEvent() XblEventsWriteInGameEvent() XblEventsWriteInGameEvent() XblEventsWriteInGameEvent() XblEventsWriteInGameEvent() XblEventsWriteInGameEvent() XblEventsWriteInGameEvent() XblEventsWriteInGameEvent() XblEventsWriteInGameEvent() XblContextCloseHandle() Sleep(2000) ValidateOfflineEventsDirectoryFileExistsAndDelete() test.stopTest() end function SetupEventUploadMock() --Force event uploads to fail HCMockCallCreate() HCMockResponseSetNetworkErrorCode() HCMockAddMock("POST", "https://vortex.data.microsoft.com/collect/v1") end test.TestOfflineEvents2 = function() common.init(TestOfflineEvents_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/gdk-gameinvite/game-invite-listen.lua ================================================ test = require 'u-test' common = require 'common' function TestGameInvite_Handler() print("LUA: calling XGameInviteRegisterForEvent") XGameInviteRegisterForEvent(); print("LUA: called XGameInviteRegisterForEvent") end function OnXGameInviteRegisterForEvent() print("LUA: OnXGameInviteRegisterForEvent"); XblMultiplayerGetSessionByHandleAsync(); end test.skip = true test.TestGameInvite = function() common.init(TestGameInvite_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/gdk-gameinvite/game-invite-send.lua ================================================ test = require 'u-test' common = require 'common' function TestGameInviteSend_Handler() XblMultiplayerSessionCreateHandle() XblMultiplayerSessionJoin() XblMultiplayerWriteSessionAsync() end function OnXblMultiplayerWriteSessionAsync() XblMultiplayerSessionReferenceCreate(); XblMultiplayerSetActivityAsync(); end function OnXblMultiplayerSetActivityAsync() XGameUiShowSendGameInviteAsync(); end function OnXGameUiShowSendGameInviteAsync() test.stopTest(); end test.skip = true test.TestGameInviteSend = function() common.init(TestGameInviteSend_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/gdk-gameinvite/game-mpainvite-send.lua ================================================ test = require 'u-test' common = require 'common' function TestGameMpaInviteSend_Handler() XblMultiplayerActivitySetActivityAsync() print("LUA: calling XGameInviteRegisterForEvent") XGameInviteRegisterForEvent(); print("LUA: called XGameInviteRegisterForEvent") end function OnXblMultiplayerActivitySetActivityAsync() XGameUiShowMultiplayerActivityGameInviteAsync(); end function OnXGameUiShowMultiplayerActivityGameInviteAsync() print("XGameUiShowMultiplayerActivityGameInviteAsync completed"); end function OnXGameInviteRegisterForEvent() print("LUA: OnXGameInviteRegisterForEvent"); end test.skip = true test.TestGameMpaInviteSend = function() common.init(TestGameMpaInviteSend_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/leaderboard/leaderboard-2017.lua ================================================ test = require 'u-test' common = require 'common' function TestLeaderboard2017_Handler() print("TestLeaderboard_Handler") XblTitleManagedStatsWriteAsync() end function OnXblTitleManagedStatsWriteAsync() XblLeaderboardGetLeaderboardAsync("", "TestStat1", "XblSocialGroupType::People", "XblLeaderboardQueryType::TitleManagedStatBackedGlobal") end local leaderboardQueryCompleteCount = 0 function OnXblLeaderboardGetLeaderboardAsync() leaderboardQueryCompleteCount = leaderboardQueryCompleteCount + 1 if leaderboardQueryCompleteCount == 2 then test.stopTest() else XblLeaderboardGetLeaderboardAsync("", "TestStat1", "XblSocialGroupType::People", "XblLeaderboardQueryType::TitleManagedStatBackedSocial") end end test.skip = true test.TestLeaderboard2017 = function() common.init(TestLeaderboard2017_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/leaderboard/leaderboard-bvt.lua ================================================ test = require 'u-test' common = require 'common' function TestLeaderboardBvt_Handler() print("TestLeaderboardBvt_Handler") XblLeaderboardGetLeaderboardAsync() end function OnXblLeaderboardGetLeaderboardAsync() print('OnXblLeaderboardGetLeaderboardAsync') test.stopTest(); end test.isbvt = true test.TestLeaderboardBvt = function() common.init(TestLeaderboardBvt_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/leaderboard/leaderboard-cpp.lua ================================================ test = require 'u-test' common = require 'common' function TestLeaderboardCpp_Handler() print("TestLeaderboardCpp_Handler") LeaderboardServiceGetLeaderboard() end function OnLeaderboardServiceGetLeaderboard() print('OnLeaderboardServiceGetLeaderboard') hasNext, hr = LeaderboardResultHasNextCpp() if hasNext == 0 then print("hasNext: false"); else print("hasNext: true"); end print("hr " .. hr) if hasNext ~= 0 then LeaderboardResultGetNextCpp() else test.stopTest(); end end function OnLeaderboardResultGetNextCpp() print("OnLeaderboardResultGetNextCpp") test.stopTest(); end test.TestLeaderboardCpp = function() common.init(TestLeaderboardCpp_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/leaderboard/leaderboard.lua ================================================ test = require 'u-test' common = require 'common' function TestLeaderboard_Handler() print("TestLeaderboard_Handler") XblLeaderboardGetLeaderboardAsync("TotalPuzzlesSolvedLB") end local leaderboardQueryCompleteCount = 0 function OnXblLeaderboardGetLeaderboardAsync() print('OnXblLeaderboardGetLeaderboardAsync') hasNext, hr = XblLeaderboardResultHasNext() if hasNext == 0 then print("hasNext: false"); else print("hasNext: true"); end print("hr " .. hr) if hasNext ~= 0 then XblLeaderboardResultGetNextAsync() else leaderboardQueryCompleteCount = leaderboardQueryCompleteCount + 1 if leaderboardQueryCompleteCount == 2 then test.stopTest(); else XblLeaderboardGetLeaderboardAsync("", "TotalPuzzlesSolved") end end end function OnXblLeaderboardResultGetNextAsync() print("OnXblLeaderboardResultGetNextAsync") OnXblLeaderboardGetLeaderboardAsync() end test.TestLeaderboard = function() common.init(TestLeaderboard_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/libHttp/httpHandle.lua ================================================ test = require 'u-test' common = require 'common' test.testHttpHandle = function() print("testHttpHandle") HCInitialize(); HCGetLibVersion(); HCHttpCallCreate(); HCHttpCallDuplicateHandle(); HCHttpCallGetId(); HCHttpCallSetTracing(); HCHttpCallRequestSetUrl(); HCHttpCallGetRequestUrl(); HCHttpCallRequestSetRequestBodyString(); HCHttpCallRequestSetHeader(); HCHttpCallRequestSetRetryAllowed(); HCHttpCallRequestSetRetryCacheId(); HCHttpCallRequestSetTimeout(); HCHttpCallRequestSetRetryDelay(); HCHttpCallRequestSetTimeoutWindow(); -- HCHttpCallRequestSetRequestBodyBytes HCHttpCallCloseHandle(); HCCleanup(); test.stopTest(); end ================================================ FILE: Tests/ApiExplorer/Tests/libHttp/httpPerform.lua ================================================ test = require 'u-test' common = require 'common' function OnHCHttpCallPerformAsync() print("OnHCHttpCallPerformAsync") HCHttpCallResponseGetResponseString(); HCHttpCallResponseGetStatusCode(); HCHttpCallResponseGetNetworkErrorCode(); HCHttpCallResponseGetHeader(); HCHttpCallResponseGetNumHeaders(); HCHttpCallResponseGetHeaderAtIndex(); --HCHttpCallResponseGetResponseBodyBytes --HCHttpCallResponseGetResponseBodyBytesSize HCHttpCallCloseHandle(); test.stopTest(); end test.testHttpPerform = function() print("testHttpPerform") HCInitialize(); --HCAddCallRoutedHandler --HCRemoveCallRoutedHandler HCHttpCallCreate(); HCHttpCallRequestSetUrl(); HCHttpCallGetRequestUrl(); HCHttpCallRequestSetRequestBodyString(); HCHttpCallRequestSetHeader(); HCHttpCallPerformAsync(); end ================================================ FILE: Tests/ApiExplorer/Tests/libHttp/libHCTrace.lua ================================================ test = require 'u-test' common = require 'common' test.testlibHCTrace = function() print("test LHC trace") HCInitialize(); HCTrace(); HCTraceLarge(); HCCleanup(); test.stopTest(); end ================================================ FILE: Tests/ApiExplorer/Tests/libHttp/manualDispatchTest.lua ================================================ test = require 'u-test' common = require 'common' callCompleted=false; function OnHCHttpCallPerformAsync() print("HCHttpCallPerformAsync Complete") HCHttpCallCloseHandle(); callCompleted=true; SetCheckHR(1); end function OnHCCleanupAsync() print("HCCleanupAsync Complete") StopManualDispatchThread(); test.assert(callCompleted) test.stopTest(); end test.skip = true test.ManualDispatchTest = function() HCInitialize(); XTaskQueueCreate() StartManualDispatchThread(); HCHttpCallCreate(); HCHttpCallRequestSetUrl(); HCHttpCallGetRequestUrl(); HCHttpCallRequestSetRequestBodyString(); HCHttpCallRequestSetHeader(); SetCheckHR(0); -- PerformAsync should fail here HCHttpCallPerformAsync(); HCCleanupAsync(); end ================================================ FILE: Tests/ApiExplorer/Tests/libHttp/websocket_cleanup_while_connected.lua ================================================ test = require 'u-test' common = require 'common' -- Requires manually running local echo server test.skip = true test.websocketCleanupWhileConnected = function() print("websocketCleanupWhileConnected") HCInitialize(); HCWebSocketCreate(); HCWebSocketConnectAsync(); end function OnHCWebSocketConnectAsync() print("OnHCWebSocketConnectAsync") HCCleanupAsync(); end function OnHCWebsocketClosed() print("OnHCWebsocketClosed"); HCWebSocketCloseHandle(); end function OnHCCleanupAsync() print("OnHCCleanupAsync"); test.stopTest(); end ================================================ FILE: Tests/ApiExplorer/Tests/libHttp/websocket_cleanup_while_connecting.lua ================================================ test = require 'u-test' common = require 'common' -- Requires manually running local echo server test.skip = true test.websocketCleanupWhileConnecting = function() print("websocketCleanupWhileConnecting") HCInitialize(); HCWebSocketCreate(); HCWebSocketConnectAsync(); HCCleanupAsync(); end function OnHCWebSocketConnectAsync() print("OnHCWebSocketConnectAsync") HCWebSocketCloseHandle(); end function OnHCWebsocketClosed() print("OnHCWebsocketClosed"); end function OnHCCleanupAsync() print("OnHCCleanupAsync"); test.stopTest(); end ================================================ FILE: Tests/ApiExplorer/Tests/libHttp/websocket_closehandle_while_connected.lua ================================================ test = require 'u-test' common = require 'common' --Bug 35915673 Disabling websocket tests due to websocket.org no longer in service test.skip = true test.testWebsocketCloseHandleWhileConnected = function() print("testWebsocketCloseHandleWhileConnected") HCInitialize(); HCWebSocketCreate(); HCWebSocketConnectAsync(); end function OnHCWebSocketConnectAsync() print("OnHCWebSocketConnectAsync") HCWebSocketCloseHandle(); test.stopTest(); end ================================================ FILE: Tests/ApiExplorer/Tests/libHttp/websocket_disconnect_while_connecting.lua ================================================ test = require 'u-test' common = require 'common' --Bug 35915673 Disabling websocket tests due to websocket.org no longer in service test.skip = true test.testWebsocketDisconnectWhileConnecting = function() print("testWebsocketDisconnectWhileConnecting") HCInitialize(); HCWebSocketCreate(); HCWebSocketConnectAsync(); SetCheckHR(0); HCWebSocketDisconnect(); local hr = GetLastError(); SetCheckHR(1); if hr ~= -2147418113 then -- -2147418113 == 0x8000FFFF == E_UNEXPECTED test.equal(hr, 0); print("Failure. hr=" .. hr) end HCWebSocketCloseHandle(); end function OnHCWebSocketConnectAsync() test.stopTest(); end ================================================ FILE: Tests/ApiExplorer/Tests/libHttp/websocket_send.lua ================================================ test = require 'u-test' common = require 'common' --Bug 35915673 Disabling websocket tests due to websocket.org no longer in service test.skip = true test.testWebsocketSend = function() print("testWebsocketSend") HCInitialize(); HCWebSocketCreate(); HCWebSocketConnectAsync(); end function OnHCWebSocketConnectAsync() print("OnHCWebSocketConnectAsync") HCWebSocketSendMessageAsync(); end function OnHCWebsocketMessageReceived() HCWebSocketDisconnect(); end function OnHCWebsocketClosed() HCWebSocketCloseHandle(); HCCleanup(); test.stopTest(); end ================================================ FILE: Tests/ApiExplorer/Tests/misc/global_state.lua ================================================ test = require 'u-test' -- Test that initializes XAL + XSAPI, adds a user and then shuts down -- without calling cleanup, making sure the global state cleanup happens correctly test.skip = true test.globalState = function() XalPlatformWebSetEventHandler() XalPlatformStorageSetEventHandlers() XalInitialize() XblInitialize() XalAddUserWithUiAsync() end function OnXalAddUserWithUiAsync() test.stopTest() end ================================================ FILE: Tests/ApiExplorer/Tests/misc/null_task_queue.lua ================================================ test = require 'u-test' -- Ensure we properly handle a null process queue -- XblInitialize should fail with E_NO_TASK_QUEUE if the process task queue is set to nullptr -- and we try and get a default queue (XblInitArgs queue is set to nullptr) test.nullTaskQueue = function() local taskQueue = XTaskQueueGetCurrentProcessTaskQueue() XTaskQueueSetCurrentProcessTaskQueue(0) XalPlatformWebSetEventHandler() XalPlatformStorageSetEventHandlers() XalInitialize() SetCheckHR(0) XblInitialize(0) local hr = GetLastError() print("XblInitialize: hr=" .. hr) print("Expected: -2147024469 (E_NO_TASK_QUEUE)") SetCheckHR(1) test.equal(hr, -2147024469) if hr ~= -2147024469 then -- -2147024469 == 0x800701AB == E_NO_TASK_QUEUE print("Failure. hr=" .. hr) else print("Succeeded.") end XTaskQueueSetCurrentProcessTaskQueue(taskQueue) test.stopTest(); end ================================================ FILE: Tests/ApiExplorer/Tests/misc/override_locale.lua ================================================ test = require 'u-test' common = require 'common' -- Ensure we properly handle overriding locales function OverrideLocale() print("OverrideLocale"); XblSetOverrideLocale(); test.stopTest(); end test.OverrideLocale = function() common.init(OverrideLocale) end ================================================ FILE: Tests/ApiExplorer/Tests/misc/simpletest.lua ================================================ test = require 'u-test' common = require 'common' test.simpletest = function() test.assert(2 == 2) test.stopTest(); end ================================================ FILE: Tests/ApiExplorer/Tests/misc/xbox_live_context.lua ================================================ test = require 'u-test' common = require 'common' function TestXblContextCreate_Handler() XblAddServiceCallRoutedHandler() XblRemoveServiceCallRoutedHandler() XblContextSettingsGetLongHttpTimeout() XblContextSettingsSetLongHttpTimeout() XblContextSettingsGetHttpRetryDelay() XblContextSettingsSetHttpRetryDelay() XblContextSettingsGetHttpTimeoutWindow() XblContextSettingsSetHttpTimeoutWindow() XblContextSettingsGetWebsocketTimeoutWindow() XblContextSettingsSetWebsocketTimeoutWindow() XblContextSettingsGetUseCrossPlatformQosServers() XblContextSettingsSetUseCrossPlatformQosServers() test.stopTest(); end test.TestXblContextCreate = function() common.init(TestXblContextCreate_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/mp/MP_JoinLobbyViaActivity.lua ================================================ test = require 'u-test' common = require 'common' function MP_JoinLobbyViaActivity_Handler() print("MP_JoinLobbyViaActivity") MultiDeviceSyncAndWait("MP_JoinLobbyViaActivity"); XblRealTimeActivityActivate() XblMultiplayerSetSubscriptionsEnabled() XblMultiplayerAddSessionChangedHandler() XblMultiplayerAddSubscriptionLostHandler() if MultiDeviceIsHost() > 0 then XblMultiplayerSessionCreateHandle() XblMultiplayerSessionJoin() XblMultiplayerSessionSetClosed(0) XblMultiplayerSessionPropertiesSetJoinRestriction(); XblMultiplayerSessionPropertiesSetReadRestriction(); XblMultiplayerWriteSessionAsync() else MultiDeviceSyncAndWait("SessionCreated"); print("Joining lobby of remote player. NOTE: Ensure remote is a friend or join will fail") XblMultiplayerGetActivitiesForUsersAsync(); end end function OnXblMultiplayerGetActivitiesForUsersAsync() if MultiDeviceIsHost() > 0 then else print("Calling XblMultiplayerWriteSessionByHandleAsync"); XblMultiplayerSessionCreateHandle() XblMultiplayerSessionJoin() XblMultiplayerWriteSessionByHandleAsync() end end function OnXblMultiplayerWriteSessionByHandleAsync() MultiDeviceSyncAndWait("SessionJoined"); XblPresenceGetPresenceAsync() end function OnXblMultiplayerWriteSessionAsync() if MultiDeviceIsHost() > 0 then XblMultiplayerSessionSessionReference(); XblMultiplayerSetActivityAsync() end end function OnXblMultiplayerSetActivityAsync() if MultiDeviceIsHost() > 0 then MultiDeviceSyncAndWait("SessionCreated"); MultiDeviceSyncAndWait("SessionJoined"); XblPresenceGetPresenceAsync() end end function OnXblPresenceGetPresenceAsync() XblPresenceRecordGetXuid() XblPresenceRecordGetDeviceRecords() XblPresenceRecordGetUserState() XblPresenceRecordCloseHandle() test.stopTest(); end test.ismultidevice = true test.MP_JoinLobbyViaActivity = function() common.init(MP_JoinLobbyViaActivity_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/mp/matchmaking_single_device-cpp.lua ================================================ test = require 'u-test' common = require 'common' function TestMatchmakingCpp_Handler() print("TestMatchmakingCpp_Handler") HCTraceSetTraceToDebugger() HCSettingsSetTraceLevel() MultiplayerSessionReferenceCreateCpp(); MultiplayerSessionReferenceIsValidCpp(); MultiplayerSessionCreateCpp() MultiplayerSessionJoinCpp() MultiplayerServiceWriteSession() end local writeCount = 0; function OnMultiplayerServiceWriteSession() writeCount = writeCount + 1 if writeCount == 1 then -- triggers from TestMatchmaking_Handler print("OnMultiplayerServiceWriteSession 1") MatchmakingServiceCreateTicket(); MultiplayerSessionSetMatchmakingResubmitCpp() MultiplayerSessionSetServerConnectionStringCandidatesCpp() MultiplayerSessionSetMatchmakingServerConnectionPathCpp() MultiplayerSessionSetMatchmakingTargetSessionConstantsJsonCpp() MultiplayerServiceWriteSession() elseif writeCount == 2 then -- triggers from OnXblMultiplayerWriteSessionAsync when writeCount == 1 print("OnMultiplayerServiceWriteSession 2") MultiplayerSessionHostCandidatesCpp() MultiplayerSessionMatchmakingServerCpp() elseif writeCount == 3 then -- triggers from OnXblMatchmakingDeleteMatchTicket print("OnMultiplayerServiceWriteSession 3") local hr = GetLastError() SetCheckHR(1) if hr ~= 0 and hr ~= -2147023084 then -- --2147023084 == 0x80070714 == __HRESULT_FROM_WIN32(ERROR_RESOURCE_DATA_NOT_FOUND) test.equal(hr, 0); end test.stopTest(); end end function OnMatchmakingServiceCreateTicket() print("OnMatchmakingServiceCreateTicket") MatchmakingServiceGetMatchTicketDetails(); end function OnMatchmakingServiceGetMatchTicketDetails() print("OnMatchmakingServiceGetMatchTicketDetails") MatchmakingServiceGetHopperStatistics(); end function OnMatchmakingServiceGetHopperStatistics() print("OnMatchmakingServiceGetHopperStatistics") MatchmakingServiceDeleteMatchTicket(); end function OnMatchmakingServiceDeleteMatchTicket() print("OnMatchmakingServiceDeleteMatchTicket") MultiplayerSessionLeaveCpp() MultiplayerServiceWriteSession() SetCheckHR(0) end test.TestMatchmaking_SingleDevice_Cpp = function() common.init(TestMatchmakingCpp_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/mp/matchmaking_single_device.lua ================================================ test = require 'u-test' common = require 'common' function TestMatchmaking_Handler() print("TestMP_Handler") HCTraceSetTraceToDebugger() HCSettingsSetTraceLevel() XblMultiplayerSessionReferenceCreate(); XblMultiplayerSessionReferenceIsValid(); XblMultiplayerSessionCreateHandle() XblMultiplayerSessionJoin() XblMultiplayerWriteSessionAsync() end local writeCount = 0; function OnXblMultiplayerWriteSessionAsync() writeCount = writeCount + 1 if writeCount == 1 then -- triggers from TestMatchmaking_Handler print("OnXblMultiplayerWriteSessionAsync 1") XblMatchmakingCreateTicket(); XblMultiplayerSessionSetMatchmakingResubmit() XblMultiplayerSessionSetServerConnectionStringCandidates() XblMultiplayerSessionSetMatchmakingServerConnectionPath() XblMultiplayerSessionSetMatchmakingTargetSessionConstantsJson() XblMultiplayerWriteSessionAsync() elseif writeCount == 2 then -- triggers from OnXblMultiplayerWriteSessionAsync when writeCount == 1 print("OnXblMultiplayerWriteSessionAsync 2") XblMultiplayerSessionHostCandidates() XblMultiplayerSessionMatchmakingServer() elseif writeCount == 3 then -- triggers from OnXblMatchmakingDeleteMatchTicket print("OnXblMultiplayerWriteSessionAsync 3") local hr = GetLastError() SetCheckHR(1) if hr ~= 0 and hr ~= -2147023084 then -- --2147023084 == 0x80070714 == __HRESULT_FROM_WIN32(ERROR_RESOURCE_DATA_NOT_FOUND) test.equal(hr, 0); end XblMultiplayerSessionCloseHandle() test.stopTest(); end end function OnXblMatchmakingCreateTicket() print("OnXblMatchmakingCreateTicket") XblMatchmakingGetMatchTicketDetails(); end function OnXblMatchmakingGetMatchTicketDetails() print("OnXblMatchmakingGetMatchTicketDetails") XblMatchmakingGetHopperStatistics(); end function OnXblMatchmakingGetHopperStatistics() print("OnXblMatchmakingGetHopperStatistics") XblMatchmakingDeleteMatchTicket(); end function OnXblMatchmakingDeleteMatchTicket() print("OnXblMatchmakingGetHopperStatistics") XblMultiplayerSessionLeave() XblMultiplayerWriteSessionAsync() SetCheckHR(0) end test.TestMatchmaking_SingleDevice = function() common.init(TestMatchmaking_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/mp/mp-cpp.lua ================================================ test = require 'u-test' common = require 'common' function TestMPCpp_Handler() print("TestMPCpp_Handler") HCTraceSetTraceToDebugger() HCSettingsSetTraceLevel() MultiplayerSessionReferenceCreateCpp() MultiplayerSessionReferenceIsValidCpp() MultiplayerSessionCreateCpp() RealTimeActivityServiceActivate() MultiplayerServiceMultiplayerSubscriptionsEnabled() MultiplayerServiceEnableMultiplayerSubscriptions() MultiplayerServiceMultiplayerSubscriptionsEnabled() MultiplayerSessionCreateCpp() MultiplayerServiceAddMultiplayerSessionChangedHandler() MultiplayerServiceAddMultiplayerSubscriptionLostHandler() MultiplayerServiceAddMultiplayerConnectionIdChangedHandler() MultiplayerServiceRemoveMultiplayerSessionChangedHandler() MultiplayerServiceRemoveMultiplayerSubscriptionLostHandler() MultiplayerServiceRemoveMultiplayerConnectionIdChangedHandler() MultiplayerServiceAddMultiplayerSessionChangedHandler() MultiplayerServiceAddMultiplayerSubscriptionLostHandler() MultiplayerSessionReferenceParseFromUriPathCpp() MultiplayerSessionJoinCpp() MultiplayerServiceWriteSession() end local writeCount = 0; function OnMultiplayerServiceWriteSession() writeCount = writeCount + 1 if writeCount == 1 then print("OnMultiplayerServiceWriteSession 1") MultiplayerSessionCurrentUserCpp() MultiplayerSessionSetCurrentUserMemberCustomPropertyJsonCpp() MultiplayerSessionCurrentUserSetEncountersCpp() MultiplayerSessionCurrentUserSetGroupsCpp() MultiplayerSessionSetCurrentUserStatusCpp() MultiplayerSessionSetCurrentUserSecureDeviceAddressBase64Cpp() MultiplayerServiceWriteSession() elseif writeCount == 2 then print("OnMultiplayerServiceWriteSession 2") MultiplayerSessionDeleteCurrentUserMemberCustomPropertyJsonCpp() MultiplayerSessionSetClosedCpp() MultiplayerSessionSetSessionCustomPropertyJsonCpp() MultiplayerSessionSetAllocateCloudComputeCpp() MultiplayerSessionSetHostDeviceTokenCpp() MultiplayerSessionSetLockedCpp() MultiplayerSessionSetSessionChangeSubscriptionCpp() MultiplayerSessionPropertiesSetJoinRestrictionCpp() MultiplayerSessionPropertiesSetReadRestrictionCpp() MultiplayerSessionPropertiesSetKeywordsCpp() MultiplayerServiceWriteSession() elseif writeCount == 3 then print("OnMultiplayerServiceWriteSession 3") MultiplayerSessionDeleteSessionCustomPropertyJsonCpp() MultiplayerSessionEtagCpp() MultiplayerSessionGetInfoCpp() MultiplayerSessionGetInitializationInfoCpp() MultiplayerSessionGetMemberCpp() MultiplayerSessionMembersCpp() MultiplayerSessionMembersAcceptedCpp() MultiplayerSessionServersJsonCpp() MultiplayerSessionRoleTypesCpp() MultiplayerSessionSessionConstantsCpp() MultiplayerSessionSessionPropertiesCpp() MultiplayerSessionSessionReferenceCpp() MultiplayerSessionSubscribedChangeTypesCpp() MultiplayerSessionTimeOfSessionCpp() MultiplayerServiceWriteSession() elseif writeCount == 4 then print("OnMultiplayerServiceWriteSession 4") MultiplayerSessionLeaveCpp() MultiplayerServiceWriteSession() SetCheckHR(0) elseif writeCount == 5 then print("OnMultiplayerServiceWriteSession 5") local hr = GetLastError() SetCheckHR(1) if hr ~= 0 and hr ~= -2147023084 then -- --2147023084 == 0x80070714 == __HRESULT_FROM_WIN32(ERROR_RESOURCE_DATA_NOT_FOUND) test.equal(hr, 0); end MultiplayerSessionCreateCpp() MultiplayerSessionJoinCpp() MultiplayerServiceWriteSession() elseif writeCount == 6 then print("OnMultiplayerServiceWriteSession 6") MultiplayerServiceWriteSession() elseif writeCount == 7 then print("OnMultiplayerServiceWriteSession 7") MultiplayerServiceSetActivity() elseif writeCount == 8 then print("OnMultiplayerServiceWriteSession 8") local hr = GetLastError() SetCheckHR(1) if hr ~= 0 and hr ~= -2147023084 then -- --2147023084 == 0x80070714 == __HRESULT_FROM_WIN32(ERROR_RESOURCE_DATA_NOT_FOUND) test.equal(hr, 0); end MultiplayerServiceRemoveMultiplayerSessionChangedHandler() MultiplayerServiceRemoveMultiplayerSubscriptionLostHandler() test.stopTest(); end end function OnMultiplayerServiceSetActivity() print("OnMultiplayerServiceSetActivity") MultiplayerServiceClearActivity() end function OnMultiplayerServiceClearActivity() print("OnMultiplayerServiceClearActivity") MultiplayerServiceGetCurrentSession() end function OnMultiplayerServiceGetCurrentSession() print("OnMultiplayerServiceGetCurrentSession") MultiplayerSessionHostCandidatesCpp() MultiplayerSessionMatchmakingServerCpp() MultiplayerSessionEtagCpp() MultiplayerSessionGetInfoCpp() MultiplayerSessionGetInitializationInfoCpp() MultiplayerSessionGetMemberCpp() MultiplayerSessionMembersCpp() MultiplayerSessionMembersAcceptedCpp() MultiplayerSessionServersJsonCpp() MultiplayerSessionRoleTypesCpp() MultiplayerSessionSessionConstantsCpp() MultiplayerSessionSessionPropertiesCpp() MultiplayerSessionSessionReferenceCpp() MultiplayerSessionSubscribedChangeTypesCpp() MultiplayerSessionTimeOfSessionCpp() MultiplayerSessionCompareCpp() MultiplayerServiceGetSessions() end function OnMultiplayerServiceGetSessions() print('OnMultiplayerServiceGetSessions') MultiplayerServiceGetActivitiesForUsers() end function OnMultiplayerServiceGetActivitiesForUsers() print('OnMultiplayerServiceGetActivitiesForUsers') MultiplayerServiceGetActivitiesForSocialGroup() end function OnMultiplayerServiceGetActivitiesForSocialGroup() print('OnMultiplayerServiceGetActivitiesForSocialGroup') --MultiplayerServiceWriteSessionByHandle() MultiplayerServiceSendInvites() end function OnMultiplayerServiceWriteSessionByHandle() print('OnMultiplayerServiceWriteSessionByHandle') MultiplayerServiceGetCurrentSessionByHandle() end function OnMultiplayerServiceGetCurrentSessionByHandle() print('OnMultiplayerServiceGetCurrentSessionByHandle') MultiplayerServiceSendInvites() end function OnMultiplayerServiceSendInvites() print('OnMultiplayerServiceSendInvites') MultiplayerSessionLeaveCpp() MultiplayerServiceWriteSession() SetCheckHR(0) end test.TestMPCpp = function() common.init(TestMPCpp_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/mp/mp.lua ================================================ test = require 'u-test' common = require 'common' function TestMP_Handler() print("TestMP_Handler") HCTraceSetTraceToDebugger() HCSettingsSetTraceLevel() XblFormatSecureDeviceAddress() XblMultiplayerSessionReferenceCreate() XblMultiplayerSessionReferenceIsValid() XblMultiplayerSessionCreateHandle() XblMultiplayerSessionDuplicateHandle() XblMultiplayerSessionCloseHandle() --XblRealTimeActivityActivate() XblMultiplayerSubscriptionsEnabled() --XblMultiplayerSetSubscriptionsEnabled() XblMultiplayerSubscriptionsEnabled() XblMultiplayerSessionCreateHandle() -- XblMultiplayerSessionSetInitializationSucceeded() --The session's 'initializationSucceeded' property can only be set during the 'evaluating' stage of initialization. -- XblMultiplayerSessionSetRawServersJson() -- The request body can't contain a 'servers' collection because the authentication principal doesn't include a server. -- The constant field **** is specified in the request, but the value conflicts with the one in the session. --XblMultiplayerSessionConstantsSetMaxMembersInSession() --XblMultiplayerSessionConstantsSetCapabilities() --XblMultiplayerSessionConstantsSetCloudComputePackageJson() --XblMultiplayerSessionConstantsSetMeasurementServerAddressesJson() --XblMultiplayerSessionConstantsSetMemberInitialization() --XblMultiplayerSessionConstantsSetPeerToHostRequirements() --XblMultiplayerSessionConstantsSetPeerToPeerRequirements() --XblMultiplayerSessionConstantsSetQosConnectivityMetrics() --XblMultiplayerSessionConstantsSetTimeouts() --XblMultiplayerSessionConstantsSetVisibility() XblMultiplayerAddSessionChangedHandler() XblMultiplayerAddSubscriptionLostHandler() XblMultiplayerRemoveSessionChangedHandler() XblMultiplayerRemoveSubscriptionLostHandler() XblMultiplayerAddSessionChangedHandler() XblMultiplayerAddSubscriptionLostHandler() XblMultiplayerSessionReferenceParseFromUriPath() XblMultiplayerSessionJoin() XblMultiplayerWriteSessionAsync() end local writeCount = 0; function OnXblMultiplayerWriteSessionAsync() writeCount = writeCount + 1 if writeCount == 1 then print("OnXblMultiplayerWriteSessionAsync 1") XblMultiplayerSessionCurrentUser() XblMultiplayerSessionCurrentUserSetCustomPropertyJson() XblMultiplayerSessionCurrentUserSetEncounters() XblMultiplayerSessionCurrentUserSetGroups() XblMultiplayerSessionCurrentUserSetStatus() XblMultiplayerSessionCurrentUserSetMembersInGroup() XblMultiplayerSessionCurrentUserSetSecureDeviceAddressBase64() XblMultiplayerWriteSessionAsync() elseif writeCount == 2 then print("OnXblMultiplayerWriteSessionAsync 2") XblMultiplayerSessionCurrentUserDeleteCustomPropertyJson() XblMultiplayerSessionSetClosed() XblMultiplayerSessionSetCustomPropertyJson() XblMultiplayerSessionSetAllocateCloudCompute() XblMultiplayerSessionSetHostDeviceToken() XblMultiplayerSessionSetLocked() XblMultiplayerSessionSetSessionChangeSubscription() XblMultiplayerSessionPropertiesSetJoinRestriction() XblMultiplayerSessionPropertiesSetReadRestriction() XblMultiplayerSessionPropertiesSetKeywords() -- XblMultiplayerSessionCurrentUserSetRoles() -- A member's 'roles' section contains a role type with an invalid name: roleTypeName1 -- XblMultiplayerSessionSetMutableRoleSettings() -- need properly formated JSON to call -- XblMultiplayerSessionPropertiesSetReadRestriction() -- Invalid session 'readRestriction' provided, cannot be set to none on sessions with the 'userAuthorizationStyle' capability. -- XblMultiplayerSessionPropertiesSetTurnCollection() -- The session's 'turn' property must be an array of numbers representing the indicies of the member(s) in the session whose turn it is. --XblMultiplayerSessionCurrentUserSetQosMeasurements() -- Invalid member 'measurements' provided, measurements can only be uploaded if session 'metrics' are configured. --XblMultiplayerSessionCurrentUserSetServerQosMeasurements() -- Invalid member property serverMeasurements 'measurements1' provided, must be a JSON object. XblMultiplayerWriteSessionAsync() elseif writeCount == 3 then print("OnXblMultiplayerWriteSessionAsync 3") XblMultiplayerSessionSetSessionChangeSubscription(0) XblMultiplayerWriteSessionAsync() elseif writeCount == 4 then print("OnXblMultiplayerWriteSessionAsync 4") XblMultiplayerSessionDeleteCustomPropertyJson() XblMultiplayerSessionEtag() XblMultiplayerSessionGetInfo() XblMultiplayerSessionGetInitializationInfo() XblMultiplayerSessionGetMember() XblMultiplayerSessionGetRoleByName() XblMultiplayerSessionMembers() XblMultiplayerSessionMembersAccepted() XblMultiplayerSessionRawServersJson() XblMultiplayerSessionRoleTypes() XblMultiplayerSessionSessionConstants() XblMultiplayerSessionSessionProperties() XblMultiplayerSessionSessionReference() XblMultiplayerSessionSubscribedChangeTypes() XblMultiplayerSessionTimeOfSession() XblMultiplayerWriteSessionAsync() elseif writeCount == 5 then print("OnXblMultiplayerWriteSessionAsync 5") XblMultiplayerSessionLeave() XblMultiplayerWriteSessionAsync() SetCheckHR(0) elseif writeCount == 6 then local hr = GetLastError() SetCheckHR(1) if hr ~= 0 and hr ~= -2147023084 then -- --2147023084 == 0x80070714 == __HRESULT_FROM_WIN32(ERROR_RESOURCE_DATA_NOT_FOUND) test.equal(hr, 0); end XblMultiplayerSessionCloseHandle() print("OnXblMultiplayerWriteSessionAsync 6") XblMultiplayerSessionCreateHandle() XblMultiplayerSessionJoin() XblMultiplayerWriteSessionAsync() elseif writeCount == 7 then print("OnXblMultiplayerWriteSessionAsync 7") -- XblMultiplayerSessionAddMemberReservation() -- The requested session cannot be accessed. The calling user must have the multiplayer privilege and must be a member of the session if the session either isn't open or has a join restriction on it that the user doesn't satisfy, multi-user requests aren't allowed for large sessions, banned xuids can't access the session, users must have the communicate permission to access sessions that require it, users must qualify to access club sessions based on the club rules, and devices other than an Xbox One can only access sessions with user-style authorization. XblMultiplayerWriteSessionAsync() elseif writeCount == 8 then print("OnXblMultiplayerWriteSessionAsync 8") XblMultiplayerSetActivityAsync() elseif writeCount == 9 then print("OnXblMultiplayerWriteSessionAsync 9") local hr = GetLastError() SetCheckHR(1) if hr ~= 0 and hr ~= -2147023084 then -- --2147023084 == 0x80070714 == __HRESULT_FROM_WIN32(ERROR_RESOURCE_DATA_NOT_FOUND) test.equal(hr, 0); end XblMultiplayerSessionCloseHandle() XblMultiplayerRemoveSessionChangedHandler() XblMultiplayerRemoveSubscriptionLostHandler() test.stopTest(); end end function OnXblMultiplayerSetActivityAsync() print("OnXblMultiplayerSetActivityAsync") XblMultiplayerClearActivityAsync() end function OnXblMultiplayerClearActivityAsync() print("OnXblMultiplayerClearActivityAsync") XblMultiplayerGetSessionAsync() end function OnXblMultiplayerGetSessionAsync() print("OnXblMultiplayerGetSessionAsync") XblMultiplayerSessionHostCandidates() XblMultiplayerSessionMatchmakingServer() XblMultiplayerSessionEtag() XblMultiplayerSessionGetInfo() XblMultiplayerSessionGetInitializationInfo() XblMultiplayerSessionGetMember() XblMultiplayerSessionGetRoleByName() XblMultiplayerSessionMembers() XblMultiplayerSessionMembersAccepted() XblMultiplayerSessionRawServersJson() XblMultiplayerSessionRoleTypes() XblMultiplayerSessionSessionConstants() XblMultiplayerSessionSessionProperties() XblMultiplayerSessionSessionReference() XblMultiplayerSessionSubscribedChangeTypes() XblMultiplayerSessionTimeOfSession() XblMultiplayerSessionCompare(); XblMultiplayerQuerySessionsAsync() end function OnXblMultiplayerQuerySessionsAsync() print('OnXblMultiplayerQuerySessionsAsync') XblMultiplayerGetActivitiesForUsersAsync() end function OnXblMultiplayerGetActivitiesForUsersAsync() print('OnXblMultiplayerGetActivitiesForUsersAsync') XblMultiplayerGetActivitiesWithPropertiesForUsersAsync() end function OnXblMultiplayerGetActivitiesWithPropertiesForUsersAsync() print('OnXblMultiplayerGetActivitiesWithPropertiesForUsersAsync') XblMultiplayerGetActivitiesForSocialGroupAsync() end function OnXblMultiplayerGetActivitiesForSocialGroupAsyncRetry() print('OnXblMultiplayerGetActivitiesForSocialGroupAsyncRetry') XblMultiplayerGetActivitiesForSocialGroupAsync() end function OnXblMultiplayerGetActivitiesForSocialGroupAsync() print('OnXblMultiplayerGetActivitiesForSocialGroupAsync') XblMultiplayerGetActivitiesWithPropertiesForSocialGroupAsync() end function OnXblMultiplayerGetActivitiesWithPropertiesForSocialGroupAsync() print('OnXblMultiplayerGetActivitiesWithPropertiesForSocialGroupAsync') XblMultiplayerSendInvitesAsync() -- XblMultiplayerWriteSessionByHandleAsync() -- The request URI contains an invalid handle ID. It may contain invalid characters or be all zeroes. end function OnXblMultiplayerWriteSessionByHandleAsync() print('OnXblMultiplayerWriteSessionByHandleAsync') XblMultiplayerGetSessionByHandleAsync() end function OnXblMultiplayerGetSessionByHandleAsync() print('OnXblMultiplayerGetSessionByHandleAsync') XblMultiplayerSendInvitesAsync() end function OnXblMultiplayerSendInvitesAsync() print('OnXblMultiplayerSendInvitesAsync') XblMultiplayerSessionLeave() XblMultiplayerWriteSessionAsync() SetCheckHR(0) end test.TestMP = function() common.init(TestMP_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/mp/mp_multiple_contexts.lua ================================================ local xblContext = 0 function TestFunc() XblMultiplayerSetSubscriptionsEnabled(1) XblMultiplayerSessionReferenceCreate() XblMultiplayerSessionCreateHandle() XblMultiplayerSessionJoin() XblMultiplayerWriteSessionAsync() end local writeCount = 0; function OnXblMultiplayerWriteSessionAsync() writeCount = writeCount + 1 if writeCount == 1 then print("OnXblMultiplayerWriteSessionAsync 1") -- first write made using default XblContext should complete successfully -- change the session and attempt to write with second XblContext xblContext = XblContextCreateHandle() XblMultiplayerSetSubscriptionsEnabled(1, xblContext) XblMultiplayerSessionCurrentUserSetStatus() XblMultiplayerWriteSessionAsync(0, xblContext) elseif writeCount == 2 then print("OnXblMultiplayerWriteSessionAsync 2") XblMultiplayerSessionCloseHandle() XblContextCloseHandle(xblContext) test.stopTest(); end end test.TestMPMultipleContexts = function() common.init(TestFunc) end ================================================ FILE: Tests/ApiExplorer/Tests/mp/mp_partial_invite_flow.lua ================================================ test = require 'u-test' common = require 'common' function TestMPInviteFlow_Handler() print("TestMPInviteFlow_Handler") XblMultiplayerSessionReferenceCreate(); session, hr = XblMultiplayerSessionCreateHandle("00000000-0000-0000-0000-000076029b4d", "MinGameSession", "", 0) XblMultiplayerSessionJoin(session) XblMultiplayerWriteSessionAsync(session) end function OnXblMultiplayerWriteSessionAsync() XblMultiplayerSendInvitesAsync() end function OnXblMultiplayerSendInvitesAsync() XblMultiplayerGetSessionByHandleAsync() end function OnXblMultiplayerGetSessionByHandleAsync() XblMultiplayerSessionCloseHandle() test.stopTest(); end test.TestMPInviteFlow = function() common.init(TestMPInviteFlow_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/mp/mp_search-cpp.lua ================================================ test = require 'u-test' common = require 'common' function TestMPSearchCpp_Handler() print("TestMPSearchCpp_Handler") MultiplayerSessionReferenceCreateCpp() MultiplayerSessionCreateCpp() MultiplayerSessionJoinCpp() MultiplayerSessionSetSessionCustomPropertyJsonCpp() MultiplayerServiceWriteSession() end function OnMultiplayerServiceWriteSession() MultiplayerServiceSetSearchHandle() end function OnMultiplayerServiceSetSearchHandle() MultiplayerServiceGetSearchHandles() end function OnMultiplayerServiceGetSearchHandles() MultiplayerSearchHandleDetailsSessionOwnerXuids() MultiplayerSearchHandleDetailsTags() MultiplayerSearchHandleDetailsStringsMetadata() MultiplayerSearchHandleDetailsNumbersMetadata() MultiplayerSearchHandleDetailsVisibility() MultiplayerSearchHandleDetailsJoinRestriction() MultiplayerSearchHandleDetailsClosed() MultiplayerSearchHandleDetailsMemberCounts() MultiplayerSearchHandleDetailsHandleCreationTime() MultiplayerSearchHandleDetailsCustomSessionPropertiesJson() MultiplayerSearchHandleDetailsCloseHandle() test.stopTest(); end test.TestMPSearchCpp = function() common.init(TestMPSearchCpp_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/mp/mp_search.lua ================================================ test = require 'u-test' common = require 'common' function TestMPSearch_Handler() print("TestMPSearch_Handler") XblMultiplayerSessionReferenceCreate() XblMultiplayerSessionCreateHandle() XblMultiplayerSessionJoin() XblMultiplayerSessionSetCustomPropertyJson() XblMultiplayerWriteSessionAsync() end function OnXblMultiplayerWriteSessionAsync() XblMultiplayerCreateSearchHandleAsync() end function OnXblMultiplayerCreateSearchHandleAsync() XblMultiplayerSearchHandleGetSessionOwnerXuids() XblMultiplayerSearchHandleGetTags() XblMultiplayerSearchHandleGetStringAttributes() XblMultiplayerSearchHandleGetNumberAttributes() XblMultiplayerSearchHandleGetVisibility() XblMultiplayerSearchHandleGetJoinRestriction() XblMultiplayerSearchHandleGetSessionClosed() XblMultiplayerSearchHandleGetMemberCounts() XblMultiplayerSearchHandleGetCreationTime() XblMultiplayerSearchHandleGetCustomSessionPropertiesJson() XblMultiplayerGetSearchHandlesAsync() end function OnXblMultiplayerGetSearchHandlesAsync() XblMultiplayerDeleteSearchHandleAsync() end function OnXblMultiplayerDeleteSearchHandleAsync() XblMultiplayerSearchHandleCloseHandle() XblMultiplayerSessionCloseHandle() test.stopTest(); end test.TestMPSearch = function() common.init(TestMPSearch_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/mp/mp_transfer-cpp.lua ================================================ test = require 'u-test' common = require 'common' function TestMPTransferCpp_Handler() print("TestMPTransferCpp_Handler") session1, hr = MultiplayerSessionCreateCpp("00000000-0000-0000-0000-000076029b4d", "MinGameSession", "", 0) session2, hr = MultiplayerSessionCreateCpp("00000000-0000-0000-0000-000076029b4d", "MinGameSession", "", 1) MultiplayerSessionJoinCpp(session1) MultiplayerSessionJoinCpp(session2) MultiplayerServiceWriteSession(session1) MultiplayerServiceWriteSession(session2) end local sessionsWritten = 0 function OnMultiplayerServiceWriteSession() sessionsWritten = sessionsWritten + 1; if sessionsWritten == 2 then MultiplayerServiceSetTransferHandle(session1, session2) end end function OnMultiplayerServiceSetTransferHandle() test.stopTest(); end test.TestMPTransferCpp = function() common.init(TestMPTransferCpp_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/mp/mp_transfer.lua ================================================ test = require 'u-test' common = require 'common' function TestMPTransfer_Handler() print("TestMPSearch_Handler") session1, hr = XblMultiplayerSessionCreateHandle("00000000-0000-0000-0000-000076029b4d", "MinGameSession", "", 0) session2, hr = XblMultiplayerSessionCreateHandle("00000000-0000-0000-0000-000076029b4d", "MinGameSession", "", 1) XblMultiplayerSessionJoin(session1) XblMultiplayerSessionJoin(session2) XblMultiplayerWriteSessionAsync(session1) XblMultiplayerWriteSessionAsync(session2) end local sessionsWritten = 0 function OnXblMultiplayerWriteSessionAsync() sessionsWritten = sessionsWritten + 1; if sessionsWritten == 2 then XblMultiplayerSetTransferHandleAsync(session1, session2) end end function OnXblMultiplayerSetTransferHandleAsync() XblMultiplayerSessionCloseHandle(session1) XblMultiplayerSessionCloseHandle(session2) test.stopTest(); end test.TestMPTransfer = function() common.init(TestMPTransfer_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/multiplayerManager/MPM_Invite.lua ================================================ test = require 'u-test' common = require 'common' function MPM_InviteTest_Handler() print("MPM_InviteTest_Handler") MultiDeviceSyncAndWait("MPM_InviteTest_Handler"); XblMultiplayerManagerInitialize(); StartDoWorkLoop(); XblMultiplayerManagerLobbySessionAddLocalUser(); end function OnXGameUiShowSendGameInviteAsync() print("OnXGameUiShowSendGameInviteAsync"); end function OnXblMultiplayerEventType_UserAdded() if MultiDeviceIsHost() then XblMultiplayerManagerSetJoinability(); else XblMultiplayerManagerJoinGame(); end end function OnXblMultiplayerEventType_JoinabilityStateChanged() if MultiDeviceIsHost() then XGameUiShowSendGameInviteAsyncToMPMLobby(); -- TODO change to non-ui version end end function OnXblMultiplayerEventType_JoinGameCompleted() MultiDeviceSyncAndWait("OnXblMultiplayerEventType_JoinGameCompleted"); test.stopTest(); end function OnXblMultiplayerEventType_MemberJoined() XblMultiplayerManagerGameSessionMembers(); end test.skip = true test.ismultidevice = true test.MPM_InviteTest = function() common.init(MPM_InviteTest_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/multiplayerManager/MPM_InviteUI.lua ================================================ test = require 'u-test' common = require 'common' function MPM_InviteUITest_Handler() print("MPM_InviteUITest_Handler") MultiDeviceSyncAndWait("MPM_InviteUITest_Handler"); XblMultiplayerManagerInitialize(); StartDoWorkLoop(); XblMultiplayerManagerLobbySessionAddLocalUser(); end function OnXGameUiShowSendGameInviteAsync() print("OnXGameUiShowSendGameInviteAsync"); end function OnXblMultiplayerEventType_UserAdded() if MultiDeviceIsHost() then XblMultiplayerManagerSetJoinability(); else XblMultiplayerManagerJoinGame(); end end function OnXblMultiplayerEventType_JoinabilityStateChanged() if MultiDeviceIsHost() then XGameUiShowSendGameInviteAsyncToMPMLobby(); end end function OnXblMultiplayerEventType_JoinGameCompleted() MultiDeviceSyncAndWait("OnXblMultiplayerEventType_JoinGameCompleted"); test.stopTest(); end function OnXblMultiplayerEventType_MemberJoined() XblMultiplayerManagerGameSessionMembers(); end test.skip = true test.ismultidevice = true test.MPM_InviteUITest = function() common.init(MPM_InviteUITest_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/multiplayerManager/MPM_JoinFixedGameSession.lua ================================================ test = require 'u-test' common = require 'common' function MPM_JoinFixedGameSession_Handler() print("MPM_JoinFixedGameSession") MultiDeviceSyncAndWait("MPM_JoinFixedGameSession"); XblMultiplayerManagerInitialize(); StartDoWorkLoop(); XblMultiplayerManagerLobbySessionAddLocalUser(); end function OnXblMultiplayerEventType_UserAdded() if MultiDeviceIsHost() > 0 then local sessionName = MultiDeviceGetRemoteXuid() .. "-session"; MultiDeviceSetLocalState("session", sessionName); MultiDeviceSyncAndWait("SessionName"); XblMultiplayerManagerJoinGame(sessionName); else MultiDeviceSyncAndWait("SessionName"); local sessionName = MultiDeviceGetRemoteState("session"); XblMultiplayerManagerJoinGame(sessionName); end end function OnXblMultiplayerEventType_MemberJoined() MultiDeviceSyncAndWait("MemberJoinedLobby"); test.stopTest(); end test.ismultidevice = true test.MPM_JoinFixedGameSession = function() common.init(MPM_JoinFixedGameSession_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/multiplayerManager/MPM_JoinLobbyViaActivity.lua ================================================ test = require 'u-test' common = require 'common' function MPM_JoinLobbyViaActivity_Handler() print("MPM_JoinLobbyViaActivity") MultiDeviceSyncAndWait("MPM_JoinLobbyViaActivity"); XblMultiplayerManagerInitialize(); StartDoWorkLoop(); if MultiDeviceIsHost() > 0 then XblMultiplayerManagerLobbySessionAddLocalUser(); XblMultiplayerManagerLobbySessionSetLocalMemberConnectionAddress("1111111111111111"); else MultiDeviceWaitTillRemoteState("stage", "JoinabilityStateChanged"); print("Joining lobby of remote player. NOTE: Ensure remote is a friend or join will fail") XblMultiplayerManagerJoinLobbyViaActivity(); XblMultiplayerManagerLobbySessionSetLocalMemberConnectionAddress("2222222222222222"); end end function OnXblMultiplayerEventType_UserAdded() if MultiDeviceIsHost() > 0 then XblMultiplayerManagerSetJoinability(); -- sets it to XblMultiplayerJoinability::JoinableByFriends end end function OnXblMultiplayerEventType_JoinabilityStateChanged() XblMultiplayerManagerJoinability(); -- not needed just for coverage if MultiDeviceIsHost() > 0 then XblMultiplayerManagerLobbySessionSetSynchronizedHost(); end end function OnXblMultiplayerEventType_SynchronizedHostWriteCompleted() MultiDeviceSetLocalState("stage", "JoinabilityStateChanged"); -- waiting till we get a OnXblMultiplayerEventType_JoinLobbyCompleted end function OnXblMultiplayerEventType_JoinLobbyCompleted() MultiDeviceSyncAndWait("MemberJoinedLobby"); XblMultiplayerManagerLobbySessionIsHost(); XblMultiplayerManagerLobbySessionHost(); end function OnXblMultiplayerEventType_MemberJoined() MultiDeviceSyncAndWait("MemberJoinedLobby"); XblMultiplayerManagerLobbySessionIsHost(); XblMultiplayerManagerLobbySessionHost(); print("XblMultiplayerManagerLobbySessionSetProperties as host") XblMultiplayerManagerLobbySessionSetProperties(); end function OnXblMultiplayerEventType_LocalMemberPropertyWriteCompleted() print('OnXblMultiplayerEventType_LocalMemberPropertyWriteCompleted'); print("Joining game from lobby") XblMultiplayerManagerJoinGameFromLobby(); end function OnXblMultiplayerEventType_MemberPropertyChanged() print('OnXblMultiplayerEventType_MemberPropertyChanged'); print("Joining game from lobby") XblMultiplayerManagerJoinGameFromLobby(); end function OnXblMultiplayerEventType_SessionPropertyWriteCompleted() print('OnXblMultiplayerEventType_SessionPropertyWriteCompleted'); MultiDeviceSyncAndWait("MemberJoinedLobbyPropsChanged"); print("XblMultiplayerManagerLobbySessionSetLocalMemberProperties") XblMultiplayerManagerLobbySessionSetLocalMemberProperties(); end function OnXblMultiplayerEventType_LobbySessionPropertyChanged() print('OnXblMultiplayerEventType_LobbySessionPropertyChanged'); MultiDeviceSyncAndWait("MemberJoinedLobbyPropsChanged"); end function OnXblMultiplayerEventType_MemberLeft() if MultiDeviceIsHost() > 0 then print("OnXblMultiplayerEventType_MemberLeft as host") MultiDeviceSyncAndWait("PeerUserRemoved"); print("Removing local user from MPM") XblMultiplayerManagerLobbySessionRemoveLocalUser(); else print("OnXblMultiplayerEventType_MemberLeft as client") end end function OnXblMultiplayerEventType_JoinGameCompleted() MultiDeviceSyncAndWait("JoinGameCompleted"); if MultiDeviceIsHost() == 0 then print("Removing local user from MPM") XblMultiplayerManagerLobbySessionRemoveLocalUser(); end end function OnXblMultiplayerEventType_UserRemoved() if MultiDeviceIsHost() > 0 then test.stopTest(); else MultiDeviceSyncAndWait("PeerUserRemoved"); test.stopTest(); end end test.ismultidevice = true test.MPM_JoinLobbyViaActivity = function() common.init(MPM_JoinLobbyViaActivity_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/multiplayerManager/MPM_Match.lua ================================================ test = require 'u-test' common = require 'common' function MPM_Match_Handler() print("MPM_Match") MultiDeviceSyncAndWait("MPM_Match"); XblMultiplayerManagerInitialize(); StartDoWorkLoop(); XblMultiplayerManagerLobbySessionAddLocalUser(); end function OnXblMultiplayerEventType_UserAdded() MultiDeviceSyncAndWait("UserAdded"); XblMultiplayerManagerFindMatch("PlayerSkillNoQoS"); end function OnXblMultiplayerEventType_FindMatchCompleted() VerifyMPMGameSessionProperites(); VerifyMPMLobbySessionProperites(); MultiDeviceSyncAndWait("FindMatch"); XblMultiplayerManagerLeaveGame() end function OnXblMultiplayerEventType_JoinGameCompleted() MultiDeviceSyncAndWait("JoinGameCompleted"); test.stopTest(); end function OnXblMultiplayerEventType_LeaveGameCompleted() MultiDeviceSyncAndWait("LeaveGameCompleted"); test.stopTest(); end test.ismultidevice = true test.MPM_Match = function() common.init(MPM_Match_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/multiplayerManager/MPM_SingleDevice_JoinLeaveGame.lua ================================================ test = require 'u-test' common = require 'common' function MPM_SingleDevice_JoinLeaveGame_Handler() print("MPM_SingleDevice_JoinLeaveGame_Handler") XblMultiplayerManagerInitialize(); XblMultiplayerManagerLobbySessionAddLocalUser(); XblMultiplayerManagerLobbySessionSetLocalMemberConnectionAddress("1234567890"); StartDoWorkLoop(); end function OnXblMultiplayerEventType_UserAdded() print(" "); print("Received OnXblMultiplayerEventType_UserAdded") XblMultiplayerManagerLobbySessionCorrelationId(); XblMultiplayerManagerLobbySessionLocalMembers(); XblMultiplayerManagerJoinability(); XblMultiplayerManagerJoinGameFromLobby(); end function OnXblMultiplayerEventType_JoinGameCompleted() print(" "); print("Received OnXblMultiplayerEventType_JoinGameCompleted") XblMultiplayerManagerGameSessionActive() XblMultiplayerManagerGameSessionCorrelationId(); XblMultiplayerManagerGameSessionMembers(); XblMultiplayerManagerGameSessionConstants(); XblMultiplayerManagerGameSessionSessionReference(); XblMultiplayerManagerLeaveGame(); end function OnXblMultiplayerEventType_LeaveGameCompleted() print(" "); print("Recived OnXblMultiplayerEventType_LeaveGameCompleted") XblMultiplayerManagerLobbySessionRemoveLocalUser(); end function OnXblMultiplayerEventType_UserRemoved() print(" "); print("Received OnXblMultiplayerEventType_UserRemoved") test.stopTest(); end test.MPM_SingleDevice_JoinLeaveGame = function() common.init(MPM_SingleDevice_JoinLeaveGame_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/multiplayerManager/MPM_SingleDevice_SyncHostWrite.lua ================================================ test = require 'u-test' common = require 'common' maxRetries = 3 syncGameHostRetries = 0 syncGamePropRetries = 0 function MPM_SingleDeviceWriteTest_Handler() print("MPM_SingleDeviceWriteTest_Handler") XblMultiplayerManagerInitialize(); StartDoWorkLoop(); XblMultiplayerManagerLobbySessionAddLocalUser(); end function OnXblMultiplayerEventType_UserAdded() print(" "); print("Received OnXblMultiplayerEventType_UserAdded") XblMultiplayerManagerJoinGameFromLobby(); end function OnXblMultiplayerEventType_JoinGameCompleted() print(" "); print("Received OnXblMultiplayerEventType_JoinGameCompleted") -- this call often results in a 412 due to a AdvertiseGameSessionDoWork which is writing the transfer handle to the MPSD doc. XblMultiplayerManagerGameSessionSetSynchronizedHost(); end function OnXblMultiplayerEventType_SynchronizedHostWriteCompleted_412_GameSession() print(" "); print("Received OnXblMultiplayerEventType_SynchronizedHostWriteCompleted_412_GameSession") syncGameHostRetries = syncGameHostRetries + 1 print("GameSessionSetSynchronizedHost Retry " .. syncGameHostRetries) test.assert(syncGameHostRetries <= maxRetries) if( syncGameHostRetries <= maxRetries ) then XblMultiplayerManagerGameSessionSetSynchronizedHost(); else test.stopTest(); end end function OnXblMultiplayerEventType_SynchronizedHostWriteCompleted() print(" "); print("Received OnXblMultiplayerEventType_SynchronizedHostWriteCompleted") XblMultiplayerManagerGameSessionSetSynchronizedProperties(); end function OnXblMultiplayerEventType_SessionSynchronizedPropertyWriteCompleted_412_GameSession() print("Received OnXblMultiplayerEventType_SessionSynchronizedPropertyWriteCompleted_412_GameSession") syncGamePropRetries = syncGamePropRetries + 1 print("GameSessionSynchronizedPropertyWriteCompleted Retry " .. syncGamePropRetries) test.assert(syncGamePropRetries <= maxRetries) if( syncGamePropRetries <= maxRetries ) then XblMultiplayerManagerGameSessionSetSynchronizedProperties(); else test.stopTest(); end end function OnXblMultiplayerEventType_GameSessionPropertyChanged() print("Received OnXblMultiplayerEventType_GameSessionPropertyChanged") XblMultiplayerManagerLeaveGame(); end function OnXblMultiplayerEventType_LeaveGameCompleted() print(" "); print("Recived OnXblMultiplayerEventType_LeaveGameCompleted") XblMultiplayerManagerLobbySessionRemoveLocalUser(); end function OnXblMultiplayerEventType_UserRemoved() print(" "); print("Received OnXblMultiplayerEventType_UserRemoved") test.stopTest(); end test.MPM_SingleDeviceWriteTest = function() common.init(MPM_SingleDeviceWriteTest_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/notification/achievement_unlock_notification.lua ================================================ test = require 'u-test' common = require 'common' -- Note: This test requires an account with achievement id 1 locked, and can only be run -- one time on that account as we currently have no way to programmatically reset the unlock -- status of an achievement from API Explorer. To successfully run the test again you will have -- to run the XblPlayerDataReset tool to reset the user or sign in with a different account. function achievement_unlock_notification_test() print("achievement_unlock_notification_test") local achievementId = "1"; local isLocked = IsAchievementLocked(achievementId); -- make sure there is something to do test.is_true(isLocked); if not isLocked then print("achievement is already unlocked"); test.stopTest(); return; end -- register achievement unlock handler invite handler local id = XblAchievementUnlockAddNotificationHandler() XblGameInviteAddNotificationHandler() -- unlock achievement RunAchievementUnlock(achievementId); local status; for i=1,10 do Sleep(500); status = CheckStatus(); if status == 0 then break; end end -- anything other than 0 is an error test.equal(status,0); -- unregister handlers XblAchievementUnlockRemoveNotificationHandler(id); XblGameInviteRemoveNotificationHandler() test.stopTest(); end function achievement_unlock_notification_test_end() print("Ending test"); test.stopTest(); end test.skip = true; -- skipping because of special account requirements test.TestAchievementUnlockNotification = function() common.init(achievement_unlock_notification_test) end ================================================ FILE: Tests/ApiExplorer/Tests/notification/gameinvitenotifications.lua ================================================ test = require 'u-test' common = require 'common' function TestGameInviteNotifications_Handler() print("TestGameInviteNotifications_Handler") MultiDeviceSyncAndWait("TestGameInviteNotifications"); if MultiDeviceIsHost() > 0 then XblGameInviteRegisterForEventAsync() else MultiDeviceSyncAndWait("SendInvite"); XblMultiplayerSessionReferenceCreate(); session, hr = XblMultiplayerSessionCreateHandle("00000000-0000-0000-0000-000076029b4d", "MinGameSession", "", 0) XblMultiplayerSessionJoin(session) XblMultiplayerWriteSessionAsync(session) end end -- First Device - Notification Receiver -- function OnXblGameInviteRegisterForEventAsync() print("OnXblGameInviteRegisterForEventAsync") XblGameInviteAddNotificationHandler(); MultiDeviceSyncAndWait("SendInvite"); end function OnXblGameInviteAddNotificationHandler() print("OnXblGameInviteAddNotificationHandler") MultiDeviceSyncAndWait("WaitForInvite"); XblGameInviteRemoveNotificationHandler(); XblGameInviteUnregisterForEventAsync(); end function OnXblGameInviteUnregisterForEventAsync() print("OnXblGameInviteUnregisterForEventAsync") test.stopTest(); end -- Second Device - Invite Sender -- function OnXblMultiplayerWriteSessionAsync() XblMultiplayerSendInvitesAsync("", "", MultiDeviceGetRemoteXuid()) end function OnXblMultiplayerSendInvitesAsync() MultiDeviceSyncAndWait("WaitForInvite"); test.stopTest(); end test.ismultidevice = true; test.TestGameInviteNotifications = function() common.init(TestGameInviteNotifications_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/notification/multiple_notification_subscriptions.lua ================================================ test = require 'u-test' common = require 'common' function TestGameInviteNotifications_Handler() StartSocialManagerDoWorkLoop() XblSocialManagerAddLocalUser() end function OnXblSocialManagerDoWork_LocalUserAddedEvent() print("OnXblSocialManagerDoWork_LocalUserAddedEvent") Sleep(5000) XblSocialManagerRemoveLocalUser() StopSocialManagerDoWorkLoop() --print("stopping test") --test.stopTest(); end test.skip = true; test.TestMultipleNotificationSubscriptions = function() common.init(TestGameInviteNotifications_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/presence/presence-cpp.lua ================================================ test = require 'u-test' common = require 'common' function TestPresenceCppHandler() print("TestPresenceCppHandler") PresenceServiceSetPresence() end function OnPresenceServiceSetPresence() PresenceServiceGetPresence() end function OnPresenceServiceGetPresence() PresenceRecordGetXuidCpp() PresenceRecordGetUserStateCpp() PresenceRecordGetDeviceRecordsCpp() PresenceRecordCloseHandleCpp() PresenceServiceGetPresenceForSocialGroup() end function OnPresenceServiceGetPresenceForSocialGroup() PresenceServiceGetPresenceForMultipleUsers() end function OnPresenceServiceGetPresenceForMultipleUsers() test.stopTest(); end test.TestPresenceCpp = function() common.init(TestPresenceCppHandler) end ================================================ FILE: Tests/ApiExplorer/Tests/presence/presence.lua ================================================ test = require 'u-test' common = require 'common' function TestPresenceHandler() print("TestPresenceHandler") XblPresenceSetPresenceAsync() end function OnXblPresenceSetPresenceAsync() XblPresenceGetPresenceAsync() end function OnXblPresenceGetPresenceAsync() XblPresenceRecordGetXuid() XblPresenceRecordGetUserState() XblPresenceRecordGetDeviceRecords() XblPresenceRecordCloseHandle() XblPresenceGetPresenceForSocialGroupAsync() end function OnXblPresenceGetPresenceForSocialGroupAsync() XblPresenceGetPresenceForMultipleUsersAsync() end function OnXblPresenceGetPresenceForMultipleUsersAsync() test.stopTest(); end test.TestPresence = function() common.init(TestPresenceHandler) end ================================================ FILE: Tests/ApiExplorer/Tests/presence/presence_rta.lua ================================================ test = require 'u-test' common = require 'common' function TestPresenceRTA_Handler() print("TestPresenceRTA_Handler") XblPresenceTrackUsers(2814656696817462) XblPresenceAddDevicePresenceChangedHandler() XblPresenceAddTitlePresenceChangedHandler()() end function OnDevicePresenceChanged() print("DevicePresenceChanged") end function OnTitlePresenceChanged() print("TitlePresenceChanged") end test.skip = true test.TestPresenceRTA = function() common.init(TestPresenceRTA_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/privacy/privacy-cpp.lua ================================================ test = require 'u-test' common = require 'common' function PrivacyTestsCpp_Handler() print("PrivacyTestsCpp_Handler") PrivacyServiceGetAvoidList() end function OnPrivacyServiceGetAvoidList() print("OnPrivacyServiceGetAvoidList") PrivacyServiceCheckPermissionWithTargetUser() end function OnPrivacyServiceCheckPermissionWithTargetUser() print("OnPrivacyServiceCheckPermissionWithTargetUser") PrivacyServiceCheckMultiplePermissionsWithMultipleTargetUsers() end function OnPrivacyServiceCheckMultiplePermissionsWithMultipleTargetUsers() print("OnPrivacyServiceCheckMultiplePermissionsWithMultipleTargetUsers") PrivacyServiceGetMuteList() end function OnPrivacyServiceGetMuteList() print("OnPrivacyServiceGetMuteList") PrivacyServiceGetAvoidOrMuteList() end function OnPrivacyServiceGetAvoidOrMuteList() print("OnPrivacyServiceGetAvoidOrMuteList") test.stopTest(); end test.PrivacyTestsCpp = function() common.init(PrivacyTestsCpp_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/privacy/privacy.lua ================================================ test = require 'u-test' common = require 'common' function PrivacyTests_Handler() print("PrivacyTests_Handler") XblPrivacyGetAvoidListAsync() end function OnXblPrivacyGetAvoidListAsync() print("OnXblPrivacyGetAvoidListAsync") XblPrivacyCheckPermissionAsync() end function OnXblPrivacyCheckPermissionAsync() print("OnXblPrivacyCheckPermissionAsync") XblPrivacyCheckPermissionForAnonymousUserAsync() end function OnXblPrivacyCheckPermissionForAnonymousUserAsync() print("OnOnXblPrivacyCheckPermissionForAnonymousUserAsync") XblPrivacyBatchCheckPermissionAsync() end function OnXblPrivacyBatchCheckPermissionAsync() print("OnXblPrivacyBatchCheckPermissionAsync") test.stopTest(); end test.PrivacyTests = function() common.init(PrivacyTests_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/profile/GetUserProfile-cpp.lua ================================================ test = require 'u-test' common = require 'common' function GetUserProfileCpp_Handler() print("GetUserProfileCpp_Handler") ProfileServiceGetUserProfile() end function OnProfileServiceGetUserProfile() print("OnProfileServiceGetUserProfile") test.stopTest(); end test.GetUserProfileCpp = function() common.init(GetUserProfileCpp_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/profile/GetUserProfile.lua ================================================ test = require 'u-test' common = require 'common' function GetUserProfile_Handler() print("GetUserProfile_Handler") XblProfileGetUserProfileAsync() end function OnXblProfileGetUserProfileAsync() print("OnXblProfileGetUserProfileAsync") test.stopTest(); end test.GetUserProfile = function() common.init(GetUserProfile_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/profile/GetUserProfiles-cpp.lua ================================================ test = require 'u-test' common = require 'common' function GetUserProfilesCpp_Handler() print("GetUserProfilesCpp_Handler") ProfileServiceGetUserProfiles() end function OnProfileServiceGetUserProfiles() print("OnProfileServiceGetUserProfiles") test.stopTest(); end test.GetUserProfilesCpp = function() common.init(GetUserProfilesCpp_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/profile/GetUserProfiles.lua ================================================ test = require 'u-test' common = require 'common' function GetUserProfiles_Handler() print("GetUserProfiles_Handler") XblProfileGetUserProfilesAsync() end function OnXblProfileGetUserProfilesAsync() print("OnXblProfileGetUserProfilesAsync") test.stopTest(); end test.GetUserProfiles = function() common.init(GetUserProfiles_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/profile/GetUserProfilesForSocialGroup-cpp.lua ================================================ test = require 'u-test' common = require 'common' function GetUserProfilesForSocialGroupCpp_Handler() print("GetUserProfilesForSocialGroupCpp_Handler") ProfileServiceGetUserProfilesForSocialGroup() end function OnProfileServiceGetUserProfilesForSocialGroup() print("OnProfileServiceGetUserProfilesForSocialGroup") test.stopTest(); end test.GetUserProfilesForSocialGroupCpp = function() common.init(GetUserProfilesForSocialGroupCpp_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/profile/GetUserProfilesForSocialGroupAsync.lua ================================================ test = require 'u-test' common = require 'common' function GetUserProfilesForSocialGroupAsync_Handler() print("GetUserProfilesForSocialGroupAsync_Handler") XblProfileGetUserProfilesForSocialGroupAsync() end function OnXblProfileGetUserProfilesForSocialGroupAsync() print("OnXblProfileGetUserProfilesForSocialGroupAsyncRetry") XblProfileGetUserProfilesForSocialGroupAsync() end function OnXblProfileGetUserProfilesForSocialGroupAsync() print("OnXblProfileGetUserProfilesForSocialGroupAsync") test.stopTest(); end test.GetUserProfilesForSocialGroupAsync = function() common.init(GetUserProfilesForSocialGroupAsync_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/rta/RTAResync.lua ================================================ test = require 'u-test' common = require 'common' function RTAResync_Handler() StartSocialManagerDoWorkLoop() XblSocialManagerAddLocalUser() end function OnXblSocialManagerDoWork_LocalUserAddedEvent() XblTestHooksTriggerRTAResync() Sleep(5000) XblSocialManagerRemoveLocalUser() StopSocialManagerDoWorkLoop() test.stopTest(); end test.RTAResync = function() common.init(RTAResync_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/rta/RTASuspendResume.lua ================================================ test = require 'u-test' common = require 'common' function RTASuspendResume_Handler() isGdk = IsGDKPlatform() if isGdk then XblRealTimeActivityAddConnectionStateChangeHandler(); -- Add a real-time handler to force RTA connection XblSocialAddSocialRelationshipChangedHandler(); else test.stopTest() end end connectCount = 0 function OnXblRealTimeActivityAddConnectionStateChangeHandler_Connected() print("RTA connection connected"); connectCount = connectCount + 1 if connectCount == 1 then --HCWinHttpSuspend(); else --XblRealTimeActivityRemoveConnectionStateChangeHandler(); --XblSocialRemoveSocialRelationshipChangedHandler(); test.stopTest() end end function OnXblRealTimeActivityAddConnectionStateChangeHandler_Disconnected() print("RTA connection disconnected"); HCWinHttpResume(); end test.skip = true; test.RTASuspendResume = function() common.init(RTASuspendResume_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/rta/RTA_MP_SM.lua ================================================ test = require 'u-test' common = require 'common' firstConnect = true -- Creates a MPSD session and ensures client can update RTA connection ID to prevent -- getting kicked from the session. To force a connection ID changed you can disconnect network -- or wait for RTA timeout function CreateSessionAndWait_Handler() StartSocialManagerDoWorkLoop(); XblSocialManagerAddLocalUser(); XblRtaMultiplayerInit(); end test.skip = true test.CreateSessionAndWait = function() common.init(CreateSessionAndWait_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/rta/RTA_activation.lua ================================================ test = require 'u-test' common = require 'common' -- Test RTA connection state based on various states of SM, Client handlers, and Legacy Activation function RTAActivation_Handler() print("RTAActivation_Handler") -- Test isn't valid on all platforms (i.e. on Win32 we set up a notification service subscription by default so the RTA connection -- won't be torn down just by removing the social relationship changed handler). isGdk = IsGDKPlatform() if isGdk then StartSocialManagerDoWorkLoop() XblRealTimeActivityAddConnectionStateChangeHandler() XblSocialAddSocialRelationshipChangedHandler() else test.stopTest() end end function OnXblRealTimeActivityAddConnectionStateChangeHandler_Connecting() print("RTA Connecting"); end connectCount = 0 function OnXblRealTimeActivityAddConnectionStateChangeHandler_Connected() print("RTA Connected"); connectCount = connectCount + 1 if connectCount == 1 then -- Removing the only handler should cause disconnection XblSocialRemoveSocialRelationshipChangedHandler() elseif connectCount == 2 then XblSocialManagerRemoveLocalUser() elseif connectCount == 3 then -- User will be removed from SM in response to the LocalUserAdded event, which should cause a disconnect XblSocialManagerAddLocalUser() XblRealTimeActivityDeactivate() end end disconnectCount = 0 function OnXblRealTimeActivityAddConnectionStateChangeHandler_Disconnected() print("RTA Disconnected") disconnectCount = disconnectCount + 1 if disconnectCount == 1 then -- Adding user to SM will cause connection to be established again. There is not a guaranteed ordering -- between the RTA connected event and the SM local user added event. That said, we can always remove the local -- from SM when the RTA connected event happens, because of the fact that it is valid to remove a user from -- SocialManager even before the LocalUserAdded event is received XblSocialManagerAddLocalUser() elseif disconnectCount == 2 then XblRealTimeActivityActivate() elseif disconnectCount == 3 then test.stopTest() end end function OnXblSocialManagerDoWork_LocalUserAddedEvent() print("SocialManager LocalUserAdded") if connectCount == 3 then XblSocialManagerRemoveLocalUser() end end test.RTAActivation = function() common.init(RTAActivation_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/rta/simpleRTA-cpp.lua ================================================ test = require 'u-test' common = require 'common' function RtaActivateDeactivateCpp() print("RtaActivateDeactivateCpp"); -- Test isn't valid on all platforms (i.e. on Win32 we set up a notification service subscription by default so the RTA connection -- won't be torn down just by removing the social relationship changed handler). isGdk = IsGDKPlatform() if isGdk then RealTimeActivityServiceAddConnectionStateChangeHandler(); RealTimeActivityServiceActivate(); else test.stopTest() end end function OnRealTimeActivityServiceAddConnectionStateChangeHandler_Connected() print("RTA connection connected"); RealTimeActivityServiceDeactivate(); end function OnRealTimeActivityServiceAddConnectionStateChangeHandler_Disconnected() print("RTA connection disconnected"); test.stopTest(); end function OnRealTimeActivityServiceAddConnectionStateChangeHandler_Disabled() test.stopTest(); end test.SimpleRTACpp = function() common.init(RtaActivateDeactivateCpp) end ================================================ FILE: Tests/ApiExplorer/Tests/rta/simpleRTA.lua ================================================ test = require 'u-test' common = require 'common' function SimpleRTA_Handler() print("SimpleRTA"); -- Test isn't valid on all platforms (i.e. on Win32 we set up a notification service subscription by default so the RTA connection -- won't be torn down just by removing the social relationship changed handler). isGdk = IsGDKPlatform() if isGdk then XblRealTimeActivityAddConnectionStateChangeHandler(); -- Add a real-time handler to force RTA connection XblSocialAddSocialRelationshipChangedHandler(); else test.stopTest() end end function OnXblRealTimeActivityAddConnectionStateChangeHandler_Connected() print("RTA connection connected"); XblSocialRemoveSocialRelationshipChangedHandler(); XblContextCloseHandle(); end function OnXblRealTimeActivityAddConnectionStateChangeHandler_Disconnected() print("RTA connection disconnected"); -- RTA connection will be torn down here since the last handler was removed test.stopTest(); end test.SimpleRTA = function() common.init(SimpleRTA_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/rta/simpleRTA_legacy.lua ================================================ test = require 'u-test' common = require 'common' -- Test ensuring legacy RTA calling patters are still functional function SimpleRTALegacy_Handler() print("SimpleRTALegacy_Handler"); -- Test isn't valid on all platforms (i.e. on Win32 we set up a notification service subscription by default so the RTA connection -- won't be torn down just by removing the social relationship changed handler). isGdk = IsGDKPlatform() if isGdk then XblRealTimeActivityAddConnectionStateChangeHandler(); XblRealTimeActivityActivate(); else test.stopTest() end end function OnXblRealTimeActivityAddConnectionStateChangeHandler_Connected() print("RTA connection connected"); XblRealTimeActivityDeactivate(); end function OnXblRealTimeActivityAddConnectionStateChangeHandler_Disconnected() print("RTA connection disconnected"); test.stopTest(); end test.SimpleRTALegacy = function() common.init(SimpleRTALegacy_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/social/reputation-cpp.lua ================================================ test = require 'u-test' common = require 'common' function ReputationServiceSubmitReputationFeedback_Handler() print("ReputationServiceSubmitReputationFeedback_Handler") ReputationServiceSubmitReputationFeedback() end function OnReputationServiceSubmitReputationFeedback() print("OnReputationServiceSubmitReputationFeedback") ReputationServiceSubmitBatchReputationFeedback() end function OnReputationServiceSubmitBatchReputationFeedback() print("OnReputationServiceSubmitBatchReputationFeedback") test.stopTest() end test.SubmitReputationFeedbackCpp = function() common.init(ReputationServiceSubmitReputationFeedback_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/social/reputation.lua ================================================ test = require 'u-test' common = require 'common' function SubmitReputationFeedback_Handler() print("GetUserProfile_Handler") XblSocialSubmitReputationFeedbackAsync() end function OnXblSocialSubmitReputationFeedbackAsync() print("OnXblProfileGetUserProfileAsync") XblSocialSubmitBatchReputationFeedbackAsync() end function OnXblSocialSubmitBatchReputationFeedbackAsync() print("OnXblSocialSubmitBatchReputationFeedbackAsync") test.stopTest() end test.SubmitReputationFeedback = function() common.init(SubmitReputationFeedback_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/social/social-cpp.lua ================================================ test = require 'u-test' common = require 'common' function SocialServiceGetSocialRelationships_Handler() print("SocialServiceGetSocialRelationships_Handler") SocialServiceGetSocialRelationships() --OnSocialServiceGetSocialRelationships() end function OnSocialServiceGetSocialRelationships() hr, hasNext = SocialRelationshipResultHasNextCpp() print("hasNext " .. hr) print("hr " .. hr) if hasNext ~= 0 then SocialRelationshipResultGetNextCpp() else SocialRelationshipResultCloseHandleCpp() test.stopTest(); end end function OnSocialRelationshipGetNextCpp() print("OnSocialRelationshipGetNextCpp") OnSocialServiceGetSocialRelationships() end test.GetSocialRelationshipsCpp = function() common.init(SocialServiceGetSocialRelationships_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/social/social.lua ================================================ test = require 'u-test' common = require 'common' function GetSocialRelationships_Handler() print("GetSocialRelationships_Handler") XblSocialGetSocialRelationshipsAsync() end function OnXblSocialGetSocialRelationshipsAsync() print("OnXblProfileGetUserProfileAsync") XblSocialRelationshipResultGetRelationships() XblSocialRelationshipResultCloseHandle() test.stopTest(); end test.GetSocialRelationships = function() common.init(GetSocialRelationships_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/social/social_manager_1-cpp.lua ================================================ test = require 'u-test' common = require 'common' function SocialManager1Cpp_Handler() StartSocialManagerDoWorkLoopCpp() Sleep(1000) StopSocialManagerDoWorkLoopCpp() test.stopTest(); end test.SocialManager1Cpp = function() common.init(SocialManager1Cpp_Handler) end function OnSocialManagerDoWorkCpp_LocalUserRemovedEvent() end function OnSocialManagerDoWorkCpp_UsersAddedToSocialGraphEvent() end function OnSocialManagerDoWorkCpp_UsersRemovedFromSocialGraphEvent() end function OnSocialManagerDoWorkCpp_PresenceChangedEvent() end function OnSocialManagerDoWorkCpp_ProfilesChangedEvent() end function OnSocialManagerDoWorkCpp_SocialRelationshipsChangedEvent() end function OnSocialManagerDoWorkCpp_LocalUserAddedEvent() end function OnSocialManagerDoWorkCpp_LocalUserRemovedEvent() end function OnSocialManagerDoWorkCpp_SocialUserGroupLoadedEvent() end function OnSocialManagerDoWorkCpp_SocialUserGroupUpdatedEvent() end function OnSocialManagerDoWorkCpp_UnknownEvent() end ================================================ FILE: Tests/ApiExplorer/Tests/social/social_manager_1.lua ================================================ test = require 'u-test' common = require 'common' function SocialManager1_Handler() StartSocialManagerDoWorkLoop() Sleep(1000) StopSocialManagerDoWorkLoop() test.stopTest(); end test.SocialManager1 = function() common.init(SocialManager1_Handler) end function OnXblSocialManagerDoWork_LocalUserRemovedEvent() end function OnXblSocialManagerDoWork_UsersAddedToSocialGraphEvent() end function OnXblSocialManagerDoWork_UsersRemovedFromSocialGraphEvent() end function OnXblSocialManagerDoWork_PresenceChangedEvent() end function OnXblSocialManagerDoWork_ProfilesChangedEvent() end function OnXblSocialManagerDoWork_SocialRelationshipsChangedEvent() end function OnXblSocialManagerDoWork_LocalUserAddedEvent() end function OnXblSocialManagerDoWork_LocalUserRemovedEvent() end function OnXblSocialManagerDoWork_SocialUserGroupLoadedEvent() end function OnXblSocialManagerDoWork_SocialUserGroupUpdatedEvent() end function OnXblSocialManagerDoWork_UnknownEvent() end ================================================ FILE: Tests/ApiExplorer/Tests/social/social_manager_2-cpp.lua ================================================ test = require 'u-test' common = require 'common' function SocialManager2Cpp_Handler() StartSocialManagerDoWorkLoopCpp() SocialManagerAddLocalUserCpp() end test.SocialManager2Cpp = function() common.init(SocialManager2Cpp_Handler) end function OnSocialManagerDoWorkCpp_LocalUserAddedEvent() SocialManagerGetLocalUsersCpp() Sleep(1000) StopSocialManagerDoWorkLoopCpp() test.stopTest(); end function OnSocialManagerDoWorkCpp_LocalUserRemovedEvent() end function OnSocialManagerDoWorkCpp_UsersAddedToSocialGraphEvent() end function OnSocialManagerDoWorkCpp_UsersRemovedFromSocialGraphEvent() end function OnSocialManagerDoWorkCpp_PresenceChangedEvent() end function OnSocialManagerDoWorkCpp_ProfilesChangedEvent() end function OnSocialManagerDoWorkCpp_SocialRelationshipsChangedEvent() end function OnSocialManagerDoWorkCpp_SocialUserGroupLoadedEvent() end function OnSocialManagerDoWorkCpp_SocialUserGroupUpdatedEvent() end function OnSocialManagerDoWorkCpp_UnknownEvent() end function OnStartSocialManagerDoWorkLoopCppDisabled() test.stopTest(); end ================================================ FILE: Tests/ApiExplorer/Tests/social/social_manager_2.lua ================================================ test = require 'u-test' common = require 'common' function SocialManager2_Handler() StartSocialManagerDoWorkLoop() XblSocialManagerAddLocalUser() end test.SocialManager2 = function() common.init(SocialManager2_Handler) end function OnXblSocialManagerDoWork_LocalUserAddedEvent() XblSocialManagerGetLocalUsers() Sleep(1000) StopSocialManagerDoWorkLoop() test.stopTest(); end function OnXblSocialManagerDoWork_LocalUserRemovedEvent() end function OnXblSocialManagerDoWork_UsersAddedToSocialGraphEvent() end function OnXblSocialManagerDoWork_UsersRemovedFromSocialGraphEvent() end function OnXblSocialManagerDoWork_PresenceChangedEvent() end function OnXblSocialManagerDoWork_ProfilesChangedEvent() end function OnXblSocialManagerDoWork_SocialRelationshipsChangedEvent() end function OnXblSocialManagerDoWork_SocialUserGroupLoadedEvent() end function OnXblSocialManagerDoWork_SocialUserGroupUpdatedEvent() end function OnXblSocialManagerDoWork_UnknownEvent() end ================================================ FILE: Tests/ApiExplorer/Tests/social/social_manager_filter-cpp.lua ================================================ test = require 'u-test' common = require 'common' function SocialManagerFilterCpp_Handler() StartSocialManagerDoWorkLoopCpp() SocialManagerAddLocalUserCpp() end test.SocialManagerFilterCpp = function() common.init(SocialManagerFilterCpp_Handler) end function OnSocialManagerDoWorkCpp_LocalUserAddedEvent() SocialManagerGetLocalUsersCpp() SocialManagerSetRichPresencePollingStatusCpp() group, hr = SocialManagerCreateSocialUserGroupFromFiltersCpp() print("group " .. group) end function OnSocialManagerDoWorkCpp_SocialUserGroupLoadedEvent() SocialManagerUserGroupGetUsersCpp() SocialManagerPresenceRecordIsUserPlayingTitleCpp() SocialManagerDestroySocialUserGroupCpp(group) SocialManagerRemoveLocalUserCpp() StopSocialManagerDoWorkLoopCpp() test.stopTest(); end function OnStartSocialManagerDoWorkLoopCppDisabled() test.stopTest(); end ================================================ FILE: Tests/ApiExplorer/Tests/social/social_manager_filter.lua ================================================ test = require 'u-test' common = require 'common' function SocialManagerFilter_Handler() StartSocialManagerDoWorkLoop() XblSocialManagerAddLocalUser() end test.SocialManagerFilter = function() common.init(SocialManagerFilter_Handler) end function OnXblSocialManagerDoWork_LocalUserAddedEvent() XblSocialManagerGetLocalUsers() XblSocialManagerSetRichPresencePollingStatus() group, hr = XblSocialManagerCreateSocialUserGroupFromFilters() XblSocialManagerUserGroupGetUsers() print("group " .. group) end function OnXblSocialManagerDoWork_SocialUserGroupLoadedEvent() XblSocialManagerUserGroupGetUsers() XblSocialManagerPresenceRecordIsUserPlayingTitle() XblSocialManagerDestroySocialUserGroup(group) XblSocialManagerRemoveLocalUser() StopSocialManagerDoWorkLoop() test.stopTest(); end ================================================ FILE: Tests/ApiExplorer/Tests/social/social_manager_list-cpp.lua ================================================ test = require 'u-test' common = require 'common' function SocialManagerListCpp_Handler() print("SocialManagerListCpp_Handler") StartSocialManagerDoWorkLoopCpp() SocialManagerAddLocalUserCpp() end test.skip = true test.SocialManagerListCpp = function() common.init(SocialManagerListCpp_Handler) end function OnSocialManagerDoWorkCpp_LocalUserAddedEvent() print("OnSocialManagerDoWorkCpp_LocalUserAddedEvent") PresenceServiceSetPresence() end function OnPresenceServiceSetPresence() print("OnPresenceServiceSetPresence") SocialManagerGetLocalUsersCpp() group, hr = SocialManagerCreateSocialUserGroupFromListCpp() print("group " .. group) end function OnSocialManagerDoWorkCpp_SocialUserGroupLoadedEvent() print("OnSocialManagerDoWorkCpp_SocialUserGroupLoadedEvent") SocialManagerUserGroupGetUsersCpp() SocialManagerUpdateSocialUserGroupCpp() end function OnSocialManagerDoWorkCpp_SocialUserGroupUpdatedEvent() print("OnSocialManagerDoWorkCpp_SocialUserGroupUpdatedEvent") SocialManagerUserGroupGetUsersTrackedByGroupCpp() SocialManagerDestroySocialUserGroupCpp(group) SocialManagerRemoveLocalUserCpp() StopSocialManagerDoWorkLoopCpp() print("stopping test") test.stopTest(); end function OnStartSocialManagerDoWorkLoopCppDisabled() test.stopTest(); end ================================================ FILE: Tests/ApiExplorer/Tests/social/social_manager_list.lua ================================================ test = require 'u-test' common = require 'common' function SocialManagerList_Handler() print("SocialManagerList_Handler") StartSocialManagerDoWorkLoop() XblSocialManagerAddLocalUser() end test.SocialManagerList = function() common.init(SocialManagerList_Handler) end function OnXblSocialManagerDoWork_LocalUserAddedEvent() print("OnXblSocialManagerDoWork_LocalUserAddedEvent") XblPresenceSetPresenceAsync() end function OnXblPresenceSetPresenceAsync() print("OnXblPresenceSetPresenceAsync") XblSocialManagerGetLocalUsers() group, hr = XblSocialManagerCreateSocialUserGroupFromList() print("group " .. group) end function OnXblSocialManagerDoWork_SocialUserGroupLoadedEvent() print("OnXblSocialManagerDoWork_SocialUserGroupLoadedEvent") XblSocialManagerUserGroupGetUsers() XblSocialManagerUpdateSocialUserGroup() end function OnXblSocialManagerDoWork_SocialUserGroupUpdatedEvent() print("OnXblSocialManagerDoWork_SocialUserGroupUpdatedEvent") XblSocialManagerUserGroupGetUsersTrackedByGroup() XblSocialManagerDestroySocialUserGroup(group) XblSocialManagerRemoveLocalUser() StopSocialManagerDoWorkLoop() print("stopping test") test.stopTest(); end ================================================ FILE: Tests/ApiExplorer/Tests/social/social_manager_remove_realloc-cpp.lua ================================================ test = require 'u-test' common = require 'common' function SocialManagerRemoveReallocCpp_Handler() print("SocialManagerListCpp_Handler") StartSocialManagerDoWorkLoopCpp() SocialManagerAddLocalUserCpp() end test.SocialManagerRemoveReallocCpp = function() common.init(SocialManagerRemoveReallocCpp_Handler) end function OnSocialManagerDoWorkCpp_LocalUserAddedEvent() group1, hr = SocialManagerCreateSocialUserGroupFromListCpp(15, 0) print("group1 = " .. group1) end local groupLoaded = 0 function OnSocialManagerDoWorkCpp_SocialUserGroupLoadedEvent() groupLoaded = groupLoaded + 1; if groupLoaded == 1 then print("group1 loaded") SocialManagerUserGroupGetUsersCpp(group1) SocialManagerUpdateSocialUserGroupCpp(group1, 10, 5) elseif groupLoaded == 2 then print("group2 loaded") print("group1 users:") SocialManagerUserGroupGetUsersCpp(group1) print("group2 users:") SocialManagerUserGroupGetUsersCpp(group2) SocialManagerDestroySocialUserGroupCpp(group1) SocialManagerDestroySocialUserGroupCpp(group2) SocialManagerRemoveLocalUserCpp() StopSocialManagerDoWorkLoopCpp() test.stopTest(); end end local groupUpdate = 0; function OnSocialManagerDoWorkCpp_SocialUserGroupUpdatedEvent() groupUpdate = groupUpdate + 1; if groupUpdate == 1 then group2, hr = SocialManagerCreateSocialUserGroupFromListCpp(20, 15) -- group needs to be large enough to force realloc print("group2 = " .. group2) else print("Unexpected GroupUpdatedEvent") end end function OnStartSocialManagerDoWorkLoopCppDisabled() test.stopTest(); end ================================================ FILE: Tests/ApiExplorer/Tests/social/social_manager_remove_realloc.lua ================================================ test = require 'u-test' common = require 'common' function SocialManagerRemoveRealloc_Handler() print("SocialManagerList_Handler") StartSocialManagerDoWorkLoop() XblSocialManagerAddLocalUser() end test.SocialManagerRemoveRealloc = function() common.init(SocialManagerRemoveRealloc_Handler) end function OnXblSocialManagerDoWork_LocalUserAddedEvent() group1, hr = XblSocialManagerCreateSocialUserGroupFromList(15, 0) print("group1 = " .. group1) end local groupLoaded = 0 function OnXblSocialManagerDoWork_SocialUserGroupLoadedEvent() groupLoaded = groupLoaded + 1; if groupLoaded == 1 then print("group1 loaded") XblSocialManagerUserGroupGetUsers(group1) XblSocialManagerUpdateSocialUserGroup(group1, 10, 5) elseif groupLoaded == 2 then print("group2 loaded") print("group1 users:") XblSocialManagerUserGroupGetUsers(group1) print("group2 users:") XblSocialManagerUserGroupGetUsers(group2) XblSocialManagerDestroySocialUserGroup(group1) XblSocialManagerDestroySocialUserGroup(group2) XblSocialManagerRemoveLocalUser() StopSocialManagerDoWorkLoop() test.stopTest(); end end local groupUpdate = 0; function OnXblSocialManagerDoWork_SocialUserGroupUpdatedEvent() groupUpdate = groupUpdate + 1; if groupUpdate == 1 then group2, hr = XblSocialManagerCreateSocialUserGroupFromList(20, 15) -- group needs to be large enough to force realloc print("group2 = " .. group2) else print("Unexpected GroupUpdatedEvent") end end ================================================ FILE: Tests/ApiExplorer/Tests/social/social_manager_wait.lua ================================================ test = require 'u-test' common = require 'common' function SocialManagerWait_Handler() StartSocialManagerDoWorkLoop() XblSocialManagerAddLocalUser() end test.skip = true; test.SocialManagerWait = function() common.init(SocialManagerWait_Handler) end function OnXblSocialManagerDoWork_LocalUserAddedEvent() XblSocialManagerGetLocalUsers() XblSocialManagerSetRichPresencePollingStatus() XblSocialManagerCreateSocialUserGroupFromFilters() end function OnXblSocialManagerDoWork_LocalUserRemovedEvent() end function OnXblSocialManagerDoWork_UsersAddedToSocialGraphEvent() end function OnXblSocialManagerDoWork_UsersRemovedFromSocialGraphEvent() end function OnXblSocialManagerDoWork_PresenceChangedEvent() end function OnXblSocialManagerDoWork_ProfilesChangedEvent() end function OnXblSocialManagerDoWork_SocialRelationshipsChangedEvent() end function OnXblSocialManagerDoWork_SocialUserGroupLoadedEvent() XblSocialManagerUserGroupGetUsers() XblSocialManagerPresenceRecordIsUserPlayingTitle() end function OnXblSocialManagerDoWork_SocialUserGroupUpdatedEvent() end function OnXblSocialManagerDoWork_UnknownEvent() end ================================================ FILE: Tests/ApiExplorer/Tests/social/social_relationship_changed.lua ================================================ test = require 'u-test' common = require 'common' function SocialRelationshipChanged_Handler() print("SocialRelationshipChanged_Handler") XblSocialAddSocialRelationshipChangedHandler() XblSocialAddFriendRequestCountChangedHandler() end function OnSocialRelationshipChanged() print("OnSocialRelationshipChanged") XblSocialRemoveSocialRelationshipChangedHandler() test.stopTest(); end function OnFriendRequestCountChanged() print("OnFriendRequestCountChanged") --XblSocialRemoveFriendRequestCountChangedHandler() end test.skip = true test.SocialRelationshipChanged = function() common.init(SocialRelationshipChanged_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/social/social_sub_unsub.lua ================================================ test = require 'u-test' common = require 'common' function SocialSubUnSub_Handler() XblRealTimeActivityAddConnectionStateChangeHandler(); XblPresenceAddDevicePresenceChangedHandler() XblPresenceAddTitlePresenceChangedHandler() XblRealTimeActivityActivate(); end function OnXblRealTimeActivityAddConnectionStateChangeHandler_Connected() print("RTA connection connected"); TestLoop() end function OnDevicePresenceChanged() end function OnTitlePresenceChanged() end function TestLoop() SubscribeToTitleAndDevicePresenceChangeForFriends(); end loopCount = 0 function OnSubscribeToTitleAndDevicePresenceChangeForFriends() loopCount = loopCount + 1 if loopCount == 3 then Sleep(500); UnsubscribeToTitleAndDevicePresenceChangeForFriends(); Sleep(500); test.stopTest(); else print("OnSubscribeToTitleAndDevicePresenceChangeForFriends") Sleep(500); UnsubscribeToTitleAndDevicePresenceChangeForFriends(); Sleep(500); TestLoop() end end test.SocialSubUnSub = function() common.init(SocialSubUnSub_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/stats/stats-bvt.lua ================================================ test = require 'u-test' common = require 'common' userStatistic1 = 0; changeHandleCalledCount = 0; userStatResultCount = 0; function TestStatsBvt_Handler() print("TestStatsBvt_Handler") XblUserStatisticsTrackStatistics(); XblUserStatisticsAddStatisticChangedHandler(); XblUserStatisticsGetSingleUserStatisticAsync(); end function OnXblUserStatisticsGetSingleUserStatisticAsync() print("OnXblUserStatisticsGetSingleUserStatisticAsync") userStatResultCount = userStatResultCount + 1 if userStatResultCount == 1 then userStatistic1 = GetLastStat(); print("UserStatistic1 " .. userStatistic1 ); XblEventsWriteInGameEvent(); elseif userStatResultCount == 2 then local userStatistic2 = GetLastStat(); print("UserStatistic1 " .. userStatistic1 ); print("UserStatistic2 " .. userStatistic2 ); test.assert(userStatistic2 > userStatistic1); test.stopTest(); end end function OnStatisticChangedHandler() print("OnStatisticChangedHandler") changeHandleCalledCount = changeHandleCalledCount + 1 if changeHandleCalledCount == 2 then XblUserStatisticsGetSingleUserStatisticAsync(); end end test.isbvt = true; test.TestStatsBvt = function() common.init(TestStatsBvt_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/stats/stats-cpp.lua ================================================ test = require 'u-test' common = require 'common' initalSubscription = true callDeactivate = false function TestStatsCpp_Handler() print("TestStatsCpp_Handler") SetCallUpdate(); RealTimeActivityServiceAddConnectionStateChangeHandler(); RealTimeActivityServiceActivate(); end function OnRealTimeActivityServiceAddConnectionStateChangeHandler_Connected() print("RTA connection connected"); UserStatisticsServiceSubscribeToStatisticChange(); UserStatisticsServiceAddStatisticChangedHandler(); UserStatisticsServiceGetSingleUserStatistic(); end function OnUserStatisticsServiceGetSingleUserStatistic() print("OnUserStatisticsServiceGetSingleUserStatistic") UserStatisticsServiceGetSingleUserStatistics(); end function OnUserStatisticsServiceGetSingleUserStatistics() print("OnUserStatisticsServiceGetSingleUserStatistics") UserStatisticsServiceGetMultipleUserStatistics(); end function OnUserStatisticsServiceGetMultipleUserStatistics() print("OnUserStatisticsServiceGetMultipleUserStatistics") UserStatisticsServiceGetMultipleUserStatisticsForMultipleServiceConfigurations(); end function OnUserStatisticsServiceGetMultipleUserStatisticsForMultipleServiceConfigurations() print("OnUserStatisticsServiceGetMultipleUserStatisticsForMultipleServiceConfigurations") EventsServiceWriteInGameEvent(); initalSubscription = false; end function OnStatisticChangedHandlerCpp() print("OnStatisticChangedHandlerCpp") StatisticChangeSubscriptionGetStateCpp(); StatisticChangeSubscriptionGetIdCpp(); if initalSubscription then initalSubscription = false; else callDeactivate = true; end end function update() if callDeactivate then UserStatisticsServiceRemoveStatisticChangedHandler(); UserStatisticsServiceUnsubscribeFromStatisticChange(); RealTimeActivityServiceDeactivate(); end end function OnRealTimeActivityServiceAddConnectionStateChangeHandler_Disconnected() print("RTA connection disconnected"); test.stopTest(); end function OnRealTimeActivityServiceAddConnectionStateChangeHandler_Disabled() test.stopTest(); end test.TestStatsCpp = function() common.init(TestStatsCpp_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/stats/stats.lua ================================================ test = require 'u-test' common = require 'common' initalSubscription = true function TestStats_Handler() print("TestStats_Handler") XblUserStatisticsTrackStatistics(); XblUserStatisticsAddStatisticChangedHandler(); XblUserStatisticsGetSingleUserStatisticAsync(); end function OnXblUserStatisticsGetSingleUserStatisticAsync() print("OnXblUserStatisticsGetSingleUserStatisticAsync") XblUserStatisticsGetSingleUserStatisticsAsync(); end function OnXblUserStatisticsGetSingleUserStatisticsAsync() print("OnXblUserStatisticsGetSingleUserStatisticsAsync") XblUserStatisticsGetMultipleUserStatisticsAsync(); end function OnXblUserStatisticsGetMultipleUserStatisticsAsync() print("OnXblUserStatisticsGetMultipleUserStatisticsAsync") XblUserStatisticsGetMultipleUserStatisticsForMultipleServiceConfigurationsAsync(); end function OnXblUserStatisticsGetMultipleUserStatisticsForMultipleServiceConfigurationsAsync() print("OnXblUserStatisticsGetMultipleUserStatisticsForMultipleServiceConfigurationsAsync") XblEventsWriteInGameEvent(); initalSubscription = false; end function OnStatisticChangedHandler() print("OnStatisticChangedHandler") if initalSubscription then initalSubscription = false; else test.stopTest(); end end test.TestStats = function() common.init(TestStats_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/stats/stats_legacy.lua ================================================ test = require 'u-test' common = require 'common' -- Test ensuring legacy RTA calling patters are still functional initalSubscription = true callDeactivate = false function TestStatsLegacy_Handler() print("TestStatsLegacy_Handler") SetCallUpdate(); XblRealTimeActivityAddConnectionStateChangeHandler(); XblRealTimeActivityActivate(); end function OnXblRealTimeActivityAddConnectionStateChangeHandler_Connected() print("RTA connection connected"); XblUserStatisticsSubscribeToStatisticChange(); XblUserStatisticsAddStatisticChangedHandler(); XblUserStatisticsGetSingleUserStatisticAsync(); end function OnXblUserStatisticsGetSingleUserStatisticAsync() print("OnXblUserStatisticsGetSingleUserStatisticAsync") XblUserStatisticsGetSingleUserStatisticsAsync(); end function OnXblUserStatisticsGetSingleUserStatisticsAsync() print("OnXblUserStatisticsGetSingleUserStatisticsAsync") XblUserStatisticsGetMultipleUserStatisticsAsync(); end function OnXblUserStatisticsGetMultipleUserStatisticsAsync() print("OnXblUserStatisticsGetMultipleUserStatisticsAsync") XblUserStatisticsGetMultipleUserStatisticsForMultipleServiceConfigurationsAsync(); end function OnXblUserStatisticsGetMultipleUserStatisticsForMultipleServiceConfigurationsAsync() print("OnXblUserStatisticsGetMultipleUserStatisticsForMultipleServiceConfigurationsAsync") XblEventsWriteInGameEvent(); initalSubscription = false; end function OnStatisticChangedHandler() print("OnStatisticChangedHandler") XblRealTimeActivitySubscriptionGetState(); XblRealTimeActivitySubscriptionGetId(); if initalSubscription then initalSubscription = false; else callDeactivate = true; end end function update() if callDeactivate then XblUserStatisticsRemoveStatisticChangedHandler(); XblUserStatisticsUnsubscribeFromStatisticChange(); XblRealTimeActivityDeactivate(); end end function OnXblRealTimeActivityAddConnectionStateChangeHandler_Disconnected() print("RTA connection disconnected"); test.stopTest(); end test.TestStatsLegacy = function() common.init(TestStatsLegacy_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/stats2017/stats2017-test429.lua ================================================ test = require 'u-test' common = require 'common' function OnXblTitleManagedStatsWriteAsync() -- this fast loop will trigger 429s XblTitleManagedStatsWriteAsync(); end function TitleManagedStatsNoSVD_Handler() XblTitleManagedStatsWriteAsync() end test.skip = true test.TitleManagedStatsNoSVD = function() SetCheckHR(0) common.init(TitleManagedStatsNoSVD_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/stats2017/stats2017.lua ================================================ test = require 'u-test' common = require 'common' function OnXblTitleManagedStatsWriteAsyncWithSVD() XblTitleManagedStatsUpdateStatsAsync() end function OnXblTitleManagedStatsUpdateStatsAsync() XblTitleManagedStatsDeleteStatsAsync() end function OnXblTitleManagedStatsDeleteStatsAsync() ValidateSVD() test.stopTest() end function OnXblTitleManagedStatsUnableToGetTokenAndSignature() print("OnXblTitleManagedStatsUnableToGetTokenAndSignature") test.stopTest() end function TitleManagedStats_Handler() if ClearSVD() == 1 then OnXblTitleManagedStatsUnableToGetTokenAndSignature() else XblTitleManagedStatsWriteAsyncWithSVD() end end test.TitleManagedStats = function() common.init(TitleManagedStats_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/stringVerify/stringVerify-cpp.lua ================================================ test = require 'u-test' common = require 'common' function TestStringVerifyCpp_Handler() print("TestStringVerifyCpp_Handler") StringServiceVerifyString() end function OnStringServiceVerifyString() print("OnStringServiceVerifyString") StringServiceVerifyStrings() end function OnStringServiceVerifyStrings() print("OnStringServiceVerifyStrings") test.stopTest(); end test.TestStringVerifyCpp = function() common.init(TestStringVerifyCpp_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/stringVerify/stringVerify.lua ================================================ test = require 'u-test' common = require 'common' function TestStringVerify_Handler() print("TestStringVerify_Handler") XblStringVerifyStringAsync() end function OnTestStringVerify() XblStringVerifyStringsAsync() test.stopTest() end test.TestStringVerify = function() common.init(TestStringVerify_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/tests.root ================================================ ================================================ FILE: Tests/ApiExplorer/Tests/titleStorage/title_storage-cpp.lua ================================================ test = require 'u-test' common = require 'common' function TitleStorageCpp() print("TitleStorageCpp"); TitleStorageServiceGetQuota(); end function OnTitleStorageServiceGetQuota() print('OnTitleStorageServiceGetQuota') print('Calling TitleStorageServiceUploadBlob') TitleStorageServiceUploadBlob( "title_storage_type::universal", "apirunner/test/path.txt", "title_storage_blob_type::binary", "Test Binary Blob Upload", "title_storage_e_tag_match_condition::not_used" ); end function OnTitleStorageServiceUploadBlob() print('OnTitleStorageServiceUploadBlob') print('Getting blob metadata') TitleStorageServiceGetBlobMetadata( "title_storage_type::universal", "", 0, 0, 2 ); end function OnTitleStorageServiceGetBlobMetadata() print('OnTitleStorageServiceGetBlobMetadata') hr, hasNext = TitleStorageBlobMetadataResultHasNextCpp() print("hasNext " .. hr) print("hr " .. hr) if hasNext ~= 0 then TitleStorageBlobMetadataResultGetNextCpp(2) else OnTitleStorageBlobMetadataResultGetNextCpp() end end function OnTitleStorageBlobMetadataResultGetNextCpp() print('OnTitleStorageBlobMetadataResultGetNextCpp') print('Calling TitleStorageServiceDownloadBlob') TitleStorageServiceDownloadBlob("") end function OnTitleStorageServiceDownloadBlob() print('OnTitleStorageServiceDownloadBlob') print('Calling TitleStorageServiceDeleteBlobAsync') TitleStorageServiceDeleteBlob(); end function OnTitleStorageServiceDeleteBlob() print('OnTitleStorageServiceDeleteBlob') test.stopTest(); end test.TitleStorageCpp = function() common.init(TitleStorageCpp) end ================================================ FILE: Tests/ApiExplorer/Tests/titleStorage/title_storage-restCalls.lua ================================================ test = require 'u-test' common = require 'common' function TitleStorageRestAPI() print("TitleStorage"); RestCallToUploadJsonBlob( "{\"difficulty\":1,\"level\":[{\"number\":\"1\",\"quest\":\"swords\"},{\"number\":\"2\",\"quest\":\"iron\"},{\"number\":\"3\",\"quest\":\"gold\"}],\"weapon\":{\"name\":\"poison\",\"timeleft\":\"2mins\"}}" ); end function OnXblTitleStorageRestUpload() print('Calling RestCallToUploadJsonBlob') RestCallForJsonMetadata(); end function OnDownloadMetadataBlobs() print('OnUploadBlobs') RestCallToDownloadJsonBlob(); end function OnDownloadBlobs() print('OnDownloadBlobs') test.stopTest(); end test.TitleStorageRestAPI = function() common.init(TitleStorageRestAPI) end ================================================ FILE: Tests/ApiExplorer/Tests/titleStorage/title_storage.lua ================================================ test = require 'u-test' common = require 'common' function TitleStorage() print("TitleStorage"); XblTitleStorageGetQuotaAsync(); end function OnXblTitleStorageGetQuotaAsync() print('Calling XblTitleStorageUploadJsonBlobAsync') XblTitleStorageUploadJsonBlobAsync( "Json Blob Upload", "apirunner/test/path/json.txt", "{\"difficulty\":1,\"level\":[{\"number\":\"1\",\"quest\":\"swords\"},{\"number\":\"2\",\"quest\":\"iron\"},{\"number\":\"3\",\"quest\":\"gold\"}],\"weapon\":{\"name\":\"poison\",\"timeleft\":\"2mins\"}}", "XblTitleStorageType::Universal", "XblTitleStorageBlobType::Json", 1024, "XblTitleStorageETagMatchCondition::NotUsed" ); end function OnXblTitleStorageUploadJsonBlobAsync() print('OnXblTitleStorageUploadJsonBlobAsync') print('Calling XblTitleStorageUploadBinaryBlobAsync') XblTitleStorageUploadBinaryBlobAsync( "Test Name 2", "apirunner/test/path.txt", "XblTitleStorageType::Universal", "XblTitleStorageBlobType::Binary", "XblTitleStorageETagMatchCondition::NotUsed" ); end function OnXblTitleStorageUploadBinaryBlobAsync() print('OnXblTitleStorageUploadBinaryBlobAsync') print('Getting blob metadata') XblTitleStorageGetBlobMetadataAsync( "XblTitleStorageType::Universal", "", 0, 0, 2 ); end function OnXblTitleStorageGetBlobMetadataAsync() print('OnXblTitleStorageGetBlobMetadataAsync') XblTitleStorageBlobMetadataResultHasNext(); print('Calling XblTitleStorageBlobMetadataResultGetNextAsync') XblTitleStorageBlobMetadataResultGetNextAsync(2); end function OnXblTitleStorageBlobMetadataResultGetNextAsync() print('OnXblTitleStorageBlobMetadataResultGetNextAsync') XblTitleStorageBlobMetadataResultGetItems(); XblTitleStorageBlobMetadataResultDuplicateHandle(); print('Calling XblTitleStorageDownloadJsonBlobAsync') XblTitleStorageDownloadJsonBlobAsync("weapon.name"); end function OnXblTitleStorageDownloadJsonBlobAsync() print('OnXblTitleStorageDownloadJsonBlobAsync') print('Calling XblTitleStorageDownloadBinaryBlobAsync') XblTitleStorageDownloadBinaryBlobAsync(); end function OnXblTitleStorageDownloadBinaryBlobAsync() print('OnXblTitleStorageDownloadBinaryBlobAsync') print('Calling XblTitleStorageDeleteJsonBlobAsync') XblTitleStorageDeleteJsonBlobAsync(); end function OnXblTitleStorageDeleteJsonBlobAsync() print('OnXblTitleStorageDeleteJsonBlobAsync') print('Calling XblTitleStorageDeleteBinaryBlobAsync') XblTitleStorageDeleteBinaryBlobAsync(); end function OnXblTitleStorageDeleteBinaryBlobAsync() print('OnXblTitleStorageDeleteBinaryBlobAsync') XblTitleStorageBlobMetadataResultCloseHandle(); test.stopTest(); end test.TitleStorage = function() common.init(TitleStorage) end ================================================ FILE: Tests/ApiExplorer/Tests/xal/addfirst.lua ================================================ test = require 'u-test' common = require 'common' function AddFirst_Handler() print("AddFirst_Handler") test.stopTest(); end test.addfirst = function() common.init(AddFirst_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/xal/signOut.lua ================================================ test = require 'u-test' common = require 'common' function SignOut_Handler() print("SignOut_Handler") if XalSignOutUserAsyncIsPresent then XalSignOutUserAsync(); else test.stopTest(); end end function OnXalSignOutUserAsync() print("OnXalSignOutUserAsync") XblContextCloseHandle(); XalUserCloseHandle(); Sleep(1000); XalAddUserWithUiAsync(); end function OnXalAddUserWithUiAsync() print("OnXalAddUserWithUiAsync") test.stopTest(); end test.skip = true test.signOut = function() common.init(SignOut_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/xblHttp/XBLHttpCall.lua ================================================ test = require 'u-test' common = require 'common' function testXblHttpCall_Handler() XblHttpCallCreate(); XblHttpCallDuplicate(); XblHttpCallValidateSetters(); XblHttpCallValidateGetters(); test.stopTest(); end test.testXblHttpCall = function() print("testXblHttpCall") common.init(testXblHttpCall_Handler) end ================================================ FILE: Tests/ApiExplorer/Tests/xblHttp/XBLHttpCallPerform.lua ================================================ test = require 'u-test' common = require 'common' function XblHttpCallPerformCompleted() test.stopTest(); end function testXblHttpCallPerform_Handler() XblHttpCallCreate(); XblHttpCallPerform(); end test.testXblHttpCallPerform = function() print("testXblHttpCallPerform") common.init(testXblHttpCallPerform_Handler) end ================================================ FILE: Tests/ApiExplorer/lua/Makefile ================================================ # Makefile for installing Lua # See doc/readme.html for installation and customization instructions. # == CHANGE THE SETTINGS BELOW TO SUIT YOUR ENVIRONMENT ======================= # Your platform. See PLATS for possible values. PLAT= none # Where to install. The installation starts in the src and doc directories, # so take care if INSTALL_TOP is not an absolute path. See the local target. # You may want to make INSTALL_LMOD and INSTALL_CMOD consistent with # LUA_ROOT, LUA_LDIR, and LUA_CDIR in luaconf.h. INSTALL_TOP= /usr/local INSTALL_BIN= $(INSTALL_TOP)/bin INSTALL_INC= $(INSTALL_TOP)/include INSTALL_LIB= $(INSTALL_TOP)/lib INSTALL_MAN= $(INSTALL_TOP)/man/man1 INSTALL_LMOD= $(INSTALL_TOP)/share/lua/$V INSTALL_CMOD= $(INSTALL_TOP)/lib/lua/$V # How to install. If your install program does not support "-p", then # you may have to run ranlib on the installed liblua.a. INSTALL= install -p INSTALL_EXEC= $(INSTALL) -m 0755 INSTALL_DATA= $(INSTALL) -m 0644 # # If you don't have "install" you can use "cp" instead. # INSTALL= cp -p # INSTALL_EXEC= $(INSTALL) # INSTALL_DATA= $(INSTALL) # Other utilities. MKDIR= mkdir -p RM= rm -f # == END OF USER SETTINGS -- NO NEED TO CHANGE ANYTHING BELOW THIS LINE ======= # Convenience platforms targets. PLATS= aix bsd c89 freebsd generic linux macosx mingw posix solaris # What to install. TO_BIN= lua luac TO_INC= lua.h luaconf.h lualib.h lauxlib.h lua.hpp TO_LIB= liblua.a TO_MAN= lua.1 luac.1 # Lua version and release. V= 5.3 R= $V.4 # Targets start here. all: $(PLAT) $(PLATS) clean: cd src && $(MAKE) $@ test: dummy src/lua -v install: dummy cd src && $(MKDIR) $(INSTALL_BIN) $(INSTALL_INC) $(INSTALL_LIB) $(INSTALL_MAN) $(INSTALL_LMOD) $(INSTALL_CMOD) cd src && $(INSTALL_EXEC) $(TO_BIN) $(INSTALL_BIN) cd src && $(INSTALL_DATA) $(TO_INC) $(INSTALL_INC) cd src && $(INSTALL_DATA) $(TO_LIB) $(INSTALL_LIB) cd doc && $(INSTALL_DATA) $(TO_MAN) $(INSTALL_MAN) uninstall: cd src && cd $(INSTALL_BIN) && $(RM) $(TO_BIN) cd src && cd $(INSTALL_INC) && $(RM) $(TO_INC) cd src && cd $(INSTALL_LIB) && $(RM) $(TO_LIB) cd doc && cd $(INSTALL_MAN) && $(RM) $(TO_MAN) local: $(MAKE) install INSTALL_TOP=../install none: @echo "Please do 'make PLATFORM' where PLATFORM is one of these:" @echo " $(PLATS)" @echo "See doc/readme.html for complete instructions." # make may get confused with test/ and install/ dummy: # echo config parameters echo: @cd src && $(MAKE) -s echo @echo "PLAT= $(PLAT)" @echo "V= $V" @echo "R= $R" @echo "TO_BIN= $(TO_BIN)" @echo "TO_INC= $(TO_INC)" @echo "TO_LIB= $(TO_LIB)" @echo "TO_MAN= $(TO_MAN)" @echo "INSTALL_TOP= $(INSTALL_TOP)" @echo "INSTALL_BIN= $(INSTALL_BIN)" @echo "INSTALL_INC= $(INSTALL_INC)" @echo "INSTALL_LIB= $(INSTALL_LIB)" @echo "INSTALL_MAN= $(INSTALL_MAN)" @echo "INSTALL_LMOD= $(INSTALL_LMOD)" @echo "INSTALL_CMOD= $(INSTALL_CMOD)" @echo "INSTALL_EXEC= $(INSTALL_EXEC)" @echo "INSTALL_DATA= $(INSTALL_DATA)" # echo pkg-config data pc: @echo "version=$R" @echo "prefix=$(INSTALL_TOP)" @echo "libdir=$(INSTALL_LIB)" @echo "includedir=$(INSTALL_INC)" # list targets that do not create files (but not all makes understand .PHONY) .PHONY: all $(PLATS) clean test install local none dummy echo pecho lecho # (end of Makefile) ================================================ FILE: Tests/ApiExplorer/lua/README ================================================ This is Lua 5.3.5, released on 26 Jun 2018. For installation instructions, license details, and further information about Lua, see doc/readme.html. ================================================ FILE: Tests/ApiExplorer/lua/doc/contents.html ================================================ Lua 5.3 Reference Manual - contents

Lua Lua 5.3 Reference Manual

The reference manual is the official definition of the Lua language.
For a complete introduction to Lua programming, see the book Programming in Lua.

Copyright © 2015–2018 Lua.org, PUC-Rio. Freely available under the terms of the Lua license.

Contents

Index

================================================ FILE: Tests/ApiExplorer/lua/doc/index.css ================================================ ul { list-style-type: none ; } ul.contents { padding: 0 ; } table { border: none ; border-spacing: 0 ; border-collapse: collapse ; } td { vertical-align: top ; padding: 0 ; text-align: left ; line-height: 1.25 ; width: 15% ; } ================================================ FILE: Tests/ApiExplorer/lua/doc/lua.1 ================================================ .\" $Id: lua.man,v 1.14 2016/10/17 15:43:50 lhf Exp $ .TH LUA 1 "$Date: 2016/10/17 15:43:50 $" .SH NAME lua \- Lua interpreter .SH SYNOPSIS .B lua [ .I options ] [ .I script [ .I args ] ] .SH DESCRIPTION .B lua is the standalone Lua interpreter. It loads and executes Lua programs, either in textual source form or in precompiled binary form. (Precompiled binaries are output by .BR luac , the Lua compiler.) .B lua can be used as a batch interpreter and also interactively. .LP The given .I options are handled in order and then the Lua program in file .I script is loaded and executed. The given .I args are available to .I script as strings in a global table named .BR arg . If no options or arguments are given, then .B "\-v \-i" is assumed when the standard input is a terminal; otherwise, .B "\-" is assumed. .LP In interactive mode, .B lua prompts the user, reads lines from the standard input, and executes them as they are read. If the line contains an expression or list of expressions, then the line is evaluated and the results are printed. If a line does not contain a complete statement, then a secondary prompt is displayed and lines are read until a complete statement is formed or a syntax error is found. .LP At the very start, before even handling the command line, .B lua checks the contents of the environment variables .B LUA_INIT_5_3 or .BR LUA_INIT , in that order. If the contents is of the form .RI '@ filename ', then .I filename is executed. Otherwise, the string is assumed to be a Lua statement and is executed. .SH OPTIONS .TP .BI \-e " stat" execute statement .IR stat . .TP .B \-i enter interactive mode after executing .IR script . .TP .BI \-l " name" execute the equivalent of .IB name =require(' name ') before executing .IR script . .TP .B \-v show version information. .TP .B \-E ignore environment variables. .TP .B \-\- stop handling options. .TP .B \- stop handling options and execute the standard input as a file. .SH "SEE ALSO" .BR luac (1) .br The documentation at lua.org, especially section 7 of the reference manual. .SH DIAGNOSTICS Error messages should be self explanatory. .SH AUTHORS R. Ierusalimschy, L. H. de Figueiredo, W. Celes .\" EOF ================================================ FILE: Tests/ApiExplorer/lua/doc/lua.css ================================================ html { background-color: #F8F8F8 ; } body { background-color: #FFFFFF ; color: #000000 ; font-family: Helvetica, Arial, sans-serif ; text-align: justify ; line-height: 1.25 ; margin: 16px auto ; padding: 32px ; border: solid #ccc 1px ; border-radius: 20px ; max-width: 70em ; width: 90% ; } h1, h2, h3, h4 { color: #000080 ; font-family: Verdana, Geneva, sans-serif ; font-weight: normal ; font-style: normal ; text-align: left ; } h1 { font-size: 28pt ; } h1 img { vertical-align: text-bottom ; } h2:before { content: "\2756" ; padding-right: 0.5em ; } a { text-decoration: none ; } a:link { color: #000080 ; } a:link:hover, a:visited:hover { background-color: #D0D0FF ; color: #000080 ; border-radius: 4px ; } a:link:active, a:visited:active { color: #FF0000 ; } div.menubar { padding-bottom: 0.5em ; } p.menubar { margin-left: 2.5em ; } .menubar a:hover { margin: -3px -3px -3px -3px ; padding: 3px 3px 3px 3px ; border-radius: 4px ; } :target { background-color: #F0F0F0 ; margin: -8px ; padding: 8px ; border-radius: 8px ; outline: none ; } hr { display: none ; } table hr { background-color: #a0a0a0 ; color: #a0a0a0 ; border: 0 ; height: 1px ; display: block ; } .footer { color: gray ; font-size: x-small ; text-transform: lowercase ; } input[type=text] { border: solid #a0a0a0 2px ; border-radius: 2em ; background-image: url('images/search.png') ; background-repeat: no-repeat ; background-position: 4px center ; padding-left: 20px ; height: 2em ; } pre.session { background-color: #F8F8F8 ; padding: 1em ; border-radius: 8px ; } table { border: none ; border-spacing: 0 ; border-collapse: collapse ; } td { padding: 0 ; margin: 0 ; } td.gutter { width: 4% ; } table.columns td { vertical-align: top ; padding-bottom: 1em ; text-align: justify ; line-height: 1.25 ; } table.book td { vertical-align: top ; } table.book td.cover { padding-right: 1em ; } table.book img { border: solid #000080 1px ; } table.book span { font-size: small ; text-align: left ; display: block ; margin-top: 0.25em ; } p.logos a:link:hover, p.logos a:visited:hover { background-color: inherit ; } img { background-color: white ; } ================================================ FILE: Tests/ApiExplorer/lua/doc/luac.1 ================================================ .\" $Id: luac.man,v 1.29 2011/11/16 13:53:40 lhf Exp $ .TH LUAC 1 "$Date: 2011/11/16 13:53:40 $" .SH NAME luac \- Lua compiler .SH SYNOPSIS .B luac [ .I options ] [ .I filenames ] .SH DESCRIPTION .B luac is the Lua compiler. It translates programs written in the Lua programming language into binary files containing precompiled chunks that can be later loaded and executed. .LP The main advantages of precompiling chunks are: faster loading, protecting source code from accidental user changes, and off-line syntax checking. Precompiling does not imply faster execution because in Lua chunks are always compiled into bytecodes before being executed. .B luac simply allows those bytecodes to be saved in a file for later execution. Precompiled chunks are not necessarily smaller than the corresponding source. The main goal in precompiling is faster loading. .LP In the command line, you can mix text files containing Lua source and binary files containing precompiled chunks. .B luac produces a single output file containing the combined bytecodes for all files given. Executing the combined file is equivalent to executing the given files. By default, the output file is named .BR luac.out , but you can change this with the .B \-o option. .LP Precompiled chunks are .I not portable across different architectures. Moreover, the internal format of precompiled chunks is likely to change when a new version of Lua is released. Make sure you save the source files of all Lua programs that you precompile. .LP .SH OPTIONS .TP .B \-l produce a listing of the compiled bytecode for Lua's virtual machine. Listing bytecodes is useful to learn about Lua's virtual machine. If no files are given, then .B luac loads .B luac.out and lists its contents. Use .B \-l \-l for a full listing. .TP .BI \-o " file" output to .IR file , instead of the default .BR luac.out . (You can use .B "'\-'" for standard output, but not on platforms that open standard output in text mode.) The output file may be one of the given files because all files are loaded before the output file is written. Be careful not to overwrite precious files. .TP .B \-p load files but do not generate any output file. Used mainly for syntax checking and for testing precompiled chunks: corrupted files will probably generate errors when loaded. If no files are given, then .B luac loads .B luac.out and tests its contents. No messages are displayed if the file loads without errors. .TP .B \-s strip debug information before writing the output file. This saves some space in very large chunks, but if errors occur when running a stripped chunk, then the error messages may not contain the full information they usually do. In particular, line numbers and names of local variables are lost. .TP .B \-v show version information. .TP .B \-\- stop handling options. .TP .B \- stop handling options and process standard input. .SH "SEE ALSO" .BR lua (1) .br The documentation at lua.org. .SH DIAGNOSTICS Error messages should be self explanatory. .SH AUTHORS R. Ierusalimschy, L. H. de Figueiredo, W. Celes .\" EOF ================================================ FILE: Tests/ApiExplorer/lua/doc/manual.css ================================================ h3 code { font-family: inherit ; font-size: inherit ; } pre, code { font-size: 12pt ; } span.apii { color: gray ; float: right ; font-family: inherit ; font-style: normal ; font-size: small ; } h2:before { content: "" ; padding-right: 0em ; } ================================================ FILE: Tests/ApiExplorer/lua/doc/manual.html ================================================ Lua 5.3 Reference Manual

Lua Lua 5.3 Reference Manual

by Roberto Ierusalimschy, Luiz Henrique de Figueiredo, Waldemar Celes

Copyright © 2015–2018 Lua.org, PUC-Rio. Freely available under the terms of the Lua license.

1 – Introduction

Lua is a powerful, efficient, lightweight, embeddable scripting language. It supports procedural programming, object-oriented programming, functional programming, data-driven programming, and data description.

Lua combines simple procedural syntax with powerful data description constructs based on associative arrays and extensible semantics. Lua is dynamically typed, runs by interpreting bytecode with a register-based virtual machine, and has automatic memory management with incremental garbage collection, making it ideal for configuration, scripting, and rapid prototyping.

Lua is implemented as a library, written in clean C, the common subset of Standard C and C++. The Lua distribution includes a host program called lua, which uses the Lua library to offer a complete, standalone Lua interpreter, for interactive or batch use. Lua is intended to be used both as a powerful, lightweight, embeddable scripting language for any program that needs one, and as a powerful but lightweight and efficient stand-alone language.

As an extension language, Lua has no notion of a "main" program: it works embedded in a host client, called the embedding program or simply the host. (Frequently, this host is the stand-alone lua program.) The host program can invoke functions to execute a piece of Lua code, can write and read Lua variables, and can register C functions to be called by Lua code. Through the use of C functions, Lua can be augmented to cope with a wide range of different domains, thus creating customized programming languages sharing a syntactical framework.

Lua is free software, and is provided as usual with no guarantees, as stated in its license. The implementation described in this manual is available at Lua's official web site, www.lua.org.

Like any other reference manual, this document is dry in places. For a discussion of the decisions behind the design of Lua, see the technical papers available at Lua's web site. For a detailed introduction to programming in Lua, see Roberto's book, Programming in Lua.

2 – Basic Concepts

This section describes the basic concepts of the language.

2.1 – Values and Types

Lua is a dynamically typed language. This means that variables do not have types; only values do. There are no type definitions in the language. All values carry their own type.

All values in Lua are first-class values. This means that all values can be stored in variables, passed as arguments to other functions, and returned as results.

There are eight basic types in Lua: nil, boolean, number, string, function, userdata, thread, and table. The type nil has one single value, nil, whose main property is to be different from any other value; it usually represents the absence of a useful value. The type boolean has two values, false and true. Both nil and false make a condition false; any other value makes it true. The type number represents both integer numbers and real (floating-point) numbers. The type string represents immutable sequences of bytes. Lua is 8-bit clean: strings can contain any 8-bit value, including embedded zeros ('\0'). Lua is also encoding-agnostic; it makes no assumptions about the contents of a string.

The type number uses two internal representations, or two subtypes, one called integer and the other called float. Lua has explicit rules about when each representation is used, but it also converts between them automatically as needed (see §3.4.3). Therefore, the programmer may choose to mostly ignore the difference between integers and floats or to assume complete control over the representation of each number. Standard Lua uses 64-bit integers and double-precision (64-bit) floats, but you can also compile Lua so that it uses 32-bit integers and/or single-precision (32-bit) floats. The option with 32 bits for both integers and floats is particularly attractive for small machines and embedded systems. (See macro LUA_32BITS in file luaconf.h.)

Lua can call (and manipulate) functions written in Lua and functions written in C (see §3.4.10). Both are represented by the type function.

The type userdata is provided to allow arbitrary C data to be stored in Lua variables. A userdata value represents a block of raw memory. There are two kinds of userdata: full userdata, which is an object with a block of memory managed by Lua, and light userdata, which is simply a C pointer value. Userdata has no predefined operations in Lua, except assignment and identity test. By using metatables, the programmer can define operations for full userdata values (see §2.4). Userdata values cannot be created or modified in Lua, only through the C API. This guarantees the integrity of data owned by the host program.

The type thread represents independent threads of execution and it is used to implement coroutines (see §2.6). Lua threads are not related to operating-system threads. Lua supports coroutines on all systems, even those that do not support threads natively.

The type table implements associative arrays, that is, arrays that can have as indices not only numbers, but any Lua value except nil and NaN. (Not a Number is a special value used to represent undefined or unrepresentable numerical results, such as 0/0.) Tables can be heterogeneous; that is, they can contain values of all types (except nil). Any key with value nil is not considered part of the table. Conversely, any key that is not part of a table has an associated value nil.

Tables are the sole data-structuring mechanism in Lua; they can be used to represent ordinary arrays, lists, symbol tables, sets, records, graphs, trees, etc. To represent records, Lua uses the field name as an index. The language supports this representation by providing a.name as syntactic sugar for a["name"]. There are several convenient ways to create tables in Lua (see §3.4.9).

Like indices, the values of table fields can be of any type. In particular, because functions are first-class values, table fields can contain functions. Thus tables can also carry methods (see §3.4.11).

The indexing of tables follows the definition of raw equality in the language. The expressions a[i] and a[j] denote the same table element if and only if i and j are raw equal (that is, equal without metamethods). In particular, floats with integral values are equal to their respective integers (e.g., 1.0 == 1). To avoid ambiguities, any float with integral value used as a key is converted to its respective integer. For instance, if you write a[2.0] = true, the actual key inserted into the table will be the integer 2. (On the other hand, 2 and "2" are different Lua values and therefore denote different table entries.)

Tables, functions, threads, and (full) userdata values are objects: variables do not actually contain these values, only references to them. Assignment, parameter passing, and function returns always manipulate references to such values; these operations do not imply any kind of copy.

The library function type returns a string describing the type of a given value (see §6.1).

2.2 – Environments and the Global Environment

As will be discussed in §3.2 and §3.3.3, any reference to a free name (that is, a name not bound to any declaration) var is syntactically translated to _ENV.var. Moreover, every chunk is compiled in the scope of an external local variable named _ENV (see §3.3.2), so _ENV itself is never a free name in a chunk.

Despite the existence of this external _ENV variable and the translation of free names, _ENV is a completely regular name. In particular, you can define new variables and parameters with that name. Each reference to a free name uses the _ENV that is visible at that point in the program, following the usual visibility rules of Lua (see §3.5).

Any table used as the value of _ENV is called an environment.

Lua keeps a distinguished environment called the global environment. This value is kept at a special index in the C registry (see §4.5). In Lua, the global variable _G is initialized with this same value. (_G is never used internally.)

When Lua loads a chunk, the default value for its _ENV upvalue is the global environment (see load). Therefore, by default, free names in Lua code refer to entries in the global environment (and, therefore, they are also called global variables). Moreover, all standard libraries are loaded in the global environment and some functions there operate on that environment. You can use load (or loadfile) to load a chunk with a different environment. (In C, you have to load the chunk and then change the value of its first upvalue.)

2.3 – Error Handling

Because Lua is an embedded extension language, all Lua actions start from C code in the host program calling a function from the Lua library. (When you use Lua standalone, the lua application is the host program.) Whenever an error occurs during the compilation or execution of a Lua chunk, control returns to the host, which can take appropriate measures (such as printing an error message).

Lua code can explicitly generate an error by calling the error function. If you need to catch errors in Lua, you can use pcall or xpcall to call a given function in protected mode.

Whenever there is an error, an error object (also called an error message) is propagated with information about the error. Lua itself only generates errors whose error object is a string, but programs may generate errors with any value as the error object. It is up to the Lua program or its host to handle such error objects.

When you use xpcall or lua_pcall, you may give a message handler to be called in case of errors. This function is called with the original error object and returns a new error object. It is called before the error unwinds the stack, so that it can gather more information about the error, for instance by inspecting the stack and creating a stack traceback. This message handler is still protected by the protected call; so, an error inside the message handler will call the message handler again. If this loop goes on for too long, Lua breaks it and returns an appropriate message. (The message handler is called only for regular runtime errors. It is not called for memory-allocation errors nor for errors while running finalizers.)

2.4 – Metatables and Metamethods

Every value in Lua can have a metatable. This metatable is an ordinary Lua table that defines the behavior of the original value under certain special operations. You can change several aspects of the behavior of operations over a value by setting specific fields in its metatable. For instance, when a non-numeric value is the operand of an addition, Lua checks for a function in the field "__add" of the value's metatable. If it finds one, Lua calls this function to perform the addition.

The key for each event in a metatable is a string with the event name prefixed by two underscores; the corresponding values are called metamethods. In the previous example, the key is "__add" and the metamethod is the function that performs the addition. Unless stated otherwise, metamethods should be function values.

You can query the metatable of any value using the getmetatable function. Lua queries metamethods in metatables using a raw access (see rawget). So, to retrieve the metamethod for event ev in object o, Lua does the equivalent to the following code:

     rawget(getmetatable(o) or {}, "__ev")

You can replace the metatable of tables using the setmetatable function. You cannot change the metatable of other types from Lua code (except by using the debug library (§6.10)); you should use the C API for that.

Tables and full userdata have individual metatables (although multiple tables and userdata can share their metatables). Values of all other types share one single metatable per type; that is, there is one single metatable for all numbers, one for all strings, etc. By default, a value has no metatable, but the string library sets a metatable for the string type (see §6.4).

A metatable controls how an object behaves in arithmetic operations, bitwise operations, order comparisons, concatenation, length operation, calls, and indexing. A metatable also can define a function to be called when a userdata or a table is garbage collected (§2.5).

For the unary operators (negation, length, and bitwise NOT), the metamethod is computed and called with a dummy second operand, equal to the first one. This extra operand is only to simplify Lua's internals (by making these operators behave like a binary operation) and may be removed in future versions. (For most uses this extra operand is irrelevant.)

A detailed list of events controlled by metatables is given next. Each operation is identified by its corresponding key.

  • __add: the addition (+) operation. If any operand for an addition is not a number (nor a string coercible to a number), Lua will try to call a metamethod. First, Lua will check the first operand (even if it is valid). If that operand does not define a metamethod for __add, then Lua will check the second operand. If Lua can find a metamethod, it calls the metamethod with the two operands as arguments, and the result of the call (adjusted to one value) is the result of the operation. Otherwise, it raises an error.
  • __sub: the subtraction (-) operation. Behavior similar to the addition operation.
  • __mul: the multiplication (*) operation. Behavior similar to the addition operation.
  • __div: the division (/) operation. Behavior similar to the addition operation.
  • __mod: the modulo (%) operation. Behavior similar to the addition operation.
  • __pow: the exponentiation (^) operation. Behavior similar to the addition operation.
  • __unm: the negation (unary -) operation. Behavior similar to the addition operation.
  • __idiv: the floor division (//) operation. Behavior similar to the addition operation.
  • __band: the bitwise AND (&) operation. Behavior similar to the addition operation, except that Lua will try a metamethod if any operand is neither an integer nor a value coercible to an integer (see §3.4.3).
  • __bor: the bitwise OR (|) operation. Behavior similar to the bitwise AND operation.
  • __bxor: the bitwise exclusive OR (binary ~) operation. Behavior similar to the bitwise AND operation.
  • __bnot: the bitwise NOT (unary ~) operation. Behavior similar to the bitwise AND operation.
  • __shl: the bitwise left shift (<<) operation. Behavior similar to the bitwise AND operation.
  • __shr: the bitwise right shift (>>) operation. Behavior similar to the bitwise AND operation.
  • __concat: the concatenation (..) operation. Behavior similar to the addition operation, except that Lua will try a metamethod if any operand is neither a string nor a number (which is always coercible to a string).
  • __len: the length (#) operation. If the object is not a string, Lua will try its metamethod. If there is a metamethod, Lua calls it with the object as argument, and the result of the call (always adjusted to one value) is the result of the operation. If there is no metamethod but the object is a table, then Lua uses the table length operation (see §3.4.7). Otherwise, Lua raises an error.
  • __eq: the equal (==) operation. Behavior similar to the addition operation, except that Lua will try a metamethod only when the values being compared are either both tables or both full userdata and they are not primitively equal. The result of the call is always converted to a boolean.
  • __lt: the less than (<) operation. Behavior similar to the addition operation, except that Lua will try a metamethod only when the values being compared are neither both numbers nor both strings. The result of the call is always converted to a boolean.
  • __le: the less equal (<=) operation. Unlike other operations, the less-equal operation can use two different events. First, Lua looks for the __le metamethod in both operands, like in the less than operation. If it cannot find such a metamethod, then it will try the __lt metamethod, assuming that a <= b is equivalent to not (b < a). As with the other comparison operators, the result is always a boolean. (This use of the __lt event can be removed in future versions; it is also slower than a real __le metamethod.)
  • __index: The indexing access operation table[key]. This event happens when table is not a table or when key is not present in table. The metamethod is looked up in table.

    Despite the name, the metamethod for this event can be either a function or a table. If it is a function, it is called with table and key as arguments, and the result of the call (adjusted to one value) is the result of the operation. If it is a table, the final result is the result of indexing this table with key. (This indexing is regular, not raw, and therefore can trigger another metamethod.)

  • __newindex: The indexing assignment table[key] = value. Like the index event, this event happens when table is not a table or when key is not present in table. The metamethod is looked up in table.

    Like with indexing, the metamethod for this event can be either a function or a table. If it is a function, it is called with table, key, and value as arguments. If it is a table, Lua does an indexing assignment to this table with the same key and value. (This assignment is regular, not raw, and therefore can trigger another metamethod.)

    Whenever there is a __newindex metamethod, Lua does not perform the primitive assignment. (If necessary, the metamethod itself can call rawset to do the assignment.)

  • __call: The call operation func(args). This event happens when Lua tries to call a non-function value (that is, func is not a function). The metamethod is looked up in func. If present, the metamethod is called with func as its first argument, followed by the arguments of the original call (args). All results of the call are the result of the operation. (This is the only metamethod that allows multiple results.)

It is a good practice to add all needed metamethods to a table before setting it as a metatable of some object. In particular, the __gc metamethod works only when this order is followed (see §2.5.1).

Because metatables are regular tables, they can contain arbitrary fields, not only the event names defined above. Some functions in the standard library (e.g., tostring) use other fields in metatables for their own purposes.

2.5 – Garbage Collection

Lua performs automatic memory management. This means that you do not have to worry about allocating memory for new objects or freeing it when the objects are no longer needed. Lua manages memory automatically by running a garbage collector to collect all dead objects (that is, objects that are no longer accessible from Lua). All memory used by Lua is subject to automatic management: strings, tables, userdata, functions, threads, internal structures, etc.

Lua implements an incremental mark-and-sweep collector. It uses two numbers to control its garbage-collection cycles: the garbage-collector pause and the garbage-collector step multiplier. Both use percentage points as units (e.g., a value of 100 means an internal value of 1).

The garbage-collector pause controls how long the collector waits before starting a new cycle. Larger values make the collector less aggressive. Values smaller than 100 mean the collector will not wait to start a new cycle. A value of 200 means that the collector waits for the total memory in use to double before starting a new cycle.

The garbage-collector step multiplier controls the relative speed of the collector relative to memory allocation. Larger values make the collector more aggressive but also increase the size of each incremental step. You should not use values smaller than 100, because they make the collector too slow and can result in the collector never finishing a cycle. The default is 200, which means that the collector runs at "twice" the speed of memory allocation.

If you set the step multiplier to a very large number (larger than 10% of the maximum number of bytes that the program may use), the collector behaves like a stop-the-world collector. If you then set the pause to 200, the collector behaves as in old Lua versions, doing a complete collection every time Lua doubles its memory usage.

You can change these numbers by calling lua_gc in C or collectgarbage in Lua. You can also use these functions to control the collector directly (e.g., stop and restart it).

2.5.1 – Garbage-Collection Metamethods

You can set garbage-collector metamethods for tables and, using the C API, for full userdata (see §2.4). These metamethods are also called finalizers. Finalizers allow you to coordinate Lua's garbage collection with external resource management (such as closing files, network or database connections, or freeing your own memory).

For an object (table or userdata) to be finalized when collected, you must mark it for finalization. You mark an object for finalization when you set its metatable and the metatable has a field indexed by the string "__gc". Note that if you set a metatable without a __gc field and later create that field in the metatable, the object will not be marked for finalization.

When a marked object becomes garbage, it is not collected immediately by the garbage collector. Instead, Lua puts it in a list. After the collection, Lua goes through that list. For each object in the list, it checks the object's __gc metamethod: If it is a function, Lua calls it with the object as its single argument; if the metamethod is not a function, Lua simply ignores it.

At the end of each garbage-collection cycle, the finalizers for objects are called in the reverse order that the objects were marked for finalization, among those collected in that cycle; that is, the first finalizer to be called is the one associated with the object marked last in the program. The execution of each finalizer may occur at any point during the execution of the regular code.

Because the object being collected must still be used by the finalizer, that object (and other objects accessible only through it) must be resurrected by Lua. Usually, this resurrection is transient, and the object memory is freed in the next garbage-collection cycle. However, if the finalizer stores the object in some global place (e.g., a global variable), then the resurrection is permanent. Moreover, if the finalizer marks a finalizing object for finalization again, its finalizer will be called again in the next cycle where the object is unreachable. In any case, the object memory is freed only in a GC cycle where the object is unreachable and not marked for finalization.

When you close a state (see lua_close), Lua calls the finalizers of all objects marked for finalization, following the reverse order that they were marked. If any finalizer marks objects for collection during that phase, these marks have no effect.

2.5.2 – Weak Tables

A weak table is a table whose elements are weak references. A weak reference is ignored by the garbage collector. In other words, if the only references to an object are weak references, then the garbage collector will collect that object.

A weak table can have weak keys, weak values, or both. A table with weak values allows the collection of its values, but prevents the collection of its keys. A table with both weak keys and weak values allows the collection of both keys and values. In any case, if either the key or the value is collected, the whole pair is removed from the table. The weakness of a table is controlled by the __mode field of its metatable. If the __mode field is a string containing the character 'k', the keys in the table are weak. If __mode contains 'v', the values in the table are weak.

A table with weak keys and strong values is also called an ephemeron table. In an ephemeron table, a value is considered reachable only if its key is reachable. In particular, if the only reference to a key comes through its value, the pair is removed.

Any change in the weakness of a table may take effect only at the next collect cycle. In particular, if you change the weakness to a stronger mode, Lua may still collect some items from that table before the change takes effect.

Only objects that have an explicit construction are removed from weak tables. Values, such as numbers and light C functions, are not subject to garbage collection, and therefore are not removed from weak tables (unless their associated values are collected). Although strings are subject to garbage collection, they do not have an explicit construction, and therefore are not removed from weak tables.

Resurrected objects (that is, objects being finalized and objects accessible only through objects being finalized) have a special behavior in weak tables. They are removed from weak values before running their finalizers, but are removed from weak keys only in the next collection after running their finalizers, when such objects are actually freed. This behavior allows the finalizer to access properties associated with the object through weak tables.

If a weak table is among the resurrected objects in a collection cycle, it may not be properly cleared until the next cycle.

2.6 – Coroutines

Lua supports coroutines, also called collaborative multithreading. A coroutine in Lua represents an independent thread of execution. Unlike threads in multithread systems, however, a coroutine only suspends its execution by explicitly calling a yield function.

You create a coroutine by calling coroutine.create. Its sole argument is a function that is the main function of the coroutine. The create function only creates a new coroutine and returns a handle to it (an object of type thread); it does not start the coroutine.

You execute a coroutine by calling coroutine.resume. When you first call coroutine.resume, passing as its first argument a thread returned by coroutine.create, the coroutine starts its execution by calling its main function. Extra arguments passed to coroutine.resume are passed as arguments to that function. After the coroutine starts running, it runs until it terminates or yields.

A coroutine can terminate its execution in two ways: normally, when its main function returns (explicitly or implicitly, after the last instruction); and abnormally, if there is an unprotected error. In case of normal termination, coroutine.resume returns true, plus any values returned by the coroutine main function. In case of errors, coroutine.resume returns false plus an error object.

A coroutine yields by calling coroutine.yield. When a coroutine yields, the corresponding coroutine.resume returns immediately, even if the yield happens inside nested function calls (that is, not in the main function, but in a function directly or indirectly called by the main function). In the case of a yield, coroutine.resume also returns true, plus any values passed to coroutine.yield. The next time you resume the same coroutine, it continues its execution from the point where it yielded, with the call to coroutine.yield returning any extra arguments passed to coroutine.resume.

Like coroutine.create, the coroutine.wrap function also creates a coroutine, but instead of returning the coroutine itself, it returns a function that, when called, resumes the coroutine. Any arguments passed to this function go as extra arguments to coroutine.resume. coroutine.wrap returns all the values returned by coroutine.resume, except the first one (the boolean error code). Unlike coroutine.resume, coroutine.wrap does not catch errors; any error is propagated to the caller.

As an example of how coroutines work, consider the following code:

     function foo (a)
       print("foo", a)
       return coroutine.yield(2*a)
     end
     
     co = coroutine.create(function (a,b)
           print("co-body", a, b)
           local r = foo(a+1)
           print("co-body", r)
           local r, s = coroutine.yield(a+b, a-b)
           print("co-body", r, s)
           return b, "end"
     end)
     
     print("main", coroutine.resume(co, 1, 10))
     print("main", coroutine.resume(co, "r"))
     print("main", coroutine.resume(co, "x", "y"))
     print("main", coroutine.resume(co, "x", "y"))

When you run it, it produces the following output:

     co-body 1       10
     foo     2
     main    true    4
     co-body r
     main    true    11      -9
     co-body x       y
     main    true    10      end
     main    false   cannot resume dead coroutine

You can also create and manipulate coroutines through the C API: see functions lua_newthread, lua_resume, and lua_yield.

3 – The Language

This section describes the lexis, the syntax, and the semantics of Lua. In other words, this section describes which tokens are valid, how they can be combined, and what their combinations mean.

Language constructs will be explained using the usual extended BNF notation, in which {a} means 0 or more a's, and [a] means an optional a. Non-terminals are shown like non-terminal, keywords are shown like kword, and other terminal symbols are shown like ‘=’. The complete syntax of Lua can be found in §9 at the end of this manual.

3.1 – Lexical Conventions

Lua is a free-form language. It ignores spaces (including new lines) and comments between lexical elements (tokens), except as delimiters between names and keywords.

Names (also called identifiers) in Lua can be any string of letters, digits, and underscores, not beginning with a digit and not being a reserved word. Identifiers are used to name variables, table fields, and labels.

The following keywords are reserved and cannot be used as names:

     and       break     do        else      elseif    end
     false     for       function  goto      if        in
     local     nil       not       or        repeat    return
     then      true      until     while

Lua is a case-sensitive language: and is a reserved word, but And and AND are two different, valid names. As a convention, programs should avoid creating names that start with an underscore followed by one or more uppercase letters (such as _VERSION).

The following strings denote other tokens:

     +     -     *     /     %     ^     #
     &     ~     |     <<    >>    //
     ==    ~=    <=    >=    <     >     =
     (     )     {     }     [     ]     ::
     ;     :     ,     .     ..    ...

A short literal string can be delimited by matching single or double quotes, and can contain the following C-like escape sequences: '\a' (bell), '\b' (backspace), '\f' (form feed), '\n' (newline), '\r' (carriage return), '\t' (horizontal tab), '\v' (vertical tab), '\\' (backslash), '\"' (quotation mark [double quote]), and '\'' (apostrophe [single quote]). A backslash followed by a line break results in a newline in the string. The escape sequence '\z' skips the following span of white-space characters, including line breaks; it is particularly useful to break and indent a long literal string into multiple lines without adding the newlines and spaces into the string contents. A short literal string cannot contain unescaped line breaks nor escapes not forming a valid escape sequence.

We can specify any byte in a short literal string by its numeric value (including embedded zeros). This can be done with the escape sequence \xXX, where XX is a sequence of exactly two hexadecimal digits, or with the escape sequence \ddd, where ddd is a sequence of up to three decimal digits. (Note that if a decimal escape sequence is to be followed by a digit, it must be expressed using exactly three digits.)

The UTF-8 encoding of a Unicode character can be inserted in a literal string with the escape sequence \u{XXX} (note the mandatory enclosing brackets), where XXX is a sequence of one or more hexadecimal digits representing the character code point.

Literal strings can also be defined using a long format enclosed by long brackets. We define an opening long bracket of level n as an opening square bracket followed by n equal signs followed by another opening square bracket. So, an opening long bracket of level 0 is written as [[, an opening long bracket of level 1 is written as [=[, and so on. A closing long bracket is defined similarly; for instance, a closing long bracket of level 4 is written as ]====]. A long literal starts with an opening long bracket of any level and ends at the first closing long bracket of the same level. It can contain any text except a closing bracket of the same level. Literals in this bracketed form can run for several lines, do not interpret any escape sequences, and ignore long brackets of any other level. Any kind of end-of-line sequence (carriage return, newline, carriage return followed by newline, or newline followed by carriage return) is converted to a simple newline.

For convenience, when the opening long bracket is immediately followed by a newline, the newline is not included in the string. As an example, in a system using ASCII (in which 'a' is coded as 97, newline is coded as 10, and '1' is coded as 49), the five literal strings below denote the same string:

     a = 'alo\n123"'
     a = "alo\n123\""
     a = '\97lo\10\04923"'
     a = [[alo
     123"]]
     a = [==[
     alo
     123"]==]

Any byte in a literal string not explicitly affected by the previous rules represents itself. However, Lua opens files for parsing in text mode, and the system file functions may have problems with some control characters. So, it is safer to represent non-text data as a quoted literal with explicit escape sequences for the non-text characters.

A numeric constant (or numeral) can be written with an optional fractional part and an optional decimal exponent, marked by a letter 'e' or 'E'. Lua also accepts hexadecimal constants, which start with 0x or 0X. Hexadecimal constants also accept an optional fractional part plus an optional binary exponent, marked by a letter 'p' or 'P'. A numeric constant with a radix point or an exponent denotes a float; otherwise, if its value fits in an integer, it denotes an integer. Examples of valid integer constants are

     3   345   0xff   0xBEBADA

Examples of valid float constants are

     3.0     3.1416     314.16e-2     0.31416E1     34e1
     0x0.1E  0xA23p-4   0X1.921FB54442D18P+1

A comment starts with a double hyphen (--) anywhere outside a string. If the text immediately after -- is not an opening long bracket, the comment is a short comment, which runs until the end of the line. Otherwise, it is a long comment, which runs until the corresponding closing long bracket. Long comments are frequently used to disable code temporarily.

3.2 – Variables

Variables are places that store values. There are three kinds of variables in Lua: global variables, local variables, and table fields.

A single name can denote a global variable or a local variable (or a function's formal parameter, which is a particular kind of local variable):

	var ::= Name

Name denotes identifiers, as defined in §3.1.

Any variable name is assumed to be global unless explicitly declared as a local (see §3.3.7). Local variables are lexically scoped: local variables can be freely accessed by functions defined inside their scope (see §3.5).

Before the first assignment to a variable, its value is nil.

Square brackets are used to index a table:

	var ::= prefixexp ‘[’ exp ‘]

The meaning of accesses to table fields can be changed via metatables (see §2.4).

The syntax var.Name is just syntactic sugar for var["Name"]:

	var ::= prefixexp ‘.’ Name

An access to a global variable x is equivalent to _ENV.x. Due to the way that chunks are compiled, _ENV is never a global name (see §2.2).

3.3 – Statements

Lua supports an almost conventional set of statements, similar to those in Pascal or C. This set includes assignments, control structures, function calls, and variable declarations.

3.3.1 – Blocks

A block is a list of statements, which are executed sequentially:

	block ::= {stat}

Lua has empty statements that allow you to separate statements with semicolons, start a block with a semicolon or write two semicolons in sequence:

	stat ::= ‘;

Function calls and assignments can start with an open parenthesis. This possibility leads to an ambiguity in Lua's grammar. Consider the following fragment:

     a = b + c
     (print or io.write)('done')

The grammar could see it in two ways:

     a = b + c(print or io.write)('done')
     
     a = b + c; (print or io.write)('done')

The current parser always sees such constructions in the first way, interpreting the open parenthesis as the start of the arguments to a call. To avoid this ambiguity, it is a good practice to always precede with a semicolon statements that start with a parenthesis:

     ;(print or io.write)('done')

A block can be explicitly delimited to produce a single statement:

	stat ::= do block end

Explicit blocks are useful to control the scope of variable declarations. Explicit blocks are also sometimes used to add a return statement in the middle of another block (see §3.3.4).

3.3.2 – Chunks

The unit of compilation of Lua is called a chunk. Syntactically, a chunk is simply a block:

	chunk ::= block

Lua handles a chunk as the body of an anonymous function with a variable number of arguments (see §3.4.11). As such, chunks can define local variables, receive arguments, and return values. Moreover, such anonymous function is compiled as in the scope of an external local variable called _ENV (see §2.2). The resulting function always has _ENV as its only upvalue, even if it does not use that variable.

A chunk can be stored in a file or in a string inside the host program. To execute a chunk, Lua first loads it, precompiling the chunk's code into instructions for a virtual machine, and then Lua executes the compiled code with an interpreter for the virtual machine.

Chunks can also be precompiled into binary form; see program luac and function string.dump for details. Programs in source and compiled forms are interchangeable; Lua automatically detects the file type and acts accordingly (see load).

3.3.3 – Assignment

Lua allows multiple assignments. Therefore, the syntax for assignment defines a list of variables on the left side and a list of expressions on the right side. The elements in both lists are separated by commas:

	stat ::= varlist ‘=’ explist
	varlist ::= var {‘,’ var}
	explist ::= exp {‘,’ exp}

Expressions are discussed in §3.4.

Before the assignment, the list of values is adjusted to the length of the list of variables. If there are more values than needed, the excess values are thrown away. If there are fewer values than needed, the list is extended with as many nil's as needed. If the list of expressions ends with a function call, then all values returned by that call enter the list of values, before the adjustment (except when the call is enclosed in parentheses; see §3.4).

The assignment statement first evaluates all its expressions and only then the assignments are performed. Thus the code

     i = 3
     i, a[i] = i+1, 20

sets a[3] to 20, without affecting a[4] because the i in a[i] is evaluated (to 3) before it is assigned 4. Similarly, the line

     x, y = y, x

exchanges the values of x and y, and

     x, y, z = y, z, x

cyclically permutes the values of x, y, and z.

An assignment to a global name x = val is equivalent to the assignment _ENV.x = val (see §2.2).

The meaning of assignments to table fields and global variables (which are actually table fields, too) can be changed via metatables (see §2.4).

3.3.4 – Control Structures

The control structures if, while, and repeat have the usual meaning and familiar syntax:

	stat ::= while exp do block end
	stat ::= repeat block until exp
	stat ::= if exp then block {elseif exp then block} [else block] end

Lua also has a for statement, in two flavors (see §3.3.5).

The condition expression of a control structure can return any value. Both false and nil are considered false. All values different from nil and false are considered true (in particular, the number 0 and the empty string are also true).

In the repeatuntil loop, the inner block does not end at the until keyword, but only after the condition. So, the condition can refer to local variables declared inside the loop block.

The goto statement transfers the program control to a label. For syntactical reasons, labels in Lua are considered statements too:

	stat ::= goto Name
	stat ::= label
	label ::= ‘::’ Name ‘::

A label is visible in the entire block where it is defined, except inside nested blocks where a label with the same name is defined and inside nested functions. A goto may jump to any visible label as long as it does not enter into the scope of a local variable.

Labels and empty statements are called void statements, as they perform no actions.

The break statement terminates the execution of a while, repeat, or for loop, skipping to the next statement after the loop:

	stat ::= break

A break ends the innermost enclosing loop.

The return statement is used to return values from a function or a chunk (which is an anonymous function). Functions can return more than one value, so the syntax for the return statement is

	stat ::= return [explist] [‘;’]

The return statement can only be written as the last statement of a block. If it is really necessary to return in the middle of a block, then an explicit inner block can be used, as in the idiom do return end, because now return is the last statement in its (inner) block.

3.3.5 – For Statement

The for statement has two forms: one numerical and one generic.

The numerical for loop repeats a block of code while a control variable runs through an arithmetic progression. It has the following syntax:

	stat ::= for Name ‘=’ exp ‘,’ exp [‘,’ exp] do block end

The block is repeated for name starting at the value of the first exp, until it passes the second exp by steps of the third exp. More precisely, a for statement like

     for v = e1, e2, e3 do block end

is equivalent to the code:

     do
       local var, limit, step = tonumber(e1), tonumber(e2), tonumber(e3)
       if not (var and limit and step) then error() end
       var = var - step
       while true do
         var = var + step
         if (step >= 0 and var > limit) or (step < 0 and var < limit) then
           break
         end
         local v = var
         block
       end
     end

Note the following:

  • All three control expressions are evaluated only once, before the loop starts. They must all result in numbers.
  • var, limit, and step are invisible variables. The names shown here are for explanatory purposes only.
  • If the third expression (the step) is absent, then a step of 1 is used.
  • You can use break and goto to exit a for loop.
  • The loop variable v is local to the loop body. If you need its value after the loop, assign it to another variable before exiting the loop.

The generic for statement works over functions, called iterators. On each iteration, the iterator function is called to produce a new value, stopping when this new value is nil. The generic for loop has the following syntax:

	stat ::= for namelist in explist do block end
	namelist ::= Name {‘,’ Name}

A for statement like

     for var_1, ···, var_n in explist do block end

is equivalent to the code:

     do
       local f, s, var = explist
       while true do
         local var_1, ···, var_n = f(s, var)
         if var_1 == nil then break end
         var = var_1
         block
       end
     end

Note the following:

  • explist is evaluated only once. Its results are an iterator function, a state, and an initial value for the first iterator variable.
  • f, s, and var are invisible variables. The names are here for explanatory purposes only.
  • You can use break to exit a for loop.
  • The loop variables var_i are local to the loop; you cannot use their values after the for ends. If you need these values, then assign them to other variables before breaking or exiting the loop.

3.3.6 – Function Calls as Statements

To allow possible side-effects, function calls can be executed as statements:

	stat ::= functioncall

In this case, all returned values are thrown away. Function calls are explained in §3.4.10.

3.3.7 – Local Declarations

Local variables can be declared anywhere inside a block. The declaration can include an initial assignment:

	stat ::= local namelist [‘=’ explist]

If present, an initial assignment has the same semantics of a multiple assignment (see §3.3.3). Otherwise, all variables are initialized with nil.

A chunk is also a block (see §3.3.2), and so local variables can be declared in a chunk outside any explicit block.

The visibility rules for local variables are explained in §3.5.

3.4 – Expressions

The basic expressions in Lua are the following:

	exp ::= prefixexp
	exp ::= nil | false | true
	exp ::= Numeral
	exp ::= LiteralString
	exp ::= functiondef
	exp ::= tableconstructor
	exp ::= ‘...’
	exp ::= exp binop exp
	exp ::= unop exp
	prefixexp ::= var | functioncall | ‘(’ exp ‘)

Numerals and literal strings are explained in §3.1; variables are explained in §3.2; function definitions are explained in §3.4.11; function calls are explained in §3.4.10; table constructors are explained in §3.4.9. Vararg expressions, denoted by three dots ('...'), can only be used when directly inside a vararg function; they are explained in §3.4.11.

Binary operators comprise arithmetic operators (see §3.4.1), bitwise operators (see §3.4.2), relational operators (see §3.4.4), logical operators (see §3.4.5), and the concatenation operator (see §3.4.6). Unary operators comprise the unary minus (see §3.4.1), the unary bitwise NOT (see §3.4.2), the unary logical not (see §3.4.5), and the unary length operator (see §3.4.7).

Both function calls and vararg expressions can result in multiple values. If a function call is used as a statement (see §3.3.6), then its return list is adjusted to zero elements, thus discarding all returned values. If an expression is used as the last (or the only) element of a list of expressions, then no adjustment is made (unless the expression is enclosed in parentheses). In all other contexts, Lua adjusts the result list to one element, either discarding all values except the first one or adding a single nil if there are no values.

Here are some examples:

     f()                -- adjusted to 0 results
     g(f(), x)          -- f() is adjusted to 1 result
     g(x, f())          -- g gets x plus all results from f()
     a,b,c = f(), x     -- f() is adjusted to 1 result (c gets nil)
     a,b = ...          -- a gets the first vararg argument, b gets
                        -- the second (both a and b can get nil if there
                        -- is no corresponding vararg argument)
     
     a,b,c = x, f()     -- f() is adjusted to 2 results
     a,b,c = f()        -- f() is adjusted to 3 results
     return f()         -- returns all results from f()
     return ...         -- returns all received vararg arguments
     return x,y,f()     -- returns x, y, and all results from f()
     {f()}              -- creates a list with all results from f()
     {...}              -- creates a list with all vararg arguments
     {f(), nil}         -- f() is adjusted to 1 result

Any expression enclosed in parentheses always results in only one value. Thus, (f(x,y,z)) is always a single value, even if f returns several values. (The value of (f(x,y,z)) is the first value returned by f or nil if f does not return any values.)

3.4.1 – Arithmetic Operators

Lua supports the following arithmetic operators:

  • +: addition
  • -: subtraction
  • *: multiplication
  • /: float division
  • //: floor division
  • %: modulo
  • ^: exponentiation
  • -: unary minus

With the exception of exponentiation and float division, the arithmetic operators work as follows: If both operands are integers, the operation is performed over integers and the result is an integer. Otherwise, if both operands are numbers or strings that can be converted to numbers (see §3.4.3), then they are converted to floats, the operation is performed following the usual rules for floating-point arithmetic (usually the IEEE 754 standard), and the result is a float.

Exponentiation and float division (/) always convert their operands to floats and the result is always a float. Exponentiation uses the ISO C function pow, so that it works for non-integer exponents too.

Floor division (//) is a division that rounds the quotient towards minus infinity, that is, the floor of the division of its operands.

Modulo is defined as the remainder of a division that rounds the quotient towards minus infinity (floor division).

In case of overflows in integer arithmetic, all operations wrap around, according to the usual rules of two-complement arithmetic. (In other words, they return the unique representable integer that is equal modulo 264 to the mathematical result.)

3.4.2 – Bitwise Operators

Lua supports the following bitwise operators:

  • &: bitwise AND
  • |: bitwise OR
  • ~: bitwise exclusive OR
  • >>: right shift
  • <<: left shift
  • ~: unary bitwise NOT

All bitwise operations convert its operands to integers (see §3.4.3), operate on all bits of those integers, and result in an integer.

Both right and left shifts fill the vacant bits with zeros. Negative displacements shift to the other direction; displacements with absolute values equal to or higher than the number of bits in an integer result in zero (as all bits are shifted out).

3.4.3 – Coercions and Conversions

Lua provides some automatic conversions between some types and representations at run time. Bitwise operators always convert float operands to integers. Exponentiation and float division always convert integer operands to floats. All other arithmetic operations applied to mixed numbers (integers and floats) convert the integer operand to a float; this is called the usual rule. The C API also converts both integers to floats and floats to integers, as needed. Moreover, string concatenation accepts numbers as arguments, besides strings.

Lua also converts strings to numbers, whenever a number is expected.

In a conversion from integer to float, if the integer value has an exact representation as a float, that is the result. Otherwise, the conversion gets the nearest higher or the nearest lower representable value. This kind of conversion never fails.

The conversion from float to integer checks whether the float has an exact representation as an integer (that is, the float has an integral value and it is in the range of integer representation). If it does, that representation is the result. Otherwise, the conversion fails.

The conversion from strings to numbers goes as follows: First, the string is converted to an integer or a float, following its syntax and the rules of the Lua lexer. (The string may have also leading and trailing spaces and a sign.) Then, the resulting number (float or integer) is converted to the type (float or integer) required by the context (e.g., the operation that forced the conversion).

All conversions from strings to numbers accept both a dot and the current locale mark as the radix character. (The Lua lexer, however, accepts only a dot.)

The conversion from numbers to strings uses a non-specified human-readable format. For complete control over how numbers are converted to strings, use the format function from the string library (see string.format).

3.4.4 – Relational Operators

Lua supports the following relational operators:

  • ==: equality
  • ~=: inequality
  • <: less than
  • >: greater than
  • <=: less or equal
  • >=: greater or equal

These operators always result in false or true.

Equality (==) first compares the type of its operands. If the types are different, then the result is false. Otherwise, the values of the operands are compared. Strings are compared in the obvious way. Numbers are equal if they denote the same mathematical value.

Tables, userdata, and threads are compared by reference: two objects are considered equal only if they are the same object. Every time you create a new object (a table, userdata, or thread), this new object is different from any previously existing object. A closure is always equal to itself. Closures with any detectable difference (different behavior, different definition) are always different. Closures created at different times but with no detectable differences may be classified as equal or not (depending on internal caching details).

You can change the way that Lua compares tables and userdata by using the "eq" metamethod (see §2.4).

Equality comparisons do not convert strings to numbers or vice versa. Thus, "0"==0 evaluates to false, and t[0] and t["0"] denote different entries in a table.

The operator ~= is exactly the negation of equality (==).

The order operators work as follows. If both arguments are numbers, then they are compared according to their mathematical values (regardless of their subtypes). Otherwise, if both arguments are strings, then their values are compared according to the current locale. Otherwise, Lua tries to call the "lt" or the "le" metamethod (see §2.4). A comparison a > b is translated to b < a and a >= b is translated to b <= a.

Following the IEEE 754 standard, NaN is considered neither smaller than, nor equal to, nor greater than any value (including itself).

3.4.5 – Logical Operators

The logical operators in Lua are and, or, and not. Like the control structures (see §3.3.4), all logical operators consider both false and nil as false and anything else as true.

The negation operator not always returns false or true. The conjunction operator and returns its first argument if this value is false or nil; otherwise, and returns its second argument. The disjunction operator or returns its first argument if this value is different from nil and false; otherwise, or returns its second argument. Both and and or use short-circuit evaluation; that is, the second operand is evaluated only if necessary. Here are some examples:

     10 or 20            --> 10
     10 or error()       --> 10
     nil or "a"          --> "a"
     nil and 10          --> nil
     false and error()   --> false
     false and nil       --> false
     false or nil        --> nil
     10 and 20           --> 20

(In this manual, --> indicates the result of the preceding expression.)

3.4.6 – Concatenation

The string concatenation operator in Lua is denoted by two dots ('..'). If both operands are strings or numbers, then they are converted to strings according to the rules described in §3.4.3. Otherwise, the __concat metamethod is called (see §2.4).

3.4.7 – The Length Operator

The length operator is denoted by the unary prefix operator #.

The length of a string is its number of bytes (that is, the usual meaning of string length when each character is one byte).

The length operator applied on a table returns a border in that table. A border in a table t is any natural number that satisfies the following condition:

     (border == 0 or t[border] ~= nil) and t[border + 1] == nil

In words, a border is any (natural) index in a table where a non-nil value is followed by a nil value (or zero, when index 1 is nil).

A table with exactly one border is called a sequence. For instance, the table {10, 20, 30, 40, 50} is a sequence, as it has only one border (5). The table {10, 20, 30, nil, 50} has two borders (3 and 5), and therefore it is not a sequence. The table {nil, 20, 30, nil, nil, 60, nil} has three borders (0, 3, and 6), so it is not a sequence, too. The table {} is a sequence with border 0. Note that non-natural keys do not interfere with whether a table is a sequence.

When t is a sequence, #t returns its only border, which corresponds to the intuitive notion of the length of the sequence. When t is not a sequence, #t can return any of its borders. (The exact one depends on details of the internal representation of the table, which in turn can depend on how the table was populated and the memory addresses of its non-numeric keys.)

The computation of the length of a table has a guaranteed worst time of O(log n), where n is the largest natural key in the table.

A program can modify the behavior of the length operator for any value but strings through the __len metamethod (see §2.4).

3.4.8 – Precedence

Operator precedence in Lua follows the table below, from lower to higher priority:

     or
     and
     <     >     <=    >=    ~=    ==
     |
     ~
     &
     <<    >>
     ..
     +     -
     *     /     //    %
     unary operators (not   #     -     ~)
     ^

As usual, you can use parentheses to change the precedences of an expression. The concatenation ('..') and exponentiation ('^') operators are right associative. All other binary operators are left associative.

3.4.9 – Table Constructors

Table constructors are expressions that create tables. Every time a constructor is evaluated, a new table is created. A constructor can be used to create an empty table or to create a table and initialize some of its fields. The general syntax for constructors is

	tableconstructor ::= ‘{’ [fieldlist] ‘}’
	fieldlist ::= field {fieldsep field} [fieldsep]
	field ::= ‘[’ exp ‘]’ ‘=’ exp | Name ‘=’ exp | exp
	fieldsep ::= ‘,’ | ‘;

Each field of the form [exp1] = exp2 adds to the new table an entry with key exp1 and value exp2. A field of the form name = exp is equivalent to ["name"] = exp. Finally, fields of the form exp are equivalent to [i] = exp, where i are consecutive integers starting with 1. Fields in the other formats do not affect this counting. For example,

     a = { [f(1)] = g; "x", "y"; x = 1, f(x), [30] = 23; 45 }

is equivalent to

     do
       local t = {}
       t[f(1)] = g
       t[1] = "x"         -- 1st exp
       t[2] = "y"         -- 2nd exp
       t.x = 1            -- t["x"] = 1
       t[3] = f(x)        -- 3rd exp
       t[30] = 23
       t[4] = 45          -- 4th exp
       a = t
     end

The order of the assignments in a constructor is undefined. (This order would be relevant only when there are repeated keys.)

If the last field in the list has the form exp and the expression is a function call or a vararg expression, then all values returned by this expression enter the list consecutively (see §3.4.10).

The field list can have an optional trailing separator, as a convenience for machine-generated code.

3.4.10 – Function Calls

A function call in Lua has the following syntax:

	functioncall ::= prefixexp args

In a function call, first prefixexp and args are evaluated. If the value of prefixexp has type function, then this function is called with the given arguments. Otherwise, the prefixexp "call" metamethod is called, having as first argument the value of prefixexp, followed by the original call arguments (see §2.4).

The form

	functioncall ::= prefixexp ‘:’ Name args

can be used to call "methods". A call v:name(args) is syntactic sugar for v.name(v,args), except that v is evaluated only once.

Arguments have the following syntax:

	args ::= ‘(’ [explist] ‘)’
	args ::= tableconstructor
	args ::= LiteralString

All argument expressions are evaluated before the call. A call of the form f{fields} is syntactic sugar for f({fields}); that is, the argument list is a single new table. A call of the form f'string' (or f"string" or f[[string]]) is syntactic sugar for f('string'); that is, the argument list is a single literal string.

A call of the form return functioncall is called a tail call. Lua implements proper tail calls (or proper tail recursion): in a tail call, the called function reuses the stack entry of the calling function. Therefore, there is no limit on the number of nested tail calls that a program can execute. However, a tail call erases any debug information about the calling function. Note that a tail call only happens with a particular syntax, where the return has one single function call as argument; this syntax makes the calling function return exactly the returns of the called function. So, none of the following examples are tail calls:

     return (f(x))        -- results adjusted to 1
     return 2 * f(x)
     return x, f(x)       -- additional results
     f(x); return         -- results discarded
     return x or f(x)     -- results adjusted to 1

3.4.11 – Function Definitions

The syntax for function definition is

	functiondef ::= function funcbody
	funcbody ::= ‘(’ [parlist] ‘)’ block end

The following syntactic sugar simplifies function definitions:

	stat ::= function funcname funcbody
	stat ::= local function Name funcbody
	funcname ::= Name {‘.’ Name} [‘:’ Name]

The statement

     function f () body end

translates to

     f = function () body end

The statement

     function t.a.b.c.f () body end

translates to

     t.a.b.c.f = function () body end

The statement

     local function f () body end

translates to

     local f; f = function () body end

not to

     local f = function () body end

(This only makes a difference when the body of the function contains references to f.)

A function definition is an executable expression, whose value has type function. When Lua precompiles a chunk, all its function bodies are precompiled too. Then, whenever Lua executes the function definition, the function is instantiated (or closed). This function instance (or closure) is the final value of the expression.

Parameters act as local variables that are initialized with the argument values:

	parlist ::= namelist [‘,’ ‘...’] | ‘...

When a function is called, the list of arguments is adjusted to the length of the list of parameters, unless the function is a vararg function, which is indicated by three dots ('...') at the end of its parameter list. A vararg function does not adjust its argument list; instead, it collects all extra arguments and supplies them to the function through a vararg expression, which is also written as three dots. The value of this expression is a list of all actual extra arguments, similar to a function with multiple results. If a vararg expression is used inside another expression or in the middle of a list of expressions, then its return list is adjusted to one element. If the expression is used as the last element of a list of expressions, then no adjustment is made (unless that last expression is enclosed in parentheses).

As an example, consider the following definitions:

     function f(a, b) end
     function g(a, b, ...) end
     function r() return 1,2,3 end

Then, we have the following mapping from arguments to parameters and to the vararg expression:

     CALL            PARAMETERS
     
     f(3)             a=3, b=nil
     f(3, 4)          a=3, b=4
     f(3, 4, 5)       a=3, b=4
     f(r(), 10)       a=1, b=10
     f(r())           a=1, b=2
     
     g(3)             a=3, b=nil, ... -->  (nothing)
     g(3, 4)          a=3, b=4,   ... -->  (nothing)
     g(3, 4, 5, 8)    a=3, b=4,   ... -->  5  8
     g(5, r())        a=5, b=1,   ... -->  2  3

Results are returned using the return statement (see §3.3.4). If control reaches the end of a function without encountering a return statement, then the function returns with no results.

There is a system-dependent limit on the number of values that a function may return. This limit is guaranteed to be larger than 1000.

The colon syntax is used for defining methods, that is, functions that have an implicit extra parameter self. Thus, the statement

     function t.a.b.c:f (params) body end

is syntactic sugar for

     t.a.b.c.f = function (self, params) body end

3.5 – Visibility Rules

Lua is a lexically scoped language. The scope of a local variable begins at the first statement after its declaration and lasts until the last non-void statement of the innermost block that includes the declaration. Consider the following example:

     x = 10                -- global variable
     do                    -- new block
       local x = x         -- new 'x', with value 10
       print(x)            --> 10
       x = x+1
       do                  -- another block
         local x = x+1     -- another 'x'
         print(x)          --> 12
       end
       print(x)            --> 11
     end
     print(x)              --> 10  (the global one)

Notice that, in a declaration like local x = x, the new x being declared is not in scope yet, and so the second x refers to the outside variable.

Because of the lexical scoping rules, local variables can be freely accessed by functions defined inside their scope. A local variable used by an inner function is called an upvalue, or external local variable, inside the inner function.

Notice that each execution of a local statement defines new local variables. Consider the following example:

     a = {}
     local x = 20
     for i=1,10 do
       local y = 0
       a[i] = function () y=y+1; return x+y end
     end

The loop creates ten closures (that is, ten instances of the anonymous function). Each of these closures uses a different y variable, while all of them share the same x.

4 – The Application Program Interface

This section describes the C API for Lua, that is, the set of C functions available to the host program to communicate with Lua. All API functions and related types and constants are declared in the header file lua.h.

Even when we use the term "function", any facility in the API may be provided as a macro instead. Except where stated otherwise, all such macros use each of their arguments exactly once (except for the first argument, which is always a Lua state), and so do not generate any hidden side-effects.

As in most C libraries, the Lua API functions do not check their arguments for validity or consistency. However, you can change this behavior by compiling Lua with the macro LUA_USE_APICHECK defined.

The Lua library is fully reentrant: it has no global variables. It keeps all information it needs in a dynamic structure, called the Lua state.

Each Lua state has one or more threads, which correspond to independent, cooperative lines of execution. The type lua_State (despite its name) refers to a thread. (Indirectly, through the thread, it also refers to the Lua state associated to the thread.)

A pointer to a thread must be passed as the first argument to every function in the library, except to lua_newstate, which creates a Lua state from scratch and returns a pointer to the main thread in the new state.

4.1 – The Stack

Lua uses a virtual stack to pass values to and from C. Each element in this stack represents a Lua value (nil, number, string, etc.). Functions in the API can access this stack through the Lua state parameter that they receive.

Whenever Lua calls C, the called function gets a new stack, which is independent of previous stacks and of stacks of C functions that are still active. This stack initially contains any arguments to the C function and it is where the C function can store temporary Lua values and must push its results to be returned to the caller (see lua_CFunction).

For convenience, most query operations in the API do not follow a strict stack discipline. Instead, they can refer to any element in the stack by using an index: A positive index represents an absolute stack position (starting at 1); a negative index represents an offset relative to the top of the stack. More specifically, if the stack has n elements, then index 1 represents the first element (that is, the element that was pushed onto the stack first) and index n represents the last element; index -1 also represents the last element (that is, the element at the top) and index -n represents the first element.

4.2 – Stack Size

When you interact with the Lua API, you are responsible for ensuring consistency. In particular, you are responsible for controlling stack overflow. You can use the function lua_checkstack to ensure that the stack has enough space for pushing new elements.

Whenever Lua calls C, it ensures that the stack has space for at least LUA_MINSTACK extra slots. LUA_MINSTACK is defined as 20, so that usually you do not have to worry about stack space unless your code has loops pushing elements onto the stack.

When you call a Lua function without a fixed number of results (see lua_call), Lua ensures that the stack has enough space for all results, but it does not ensure any extra space. So, before pushing anything in the stack after such a call you should use lua_checkstack.

4.3 – Valid and Acceptable Indices

Any function in the API that receives stack indices works only with valid indices or acceptable indices.

A valid index is an index that refers to a position that stores a modifiable Lua value. It comprises stack indices between 1 and the stack top (1 ≤ abs(index) ≤ top) plus pseudo-indices, which represent some positions that are accessible to C code but that are not in the stack. Pseudo-indices are used to access the registry (see §4.5) and the upvalues of a C function (see §4.4).

Functions that do not need a specific mutable position, but only a value (e.g., query functions), can be called with acceptable indices. An acceptable index can be any valid index, but it also can be any positive index after the stack top within the space allocated for the stack, that is, indices up to the stack size. (Note that 0 is never an acceptable index.) Except when noted otherwise, functions in the API work with acceptable indices.

Acceptable indices serve to avoid extra tests against the stack top when querying the stack. For instance, a C function can query its third argument without the need to first check whether there is a third argument, that is, without the need to check whether 3 is a valid index.

For functions that can be called with acceptable indices, any non-valid index is treated as if it contains a value of a virtual type LUA_TNONE, which behaves like a nil value.

4.4 – C Closures

When a C function is created, it is possible to associate some values with it, thus creating a C closure (see lua_pushcclosure); these values are called upvalues and are accessible to the function whenever it is called.

Whenever a C function is called, its upvalues are located at specific pseudo-indices. These pseudo-indices are produced by the macro lua_upvalueindex. The first upvalue associated with a function is at index lua_upvalueindex(1), and so on. Any access to lua_upvalueindex(n), where n is greater than the number of upvalues of the current function (but not greater than 256, which is one plus the maximum number of upvalues in a closure), produces an acceptable but invalid index.

4.5 – Registry

Lua provides a registry, a predefined table that can be used by any C code to store whatever Lua values it needs to store. The registry table is always located at pseudo-index LUA_REGISTRYINDEX. Any C library can store data into this table, but it must take care to choose keys that are different from those used by other libraries, to avoid collisions. Typically, you should use as key a string containing your library name, or a light userdata with the address of a C object in your code, or any Lua object created by your code. As with variable names, string keys starting with an underscore followed by uppercase letters are reserved for Lua.

The integer keys in the registry are used by the reference mechanism (see luaL_ref) and by some predefined values. Therefore, integer keys must not be used for other purposes.

When you create a new Lua state, its registry comes with some predefined values. These predefined values are indexed with integer keys defined as constants in lua.h. The following constants are defined:

  • LUA_RIDX_MAINTHREAD: At this index the registry has the main thread of the state. (The main thread is the one created together with the state.)
  • LUA_RIDX_GLOBALS: At this index the registry has the global environment.

4.6 – Error Handling in C

Internally, Lua uses the C longjmp facility to handle errors. (Lua will use exceptions if you compile it as C++; search for LUAI_THROW in the source code for details.) When Lua faces any error (such as a memory allocation error or a type error) it raises an error; that is, it does a long jump. A protected environment uses setjmp to set a recovery point; any error jumps to the most recent active recovery point.

Inside a C function you can raise an error by calling lua_error.

Most functions in the API can raise an error, for instance due to a memory allocation error. The documentation for each function indicates whether it can raise errors.

If an error happens outside any protected environment, Lua calls a panic function (see lua_atpanic) and then calls abort, thus exiting the host application. Your panic function can avoid this exit by never returning (e.g., doing a long jump to your own recovery point outside Lua).

The panic function, as its name implies, is a mechanism of last resort. Programs should avoid it. As a general rule, when a C function is called by Lua with a Lua state, it can do whatever it wants on that Lua state, as it should be already protected. However, when C code operates on other Lua states (e.g., a Lua argument to the function, a Lua state stored in the registry, or the result of lua_newthread), it should use them only in API calls that cannot raise errors.

The panic function runs as if it were a message handler (see §2.3); in particular, the error object is at the top of the stack. However, there is no guarantee about stack space. To push anything on the stack, the panic function must first check the available space (see §4.2).

4.7 – Handling Yields in C

Internally, Lua uses the C longjmp facility to yield a coroutine. Therefore, if a C function foo calls an API function and this API function yields (directly or indirectly by calling another function that yields), Lua cannot return to foo any more, because the longjmp removes its frame from the C stack.

To avoid this kind of problem, Lua raises an error whenever it tries to yield across an API call, except for three functions: lua_yieldk, lua_callk, and lua_pcallk. All those functions receive a continuation function (as a parameter named k) to continue execution after a yield.

We need to set some terminology to explain continuations. We have a C function called from Lua which we will call the original function. This original function then calls one of those three functions in the C API, which we will call the callee function, that then yields the current thread. (This can happen when the callee function is lua_yieldk, or when the callee function is either lua_callk or lua_pcallk and the function called by them yields.)

Suppose the running thread yields while executing the callee function. After the thread resumes, it eventually will finish running the callee function. However, the callee function cannot return to the original function, because its frame in the C stack was destroyed by the yield. Instead, Lua calls a continuation function, which was given as an argument to the callee function. As the name implies, the continuation function should continue the task of the original function.

As an illustration, consider the following function:

     int original_function (lua_State *L) {
       ...     /* code 1 */
       status = lua_pcall(L, n, m, h);  /* calls Lua */
       ...     /* code 2 */
     }

Now we want to allow the Lua code being run by lua_pcall to yield. First, we can rewrite our function like here:

     int k (lua_State *L, int status, lua_KContext ctx) {
       ...  /* code 2 */
     }
     
     int original_function (lua_State *L) {
       ...     /* code 1 */
       return k(L, lua_pcall(L, n, m, h), ctx);
     }

In the above code, the new function k is a continuation function (with type lua_KFunction), which should do all the work that the original function was doing after calling lua_pcall. Now, we must inform Lua that it must call k if the Lua code being executed by lua_pcall gets interrupted in some way (errors or yielding), so we rewrite the code as here, replacing lua_pcall by lua_pcallk:

     int original_function (lua_State *L) {
       ...     /* code 1 */
       return k(L, lua_pcallk(L, n, m, h, ctx2, k), ctx1);
     }

Note the external, explicit call to the continuation: Lua will call the continuation only if needed, that is, in case of errors or resuming after a yield. If the called function returns normally without ever yielding, lua_pcallk (and lua_callk) will also return normally. (Of course, instead of calling the continuation in that case, you can do the equivalent work directly inside the original function.)

Besides the Lua state, the continuation function has two other parameters: the final status of the call plus the context value (ctx) that was passed originally to lua_pcallk. (Lua does not use this context value; it only passes this value from the original function to the continuation function.) For lua_pcallk, the status is the same value that would be returned by lua_pcallk, except that it is LUA_YIELD when being executed after a yield (instead of LUA_OK). For lua_yieldk and lua_callk, the status is always LUA_YIELD when Lua calls the continuation. (For these two functions, Lua will not call the continuation in case of errors, because they do not handle errors.) Similarly, when using lua_callk, you should call the continuation function with LUA_OK as the status. (For lua_yieldk, there is not much point in calling directly the continuation function, because lua_yieldk usually does not return.)

Lua treats the continuation function as if it were the original function. The continuation function receives the same Lua stack from the original function, in the same state it would be if the callee function had returned. (For instance, after a lua_callk the function and its arguments are removed from the stack and replaced by the results from the call.) It also has the same upvalues. Whatever it returns is handled by Lua as if it were the return of the original function.

4.8 – Functions and Types

Here we list all functions and types from the C API in alphabetical order. Each function has an indicator like this: [-o, +p, x]

The first field, o, is how many elements the function pops from the stack. The second field, p, is how many elements the function pushes onto the stack. (Any function always pushes its results after popping its arguments.) A field in the form x|y means the function can push (or pop) x or y elements, depending on the situation; an interrogation mark '?' means that we cannot know how many elements the function pops/pushes by looking only at its arguments (e.g., they may depend on what is on the stack). The third field, x, tells whether the function may raise errors: '-' means the function never raises any error; 'm' means the function may raise out-of-memory errors and errors running a __gc metamethod; 'e' means the function may raise any errors (it can run arbitrary Lua code, either directly or through metamethods); 'v' means the function may raise an error on purpose.


lua_absindex

[-0, +0, –]

int lua_absindex (lua_State *L, int idx);

Converts the acceptable index idx into an equivalent absolute index (that is, one that does not depend on the stack top).


lua_Alloc

typedef void * (*lua_Alloc) (void *ud,
                             void *ptr,
                             size_t osize,
                             size_t nsize);

The type of the memory-allocation function used by Lua states. The allocator function must provide a functionality similar to realloc, but not exactly the same. Its arguments are ud, an opaque pointer passed to lua_newstate; ptr, a pointer to the block being allocated/reallocated/freed; osize, the original size of the block or some code about what is being allocated; and nsize, the new size of the block.

When ptr is not NULL, osize is the size of the block pointed by ptr, that is, the size given when it was allocated or reallocated.

When ptr is NULL, osize encodes the kind of object that Lua is allocating. osize is any of LUA_TSTRING, LUA_TTABLE, LUA_TFUNCTION, LUA_TUSERDATA, or LUA_TTHREAD when (and only when) Lua is creating a new object of that type. When osize is some other value, Lua is allocating memory for something else.

Lua assumes the following behavior from the allocator function:

When nsize is zero, the allocator must behave like free and return NULL.

When nsize is not zero, the allocator must behave like realloc. The allocator returns NULL if and only if it cannot fulfill the request. Lua assumes that the allocator never fails when osize >= nsize.

Here is a simple implementation for the allocator function. It is used in the auxiliary library by luaL_newstate.

     static void *l_alloc (void *ud, void *ptr, size_t osize,
                                                size_t nsize) {
       (void)ud;  (void)osize;  /* not used */
       if (nsize == 0) {
         free(ptr);
         return NULL;
       }
       else
         return realloc(ptr, nsize);
     }

Note that Standard C ensures that free(NULL) has no effect and that realloc(NULL,size) is equivalent to malloc(size). This code assumes that realloc does not fail when shrinking a block. (Although Standard C does not ensure this behavior, it seems to be a safe assumption.)


lua_arith

[-(2|1), +1, e]

void lua_arith (lua_State *L, int op);

Performs an arithmetic or bitwise operation over the two values (or one, in the case of negations) at the top of the stack, with the value at the top being the second operand, pops these values, and pushes the result of the operation. The function follows the semantics of the corresponding Lua operator (that is, it may call metamethods).

The value of op must be one of the following constants:


lua_atpanic

[-0, +0, –]

lua_CFunction lua_atpanic (lua_State *L, lua_CFunction panicf);

Sets a new panic function and returns the old one (see §4.6).


lua_call

[-(nargs+1), +nresults, e]

void lua_call (lua_State *L, int nargs, int nresults);

Calls a function.

To call a function you must use the following protocol: first, the function to be called is pushed onto the stack; then, the arguments to the function are pushed in direct order; that is, the first argument is pushed first. Finally you call lua_call; nargs is the number of arguments that you pushed onto the stack. All arguments and the function value are popped from the stack when the function is called. The function results are pushed onto the stack when the function returns. The number of results is adjusted to nresults, unless nresults is LUA_MULTRET. In this case, all results from the function are pushed; Lua takes care that the returned values fit into the stack space, but it does not ensure any extra space in the stack. The function results are pushed onto the stack in direct order (the first result is pushed first), so that after the call the last result is on the top of the stack.

Any error inside the called function is propagated upwards (with a longjmp).

The following example shows how the host program can do the equivalent to this Lua code:

     a = f("how", t.x, 14)

Here it is in C:

     lua_getglobal(L, "f");                  /* function to be called */
     lua_pushliteral(L, "how");                       /* 1st argument */
     lua_getglobal(L, "t");                    /* table to be indexed */
     lua_getfield(L, -1, "x");        /* push result of t.x (2nd arg) */
     lua_remove(L, -2);                  /* remove 't' from the stack */
     lua_pushinteger(L, 14);                          /* 3rd argument */
     lua_call(L, 3, 1);     /* call 'f' with 3 arguments and 1 result */
     lua_setglobal(L, "a");                         /* set global 'a' */

Note that the code above is balanced: at its end, the stack is back to its original configuration. This is considered good programming practice.


lua_callk

[-(nargs + 1), +nresults, e]

void lua_callk (lua_State *L,
                int nargs,
                int nresults,
                lua_KContext ctx,
                lua_KFunction k);

This function behaves exactly like lua_call, but allows the called function to yield (see §4.7).


lua_CFunction

typedef int (*lua_CFunction) (lua_State *L);

Type for C functions.

In order to communicate properly with Lua, a C function must use the following protocol, which defines the way parameters and results are passed: a C function receives its arguments from Lua in its stack in direct order (the first argument is pushed first). So, when the function starts, lua_gettop(L) returns the number of arguments received by the function. The first argument (if any) is at index 1 and its last argument is at index lua_gettop(L). To return values to Lua, a C function just pushes them onto the stack, in direct order (the first result is pushed first), and returns the number of results. Any other value in the stack below the results will be properly discarded by Lua. Like a Lua function, a C function called by Lua can also return many results.

As an example, the following function receives a variable number of numeric arguments and returns their average and their sum:

     static int foo (lua_State *L) {
       int n = lua_gettop(L);    /* number of arguments */
       lua_Number sum = 0.0;
       int i;
       for (i = 1; i <= n; i++) {
         if (!lua_isnumber(L, i)) {
           lua_pushliteral(L, "incorrect argument");
           lua_error(L);
         }
         sum += lua_tonumber(L, i);
       }
       lua_pushnumber(L, sum/n);        /* first result */
       lua_pushnumber(L, sum);         /* second result */
       return 2;                   /* number of results */
     }

lua_checkstack

[-0, +0, –]

int lua_checkstack (lua_State *L, int n);

Ensures that the stack has space for at least n extra slots (that is, that you can safely push up to n values into it). It returns false if it cannot fulfill the request, either because it would cause the stack to be larger than a fixed maximum size (typically at least several thousand elements) or because it cannot allocate memory for the extra space. This function never shrinks the stack; if the stack already has space for the extra slots, it is left unchanged.


lua_close

[-0, +0, –]

void lua_close (lua_State *L);

Destroys all objects in the given Lua state (calling the corresponding garbage-collection metamethods, if any) and frees all dynamic memory used by this state. In several platforms, you may not need to call this function, because all resources are naturally released when the host program ends. On the other hand, long-running programs that create multiple states, such as daemons or web servers, will probably need to close states as soon as they are not needed.


lua_compare

[-0, +0, e]

int lua_compare (lua_State *L, int index1, int index2, int op);

Compares two Lua values. Returns 1 if the value at index index1 satisfies op when compared with the value at index index2, following the semantics of the corresponding Lua operator (that is, it may call metamethods). Otherwise returns 0. Also returns 0 if any of the indices is not valid.

The value of op must be one of the following constants:


lua_concat

[-n, +1, e]

void lua_concat (lua_State *L, int n);

Concatenates the n values at the top of the stack, pops them, and leaves the result at the top. If n is 1, the result is the single value on the stack (that is, the function does nothing); if n is 0, the result is the empty string. Concatenation is performed following the usual semantics of Lua (see §3.4.6).


lua_copy

[-0, +0, –]

void lua_copy (lua_State *L, int fromidx, int toidx);

Copies the element at index fromidx into the valid index toidx, replacing the value at that position. Values at other positions are not affected.


lua_createtable

[-0, +1, m]

void lua_createtable (lua_State *L, int narr, int nrec);

Creates a new empty table and pushes it onto the stack. Parameter narr is a hint for how many elements the table will have as a sequence; parameter nrec is a hint for how many other elements the table will have. Lua may use these hints to preallocate memory for the new table. This preallocation is useful for performance when you know in advance how many elements the table will have. Otherwise you can use the function lua_newtable.


lua_dump

[-0, +0, –]

int lua_dump (lua_State *L,
                        lua_Writer writer,
                        void *data,
                        int strip);

Dumps a function as a binary chunk. Receives a Lua function on the top of the stack and produces a binary chunk that, if loaded again, results in a function equivalent to the one dumped. As it produces parts of the chunk, lua_dump calls function writer (see lua_Writer) with the given data to write them.

If strip is true, the binary representation may not include all debug information about the function, to save space.

The value returned is the error code returned by the last call to the writer; 0 means no errors.

This function does not pop the Lua function from the stack.


lua_error

[-1, +0, v]

int lua_error (lua_State *L);

Generates a Lua error, using the value at the top of the stack as the error object. This function does a long jump, and therefore never returns (see luaL_error).


lua_gc

[-0, +0, m]

int lua_gc (lua_State *L, int what, int data);

Controls the garbage collector.

This function performs several tasks, according to the value of the parameter what:

  • LUA_GCSTOP: stops the garbage collector.
  • LUA_GCRESTART: restarts the garbage collector.
  • LUA_GCCOLLECT: performs a full garbage-collection cycle.
  • LUA_GCCOUNT: returns the current amount of memory (in Kbytes) in use by Lua.
  • LUA_GCCOUNTB: returns the remainder of dividing the current amount of bytes of memory in use by Lua by 1024.
  • LUA_GCSTEP: performs an incremental step of garbage collection.
  • LUA_GCSETPAUSE: sets data as the new value for the pause of the collector (see §2.5) and returns the previous value of the pause.
  • LUA_GCSETSTEPMUL: sets data as the new value for the step multiplier of the collector (see §2.5) and returns the previous value of the step multiplier.
  • LUA_GCISRUNNING: returns a boolean that tells whether the collector is running (i.e., not stopped).

For more details about these options, see collectgarbage.


lua_getallocf

[-0, +0, –]

lua_Alloc lua_getallocf (lua_State *L, void **ud);

Returns the memory-allocation function of a given state. If ud is not NULL, Lua stores in *ud the opaque pointer given when the memory-allocator function was set.


lua_getfield

[-0, +1, e]

int lua_getfield (lua_State *L, int index, const char *k);

Pushes onto the stack the value t[k], where t is the value at the given index. As in Lua, this function may trigger a metamethod for the "index" event (see §2.4).

Returns the type of the pushed value.


lua_getextraspace

[-0, +0, –]

void *lua_getextraspace (lua_State *L);

Returns a pointer to a raw memory area associated with the given Lua state. The application can use this area for any purpose; Lua does not use it for anything.

Each new thread has this area initialized with a copy of the area of the main thread.

By default, this area has the size of a pointer to void, but you can recompile Lua with a different size for this area. (See LUA_EXTRASPACE in luaconf.h.)


lua_getglobal

[-0, +1, e]

int lua_getglobal (lua_State *L, const char *name);

Pushes onto the stack the value of the global name. Returns the type of that value.


lua_geti

[-0, +1, e]

int lua_geti (lua_State *L, int index, lua_Integer i);

Pushes onto the stack the value t[i], where t is the value at the given index. As in Lua, this function may trigger a metamethod for the "index" event (see §2.4).

Returns the type of the pushed value.


lua_getmetatable

[-0, +(0|1), –]

int lua_getmetatable (lua_State *L, int index);

If the value at the given index has a metatable, the function pushes that metatable onto the stack and returns 1. Otherwise, the function returns 0 and pushes nothing on the stack.


lua_gettable

[-1, +1, e]

int lua_gettable (lua_State *L, int index);

Pushes onto the stack the value t[k], where t is the value at the given index and k is the value at the top of the stack.

This function pops the key from the stack, pushing the resulting value in its place. As in Lua, this function may trigger a metamethod for the "index" event (see §2.4).

Returns the type of the pushed value.


lua_gettop

[-0, +0, –]

int lua_gettop (lua_State *L);

Returns the index of the top element in the stack. Because indices start at 1, this result is equal to the number of elements in the stack; in particular, 0 means an empty stack.


lua_getuservalue

[-0, +1, –]

int lua_getuservalue (lua_State *L, int index);

Pushes onto the stack the Lua value associated with the full userdata at the given index.

Returns the type of the pushed value.


lua_insert

[-1, +1, –]

void lua_insert (lua_State *L, int index);

Moves the top element into the given valid index, shifting up the elements above this index to open space. This function cannot be called with a pseudo-index, because a pseudo-index is not an actual stack position.


lua_Integer

typedef ... lua_Integer;

The type of integers in Lua.

By default this type is long long, (usually a 64-bit two-complement integer), but that can be changed to long or int (usually a 32-bit two-complement integer). (See LUA_INT_TYPE in luaconf.h.)

Lua also defines the constants LUA_MININTEGER and LUA_MAXINTEGER, with the minimum and the maximum values that fit in this type.


lua_isboolean

[-0, +0, –]

int lua_isboolean (lua_State *L, int index);

Returns 1 if the value at the given index is a boolean, and 0 otherwise.


lua_iscfunction

[-0, +0, –]

int lua_iscfunction (lua_State *L, int index);

Returns 1 if the value at the given index is a C function, and 0 otherwise.


lua_isfunction

[-0, +0, –]

int lua_isfunction (lua_State *L, int index);

Returns 1 if the value at the given index is a function (either C or Lua), and 0 otherwise.


lua_isinteger

[-0, +0, –]

int lua_isinteger (lua_State *L, int index);

Returns 1 if the value at the given index is an integer (that is, the value is a number and is represented as an integer), and 0 otherwise.


lua_islightuserdata

[-0, +0, –]

int lua_islightuserdata (lua_State *L, int index);

Returns 1 if the value at the given index is a light userdata, and 0 otherwise.


lua_isnil

[-0, +0, –]

int lua_isnil (lua_State *L, int index);

Returns 1 if the value at the given index is nil, and 0 otherwise.


lua_isnone

[-0, +0, –]

int lua_isnone (lua_State *L, int index);

Returns 1 if the given index is not valid, and 0 otherwise.


lua_isnoneornil

[-0, +0, –]

int lua_isnoneornil (lua_State *L, int index);

Returns 1 if the given index is not valid or if the value at this index is nil, and 0 otherwise.


lua_isnumber

[-0, +0, –]

int lua_isnumber (lua_State *L, int index);

Returns 1 if the value at the given index is a number or a string convertible to a number, and 0 otherwise.


lua_isstring

[-0, +0, –]

int lua_isstring (lua_State *L, int index);

Returns 1 if the value at the given index is a string or a number (which is always convertible to a string), and 0 otherwise.


lua_istable

[-0, +0, –]

int lua_istable (lua_State *L, int index);

Returns 1 if the value at the given index is a table, and 0 otherwise.


lua_isthread

[-0, +0, –]

int lua_isthread (lua_State *L, int index);

Returns 1 if the value at the given index is a thread, and 0 otherwise.


lua_isuserdata

[-0, +0, –]

int lua_isuserdata (lua_State *L, int index);

Returns 1 if the value at the given index is a userdata (either full or light), and 0 otherwise.


lua_isyieldable

[-0, +0, –]

int lua_isyieldable (lua_State *L);

Returns 1 if the given coroutine can yield, and 0 otherwise.


lua_KContext

typedef ... lua_KContext;

The type for continuation-function contexts. It must be a numeric type. This type is defined as intptr_t when intptr_t is available, so that it can store pointers too. Otherwise, it is defined as ptrdiff_t.


lua_KFunction

typedef int (*lua_KFunction) (lua_State *L, int status, lua_KContext ctx);

Type for continuation functions (see §4.7).


lua_len

[-0, +1, e]

void lua_len (lua_State *L, int index);

Returns the length of the value at the given index. It is equivalent to the '#' operator in Lua (see §3.4.7) and may trigger a metamethod for the "length" event (see §2.4). The result is pushed on the stack.


lua_load

[-0, +1, –]

int lua_load (lua_State *L,
              lua_Reader reader,
              void *data,
              const char *chunkname,
              const char *mode);

Loads a Lua chunk without running it. If there are no errors, lua_load pushes the compiled chunk as a Lua function on top of the stack. Otherwise, it pushes an error message.

The return values of lua_load are:

  • LUA_OK: no errors;
  • LUA_ERRSYNTAX: syntax error during precompilation;
  • LUA_ERRMEM: memory allocation (out-of-memory) error;
  • LUA_ERRGCMM: error while running a __gc metamethod. (This error has no relation with the chunk being loaded. It is generated by the garbage collector.)

The lua_load function uses a user-supplied reader function to read the chunk (see lua_Reader). The data argument is an opaque value passed to the reader function.

The chunkname argument gives a name to the chunk, which is used for error messages and in debug information (see §4.9).

lua_load automatically detects whether the chunk is text or binary and loads it accordingly (see program luac). The string mode works as in function load, with the addition that a NULL value is equivalent to the string "bt".

lua_load uses the stack internally, so the reader function must always leave the stack unmodified when returning.

If the resulting function has upvalues, its first upvalue is set to the value of the global environment stored at index LUA_RIDX_GLOBALS in the registry (see §4.5). When loading main chunks, this upvalue will be the _ENV variable (see §2.2). Other upvalues are initialized with nil.


lua_newstate

[-0, +0, –]

lua_State *lua_newstate (lua_Alloc f, void *ud);

Creates a new thread running in a new, independent state. Returns NULL if it cannot create the thread or the state (due to lack of memory). The argument f is the allocator function; Lua does all memory allocation for this state through this function (see lua_Alloc). The second argument, ud, is an opaque pointer that Lua passes to the allocator in every call.


lua_newtable

[-0, +1, m]

void lua_newtable (lua_State *L);

Creates a new empty table and pushes it onto the stack. It is equivalent to lua_createtable(L, 0, 0).


lua_newthread

[-0, +1, m]

lua_State *lua_newthread (lua_State *L);

Creates a new thread, pushes it on the stack, and returns a pointer to a lua_State that represents this new thread. The new thread returned by this function shares with the original thread its global environment, but has an independent execution stack.

There is no explicit function to close or to destroy a thread. Threads are subject to garbage collection, like any Lua object.


lua_newuserdata

[-0, +1, m]

void *lua_newuserdata (lua_State *L, size_t size);

This function allocates a new block of memory with the given size, pushes onto the stack a new full userdata with the block address, and returns this address. The host program can freely use this memory.


lua_next

[-1, +(2|0), e]

int lua_next (lua_State *L, int index);

Pops a key from the stack, and pushes a key–value pair from the table at the given index (the "next" pair after the given key). If there are no more elements in the table, then lua_next returns 0 (and pushes nothing).

A typical traversal looks like this:

     /* table is in the stack at index 't' */
     lua_pushnil(L);  /* first key */
     while (lua_next(L, t) != 0) {
       /* uses 'key' (at index -2) and 'value' (at index -1) */
       printf("%s - %s\n",
              lua_typename(L, lua_type(L, -2)),
              lua_typename(L, lua_type(L, -1)));
       /* removes 'value'; keeps 'key' for next iteration */
       lua_pop(L, 1);
     }

While traversing a table, do not call lua_tolstring directly on a key, unless you know that the key is actually a string. Recall that lua_tolstring may change the value at the given index; this confuses the next call to lua_next.

See function next for the caveats of modifying the table during its traversal.


lua_Number

typedef ... lua_Number;

The type of floats in Lua.

By default this type is double, but that can be changed to a single float or a long double. (See LUA_FLOAT_TYPE in luaconf.h.)


lua_numbertointeger

int lua_numbertointeger (lua_Number n, lua_Integer *p);

Converts a Lua float to a Lua integer. This macro assumes that n has an integral value. If that value is within the range of Lua integers, it is converted to an integer and assigned to *p. The macro results in a boolean indicating whether the conversion was successful. (Note that this range test can be tricky to do correctly without this macro, due to roundings.)

This macro may evaluate its arguments more than once.


lua_pcall

[-(nargs + 1), +(nresults|1), –]

int lua_pcall (lua_State *L, int nargs, int nresults, int msgh);

Calls a function in protected mode.

Both nargs and nresults have the same meaning as in lua_call. If there are no errors during the call, lua_pcall behaves exactly like lua_call. However, if there is any error, lua_pcall catches it, pushes a single value on the stack (the error object), and returns an error code. Like lua_call, lua_pcall always removes the function and its arguments from the stack.

If msgh is 0, then the error object returned on the stack is exactly the original error object. Otherwise, msgh is the stack index of a message handler. (This index cannot be a pseudo-index.) In case of runtime errors, this function will be called with the error object and its return value will be the object returned on the stack by lua_pcall.

Typically, the message handler is used to add more debug information to the error object, such as a stack traceback. Such information cannot be gathered after the return of lua_pcall, since by then the stack has unwound.

The lua_pcall function returns one of the following constants (defined in lua.h):

  • LUA_OK (0): success.
  • LUA_ERRRUN: a runtime error.
  • LUA_ERRMEM: memory allocation error. For such errors, Lua does not call the message handler.
  • LUA_ERRERR: error while running the message handler.
  • LUA_ERRGCMM: error while running a __gc metamethod. For such errors, Lua does not call the message handler (as this kind of error typically has no relation with the function being called).

lua_pcallk

[-(nargs + 1), +(nresults|1), –]

int lua_pcallk (lua_State *L,
                int nargs,
                int nresults,
                int msgh,
                lua_KContext ctx,
                lua_KFunction k);

This function behaves exactly like lua_pcall, but allows the called function to yield (see §4.7).


lua_pop

[-n, +0, –]

void lua_pop (lua_State *L, int n);

Pops n elements from the stack.


lua_pushboolean

[-0, +1, –]

void lua_pushboolean (lua_State *L, int b);

Pushes a boolean value with value b onto the stack.


lua_pushcclosure

[-n, +1, m]

void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n);

Pushes a new C closure onto the stack.

When a C function is created, it is possible to associate some values with it, thus creating a C closure (see §4.4); these values are then accessible to the function whenever it is called. To associate values with a C function, first these values must be pushed onto the stack (when there are multiple values, the first value is pushed first). Then lua_pushcclosure is called to create and push the C function onto the stack, with the argument n telling how many values will be associated with the function. lua_pushcclosure also pops these values from the stack.

The maximum value for n is 255.

When n is zero, this function creates a light C function, which is just a pointer to the C function. In that case, it never raises a memory error.


lua_pushcfunction

[-0, +1, –]

void lua_pushcfunction (lua_State *L, lua_CFunction f);

Pushes a C function onto the stack. This function receives a pointer to a C function and pushes onto the stack a Lua value of type function that, when called, invokes the corresponding C function.

Any function to be callable by Lua must follow the correct protocol to receive its parameters and return its results (see lua_CFunction).


lua_pushfstring

[-0, +1, e]

const char *lua_pushfstring (lua_State *L, const char *fmt, ...);

Pushes onto the stack a formatted string and returns a pointer to this string. It is similar to the ISO C function sprintf, but has some important differences:

  • You do not have to allocate space for the result: the result is a Lua string and Lua takes care of memory allocation (and deallocation, through garbage collection).
  • The conversion specifiers are quite restricted. There are no flags, widths, or precisions. The conversion specifiers can only be '%%' (inserts the character '%'), '%s' (inserts a zero-terminated string, with no size restrictions), '%f' (inserts a lua_Number), '%I' (inserts a lua_Integer), '%p' (inserts a pointer as a hexadecimal numeral), '%d' (inserts an int), '%c' (inserts an int as a one-byte character), and '%U' (inserts a long int as a UTF-8 byte sequence).

Unlike other push functions, this function checks for the stack space it needs, including the slot for its result.


lua_pushglobaltable

[-0, +1, –]

void lua_pushglobaltable (lua_State *L);

Pushes the global environment onto the stack.


lua_pushinteger

[-0, +1, –]

void lua_pushinteger (lua_State *L, lua_Integer n);

Pushes an integer with value n onto the stack.


lua_pushlightuserdata

[-0, +1, –]

void lua_pushlightuserdata (lua_State *L, void *p);

Pushes a light userdata onto the stack.

Userdata represent C values in Lua. A light userdata represents a pointer, a void*. It is a value (like a number): you do not create it, it has no individual metatable, and it is not collected (as it was never created). A light userdata is equal to "any" light userdata with the same C address.


lua_pushliteral

[-0, +1, m]

const char *lua_pushliteral (lua_State *L, const char *s);

This macro is equivalent to lua_pushstring, but should be used only when s is a literal string.


lua_pushlstring

[-0, +1, m]

const char *lua_pushlstring (lua_State *L, const char *s, size_t len);

Pushes the string pointed to by s with size len onto the stack. Lua makes (or reuses) an internal copy of the given string, so the memory at s can be freed or reused immediately after the function returns. The string can contain any binary data, including embedded zeros.

Returns a pointer to the internal copy of the string.


lua_pushnil

[-0, +1, –]

void lua_pushnil (lua_State *L);

Pushes a nil value onto the stack.


lua_pushnumber

[-0, +1, –]

void lua_pushnumber (lua_State *L, lua_Number n);

Pushes a float with value n onto the stack.


lua_pushstring

[-0, +1, m]

const char *lua_pushstring (lua_State *L, const char *s);

Pushes the zero-terminated string pointed to by s onto the stack. Lua makes (or reuses) an internal copy of the given string, so the memory at s can be freed or reused immediately after the function returns.

Returns a pointer to the internal copy of the string.

If s is NULL, pushes nil and returns NULL.


lua_pushthread

[-0, +1, –]

int lua_pushthread (lua_State *L);

Pushes the thread represented by L onto the stack. Returns 1 if this thread is the main thread of its state.


lua_pushvalue

[-0, +1, –]

void lua_pushvalue (lua_State *L, int index);

Pushes a copy of the element at the given index onto the stack.


lua_pushvfstring

[-0, +1, m]

const char *lua_pushvfstring (lua_State *L,
                              const char *fmt,
                              va_list argp);

Equivalent to lua_pushfstring, except that it receives a va_list instead of a variable number of arguments.


lua_rawequal

[-0, +0, –]

int lua_rawequal (lua_State *L, int index1, int index2);

Returns 1 if the two values in indices index1 and index2 are primitively equal (that is, without calling the __eq metamethod). Otherwise returns 0. Also returns 0 if any of the indices are not valid.


lua_rawget

[-1, +1, –]

int lua_rawget (lua_State *L, int index);

Similar to lua_gettable, but does a raw access (i.e., without metamethods).


lua_rawgeti

[-0, +1, –]

int lua_rawgeti (lua_State *L, int index, lua_Integer n);

Pushes onto the stack the value t[n], where t is the table at the given index. The access is raw, that is, it does not invoke the __index metamethod.

Returns the type of the pushed value.


lua_rawgetp

[-0, +1, –]

int lua_rawgetp (lua_State *L, int index, const void *p);

Pushes onto the stack the value t[k], where t is the table at the given index and k is the pointer p represented as a light userdata. The access is raw; that is, it does not invoke the __index metamethod.

Returns the type of the pushed value.


lua_rawlen

[-0, +0, –]

size_t lua_rawlen (lua_State *L, int index);

Returns the raw "length" of the value at the given index: for strings, this is the string length; for tables, this is the result of the length operator ('#') with no metamethods; for userdata, this is the size of the block of memory allocated for the userdata; for other values, it is 0.


lua_rawset

[-2, +0, m]

void lua_rawset (lua_State *L, int index);

Similar to lua_settable, but does a raw assignment (i.e., without metamethods).


lua_rawseti

[-1, +0, m]

void lua_rawseti (lua_State *L, int index, lua_Integer i);

Does the equivalent of t[i] = v, where t is the table at the given index and v is the value at the top of the stack.

This function pops the value from the stack. The assignment is raw, that is, it does not invoke the __newindex metamethod.


lua_rawsetp

[-1, +0, m]

void lua_rawsetp (lua_State *L, int index, const void *p);

Does the equivalent of t[p] = v, where t is the table at the given index, p is encoded as a light userdata, and v is the value at the top of the stack.

This function pops the value from the stack. The assignment is raw, that is, it does not invoke __newindex metamethod.


lua_Reader

typedef const char * (*lua_Reader) (lua_State *L,
                                    void *data,
                                    size_t *size);

The reader function used by lua_load. Every time it needs another piece of the chunk, lua_load calls the reader, passing along its data parameter. The reader must return a pointer to a block of memory with a new piece of the chunk and set size to the block size. The block must exist until the reader function is called again. To signal the end of the chunk, the reader must return NULL or set size to zero. The reader function may return pieces of any size greater than zero.


lua_register

[-0, +0, e]

void lua_register (lua_State *L, const char *name, lua_CFunction f);

Sets the C function f as the new value of global name. It is defined as a macro:

     #define lua_register(L,n,f) \
            (lua_pushcfunction(L, f), lua_setglobal(L, n))

lua_remove

[-1, +0, –]

void lua_remove (lua_State *L, int index);

Removes the element at the given valid index, shifting down the elements above this index to fill the gap. This function cannot be called with a pseudo-index, because a pseudo-index is not an actual stack position.


lua_replace

[-1, +0, –]

void lua_replace (lua_State *L, int index);

Moves the top element into the given valid index without shifting any element (therefore replacing the value at that given index), and then pops the top element.


lua_resume

[-?, +?, –]

int lua_resume (lua_State *L, lua_State *from, int nargs);

Starts and resumes a coroutine in the given thread L.

To start a coroutine, you push onto the thread stack the main function plus any arguments; then you call lua_resume, with nargs being the number of arguments. This call returns when the coroutine suspends or finishes its execution. When it returns, the stack contains all values passed to lua_yield, or all values returned by the body function. lua_resume returns LUA_YIELD if the coroutine yields, LUA_OK if the coroutine finishes its execution without errors, or an error code in case of errors (see lua_pcall).

In case of errors, the stack is not unwound, so you can use the debug API over it. The error object is on the top of the stack.

To resume a coroutine, you remove any results from the last lua_yield, put on its stack only the values to be passed as results from yield, and then call lua_resume.

The parameter from represents the coroutine that is resuming L. If there is no such coroutine, this parameter can be NULL.


lua_rotate

[-0, +0, –]

void lua_rotate (lua_State *L, int idx, int n);

Rotates the stack elements between the valid index idx and the top of the stack. The elements are rotated n positions in the direction of the top, for a positive n, or -n positions in the direction of the bottom, for a negative n. The absolute value of n must not be greater than the size of the slice being rotated. This function cannot be called with a pseudo-index, because a pseudo-index is not an actual stack position.


lua_setallocf

[-0, +0, –]

void lua_setallocf (lua_State *L, lua_Alloc f, void *ud);

Changes the allocator function of a given state to f with user data ud.


lua_setfield

[-1, +0, e]

void lua_setfield (lua_State *L, int index, const char *k);

Does the equivalent to t[k] = v, where t is the value at the given index and v is the value at the top of the stack.

This function pops the value from the stack. As in Lua, this function may trigger a metamethod for the "newindex" event (see §2.4).


lua_setglobal

[-1, +0, e]

void lua_setglobal (lua_State *L, const char *name);

Pops a value from the stack and sets it as the new value of global name.


lua_seti

[-1, +0, e]

void lua_seti (lua_State *L, int index, lua_Integer n);

Does the equivalent to t[n] = v, where t is the value at the given index and v is the value at the top of the stack.

This function pops the value from the stack. As in Lua, this function may trigger a metamethod for the "newindex" event (see §2.4).


lua_setmetatable

[-1, +0, –]

void lua_setmetatable (lua_State *L, int index);

Pops a table from the stack and sets it as the new metatable for the value at the given index.


lua_settable

[-2, +0, e]

void lua_settable (lua_State *L, int index);

Does the equivalent to t[k] = v, where t is the value at the given index, v is the value at the top of the stack, and k is the value just below the top.

This function pops both the key and the value from the stack. As in Lua, this function may trigger a metamethod for the "newindex" event (see §2.4).


lua_settop

[-?, +?, –]

void lua_settop (lua_State *L, int index);

Accepts any index, or 0, and sets the stack top to this index. If the new top is larger than the old one, then the new elements are filled with nil. If index is 0, then all stack elements are removed.


lua_setuservalue

[-1, +0, –]

void lua_setuservalue (lua_State *L, int index);

Pops a value from the stack and sets it as the new value associated to the full userdata at the given index.


lua_State

typedef struct lua_State lua_State;

An opaque structure that points to a thread and indirectly (through the thread) to the whole state of a Lua interpreter. The Lua library is fully reentrant: it has no global variables. All information about a state is accessible through this structure.

A pointer to this structure must be passed as the first argument to every function in the library, except to lua_newstate, which creates a Lua state from scratch.


lua_status

[-0, +0, –]

int lua_status (lua_State *L);

Returns the status of the thread L.

The status can be 0 (LUA_OK) for a normal thread, an error code if the thread finished the execution of a lua_resume with an error, or LUA_YIELD if the thread is suspended.

You can only call functions in threads with status LUA_OK. You can resume threads with status LUA_OK (to start a new coroutine) or LUA_YIELD (to resume a coroutine).


lua_stringtonumber

[-0, +1, –]

size_t lua_stringtonumber (lua_State *L, const char *s);

Converts the zero-terminated string s to a number, pushes that number into the stack, and returns the total size of the string, that is, its length plus one. The conversion can result in an integer or a float, according to the lexical conventions of Lua (see §3.1). The string may have leading and trailing spaces and a sign. If the string is not a valid numeral, returns 0 and pushes nothing. (Note that the result can be used as a boolean, true if the conversion succeeds.)


lua_toboolean

[-0, +0, –]

int lua_toboolean (lua_State *L, int index);

Converts the Lua value at the given index to a C boolean value (0 or 1). Like all tests in Lua, lua_toboolean returns true for any Lua value different from false and nil; otherwise it returns false. (If you want to accept only actual boolean values, use lua_isboolean to test the value's type.)


lua_tocfunction

[-0, +0, –]

lua_CFunction lua_tocfunction (lua_State *L, int index);

Converts a value at the given index to a C function. That value must be a C function; otherwise, returns NULL.


lua_tointeger

[-0, +0, –]

lua_Integer lua_tointeger (lua_State *L, int index);

Equivalent to lua_tointegerx with isnum equal to NULL.


lua_tointegerx

[-0, +0, –]

lua_Integer lua_tointegerx (lua_State *L, int index, int *isnum);

Converts the Lua value at the given index to the signed integral type lua_Integer. The Lua value must be an integer, or a number or string convertible to an integer (see §3.4.3); otherwise, lua_tointegerx returns 0.

If isnum is not NULL, its referent is assigned a boolean value that indicates whether the operation succeeded.


lua_tolstring

[-0, +0, m]

const char *lua_tolstring (lua_State *L, int index, size_t *len);

Converts the Lua value at the given index to a C string. If len is not NULL, it sets *len with the string length. The Lua value must be a string or a number; otherwise, the function returns NULL. If the value is a number, then lua_tolstring also changes the actual value in the stack to a string. (This change confuses lua_next when lua_tolstring is applied to keys during a table traversal.)

lua_tolstring returns a pointer to a string inside the Lua state. This string always has a zero ('\0') after its last character (as in C), but can contain other zeros in its body.

Because Lua has garbage collection, there is no guarantee that the pointer returned by lua_tolstring will be valid after the corresponding Lua value is removed from the stack.


lua_tonumber

[-0, +0, –]

lua_Number lua_tonumber (lua_State *L, int index);

Equivalent to lua_tonumberx with isnum equal to NULL.


lua_tonumberx

[-0, +0, –]

lua_Number lua_tonumberx (lua_State *L, int index, int *isnum);

Converts the Lua value at the given index to the C type lua_Number (see lua_Number). The Lua value must be a number or a string convertible to a number (see §3.4.3); otherwise, lua_tonumberx returns 0.

If isnum is not NULL, its referent is assigned a boolean value that indicates whether the operation succeeded.


lua_topointer

[-0, +0, –]

const void *lua_topointer (lua_State *L, int index);

Converts the value at the given index to a generic C pointer (void*). The value can be a userdata, a table, a thread, or a function; otherwise, lua_topointer returns NULL. Different objects will give different pointers. There is no way to convert the pointer back to its original value.

Typically this function is used only for hashing and debug information.


lua_tostring

[-0, +0, m]

const char *lua_tostring (lua_State *L, int index);

Equivalent to lua_tolstring with len equal to NULL.


lua_tothread

[-0, +0, –]

lua_State *lua_tothread (lua_State *L, int index);

Converts the value at the given index to a Lua thread (represented as lua_State*). This value must be a thread; otherwise, the function returns NULL.


lua_touserdata

[-0, +0, –]

void *lua_touserdata (lua_State *L, int index);

If the value at the given index is a full userdata, returns its block address. If the value is a light userdata, returns its pointer. Otherwise, returns NULL.


lua_type

[-0, +0, –]

int lua_type (lua_State *L, int index);

Returns the type of the value in the given valid index, or LUA_TNONE for a non-valid (but acceptable) index. The types returned by lua_type are coded by the following constants defined in lua.h: LUA_TNIL (0), LUA_TNUMBER, LUA_TBOOLEAN, LUA_TSTRING, LUA_TTABLE, LUA_TFUNCTION, LUA_TUSERDATA, LUA_TTHREAD, and LUA_TLIGHTUSERDATA.


lua_typename

[-0, +0, –]

const char *lua_typename (lua_State *L, int tp);

Returns the name of the type encoded by the value tp, which must be one the values returned by lua_type.


lua_Unsigned

typedef ... lua_Unsigned;

The unsigned version of lua_Integer.


lua_upvalueindex

[-0, +0, –]

int lua_upvalueindex (int i);

Returns the pseudo-index that represents the i-th upvalue of the running function (see §4.4).


lua_version

[-0, +0, –]

const lua_Number *lua_version (lua_State *L);

Returns the address of the version number (a C static variable) stored in the Lua core. When called with a valid lua_State, returns the address of the version used to create that state. When called with NULL, returns the address of the version running the call.


lua_Writer

typedef int (*lua_Writer) (lua_State *L,
                           const void* p,
                           size_t sz,
                           void* ud);

The type of the writer function used by lua_dump. Every time it produces another piece of chunk, lua_dump calls the writer, passing along the buffer to be written (p), its size (sz), and the data parameter supplied to lua_dump.

The writer returns an error code: 0 means no errors; any other value means an error and stops lua_dump from calling the writer again.


lua_xmove

[-?, +?, –]

void lua_xmove (lua_State *from, lua_State *to, int n);

Exchange values between different threads of the same state.

This function pops n values from the stack from, and pushes them onto the stack to.


lua_yield

[-?, +?, e]

int lua_yield (lua_State *L, int nresults);

This function is equivalent to lua_yieldk, but it has no continuation (see §4.7). Therefore, when the thread resumes, it continues the function that called the function calling lua_yield.


lua_yieldk

[-?, +?, e]

int lua_yieldk (lua_State *L,
                int nresults,
                lua_KContext ctx,
                lua_KFunction k);

Yields a coroutine (thread).

When a C function calls lua_yieldk, the running coroutine suspends its execution, and the call to lua_resume that started this coroutine returns. The parameter nresults is the number of values from the stack that will be passed as results to lua_resume.

When the coroutine is resumed again, Lua calls the given continuation function k to continue the execution of the C function that yielded (see §4.7). This continuation function receives the same stack from the previous function, with the n results removed and replaced by the arguments passed to lua_resume. Moreover, the continuation function receives the value ctx that was passed to lua_yieldk.

Usually, this function does not return; when the coroutine eventually resumes, it continues executing the continuation function. However, there is one special case, which is when this function is called from inside a line or a count hook (see §4.9). In that case, lua_yieldk should be called with no continuation (probably in the form of lua_yield) and no results, and the hook should return immediately after the call. Lua will yield and, when the coroutine resumes again, it will continue the normal execution of the (Lua) function that triggered the hook.

This function can raise an error if it is called from a thread with a pending C call with no continuation function, or it is called from a thread that is not running inside a resume (e.g., the main thread).

4.9 – The Debug Interface

Lua has no built-in debugging facilities. Instead, it offers a special interface by means of functions and hooks. This interface allows the construction of different kinds of debuggers, profilers, and other tools that need "inside information" from the interpreter.


lua_Debug

typedef struct lua_Debug {
  int event;
  const char *name;           /* (n) */
  const char *namewhat;       /* (n) */
  const char *what;           /* (S) */
  const char *source;         /* (S) */
  int currentline;            /* (l) */
  int linedefined;            /* (S) */
  int lastlinedefined;        /* (S) */
  unsigned char nups;         /* (u) number of upvalues */
  unsigned char nparams;      /* (u) number of parameters */
  char isvararg;              /* (u) */
  char istailcall;            /* (t) */
  char short_src[LUA_IDSIZE]; /* (S) */
  /* private part */
  other fields
} lua_Debug;

A structure used to carry different pieces of information about a function or an activation record. lua_getstack fills only the private part of this structure, for later use. To fill the other fields of lua_Debug with useful information, call lua_getinfo.

The fields of lua_Debug have the following meaning:

  • source: the name of the chunk that created the function. If source starts with a '@', it means that the function was defined in a file where the file name follows the '@'. If source starts with a '=', the remainder of its contents describe the source in a user-dependent manner. Otherwise, the function was defined in a string where source is that string.
  • short_src: a "printable" version of source, to be used in error messages.
  • linedefined: the line number where the definition of the function starts.
  • lastlinedefined: the line number where the definition of the function ends.
  • what: the string "Lua" if the function is a Lua function, "C" if it is a C function, "main" if it is the main part of a chunk.
  • currentline: the current line where the given function is executing. When no line information is available, currentline is set to -1.
  • name: a reasonable name for the given function. Because functions in Lua are first-class values, they do not have a fixed name: some functions can be the value of multiple global variables, while others can be stored only in a table field. The lua_getinfo function checks how the function was called to find a suitable name. If it cannot find a name, then name is set to NULL.
  • namewhat: explains the name field. The value of namewhat can be "global", "local", "method", "field", "upvalue", or "" (the empty string), according to how the function was called. (Lua uses the empty string when no other option seems to apply.)
  • istailcall: true if this function invocation was called by a tail call. In this case, the caller of this level is not in the stack.
  • nups: the number of upvalues of the function.
  • nparams: the number of fixed parameters of the function (always 0 for C functions).
  • isvararg: true if the function is a vararg function (always true for C functions).

lua_gethook

[-0, +0, –]

lua_Hook lua_gethook (lua_State *L);

Returns the current hook function.


lua_gethookcount

[-0, +0, –]

int lua_gethookcount (lua_State *L);

Returns the current hook count.


lua_gethookmask

[-0, +0, –]

int lua_gethookmask (lua_State *L);

Returns the current hook mask.


lua_getinfo

[-(0|1), +(0|1|2), e]

int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar);

Gets information about a specific function or function invocation.

To get information about a function invocation, the parameter ar must be a valid activation record that was filled by a previous call to lua_getstack or given as argument to a hook (see lua_Hook).

To get information about a function, you push it onto the stack and start the what string with the character '>'. (In that case, lua_getinfo pops the function from the top of the stack.) For instance, to know in which line a function f was defined, you can write the following code:

     lua_Debug ar;
     lua_getglobal(L, "f");  /* get global 'f' */
     lua_getinfo(L, ">S", &ar);
     printf("%d\n", ar.linedefined);

Each character in the string what selects some fields of the structure ar to be filled or a value to be pushed on the stack:

  • 'n': fills in the field name and namewhat;
  • 'S': fills in the fields source, short_src, linedefined, lastlinedefined, and what;
  • 'l': fills in the field currentline;
  • 't': fills in the field istailcall;
  • 'u': fills in the fields nups, nparams, and isvararg;
  • 'f': pushes onto the stack the function that is running at the given level;
  • 'L': pushes onto the stack a table whose indices are the numbers of the lines that are valid on the function. (A valid line is a line with some associated code, that is, a line where you can put a break point. Non-valid lines include empty lines and comments.)

    If this option is given together with option 'f', its table is pushed after the function.

This function returns 0 on error (for instance, an invalid option in what).


lua_getlocal

[-0, +(0|1), –]

const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n);

Gets information about a local variable of a given activation record or a given function.

In the first case, the parameter ar must be a valid activation record that was filled by a previous call to lua_getstack or given as argument to a hook (see lua_Hook). The index n selects which local variable to inspect; see debug.getlocal for details about variable indices and names.

lua_getlocal pushes the variable's value onto the stack and returns its name.

In the second case, ar must be NULL and the function to be inspected must be at the top of the stack. In this case, only parameters of Lua functions are visible (as there is no information about what variables are active) and no values are pushed onto the stack.

Returns NULL (and pushes nothing) when the index is greater than the number of active local variables.


lua_getstack

[-0, +0, –]

int lua_getstack (lua_State *L, int level, lua_Debug *ar);

Gets information about the interpreter runtime stack.

This function fills parts of a lua_Debug structure with an identification of the activation record of the function executing at a given level. Level 0 is the current running function, whereas level n+1 is the function that has called level n (except for tail calls, which do not count on the stack). When there are no errors, lua_getstack returns 1; when called with a level greater than the stack depth, it returns 0.


lua_getupvalue

[-0, +(0|1), –]

const char *lua_getupvalue (lua_State *L, int funcindex, int n);

Gets information about the n-th upvalue of the closure at index funcindex. It pushes the upvalue's value onto the stack and returns its name. Returns NULL (and pushes nothing) when the index n is greater than the number of upvalues.

For C functions, this function uses the empty string "" as a name for all upvalues. (For Lua functions, upvalues are the external local variables that the function uses, and that are consequently included in its closure.)

Upvalues have no particular order, as they are active through the whole function. They are numbered in an arbitrary order.


lua_Hook

typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar);

Type for debugging hook functions.

Whenever a hook is called, its ar argument has its field event set to the specific event that triggered the hook. Lua identifies these events with the following constants: LUA_HOOKCALL, LUA_HOOKRET, LUA_HOOKTAILCALL, LUA_HOOKLINE, and LUA_HOOKCOUNT. Moreover, for line events, the field currentline is also set. To get the value of any other field in ar, the hook must call lua_getinfo.

For call events, event can be LUA_HOOKCALL, the normal value, or LUA_HOOKTAILCALL, for a tail call; in this case, there will be no corresponding return event.

While Lua is running a hook, it disables other calls to hooks. Therefore, if a hook calls back Lua to execute a function or a chunk, this execution occurs without any calls to hooks.

Hook functions cannot have continuations, that is, they cannot call lua_yieldk, lua_pcallk, or lua_callk with a non-null k.

Hook functions can yield under the following conditions: Only count and line events can yield; to yield, a hook function must finish its execution calling lua_yield with nresults equal to zero (that is, with no values).


lua_sethook

[-0, +0, –]

void lua_sethook (lua_State *L, lua_Hook f, int mask, int count);

Sets the debugging hook function.

Argument f is the hook function. mask specifies on which events the hook will be called: it is formed by a bitwise OR of the constants LUA_MASKCALL, LUA_MASKRET, LUA_MASKLINE, and LUA_MASKCOUNT. The count argument is only meaningful when the mask includes LUA_MASKCOUNT. For each event, the hook is called as explained below:

  • The call hook: is called when the interpreter calls a function. The hook is called just after Lua enters the new function, before the function gets its arguments.
  • The return hook: is called when the interpreter returns from a function. The hook is called just before Lua leaves the function. There is no standard way to access the values to be returned by the function.
  • The line hook: is called when the interpreter is about to start the execution of a new line of code, or when it jumps back in the code (even to the same line). (This event only happens while Lua is executing a Lua function.)
  • The count hook: is called after the interpreter executes every count instructions. (This event only happens while Lua is executing a Lua function.)

A hook is disabled by setting mask to zero.


lua_setlocal

[-(0|1), +0, –]

const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n);

Sets the value of a local variable of a given activation record. It assigns the value at the top of the stack to the variable and returns its name. It also pops the value from the stack.

Returns NULL (and pops nothing) when the index is greater than the number of active local variables.

Parameters ar and n are as in function lua_getlocal.


lua_setupvalue

[-(0|1), +0, –]

const char *lua_setupvalue (lua_State *L, int funcindex, int n);

Sets the value of a closure's upvalue. It assigns the value at the top of the stack to the upvalue and returns its name. It also pops the value from the stack.

Returns NULL (and pops nothing) when the index n is greater than the number of upvalues.

Parameters funcindex and n are as in function lua_getupvalue.


lua_upvalueid

[-0, +0, –]

void *lua_upvalueid (lua_State *L, int funcindex, int n);

Returns a unique identifier for the upvalue numbered n from the closure at index funcindex.

These unique identifiers allow a program to check whether different closures share upvalues. Lua closures that share an upvalue (that is, that access a same external local variable) will return identical ids for those upvalue indices.

Parameters funcindex and n are as in function lua_getupvalue, but n cannot be greater than the number of upvalues.


lua_upvaluejoin

[-0, +0, –]

void lua_upvaluejoin (lua_State *L, int funcindex1, int n1,
                                    int funcindex2, int n2);

Make the n1-th upvalue of the Lua closure at index funcindex1 refer to the n2-th upvalue of the Lua closure at index funcindex2.

5 – The Auxiliary Library

The auxiliary library provides several convenient functions to interface C with Lua. While the basic API provides the primitive functions for all interactions between C and Lua, the auxiliary library provides higher-level functions for some common tasks.

All functions and types from the auxiliary library are defined in header file lauxlib.h and have a prefix luaL_.

All functions in the auxiliary library are built on top of the basic API, and so they provide nothing that cannot be done with that API. Nevertheless, the use of the auxiliary library ensures more consistency to your code.

Several functions in the auxiliary library use internally some extra stack slots. When a function in the auxiliary library uses less than five slots, it does not check the stack size; it simply assumes that there are enough slots.

Several functions in the auxiliary library are used to check C function arguments. Because the error message is formatted for arguments (e.g., "bad argument #1"), you should not use these functions for other stack values.

Functions called luaL_check* always raise an error if the check is not satisfied.

5.1 – Functions and Types

Here we list all functions and types from the auxiliary library in alphabetical order.


luaL_addchar

[-?, +?, m]

void luaL_addchar (luaL_Buffer *B, char c);

Adds the byte c to the buffer B (see luaL_Buffer).


luaL_addlstring

[-?, +?, m]

void luaL_addlstring (luaL_Buffer *B, const char *s, size_t l);

Adds the string pointed to by s with length l to the buffer B (see luaL_Buffer). The string can contain embedded zeros.


luaL_addsize

[-?, +?, –]

void luaL_addsize (luaL_Buffer *B, size_t n);

Adds to the buffer B (see luaL_Buffer) a string of length n previously copied to the buffer area (see luaL_prepbuffer).


luaL_addstring

[-?, +?, m]

void luaL_addstring (luaL_Buffer *B, const char *s);

Adds the zero-terminated string pointed to by s to the buffer B (see luaL_Buffer).


luaL_addvalue

[-1, +?, m]

void luaL_addvalue (luaL_Buffer *B);

Adds the value at the top of the stack to the buffer B (see luaL_Buffer). Pops the value.

This is the only function on string buffers that can (and must) be called with an extra element on the stack, which is the value to be added to the buffer.


luaL_argcheck

[-0, +0, v]

void luaL_argcheck (lua_State *L,
                    int cond,
                    int arg,
                    const char *extramsg);

Checks whether cond is true. If it is not, raises an error with a standard message (see luaL_argerror).


luaL_argerror

[-0, +0, v]

int luaL_argerror (lua_State *L, int arg, const char *extramsg);

Raises an error reporting a problem with argument arg of the C function that called it, using a standard message that includes extramsg as a comment:

     bad argument #arg to 'funcname' (extramsg)

This function never returns.


luaL_Buffer

typedef struct luaL_Buffer luaL_Buffer;

Type for a string buffer.

A string buffer allows C code to build Lua strings piecemeal. Its pattern of use is as follows:

  • First declare a variable b of type luaL_Buffer.
  • Then initialize it with a call luaL_buffinit(L, &b).
  • Then add string pieces to the buffer calling any of the luaL_add* functions.
  • Finish by calling luaL_pushresult(&b). This call leaves the final string on the top of the stack.

If you know beforehand the total size of the resulting string, you can use the buffer like this:

  • First declare a variable b of type luaL_Buffer.
  • Then initialize it and preallocate a space of size sz with a call luaL_buffinitsize(L, &b, sz).
  • Then copy the string into that space.
  • Finish by calling luaL_pushresultsize(&b, sz), where sz is the total size of the resulting string copied into that space.

During its normal operation, a string buffer uses a variable number of stack slots. So, while using a buffer, you cannot assume that you know where the top of the stack is. You can use the stack between successive calls to buffer operations as long as that use is balanced; that is, when you call a buffer operation, the stack is at the same level it was immediately after the previous buffer operation. (The only exception to this rule is luaL_addvalue.) After calling luaL_pushresult the stack is back to its level when the buffer was initialized, plus the final string on its top.


luaL_buffinit

[-0, +0, –]

void luaL_buffinit (lua_State *L, luaL_Buffer *B);

Initializes a buffer B. This function does not allocate any space; the buffer must be declared as a variable (see luaL_Buffer).


luaL_buffinitsize

[-?, +?, m]

char *luaL_buffinitsize (lua_State *L, luaL_Buffer *B, size_t sz);

Equivalent to the sequence luaL_buffinit, luaL_prepbuffsize.


luaL_callmeta

[-0, +(0|1), e]

int luaL_callmeta (lua_State *L, int obj, const char *e);

Calls a metamethod.

If the object at index obj has a metatable and this metatable has a field e, this function calls this field passing the object as its only argument. In this case this function returns true and pushes onto the stack the value returned by the call. If there is no metatable or no metamethod, this function returns false (without pushing any value on the stack).


luaL_checkany

[-0, +0, v]

void luaL_checkany (lua_State *L, int arg);

Checks whether the function has an argument of any type (including nil) at position arg.


luaL_checkinteger

[-0, +0, v]

lua_Integer luaL_checkinteger (lua_State *L, int arg);

Checks whether the function argument arg is an integer (or can be converted to an integer) and returns this integer cast to a lua_Integer.


luaL_checklstring

[-0, +0, v]

const char *luaL_checklstring (lua_State *L, int arg, size_t *l);

Checks whether the function argument arg is a string and returns this string; if l is not NULL fills *l with the string's length.

This function uses lua_tolstring to get its result, so all conversions and caveats of that function apply here.


luaL_checknumber

[-0, +0, v]

lua_Number luaL_checknumber (lua_State *L, int arg);

Checks whether the function argument arg is a number and returns this number.


luaL_checkoption

[-0, +0, v]

int luaL_checkoption (lua_State *L,
                      int arg,
                      const char *def,
                      const char *const lst[]);

Checks whether the function argument arg is a string and searches for this string in the array lst (which must be NULL-terminated). Returns the index in the array where the string was found. Raises an error if the argument is not a string or if the string cannot be found.

If def is not NULL, the function uses def as a default value when there is no argument arg or when this argument is nil.

This is a useful function for mapping strings to C enums. (The usual convention in Lua libraries is to use strings instead of numbers to select options.)


luaL_checkstack

[-0, +0, v]

void luaL_checkstack (lua_State *L, int sz, const char *msg);

Grows the stack size to top + sz elements, raising an error if the stack cannot grow to that size. msg is an additional text to go into the error message (or NULL for no additional text).


luaL_checkstring

[-0, +0, v]

const char *luaL_checkstring (lua_State *L, int arg);

Checks whether the function argument arg is a string and returns this string.

This function uses lua_tolstring to get its result, so all conversions and caveats of that function apply here.


luaL_checktype

[-0, +0, v]

void luaL_checktype (lua_State *L, int arg, int t);

Checks whether the function argument arg has type t. See lua_type for the encoding of types for t.


luaL_checkudata

[-0, +0, v]

void *luaL_checkudata (lua_State *L, int arg, const char *tname);

Checks whether the function argument arg is a userdata of the type tname (see luaL_newmetatable) and returns the userdata address (see lua_touserdata).


luaL_checkversion

[-0, +0, v]

void luaL_checkversion (lua_State *L);

Checks whether the core running the call, the core that created the Lua state, and the code making the call are all using the same version of Lua. Also checks whether the core running the call and the core that created the Lua state are using the same address space.


luaL_dofile

[-0, +?, e]

int luaL_dofile (lua_State *L, const char *filename);

Loads and runs the given file. It is defined as the following macro:

     (luaL_loadfile(L, filename) || lua_pcall(L, 0, LUA_MULTRET, 0))

It returns false if there are no errors or true in case of errors.


luaL_dostring

[-0, +?, –]

int luaL_dostring (lua_State *L, const char *str);

Loads and runs the given string. It is defined as the following macro:

     (luaL_loadstring(L, str) || lua_pcall(L, 0, LUA_MULTRET, 0))

It returns false if there are no errors or true in case of errors.


luaL_error

[-0, +0, v]

int luaL_error (lua_State *L, const char *fmt, ...);

Raises an error. The error message format is given by fmt plus any extra arguments, following the same rules of lua_pushfstring. It also adds at the beginning of the message the file name and the line number where the error occurred, if this information is available.

This function never returns, but it is an idiom to use it in C functions as return luaL_error(args).


luaL_execresult

[-0, +3, m]

int luaL_execresult (lua_State *L, int stat);

This function produces the return values for process-related functions in the standard library (os.execute and io.close).


luaL_fileresult

[-0, +(1|3), m]

int luaL_fileresult (lua_State *L, int stat, const char *fname);

This function produces the return values for file-related functions in the standard library (io.open, os.rename, file:seek, etc.).


luaL_getmetafield

[-0, +(0|1), m]

int luaL_getmetafield (lua_State *L, int obj, const char *e);

Pushes onto the stack the field e from the metatable of the object at index obj and returns the type of the pushed value. If the object does not have a metatable, or if the metatable does not have this field, pushes nothing and returns LUA_TNIL.


luaL_getmetatable

[-0, +1, m]

int luaL_getmetatable (lua_State *L, const char *tname);

Pushes onto the stack the metatable associated with name tname in the registry (see luaL_newmetatable) (nil if there is no metatable associated with that name). Returns the type of the pushed value.


luaL_getsubtable

[-0, +1, e]

int luaL_getsubtable (lua_State *L, int idx, const char *fname);

Ensures that the value t[fname], where t is the value at index idx, is a table, and pushes that table onto the stack. Returns true if it finds a previous table there and false if it creates a new table.


luaL_gsub

[-0, +1, m]

const char *luaL_gsub (lua_State *L,
                       const char *s,
                       const char *p,
                       const char *r);

Creates a copy of string s by replacing any occurrence of the string p with the string r. Pushes the resulting string on the stack and returns it.


luaL_len

[-0, +0, e]

lua_Integer luaL_len (lua_State *L, int index);

Returns the "length" of the value at the given index as a number; it is equivalent to the '#' operator in Lua (see §3.4.7). Raises an error if the result of the operation is not an integer. (This case only can happen through metamethods.)


luaL_loadbuffer

[-0, +1, –]

int luaL_loadbuffer (lua_State *L,
                     const char *buff,
                     size_t sz,
                     const char *name);

Equivalent to luaL_loadbufferx with mode equal to NULL.


luaL_loadbufferx

[-0, +1, –]

int luaL_loadbufferx (lua_State *L,
                      const char *buff,
                      size_t sz,
                      const char *name,
                      const char *mode);

Loads a buffer as a Lua chunk. This function uses lua_load to load the chunk in the buffer pointed to by buff with size sz.

This function returns the same results as lua_load. name is the chunk name, used for debug information and error messages. The string mode works as in function lua_load.


luaL_loadfile

[-0, +1, m]

int luaL_loadfile (lua_State *L, const char *filename);

Equivalent to luaL_loadfilex with mode equal to NULL.


luaL_loadfilex

[-0, +1, m]

int luaL_loadfilex (lua_State *L, const char *filename,
                                            const char *mode);

Loads a file as a Lua chunk. This function uses lua_load to load the chunk in the file named filename. If filename is NULL, then it loads from the standard input. The first line in the file is ignored if it starts with a #.

The string mode works as in function lua_load.

This function returns the same results as lua_load, but it has an extra error code LUA_ERRFILE for file-related errors (e.g., it cannot open or read the file).

As lua_load, this function only loads the chunk; it does not run it.


luaL_loadstring

[-0, +1, –]

int luaL_loadstring (lua_State *L, const char *s);

Loads a string as a Lua chunk. This function uses lua_load to load the chunk in the zero-terminated string s.

This function returns the same results as lua_load.

Also as lua_load, this function only loads the chunk; it does not run it.


luaL_newlib

[-0, +1, m]

void luaL_newlib (lua_State *L, const luaL_Reg l[]);

Creates a new table and registers there the functions in list l.

It is implemented as the following macro:

     (luaL_newlibtable(L,l), luaL_setfuncs(L,l,0))

The array l must be the actual array, not a pointer to it.


luaL_newlibtable

[-0, +1, m]

void luaL_newlibtable (lua_State *L, const luaL_Reg l[]);

Creates a new table with a size optimized to store all entries in the array l (but does not actually store them). It is intended to be used in conjunction with luaL_setfuncs (see luaL_newlib).

It is implemented as a macro. The array l must be the actual array, not a pointer to it.


luaL_newmetatable

[-0, +1, m]

int luaL_newmetatable (lua_State *L, const char *tname);

If the registry already has the key tname, returns 0. Otherwise, creates a new table to be used as a metatable for userdata, adds to this new table the pair __name = tname, adds to the registry the pair [tname] = new table, and returns 1. (The entry __name is used by some error-reporting functions.)

In both cases pushes onto the stack the final value associated with tname in the registry.


luaL_newstate

[-0, +0, –]

lua_State *luaL_newstate (void);

Creates a new Lua state. It calls lua_newstate with an allocator based on the standard C realloc function and then sets a panic function (see §4.6) that prints an error message to the standard error output in case of fatal errors.

Returns the new state, or NULL if there is a memory allocation error.


luaL_openlibs

[-0, +0, e]

void luaL_openlibs (lua_State *L);

Opens all standard Lua libraries into the given state.


luaL_opt

[-0, +0, e]

T luaL_opt (L, func, arg, dflt);

This macro is defined as follows:

     (lua_isnoneornil(L,(arg)) ? (dflt) : func(L,(arg)))

In words, if the argument arg is nil or absent, the macro results in the default dflt. Otherwise, it results in the result of calling func with the state L and the argument index arg as arguments. Note that it evaluates the expression dflt only if needed.


luaL_optinteger

[-0, +0, v]

lua_Integer luaL_optinteger (lua_State *L,
                             int arg,
                             lua_Integer d);

If the function argument arg is an integer (or convertible to an integer), returns this integer. If this argument is absent or is nil, returns d. Otherwise, raises an error.


luaL_optlstring

[-0, +0, v]

const char *luaL_optlstring (lua_State *L,
                             int arg,
                             const char *d,
                             size_t *l);

If the function argument arg is a string, returns this string. If this argument is absent or is nil, returns d. Otherwise, raises an error.

If l is not NULL, fills the position *l with the result's length. If the result is NULL (only possible when returning d and d == NULL), its length is considered zero.

This function uses lua_tolstring to get its result, so all conversions and caveats of that function apply here.


luaL_optnumber

[-0, +0, v]

lua_Number luaL_optnumber (lua_State *L, int arg, lua_Number d);

If the function argument arg is a number, returns this number. If this argument is absent or is nil, returns d. Otherwise, raises an error.


luaL_optstring

[-0, +0, v]

const char *luaL_optstring (lua_State *L,
                            int arg,
                            const char *d);

If the function argument arg is a string, returns this string. If this argument is absent or is nil, returns d. Otherwise, raises an error.


luaL_prepbuffer

[-?, +?, m]

char *luaL_prepbuffer (luaL_Buffer *B);

Equivalent to luaL_prepbuffsize with the predefined size LUAL_BUFFERSIZE.


luaL_prepbuffsize

[-?, +?, m]

char *luaL_prepbuffsize (luaL_Buffer *B, size_t sz);

Returns an address to a space of size sz where you can copy a string to be added to buffer B (see luaL_Buffer). After copying the string into this space you must call luaL_addsize with the size of the string to actually add it to the buffer.


luaL_pushresult

[-?, +1, m]

void luaL_pushresult (luaL_Buffer *B);

Finishes the use of buffer B leaving the final string on the top of the stack.


luaL_pushresultsize

[-?, +1, m]

void luaL_pushresultsize (luaL_Buffer *B, size_t sz);

Equivalent to the sequence luaL_addsize, luaL_pushresult.


luaL_ref

[-1, +0, m]

int luaL_ref (lua_State *L, int t);

Creates and returns a reference, in the table at index t, for the object at the top of the stack (and pops the object).

A reference is a unique integer key. As long as you do not manually add integer keys into table t, luaL_ref ensures the uniqueness of the key it returns. You can retrieve an object referred by reference r by calling lua_rawgeti(L, t, r). Function luaL_unref frees a reference and its associated object.

If the object at the top of the stack is nil, luaL_ref returns the constant LUA_REFNIL. The constant LUA_NOREF is guaranteed to be different from any reference returned by luaL_ref.


luaL_Reg

typedef struct luaL_Reg {
  const char *name;
  lua_CFunction func;
} luaL_Reg;

Type for arrays of functions to be registered by luaL_setfuncs. name is the function name and func is a pointer to the function. Any array of luaL_Reg must end with a sentinel entry in which both name and func are NULL.


luaL_requiref

[-0, +1, e]

void luaL_requiref (lua_State *L, const char *modname,
                    lua_CFunction openf, int glb);

If modname is not already present in package.loaded, calls function openf with string modname as an argument and sets the call result in package.loaded[modname], as if that function has been called through require.

If glb is true, also stores the module into global modname.

Leaves a copy of the module on the stack.


luaL_setfuncs

[-nup, +0, m]

void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup);

Registers all functions in the array l (see luaL_Reg) into the table on the top of the stack (below optional upvalues, see next).

When nup is not zero, all functions are created sharing nup upvalues, which must be previously pushed on the stack on top of the library table. These values are popped from the stack after the registration.


luaL_setmetatable

[-0, +0, –]

void luaL_setmetatable (lua_State *L, const char *tname);

Sets the metatable of the object at the top of the stack as the metatable associated with name tname in the registry (see luaL_newmetatable).


luaL_Stream

typedef struct luaL_Stream {
  FILE *f;
  lua_CFunction closef;
} luaL_Stream;

The standard representation for file handles, which is used by the standard I/O library.

A file handle is implemented as a full userdata, with a metatable called LUA_FILEHANDLE (where LUA_FILEHANDLE is a macro with the actual metatable's name). The metatable is created by the I/O library (see luaL_newmetatable).

This userdata must start with the structure luaL_Stream; it can contain other data after this initial structure. Field f points to the corresponding C stream (or it can be NULL to indicate an incompletely created handle). Field closef points to a Lua function that will be called to close the stream when the handle is closed or collected; this function receives the file handle as its sole argument and must return either true (in case of success) or nil plus an error message (in case of error). Once Lua calls this field, it changes the field value to NULL to signal that the handle is closed.


luaL_testudata

[-0, +0, m]

void *luaL_testudata (lua_State *L, int arg, const char *tname);

This function works like luaL_checkudata, except that, when the test fails, it returns NULL instead of raising an error.


luaL_tolstring

[-0, +1, e]

const char *luaL_tolstring (lua_State *L, int idx, size_t *len);

Converts any Lua value at the given index to a C string in a reasonable format. The resulting string is pushed onto the stack and also returned by the function. If len is not NULL, the function also sets *len with the string length.

If the value has a metatable with a __tostring field, then luaL_tolstring calls the corresponding metamethod with the value as argument, and uses the result of the call as its result.


luaL_traceback

[-0, +1, m]

void luaL_traceback (lua_State *L, lua_State *L1, const char *msg,
                     int level);

Creates and pushes a traceback of the stack L1. If msg is not NULL it is appended at the beginning of the traceback. The level parameter tells at which level to start the traceback.


luaL_typename

[-0, +0, –]

const char *luaL_typename (lua_State *L, int index);

Returns the name of the type of the value at the given index.


luaL_unref

[-0, +0, –]

void luaL_unref (lua_State *L, int t, int ref);

Releases reference ref from the table at index t (see luaL_ref). The entry is removed from the table, so that the referred object can be collected. The reference ref is also freed to be used again.

If ref is LUA_NOREF or LUA_REFNIL, luaL_unref does nothing.


luaL_where

[-0, +1, m]

void luaL_where (lua_State *L, int lvl);

Pushes onto the stack a string identifying the current position of the control at level lvl in the call stack. Typically this string has the following format:

     chunkname:currentline:

Level 0 is the running function, level 1 is the function that called the running function, etc.

This function is used to build a prefix for error messages.

6 – Standard Libraries

The standard Lua libraries provide useful functions that are implemented directly through the C API. Some of these functions provide essential services to the language (e.g., type and getmetatable); others provide access to "outside" services (e.g., I/O); and others could be implemented in Lua itself, but are quite useful or have critical performance requirements that deserve an implementation in C (e.g., table.sort).

All libraries are implemented through the official C API and are provided as separate C modules. Currently, Lua has the following standard libraries:

  • basic library (§6.1);
  • coroutine library (§6.2);
  • package library (§6.3);
  • string manipulation (§6.4);
  • basic UTF-8 support (§6.5);
  • table manipulation (§6.6);
  • mathematical functions (§6.7) (sin, log, etc.);
  • input and output (§6.8);
  • operating system facilities (§6.9);
  • debug facilities (§6.10).

Except for the basic and the package libraries, each library provides all its functions as fields of a global table or as methods of its objects.

To have access to these libraries, the C host program should call the luaL_openlibs function, which opens all standard libraries. Alternatively, the host program can open them individually by using luaL_requiref to call luaopen_base (for the basic library), luaopen_package (for the package library), luaopen_coroutine (for the coroutine library), luaopen_string (for the string library), luaopen_utf8 (for the UTF8 library), luaopen_table (for the table library), luaopen_math (for the mathematical library), luaopen_io (for the I/O library), luaopen_os (for the operating system library), and luaopen_debug (for the debug library). These functions are declared in lualib.h.

6.1 – Basic Functions

The basic library provides core functions to Lua. If you do not include this library in your application, you should check carefully whether you need to provide implementations for some of its facilities.


assert (v [, message])

Calls error if the value of its argument v is false (i.e., nil or false); otherwise, returns all its arguments. In case of error, message is the error object; when absent, it defaults to "assertion failed!"


collectgarbage ([opt [, arg]])

This function is a generic interface to the garbage collector. It performs different functions according to its first argument, opt:

  • "collect": performs a full garbage-collection cycle. This is the default option.
  • "stop": stops automatic execution of the garbage collector. The collector will run only when explicitly invoked, until a call to restart it.
  • "restart": restarts automatic execution of the garbage collector.
  • "count": returns the total memory in use by Lua in Kbytes. The value has a fractional part, so that it multiplied by 1024 gives the exact number of bytes in use by Lua (except for overflows).
  • "step": performs a garbage-collection step. The step "size" is controlled by arg. With a zero value, the collector will perform one basic (indivisible) step. For non-zero values, the collector will perform as if that amount of memory (in KBytes) had been allocated by Lua. Returns true if the step finished a collection cycle.
  • "setpause": sets arg as the new value for the pause of the collector (see §2.5). Returns the previous value for pause.
  • "setstepmul": sets arg as the new value for the step multiplier of the collector (see §2.5). Returns the previous value for step.
  • "isrunning": returns a boolean that tells whether the collector is running (i.e., not stopped).


dofile ([filename])

Opens the named file and executes its contents as a Lua chunk. When called without arguments, dofile executes the contents of the standard input (stdin). Returns all values returned by the chunk. In case of errors, dofile propagates the error to its caller (that is, dofile does not run in protected mode).


error (message [, level])

Terminates the last protected function called and returns message as the error object. Function error never returns.

Usually, error adds some information about the error position at the beginning of the message, if the message is a string. The level argument specifies how to get the error position. With level 1 (the default), the error position is where the error function was called. Level 2 points the error to where the function that called error was called; and so on. Passing a level 0 avoids the addition of error position information to the message.


_G

A global variable (not a function) that holds the global environment (see §2.2). Lua itself does not use this variable; changing its value does not affect any environment, nor vice versa.


getmetatable (object)

If object does not have a metatable, returns nil. Otherwise, if the object's metatable has a __metatable field, returns the associated value. Otherwise, returns the metatable of the given object.


ipairs (t)

Returns three values (an iterator function, the table t, and 0) so that the construction

     for i,v in ipairs(t) do body end

will iterate over the key–value pairs (1,t[1]), (2,t[2]), ..., up to the first nil value.


load (chunk [, chunkname [, mode [, env]]])

Loads a chunk.

If chunk is a string, the chunk is this string. If chunk is a function, load calls it repeatedly to get the chunk pieces. Each call to chunk must return a string that concatenates with previous results. A return of an empty string, nil, or no value signals the end of the chunk.

If there are no syntactic errors, returns the compiled chunk as a function; otherwise, returns nil plus the error message.

If the resulting function has upvalues, the first upvalue is set to the value of env, if that parameter is given, or to the value of the global environment. Other upvalues are initialized with nil. (When you load a main chunk, the resulting function will always have exactly one upvalue, the _ENV variable (see §2.2). However, when you load a binary chunk created from a function (see string.dump), the resulting function can have an arbitrary number of upvalues.) All upvalues are fresh, that is, they are not shared with any other function.

chunkname is used as the name of the chunk for error messages and debug information (see §4.9). When absent, it defaults to chunk, if chunk is a string, or to "=(load)" otherwise.

The string mode controls whether the chunk can be text or binary (that is, a precompiled chunk). It may be the string "b" (only binary chunks), "t" (only text chunks), or "bt" (both binary and text). The default is "bt".

Lua does not check the consistency of binary chunks. Maliciously crafted binary chunks can crash the interpreter.


loadfile ([filename [, mode [, env]]])

Similar to load, but gets the chunk from file filename or from the standard input, if no file name is given.


next (table [, index])

Allows a program to traverse all fields of a table. Its first argument is a table and its second argument is an index in this table. next returns the next index of the table and its associated value. When called with nil as its second argument, next returns an initial index and its associated value. When called with the last index, or with nil in an empty table, next returns nil. If the second argument is absent, then it is interpreted as nil. In particular, you can use next(t) to check whether a table is empty.

The order in which the indices are enumerated is not specified, even for numeric indices. (To traverse a table in numerical order, use a numerical for.)

The behavior of next is undefined if, during the traversal, you assign any value to a non-existent field in the table. You may however modify existing fields. In particular, you may clear existing fields.


pairs (t)

If t has a metamethod __pairs, calls it with t as argument and returns the first three results from the call.

Otherwise, returns three values: the next function, the table t, and nil, so that the construction

     for k,v in pairs(t) do body end

will iterate over all key–value pairs of table t.

See function next for the caveats of modifying the table during its traversal.


pcall (f [, arg1, ···])

Calls function f with the given arguments in protected mode. This means that any error inside f is not propagated; instead, pcall catches the error and returns a status code. Its first result is the status code (a boolean), which is true if the call succeeds without errors. In such case, pcall also returns all results from the call, after this first result. In case of any error, pcall returns false plus the error message.


print (···)

Receives any number of arguments and prints their values to stdout, using the tostring function to convert each argument to a string. print is not intended for formatted output, but only as a quick way to show a value, for instance for debugging. For complete control over the output, use string.format and io.write.


rawequal (v1, v2)

Checks whether v1 is equal to v2, without invoking the __eq metamethod. Returns a boolean.


rawget (table, index)

Gets the real value of table[index], without invoking the __index metamethod. table must be a table; index may be any value.


rawlen (v)

Returns the length of the object v, which must be a table or a string, without invoking the __len metamethod. Returns an integer.


rawset (table, index, value)

Sets the real value of table[index] to value, without invoking the __newindex metamethod. table must be a table, index any value different from nil and NaN, and value any Lua value.

This function returns table.


select (index, ···)

If index is a number, returns all arguments after argument number index; a negative number indexes from the end (-1 is the last argument). Otherwise, index must be the string "#", and select returns the total number of extra arguments it received.


setmetatable (table, metatable)

Sets the metatable for the given table. (To change the metatable of other types from Lua code, you must use the debug library (§6.10).) If metatable is nil, removes the metatable of the given table. If the original metatable has a __metatable field, raises an error.

This function returns table.


tonumber (e [, base])

When called with no base, tonumber tries to convert its argument to a number. If the argument is already a number or a string convertible to a number, then tonumber returns this number; otherwise, it returns nil.

The conversion of strings can result in integers or floats, according to the lexical conventions of Lua (see §3.1). (The string may have leading and trailing spaces and a sign.)

When called with base, then e must be a string to be interpreted as an integer numeral in that base. The base may be any integer between 2 and 36, inclusive. In bases above 10, the letter 'A' (in either upper or lower case) represents 10, 'B' represents 11, and so forth, with 'Z' representing 35. If the string e is not a valid numeral in the given base, the function returns nil.


tostring (v)

Receives a value of any type and converts it to a string in a human-readable format. (For complete control of how numbers are converted, use string.format.)

If the metatable of v has a __tostring field, then tostring calls the corresponding value with v as argument, and uses the result of the call as its result.


type (v)

Returns the type of its only argument, coded as a string. The possible results of this function are "nil" (a string, not the value nil), "number", "string", "boolean", "table", "function", "thread", and "userdata".


_VERSION

A global variable (not a function) that holds a string containing the running Lua version. The current value of this variable is "Lua 5.3".


xpcall (f, msgh [, arg1, ···])

This function is similar to pcall, except that it sets a new message handler msgh.

6.2 – Coroutine Manipulation

This library comprises the operations to manipulate coroutines, which come inside the table coroutine. See §2.6 for a general description of coroutines.


coroutine.create (f)

Creates a new coroutine, with body f. f must be a function. Returns this new coroutine, an object with type "thread".


coroutine.isyieldable ()

Returns true when the running coroutine can yield.

A running coroutine is yieldable if it is not the main thread and it is not inside a non-yieldable C function.


coroutine.resume (co [, val1, ···])

Starts or continues the execution of coroutine co. The first time you resume a coroutine, it starts running its body. The values val1, ... are passed as the arguments to the body function. If the coroutine has yielded, resume restarts it; the values val1, ... are passed as the results from the yield.

If the coroutine runs without any errors, resume returns true plus any values passed to yield (when the coroutine yields) or any values returned by the body function (when the coroutine terminates). If there is any error, resume returns false plus the error message.


coroutine.running ()

Returns the running coroutine plus a boolean, true when the running coroutine is the main one.


coroutine.status (co)

Returns the status of coroutine co, as a string: "running", if the coroutine is running (that is, it called status); "suspended", if the coroutine is suspended in a call to yield, or if it has not started running yet; "normal" if the coroutine is active but not running (that is, it has resumed another coroutine); and "dead" if the coroutine has finished its body function, or if it has stopped with an error.


coroutine.wrap (f)

Creates a new coroutine, with body f. f must be a function. Returns a function that resumes the coroutine each time it is called. Any arguments passed to the function behave as the extra arguments to resume. Returns the same values returned by resume, except the first boolean. In case of error, propagates the error.


coroutine.yield (···)

Suspends the execution of the calling coroutine. Any arguments to yield are passed as extra results to resume.

6.3 – Modules

The package library provides basic facilities for loading modules in Lua. It exports one function directly in the global environment: require. Everything else is exported in a table package.


require (modname)

Loads the given module. The function starts by looking into the package.loaded table to determine whether modname is already loaded. If it is, then require returns the value stored at package.loaded[modname]. Otherwise, it tries to find a loader for the module.

To find a loader, require is guided by the package.searchers sequence. By changing this sequence, we can change how require looks for a module. The following explanation is based on the default configuration for package.searchers.

First require queries package.preload[modname]. If it has a value, this value (which must be a function) is the loader. Otherwise require searches for a Lua loader using the path stored in package.path. If that also fails, it searches for a C loader using the path stored in package.cpath. If that also fails, it tries an all-in-one loader (see package.searchers).

Once a loader is found, require calls the loader with two arguments: modname and an extra value dependent on how it got the loader. (If the loader came from a file, this extra value is the file name.) If the loader returns any non-nil value, require assigns the returned value to package.loaded[modname]. If the loader does not return a non-nil value and has not assigned any value to package.loaded[modname], then require assigns true to this entry. In any case, require returns the final value of package.loaded[modname].

If there is any error loading or running the module, or if it cannot find any loader for the module, then require raises an error.


package.config

A string describing some compile-time configurations for packages. This string is a sequence of lines:

  • The first line is the directory separator string. Default is '\' for Windows and '/' for all other systems.
  • The second line is the character that separates templates in a path. Default is ';'.
  • The third line is the string that marks the substitution points in a template. Default is '?'.
  • The fourth line is a string that, in a path in Windows, is replaced by the executable's directory. Default is '!'.
  • The fifth line is a mark to ignore all text after it when building the luaopen_ function name. Default is '-'.


package.cpath

The path used by require to search for a C loader.

Lua initializes the C path package.cpath in the same way it initializes the Lua path package.path, using the environment variable LUA_CPATH_5_3, or the environment variable LUA_CPATH, or a default path defined in luaconf.h.


package.loaded

A table used by require to control which modules are already loaded. When you require a module modname and package.loaded[modname] is not false, require simply returns the value stored there.

This variable is only a reference to the real table; assignments to this variable do not change the table used by require.


package.loadlib (libname, funcname)

Dynamically links the host program with the C library libname.

If funcname is "*", then it only links with the library, making the symbols exported by the library available to other dynamically linked libraries. Otherwise, it looks for a function funcname inside the library and returns this function as a C function. So, funcname must follow the lua_CFunction prototype (see lua_CFunction).

This is a low-level function. It completely bypasses the package and module system. Unlike require, it does not perform any path searching and does not automatically adds extensions. libname must be the complete file name of the C library, including if necessary a path and an extension. funcname must be the exact name exported by the C library (which may depend on the C compiler and linker used).

This function is not supported by Standard C. As such, it is only available on some platforms (Windows, Linux, Mac OS X, Solaris, BSD, plus other Unix systems that support the dlfcn standard).


package.path

The path used by require to search for a Lua loader.

At start-up, Lua initializes this variable with the value of the environment variable LUA_PATH_5_3 or the environment variable LUA_PATH or with a default path defined in luaconf.h, if those environment variables are not defined. Any ";;" in the value of the environment variable is replaced by the default path.


package.preload

A table to store loaders for specific modules (see require).

This variable is only a reference to the real table; assignments to this variable do not change the table used by require.


package.searchers

A table used by require to control how to load modules.

Each entry in this table is a searcher function. When looking for a module, require calls each of these searchers in ascending order, with the module name (the argument given to require) as its sole parameter. The function can return another function (the module loader) plus an extra value that will be passed to that loader, or a string explaining why it did not find that module (or nil if it has nothing to say).

Lua initializes this table with four searcher functions.

The first searcher simply looks for a loader in the package.preload table.

The second searcher looks for a loader as a Lua library, using the path stored at package.path. The search is done as described in function package.searchpath.

The third searcher looks for a loader as a C library, using the path given by the variable package.cpath. Again, the search is done as described in function package.searchpath. For instance, if the C path is the string

     "./?.so;./?.dll;/usr/local/?/init.so"

the searcher for module foo will try to open the files ./foo.so, ./foo.dll, and /usr/local/foo/init.so, in that order. Once it finds a C library, this searcher first uses a dynamic link facility to link the application with the library. Then it tries to find a C function inside the library to be used as the loader. The name of this C function is the string "luaopen_" concatenated with a copy of the module name where each dot is replaced by an underscore. Moreover, if the module name has a hyphen, its suffix after (and including) the first hyphen is removed. For instance, if the module name is a.b.c-v2.1, the function name will be luaopen_a_b_c.

The fourth searcher tries an all-in-one loader. It searches the C path for a library for the root name of the given module. For instance, when requiring a.b.c, it will search for a C library for a. If found, it looks into it for an open function for the submodule; in our example, that would be luaopen_a_b_c. With this facility, a package can pack several C submodules into one single library, with each submodule keeping its original open function.

All searchers except the first one (preload) return as the extra value the file name where the module was found, as returned by package.searchpath. The first searcher returns no extra value.


package.searchpath (name, path [, sep [, rep]])

Searches for the given name in the given path.

A path is a string containing a sequence of templates separated by semicolons. For each template, the function replaces each interrogation mark (if any) in the template with a copy of name wherein all occurrences of sep (a dot, by default) were replaced by rep (the system's directory separator, by default), and then tries to open the resulting file name.

For instance, if the path is the string

     "./?.lua;./?.lc;/usr/local/?/init.lua"

the search for the name foo.a will try to open the files ./foo/a.lua, ./foo/a.lc, and /usr/local/foo/a/init.lua, in that order.

Returns the resulting name of the first file that it can open in read mode (after closing the file), or nil plus an error message if none succeeds. (This error message lists all file names it tried to open.)

6.4 – String Manipulation

This library provides generic functions for string manipulation, such as finding and extracting substrings, and pattern matching. When indexing a string in Lua, the first character is at position 1 (not at 0, as in C). Indices are allowed to be negative and are interpreted as indexing backwards, from the end of the string. Thus, the last character is at position -1, and so on.

The string library provides all its functions inside the table string. It also sets a metatable for strings where the __index field points to the string table. Therefore, you can use the string functions in object-oriented style. For instance, string.byte(s,i) can be written as s:byte(i).

The string library assumes one-byte character encodings.


string.byte (s [, i [, j]])

Returns the internal numeric codes of the characters s[i], s[i+1], ..., s[j]. The default value for i is 1; the default value for j is i. These indices are corrected following the same rules of function string.sub.

Numeric codes are not necessarily portable across platforms.


string.char (···)

Receives zero or more integers. Returns a string with length equal to the number of arguments, in which each character has the internal numeric code equal to its corresponding argument.

Numeric codes are not necessarily portable across platforms.


string.dump (function [, strip])

Returns a string containing a binary representation (a binary chunk) of the given function, so that a later load on this string returns a copy of the function (but with new upvalues). If strip is a true value, the binary representation may not include all debug information about the function, to save space.

Functions with upvalues have only their number of upvalues saved. When (re)loaded, those upvalues receive fresh instances containing nil. (You can use the debug library to serialize and reload the upvalues of a function in a way adequate to your needs.)


string.find (s, pattern [, init [, plain]])

Looks for the first match of pattern (see §6.4.1) in the string s. If it finds a match, then find returns the indices of s where this occurrence starts and ends; otherwise, it returns nil. A third, optional numeric argument init specifies where to start the search; its default value is 1 and can be negative. A value of true as a fourth, optional argument plain turns off the pattern matching facilities, so the function does a plain "find substring" operation, with no characters in pattern being considered magic. Note that if plain is given, then init must be given as well.

If the pattern has captures, then in a successful match the captured values are also returned, after the two indices.


string.format (formatstring, ···)

Returns a formatted version of its variable number of arguments following the description given in its first argument (which must be a string). The format string follows the same rules as the ISO C function sprintf. The only differences are that the options/modifiers *, h, L, l, n, and p are not supported and that there is an extra option, q.

The q option formats a string between double quotes, using escape sequences when necessary to ensure that it can safely be read back by the Lua interpreter. For instance, the call

     string.format('%q', 'a string with "quotes" and \n new line')

may produce the string:

     "a string with \"quotes\" and \
      new line"

Options A, a, E, e, f, G, and g all expect a number as argument. Options c, d, i, o, u, X, and x expect an integer. When Lua is compiled with a C89 compiler, options A and a (hexadecimal floats) do not support any modifier (flags, width, length).

Option s expects a string; if its argument is not a string, it is converted to one following the same rules of tostring. If the option has any modifier (flags, width, length), the string argument should not contain embedded zeros.


string.gmatch (s, pattern)

Returns an iterator function that, each time it is called, returns the next captures from pattern (see §6.4.1) over the string s. If pattern specifies no captures, then the whole match is produced in each call.

As an example, the following loop will iterate over all the words from string s, printing one per line:

     s = "hello world from Lua"
     for w in string.gmatch(s, "%a+") do
       print(w)
     end

The next example collects all pairs key=value from the given string into a table:

     t = {}
     s = "from=world, to=Lua"
     for k, v in string.gmatch(s, "(%w+)=(%w+)") do
       t[k] = v
     end

For this function, a caret '^' at the start of a pattern does not work as an anchor, as this would prevent the iteration.


string.gsub (s, pattern, repl [, n])

Returns a copy of s in which all (or the first n, if given) occurrences of the pattern (see §6.4.1) have been replaced by a replacement string specified by repl, which can be a string, a table, or a function. gsub also returns, as its second value, the total number of matches that occurred. The name gsub comes from Global SUBstitution.

If repl is a string, then its value is used for replacement. The character % works as an escape character: any sequence in repl of the form %d, with d between 1 and 9, stands for the value of the d-th captured substring. The sequence %0 stands for the whole match. The sequence %% stands for a single %.

If repl is a table, then the table is queried for every match, using the first capture as the key.

If repl is a function, then this function is called every time a match occurs, with all captured substrings passed as arguments, in order.

In any case, if the pattern specifies no captures, then it behaves as if the whole pattern was inside a capture.

If the value returned by the table query or by the function call is a string or a number, then it is used as the replacement string; otherwise, if it is false or nil, then there is no replacement (that is, the original match is kept in the string).

Here are some examples:

     x = string.gsub("hello world", "(%w+)", "%1 %1")
     --> x="hello hello world world"
     
     x = string.gsub("hello world", "%w+", "%0 %0", 1)
     --> x="hello hello world"
     
     x = string.gsub("hello world from Lua", "(%w+)%s*(%w+)", "%2 %1")
     --> x="world hello Lua from"
     
     x = string.gsub("home = $HOME, user = $USER", "%$(%w+)", os.getenv)
     --> x="home = /home/roberto, user = roberto"
     
     x = string.gsub("4+5 = $return 4+5$", "%$(.-)%$", function (s)
           return load(s)()
         end)
     --> x="4+5 = 9"
     
     local t = {name="lua", version="5.3"}
     x = string.gsub("$name-$version.tar.gz", "%$(%w+)", t)
     --> x="lua-5.3.tar.gz"


string.len (s)

Receives a string and returns its length. The empty string "" has length 0. Embedded zeros are counted, so "a\000bc\000" has length 5.


string.lower (s)

Receives a string and returns a copy of this string with all uppercase letters changed to lowercase. All other characters are left unchanged. The definition of what an uppercase letter is depends on the current locale.


string.match (s, pattern [, init])

Looks for the first match of pattern (see §6.4.1) in the string s. If it finds one, then match returns the captures from the pattern; otherwise it returns nil. If pattern specifies no captures, then the whole match is returned. A third, optional numeric argument init specifies where to start the search; its default value is 1 and can be negative.


string.pack (fmt, v1, v2, ···)

Returns a binary string containing the values v1, v2, etc. packed (that is, serialized in binary form) according to the format string fmt (see §6.4.2).


string.packsize (fmt)

Returns the size of a string resulting from string.pack with the given format. The format string cannot have the variable-length options 's' or 'z' (see §6.4.2).


string.rep (s, n [, sep])

Returns a string that is the concatenation of n copies of the string s separated by the string sep. The default value for sep is the empty string (that is, no separator). Returns the empty string if n is not positive.

(Note that it is very easy to exhaust the memory of your machine with a single call to this function.)


string.reverse (s)

Returns a string that is the string s reversed.


string.sub (s, i [, j])

Returns the substring of s that starts at i and continues until j; i and j can be negative. If j is absent, then it is assumed to be equal to -1 (which is the same as the string length). In particular, the call string.sub(s,1,j) returns a prefix of s with length j, and string.sub(s, -i) (for a positive i) returns a suffix of s with length i.

If, after the translation of negative indices, i is less than 1, it is corrected to 1. If j is greater than the string length, it is corrected to that length. If, after these corrections, i is greater than j, the function returns the empty string.


string.unpack (fmt, s [, pos])

Returns the values packed in string s (see string.pack) according to the format string fmt (see §6.4.2). An optional pos marks where to start reading in s (default is 1). After the read values, this function also returns the index of the first unread byte in s.


string.upper (s)

Receives a string and returns a copy of this string with all lowercase letters changed to uppercase. All other characters are left unchanged. The definition of what a lowercase letter is depends on the current locale.

6.4.1 – Patterns

Patterns in Lua are described by regular strings, which are interpreted as patterns by the pattern-matching functions string.find, string.gmatch, string.gsub, and string.match. This section describes the syntax and the meaning (that is, what they match) of these strings.

Character Class:

A character class is used to represent a set of characters. The following combinations are allowed in describing a character class:

  • x: (where x is not one of the magic characters ^$()%.[]*+-?) represents the character x itself.
  • .: (a dot) represents all characters.
  • %a: represents all letters.
  • %c: represents all control characters.
  • %d: represents all digits.
  • %g: represents all printable characters except space.
  • %l: represents all lowercase letters.
  • %p: represents all punctuation characters.
  • %s: represents all space characters.
  • %u: represents all uppercase letters.
  • %w: represents all alphanumeric characters.
  • %x: represents all hexadecimal digits.
  • %x: (where x is any non-alphanumeric character) represents the character x. This is the standard way to escape the magic characters. Any non-alphanumeric character (including all punctuation characters, even the non-magical) can be preceded by a '%' when used to represent itself in a pattern.
  • [set]: represents the class which is the union of all characters in set. A range of characters can be specified by separating the end characters of the range, in ascending order, with a '-'. All classes %x described above can also be used as components in set. All other characters in set represent themselves. For example, [%w_] (or [_%w]) represents all alphanumeric characters plus the underscore, [0-7] represents the octal digits, and [0-7%l%-] represents the octal digits plus the lowercase letters plus the '-' character.

    You can put a closing square bracket in a set by positioning it as the first character in the set. You can put a hyphen in a set by positioning it as the first or the last character in the set. (You can also use an escape for both cases.)

    The interaction between ranges and classes is not defined. Therefore, patterns like [%a-z] or [a-%%] have no meaning.

  • [^set]: represents the complement of set, where set is interpreted as above.

For all classes represented by single letters (%a, %c, etc.), the corresponding uppercase letter represents the complement of the class. For instance, %S represents all non-space characters.

The definitions of letter, space, and other character groups depend on the current locale. In particular, the class [a-z] may not be equivalent to %l.

Pattern Item:

A pattern item can be

  • a single character class, which matches any single character in the class;
  • a single character class followed by '*', which matches zero or more repetitions of characters in the class. These repetition items will always match the longest possible sequence;
  • a single character class followed by '+', which matches one or more repetitions of characters in the class. These repetition items will always match the longest possible sequence;
  • a single character class followed by '-', which also matches zero or more repetitions of characters in the class. Unlike '*', these repetition items will always match the shortest possible sequence;
  • a single character class followed by '?', which matches zero or one occurrence of a character in the class. It always matches one occurrence if possible;
  • %n, for n between 1 and 9; such item matches a substring equal to the n-th captured string (see below);
  • %bxy, where x and y are two distinct characters; such item matches strings that start with x, end with y, and where the x and y are balanced. This means that, if one reads the string from left to right, counting +1 for an x and -1 for a y, the ending y is the first y where the count reaches 0. For instance, the item %b() matches expressions with balanced parentheses.
  • %f[set], a frontier pattern; such item matches an empty string at any position such that the next character belongs to set and the previous character does not belong to set. The set set is interpreted as previously described. The beginning and the end of the subject are handled as if they were the character '\0'.

Pattern:

A pattern is a sequence of pattern items. A caret '^' at the beginning of a pattern anchors the match at the beginning of the subject string. A '$' at the end of a pattern anchors the match at the end of the subject string. At other positions, '^' and '$' have no special meaning and represent themselves.

Captures:

A pattern can contain sub-patterns enclosed in parentheses; they describe captures. When a match succeeds, the substrings of the subject string that match captures are stored (captured) for future use. Captures are numbered according to their left parentheses. For instance, in the pattern "(a*(.)%w(%s*))", the part of the string matching "a*(.)%w(%s*)" is stored as the first capture (and therefore has number 1); the character matching "." is captured with number 2, and the part matching "%s*" has number 3.

As a special case, the empty capture () captures the current string position (a number). For instance, if we apply the pattern "()aa()" on the string "flaaap", there will be two captures: 3 and 5.

6.4.2 – Format Strings for Pack and Unpack

The first argument to string.pack, string.packsize, and string.unpack is a format string, which describes the layout of the structure being created or read.

A format string is a sequence of conversion options. The conversion options are as follows:

  • <: sets little endian
  • >: sets big endian
  • =: sets native endian
  • ![n]: sets maximum alignment to n (default is native alignment)
  • b: a signed byte (char)
  • B: an unsigned byte (char)
  • h: a signed short (native size)
  • H: an unsigned short (native size)
  • l: a signed long (native size)
  • L: an unsigned long (native size)
  • j: a lua_Integer
  • J: a lua_Unsigned
  • T: a size_t (native size)
  • i[n]: a signed int with n bytes (default is native size)
  • I[n]: an unsigned int with n bytes (default is native size)
  • f: a float (native size)
  • d: a double (native size)
  • n: a lua_Number
  • cn: a fixed-sized string with n bytes
  • z: a zero-terminated string
  • s[n]: a string preceded by its length coded as an unsigned integer with n bytes (default is a size_t)
  • x: one byte of padding
  • Xop: an empty item that aligns according to option op (which is otherwise ignored)
  • ' ': (empty space) ignored

(A "[n]" means an optional integral numeral.) Except for padding, spaces, and configurations (options "xX <=>!"), each option corresponds to an argument (in string.pack) or a result (in string.unpack).

For options "!n", "sn", "in", and "In", n can be any integer between 1 and 16. All integral options check overflows; string.pack checks whether the given value fits in the given size; string.unpack checks whether the read value fits in a Lua integer.

Any format string starts as if prefixed by "!1=", that is, with maximum alignment of 1 (no alignment) and native endianness.

Alignment works as follows: For each option, the format gets extra padding until the data starts at an offset that is a multiple of the minimum between the option size and the maximum alignment; this minimum must be a power of 2. Options "c" and "z" are not aligned; option "s" follows the alignment of its starting integer.

All padding is filled with zeros by string.pack (and ignored by string.unpack).

6.5 – UTF-8 Support

This library provides basic support for UTF-8 encoding. It provides all its functions inside the table utf8. This library does not provide any support for Unicode other than the handling of the encoding. Any operation that needs the meaning of a character, such as character classification, is outside its scope.

Unless stated otherwise, all functions that expect a byte position as a parameter assume that the given position is either the start of a byte sequence or one plus the length of the subject string. As in the string library, negative indices count from the end of the string.


utf8.char (···)

Receives zero or more integers, converts each one to its corresponding UTF-8 byte sequence and returns a string with the concatenation of all these sequences.


utf8.charpattern

The pattern (a string, not a function) "[\0-\x7F\xC2-\xF4][\x80-\xBF]*" (see §6.4.1), which matches exactly one UTF-8 byte sequence, assuming that the subject is a valid UTF-8 string.


utf8.codes (s)

Returns values so that the construction

     for p, c in utf8.codes(s) do body end

will iterate over all characters in string s, with p being the position (in bytes) and c the code point of each character. It raises an error if it meets any invalid byte sequence.


utf8.codepoint (s [, i [, j]])

Returns the codepoints (as integers) from all characters in s that start between byte position i and j (both included). The default for i is 1 and for j is i. It raises an error if it meets any invalid byte sequence.


utf8.len (s [, i [, j]])

Returns the number of UTF-8 characters in string s that start between positions i and j (both inclusive). The default for i is 1 and for j is -1. If it finds any invalid byte sequence, returns a false value plus the position of the first invalid byte.


utf8.offset (s, n [, i])

Returns the position (in bytes) where the encoding of the n-th character of s (counting from position i) starts. A negative n gets characters before position i. The default for i is 1 when n is non-negative and #s + 1 otherwise, so that utf8.offset(s, -n) gets the offset of the n-th character from the end of the string. If the specified character is neither in the subject nor right after its end, the function returns nil.

As a special case, when n is 0 the function returns the start of the encoding of the character that contains the i-th byte of s.

This function assumes that s is a valid UTF-8 string.

6.6 – Table Manipulation

This library provides generic functions for table manipulation. It provides all its functions inside the table table.

Remember that, whenever an operation needs the length of a table, all caveats about the length operator apply (see §3.4.7). All functions ignore non-numeric keys in the tables given as arguments.


table.concat (list [, sep [, i [, j]]])

Given a list where all elements are strings or numbers, returns the string list[i]..sep..list[i+1] ··· sep..list[j]. The default value for sep is the empty string, the default for i is 1, and the default for j is #list. If i is greater than j, returns the empty string.


table.insert (list, [pos,] value)

Inserts element value at position pos in list, shifting up the elements list[pos], list[pos+1], ···, list[#list]. The default value for pos is #list+1, so that a call table.insert(t,x) inserts x at the end of list t.


table.move (a1, f, e, t [,a2])

Moves elements from table a1 to table a2, performing the equivalent to the following multiple assignment: a2[t],··· = a1[f],···,a1[e]. The default for a2 is a1. The destination range can overlap with the source range. The number of elements to be moved must fit in a Lua integer.

Returns the destination table a2.


table.pack (···)

Returns a new table with all arguments stored into keys 1, 2, etc. and with a field "n" with the total number of arguments. Note that the resulting table may not be a sequence.


table.remove (list [, pos])

Removes from list the element at position pos, returning the value of the removed element. When pos is an integer between 1 and #list, it shifts down the elements list[pos+1], list[pos+2], ···, list[#list] and erases element list[#list]; The index pos can also be 0 when #list is 0, or #list + 1; in those cases, the function erases the element list[pos].

The default value for pos is #list, so that a call table.remove(l) removes the last element of list l.


table.sort (list [, comp])

Sorts list elements in a given order, in-place, from list[1] to list[#list]. If comp is given, then it must be a function that receives two list elements and returns true when the first element must come before the second in the final order (so that, after the sort, i < j implies not comp(list[j],list[i])). If comp is not given, then the standard Lua operator < is used instead.

Note that the comp function must define a strict partial order over the elements in the list; that is, it must be asymmetric and transitive. Otherwise, no valid sort may be possible.

The sort algorithm is not stable: elements considered equal by the given order may have their relative positions changed by the sort.


table.unpack (list [, i [, j]])

Returns the elements from the given list. This function is equivalent to

     return list[i], list[i+1], ···, list[j]

By default, i is 1 and j is #list.

6.7 – Mathematical Functions

This library provides basic mathematical functions. It provides all its functions and constants inside the table math. Functions with the annotation "integer/float" give integer results for integer arguments and float results for float (or mixed) arguments. Rounding functions (math.ceil, math.floor, and math.modf) return an integer when the result fits in the range of an integer, or a float otherwise.


math.abs (x)

Returns the absolute value of x. (integer/float)


math.acos (x)

Returns the arc cosine of x (in radians).


math.asin (x)

Returns the arc sine of x (in radians).


math.atan (y [, x])

Returns the arc tangent of y/x (in radians), but uses the signs of both arguments to find the quadrant of the result. (It also handles correctly the case of x being zero.)

The default value for x is 1, so that the call math.atan(y) returns the arc tangent of y.


math.ceil (x)

Returns the smallest integral value larger than or equal to x.


math.cos (x)

Returns the cosine of x (assumed to be in radians).


math.deg (x)

Converts the angle x from radians to degrees.


math.exp (x)

Returns the value ex (where e is the base of natural logarithms).


math.floor (x)

Returns the largest integral value smaller than or equal to x.


math.fmod (x, y)

Returns the remainder of the division of x by y that rounds the quotient towards zero. (integer/float)


math.huge

The float value HUGE_VAL, a value larger than any other numeric value.


math.log (x [, base])

Returns the logarithm of x in the given base. The default for base is e (so that the function returns the natural logarithm of x).


math.max (x, ···)

Returns the argument with the maximum value, according to the Lua operator <. (integer/float)


math.maxinteger

An integer with the maximum value for an integer.


math.min (x, ···)

Returns the argument with the minimum value, according to the Lua operator <. (integer/float)


math.mininteger

An integer with the minimum value for an integer.


math.modf (x)

Returns the integral part of x and the fractional part of x. Its second result is always a float.


math.pi

The value of π.


math.rad (x)

Converts the angle x from degrees to radians.


math.random ([m [, n]])

When called without arguments, returns a pseudo-random float with uniform distribution in the range [0,1). When called with two integers m and n, math.random returns a pseudo-random integer with uniform distribution in the range [m, n]. (The value n-m cannot be negative and must fit in a Lua integer.) The call math.random(n) is equivalent to math.random(1,n).

This function is an interface to the underling pseudo-random generator function provided by C.


math.randomseed (x)

Sets x as the "seed" for the pseudo-random generator: equal seeds produce equal sequences of numbers.


math.sin (x)

Returns the sine of x (assumed to be in radians).


math.sqrt (x)

Returns the square root of x. (You can also use the expression x^0.5 to compute this value.)


math.tan (x)

Returns the tangent of x (assumed to be in radians).


math.tointeger (x)

If the value x is convertible to an integer, returns that integer. Otherwise, returns nil.


math.type (x)

Returns "integer" if x is an integer, "float" if it is a float, or nil if x is not a number.


math.ult (m, n)

Returns a boolean, true if and only if integer m is below integer n when they are compared as unsigned integers.

6.8 – Input and Output Facilities

The I/O library provides two different styles for file manipulation. The first one uses implicit file handles; that is, there are operations to set a default input file and a default output file, and all input/output operations are over these default files. The second style uses explicit file handles.

When using implicit file handles, all operations are supplied by table io. When using explicit file handles, the operation io.open returns a file handle and then all operations are supplied as methods of the file handle.

The table io also provides three predefined file handles with their usual meanings from C: io.stdin, io.stdout, and io.stderr. The I/O library never closes these files.

Unless otherwise stated, all I/O functions return nil on failure (plus an error message as a second result and a system-dependent error code as a third result) and some value different from nil on success. In non-POSIX systems, the computation of the error message and error code in case of errors may be not thread safe, because they rely on the global C variable errno.


io.close ([file])

Equivalent to file:close(). Without a file, closes the default output file.


io.flush ()

Equivalent to io.output():flush().


io.input ([file])

When called with a file name, it opens the named file (in text mode), and sets its handle as the default input file. When called with a file handle, it simply sets this file handle as the default input file. When called without arguments, it returns the current default input file.

In case of errors this function raises the error, instead of returning an error code.


io.lines ([filename, ···])

Opens the given file name in read mode and returns an iterator function that works like file:lines(···) over the opened file. When the iterator function detects the end of file, it returns no values (to finish the loop) and automatically closes the file.

The call io.lines() (with no file name) is equivalent to io.input():lines("*l"); that is, it iterates over the lines of the default input file. In this case, the iterator does not close the file when the loop ends.

In case of errors this function raises the error, instead of returning an error code.


io.open (filename [, mode])

This function opens a file, in the mode specified in the string mode. In case of success, it returns a new file handle.

The mode string can be any of the following:

  • "r": read mode (the default);
  • "w": write mode;
  • "a": append mode;
  • "r+": update mode, all previous data is preserved;
  • "w+": update mode, all previous data is erased;
  • "a+": append update mode, previous data is preserved, writing is only allowed at the end of file.

The mode string can also have a 'b' at the end, which is needed in some systems to open the file in binary mode.


io.output ([file])

Similar to io.input, but operates over the default output file.


io.popen (prog [, mode])

This function is system dependent and is not available on all platforms.

Starts program prog in a separated process and returns a file handle that you can use to read data from this program (if mode is "r", the default) or to write data to this program (if mode is "w").


io.read (···)

Equivalent to io.input():read(···).


io.tmpfile ()

In case of success, returns a handle for a temporary file. This file is opened in update mode and it is automatically removed when the program ends.


io.type (obj)

Checks whether obj is a valid file handle. Returns the string "file" if obj is an open file handle, "closed file" if obj is a closed file handle, or nil if obj is not a file handle.


io.write (···)

Equivalent to io.output():write(···).


file:close ()

Closes file. Note that files are automatically closed when their handles are garbage collected, but that takes an unpredictable amount of time to happen.

When closing a file handle created with io.popen, file:close returns the same values returned by os.execute.


file:flush ()

Saves any written data to file.


file:lines (···)

Returns an iterator function that, each time it is called, reads the file according to the given formats. When no format is given, uses "l" as a default. As an example, the construction

     for c in file:lines(1) do body end

will iterate over all characters of the file, starting at the current position. Unlike io.lines, this function does not close the file when the loop ends.

In case of errors this function raises the error, instead of returning an error code.


file:read (···)

Reads the file file, according to the given formats, which specify what to read. For each format, the function returns a string or a number with the characters read, or nil if it cannot read data with the specified format. (In this latter case, the function does not read subsequent formats.) When called without formats, it uses a default format that reads the next line (see below).

The available formats are

  • "n": reads a numeral and returns it as a float or an integer, following the lexical conventions of Lua. (The numeral may have leading spaces and a sign.) This format always reads the longest input sequence that is a valid prefix for a numeral; if that prefix does not form a valid numeral (e.g., an empty string, "0x", or "3.4e-"), it is discarded and the function returns nil.
  • "a": reads the whole file, starting at the current position. On end of file, it returns the empty string.
  • "l": reads the next line skipping the end of line, returning nil on end of file. This is the default format.
  • "L": reads the next line keeping the end-of-line character (if present), returning nil on end of file.
  • number: reads a string with up to this number of bytes, returning nil on end of file. If number is zero, it reads nothing and returns an empty string, or nil on end of file.

The formats "l" and "L" should be used only for text files.


file:seek ([whence [, offset]])

Sets and gets the file position, measured from the beginning of the file, to the position given by offset plus a base specified by the string whence, as follows:

  • "set": base is position 0 (beginning of the file);
  • "cur": base is current position;
  • "end": base is end of file;

In case of success, seek returns the final file position, measured in bytes from the beginning of the file. If seek fails, it returns nil, plus a string describing the error.

The default value for whence is "cur", and for offset is 0. Therefore, the call file:seek() returns the current file position, without changing it; the call file:seek("set") sets the position to the beginning of the file (and returns 0); and the call file:seek("end") sets the position to the end of the file, and returns its size.


file:setvbuf (mode [, size])

Sets the buffering mode for an output file. There are three available modes:

  • "no": no buffering; the result of any output operation appears immediately.
  • "full": full buffering; output operation is performed only when the buffer is full or when you explicitly flush the file (see io.flush).
  • "line": line buffering; output is buffered until a newline is output or there is any input from some special files (such as a terminal device).

For the last two cases, size specifies the size of the buffer, in bytes. The default is an appropriate size.


file:write (···)

Writes the value of each of its arguments to file. The arguments must be strings or numbers.

In case of success, this function returns file. Otherwise it returns nil plus a string describing the error.

6.9 – Operating System Facilities

This library is implemented through table os.


os.clock ()

Returns an approximation of the amount in seconds of CPU time used by the program.


os.date ([format [, time]])

Returns a string or a table containing date and time, formatted according to the given string format.

If the time argument is present, this is the time to be formatted (see the os.time function for a description of this value). Otherwise, date formats the current time.

If format starts with '!', then the date is formatted in Coordinated Universal Time. After this optional character, if format is the string "*t", then date returns a table with the following fields: year, month (1–12), day (1–31), hour (0–23), min (0–59), sec (0–61), wday (weekday, 1–7, Sunday is 1), yday (day of the year, 1–366), and isdst (daylight saving flag, a boolean). This last field may be absent if the information is not available.

If format is not "*t", then date returns the date as a string, formatted according to the same rules as the ISO C function strftime.

When called without arguments, date returns a reasonable date and time representation that depends on the host system and on the current locale. (More specifically, os.date() is equivalent to os.date("%c").)

In non-POSIX systems, this function may be not thread safe because of its reliance on C function gmtime and C function localtime.


os.difftime (t2, t1)

Returns the difference, in seconds, from time t1 to time t2 (where the times are values returned by os.time). In POSIX, Windows, and some other systems, this value is exactly t2-t1.


os.execute ([command])

This function is equivalent to the ISO C function system. It passes command to be executed by an operating system shell. Its first result is true if the command terminated successfully, or nil otherwise. After this first result the function returns a string plus a number, as follows:

  • "exit": the command terminated normally; the following number is the exit status of the command.
  • "signal": the command was terminated by a signal; the following number is the signal that terminated the command.

When called without a command, os.execute returns a boolean that is true if a shell is available.


os.exit ([code [, close]])

Calls the ISO C function exit to terminate the host program. If code is true, the returned status is EXIT_SUCCESS; if code is false, the returned status is EXIT_FAILURE; if code is a number, the returned status is this number. The default value for code is true.

If the optional second argument close is true, closes the Lua state before exiting.


os.getenv (varname)

Returns the value of the process environment variable varname, or nil if the variable is not defined.


os.remove (filename)

Deletes the file (or empty directory, on POSIX systems) with the given name. If this function fails, it returns nil, plus a string describing the error and the error code. Otherwise, it returns true.


os.rename (oldname, newname)

Renames the file or directory named oldname to newname. If this function fails, it returns nil, plus a string describing the error and the error code. Otherwise, it returns true.


os.setlocale (locale [, category])

Sets the current locale of the program. locale is a system-dependent string specifying a locale; category is an optional string describing which category to change: "all", "collate", "ctype", "monetary", "numeric", or "time"; the default category is "all". The function returns the name of the new locale, or nil if the request cannot be honored.

If locale is the empty string, the current locale is set to an implementation-defined native locale. If locale is the string "C", the current locale is set to the standard C locale.

When called with nil as the first argument, this function only returns the name of the current locale for the given category.

This function may be not thread safe because of its reliance on C function setlocale.


os.time ([table])

Returns the current time when called without arguments, or a time representing the local date and time specified by the given table. This table must have fields year, month, and day, and may have fields hour (default is 12), min (default is 0), sec (default is 0), and isdst (default is nil). Other fields are ignored. For a description of these fields, see the os.date function.

The values in these fields do not need to be inside their valid ranges. For instance, if sec is -10, it means -10 seconds from the time specified by the other fields; if hour is 1000, it means +1000 hours from the time specified by the other fields.

The returned value is a number, whose meaning depends on your system. In POSIX, Windows, and some other systems, this number counts the number of seconds since some given start time (the "epoch"). In other systems, the meaning is not specified, and the number returned by time can be used only as an argument to os.date and os.difftime.


os.tmpname ()

Returns a string with a file name that can be used for a temporary file. The file must be explicitly opened before its use and explicitly removed when no longer needed.

In POSIX systems, this function also creates a file with that name, to avoid security risks. (Someone else might create the file with wrong permissions in the time between getting the name and creating the file.) You still have to open the file to use it and to remove it (even if you do not use it).

When possible, you may prefer to use io.tmpfile, which automatically removes the file when the program ends.

6.10 – The Debug Library

This library provides the functionality of the debug interface (§4.9) to Lua programs. You should exert care when using this library. Several of its functions violate basic assumptions about Lua code (e.g., that variables local to a function cannot be accessed from outside; that userdata metatables cannot be changed by Lua code; that Lua programs do not crash) and therefore can compromise otherwise secure code. Moreover, some functions in this library may be slow.

All functions in this library are provided inside the debug table. All functions that operate over a thread have an optional first argument which is the thread to operate over. The default is always the current thread.


debug.debug ()

Enters an interactive mode with the user, running each string that the user enters. Using simple commands and other debug facilities, the user can inspect global and local variables, change their values, evaluate expressions, and so on. A line containing only the word cont finishes this function, so that the caller continues its execution.

Note that commands for debug.debug are not lexically nested within any function and so have no direct access to local variables.


debug.gethook ([thread])

Returns the current hook settings of the thread, as three values: the current hook function, the current hook mask, and the current hook count (as set by the debug.sethook function).


debug.getinfo ([thread,] f [, what])

Returns a table with information about a function. You can give the function directly or you can give a number as the value of f, which means the function running at level f of the call stack of the given thread: level 0 is the current function (getinfo itself); level 1 is the function that called getinfo (except for tail calls, which do not count on the stack); and so on. If f is a number larger than the number of active functions, then getinfo returns nil.

The returned table can contain all the fields returned by lua_getinfo, with the string what describing which fields to fill in. The default for what is to get all information available, except the table of valid lines. If present, the option 'f' adds a field named func with the function itself. If present, the option 'L' adds a field named activelines with the table of valid lines.

For instance, the expression debug.getinfo(1,"n").name returns a name for the current function, if a reasonable name can be found, and the expression debug.getinfo(print) returns a table with all available information about the print function.


debug.getlocal ([thread,] f, local)

This function returns the name and the value of the local variable with index local of the function at level f of the stack. This function accesses not only explicit local variables, but also parameters, temporaries, etc.

The first parameter or local variable has index 1, and so on, following the order that they are declared in the code, counting only the variables that are active in the current scope of the function. Negative indices refer to vararg arguments; -1 is the first vararg argument. The function returns nil if there is no variable with the given index, and raises an error when called with a level out of range. (You can call debug.getinfo to check whether the level is valid.)

Variable names starting with '(' (open parenthesis) represent variables with no known names (internal variables such as loop control variables, and variables from chunks saved without debug information).

The parameter f may also be a function. In that case, getlocal returns only the name of function parameters.


debug.getmetatable (value)

Returns the metatable of the given value or nil if it does not have a metatable.


debug.getregistry ()

Returns the registry table (see §4.5).


debug.getupvalue (f, up)

This function returns the name and the value of the upvalue with index up of the function f. The function returns nil if there is no upvalue with the given index.

Variable names starting with '(' (open parenthesis) represent variables with no known names (variables from chunks saved without debug information).


debug.getuservalue (u)

Returns the Lua value associated to u. If u is not a full userdata, returns nil.


debug.sethook ([thread,] hook, mask [, count])

Sets the given function as a hook. The string mask and the number count describe when the hook will be called. The string mask may have any combination of the following characters, with the given meaning:

  • 'c': the hook is called every time Lua calls a function;
  • 'r': the hook is called every time Lua returns from a function;
  • 'l': the hook is called every time Lua enters a new line of code.

Moreover, with a count different from zero, the hook is called also after every count instructions.

When called without arguments, debug.sethook turns off the hook.

When the hook is called, its first argument is a string describing the event that has triggered its call: "call" (or "tail call"), "return", "line", and "count". For line events, the hook also gets the new line number as its second parameter. Inside a hook, you can call getinfo with level 2 to get more information about the running function (level 0 is the getinfo function, and level 1 is the hook function).


debug.setlocal ([thread,] level, local, value)

This function assigns the value value to the local variable with index local of the function at level level of the stack. The function returns nil if there is no local variable with the given index, and raises an error when called with a level out of range. (You can call getinfo to check whether the level is valid.) Otherwise, it returns the name of the local variable.

See debug.getlocal for more information about variable indices and names.


debug.setmetatable (value, table)

Sets the metatable for the given value to the given table (which can be nil). Returns value.


debug.setupvalue (f, up, value)

This function assigns the value value to the upvalue with index up of the function f. The function returns nil if there is no upvalue with the given index. Otherwise, it returns the name of the upvalue.


debug.setuservalue (udata, value)

Sets the given value as the Lua value associated to the given udata. udata must be a full userdata.

Returns udata.


debug.traceback ([thread,] [message [, level]])

If message is present but is neither a string nor nil, this function returns message without further processing. Otherwise, it returns a string with a traceback of the call stack. The optional message string is appended at the beginning of the traceback. An optional level number tells at which level to start the traceback (default is 1, the function calling traceback).


debug.upvalueid (f, n)

Returns a unique identifier (as a light userdata) for the upvalue numbered n from the given function.

These unique identifiers allow a program to check whether different closures share upvalues. Lua closures that share an upvalue (that is, that access a same external local variable) will return identical ids for those upvalue indices.


debug.upvaluejoin (f1, n1, f2, n2)

Make the n1-th upvalue of the Lua closure f1 refer to the n2-th upvalue of the Lua closure f2.

7 – Lua Standalone

Although Lua has been designed as an extension language, to be embedded in a host C program, it is also frequently used as a standalone language. An interpreter for Lua as a standalone language, called simply lua, is provided with the standard distribution. The standalone interpreter includes all standard libraries, including the debug library. Its usage is:

     lua [options] [script [args]]

The options are:

  • -e stat: executes string stat;
  • -l mod: "requires" mod and assigns the result to global @mod;
  • -i: enters interactive mode after running script;
  • -v: prints version information;
  • -E: ignores environment variables;
  • --: stops handling options;
  • -: executes stdin as a file and stops handling options.

After handling its options, lua runs the given script. When called without arguments, lua behaves as lua -v -i when the standard input (stdin) is a terminal, and as lua - otherwise.

When called without option -E, the interpreter checks for an environment variable LUA_INIT_5_3 (or LUA_INIT if the versioned name is not defined) before running any argument. If the variable content has the format @filename, then lua executes the file. Otherwise, lua executes the string itself.

When called with option -E, besides ignoring LUA_INIT, Lua also ignores the values of LUA_PATH and LUA_CPATH, setting the values of package.path and package.cpath with the default paths defined in luaconf.h.

All options are handled in order, except -i and -E. For instance, an invocation like

     $ lua -e'a=1' -e 'print(a)' script.lua

will first set a to 1, then print the value of a, and finally run the file script.lua with no arguments. (Here $ is the shell prompt. Your prompt may be different.)

Before running any code, lua collects all command-line arguments in a global table called arg. The script name goes to index 0, the first argument after the script name goes to index 1, and so on. Any arguments before the script name (that is, the interpreter name plus its options) go to negative indices. For instance, in the call

     $ lua -la b.lua t1 t2

the table is like this:

     arg = { [-2] = "lua", [-1] = "-la",
             [0] = "b.lua",
             [1] = "t1", [2] = "t2" }

If there is no script in the call, the interpreter name goes to index 0, followed by the other arguments. For instance, the call

     $ lua -e "print(arg[1])"

will print "-e". If there is a script, the script is called with arguments arg[1], ···, arg[#arg]. (Like all chunks in Lua, the script is compiled as a vararg function.)

In interactive mode, Lua repeatedly prompts and waits for a line. After reading a line, Lua first try to interpret the line as an expression. If it succeeds, it prints its value. Otherwise, it interprets the line as a statement. If you write an incomplete statement, the interpreter waits for its completion by issuing a different prompt.

If the global variable _PROMPT contains a string, then its value is used as the prompt. Similarly, if the global variable _PROMPT2 contains a string, its value is used as the secondary prompt (issued during incomplete statements).

In case of unprotected errors in the script, the interpreter reports the error to the standard error stream. If the error object is not a string but has a metamethod __tostring, the interpreter calls this metamethod to produce the final message. Otherwise, the interpreter converts the error object to a string and adds a stack traceback to it.

When finishing normally, the interpreter closes its main Lua state (see lua_close). The script can avoid this step by calling os.exit to terminate.

To allow the use of Lua as a script interpreter in Unix systems, the standalone interpreter skips the first line of a chunk if it starts with #. Therefore, Lua scripts can be made into executable programs by using chmod +x and the #! form, as in

     #!/usr/local/bin/lua

(Of course, the location of the Lua interpreter may be different in your machine. If lua is in your PATH, then

     #!/usr/bin/env lua

is a more portable solution.)

8 – Incompatibilities with the Previous Version

Here we list the incompatibilities that you may find when moving a program from Lua 5.2 to Lua 5.3. You can avoid some incompatibilities by compiling Lua with appropriate options (see file luaconf.h). However, all these compatibility options will be removed in the future.

Lua versions can always change the C API in ways that do not imply source-code changes in a program, such as the numeric values for constants or the implementation of functions as macros. Therefore, you should not assume that binaries are compatible between different Lua versions. Always recompile clients of the Lua API when using a new version.

Similarly, Lua versions can always change the internal representation of precompiled chunks; precompiled chunks are not compatible between different Lua versions.

The standard paths in the official distribution may change between versions.

8.1 – Changes in the Language

  • The main difference between Lua 5.2 and Lua 5.3 is the introduction of an integer subtype for numbers. Although this change should not affect "normal" computations, some computations (mainly those that involve some kind of overflow) can give different results.

    You can fix these differences by forcing a number to be a float (in Lua 5.2 all numbers were float), in particular writing constants with an ending .0 or using x = x + 0.0 to convert a variable. (This recommendation is only for a quick fix for an occasional incompatibility; it is not a general guideline for good programming. For good programming, use floats where you need floats and integers where you need integers.)

  • The conversion of a float to a string now adds a .0 suffix to the result if it looks like an integer. (For instance, the float 2.0 will be printed as 2.0, not as 2.) You should always use an explicit format when you need a specific format for numbers.

    (Formally this is not an incompatibility, because Lua does not specify how numbers are formatted as strings, but some programs assumed a specific format.)

  • The generational mode for the garbage collector was removed. (It was an experimental feature in Lua 5.2.)

8.2 – Changes in the Libraries

  • The bit32 library has been deprecated. It is easy to require a compatible external library or, better yet, to replace its functions with appropriate bitwise operations. (Keep in mind that bit32 operates on 32-bit integers, while the bitwise operators in Lua 5.3 operate on Lua integers, which by default have 64 bits.)
  • The Table library now respects metamethods for setting and getting elements.
  • The ipairs iterator now respects metamethods and its __ipairs metamethod has been deprecated.
  • Option names in io.read do not have a starting '*' anymore. For compatibility, Lua will continue to accept (and ignore) this character.
  • The following functions were deprecated in the mathematical library: atan2, cosh, sinh, tanh, pow, frexp, and ldexp. You can replace math.pow(x,y) with x^y; you can replace math.atan2 with math.atan, which now accepts one or two arguments; you can replace math.ldexp(x,exp) with x * 2.0^exp. For the other operations, you can either use an external library or implement them in Lua.
  • The searcher for C loaders used by require changed the way it handles versioned names. Now, the version should come after the module name (as is usual in most other tools). For compatibility, that searcher still tries the old format if it cannot find an open function according to the new style. (Lua 5.2 already worked that way, but it did not document the change.)
  • The call collectgarbage("count") now returns only one result. (You can compute that second result from the fractional part of the first result.)

8.3 – Changes in the API

  • Continuation functions now receive as arguments what they needed to get through lua_getctx, so lua_getctx has been removed. Adapt your code accordingly.
  • Function lua_dump has an extra parameter, strip. Use 0 as the value of this parameter to get the old behavior.
  • Functions to inject/project unsigned integers (lua_pushunsigned, lua_tounsigned, lua_tounsignedx, luaL_checkunsigned, luaL_optunsigned) were deprecated. Use their signed equivalents with a type cast.
  • Macros to project non-default integer types (luaL_checkint, luaL_optint, luaL_checklong, luaL_optlong) were deprecated. Use their equivalent over lua_Integer with a type cast (or, when possible, use lua_Integer in your code).

9 – The Complete Syntax of Lua

Here is the complete syntax of Lua in extended BNF. As usual in extended BNF, {A} means 0 or more As, and [A] means an optional A. (For operator precedences, see §3.4.8; for a description of the terminals Name, Numeral, and LiteralString, see §3.1.)


	chunk ::= block

	block ::= {stat} [retstat]

	stat ::=  ‘;’ | 
		 varlist ‘=’ explist | 
		 functioncall | 
		 label | 
		 break | 
		 goto Name | 
		 do block end | 
		 while exp do block end | 
		 repeat block until exp | 
		 if exp then block {elseif exp then block} [else block] end | 
		 for Name ‘=’ exp ‘,’ exp [‘,’ exp] do block end | 
		 for namelist in explist do block end | 
		 function funcname funcbody | 
		 local function Name funcbody | 
		 local namelist [‘=’ explist] 

	retstat ::= return [explist] [‘;’]

	label ::= ‘::’ Name ‘::’

	funcname ::= Name {‘.’ Name} [‘:’ Name]

	varlist ::= var {‘,’ var}

	var ::=  Name | prefixexp ‘[’ exp ‘]’ | prefixexp ‘.’ Name 

	namelist ::= Name {‘,’ Name}

	explist ::= exp {‘,’ exp}

	exp ::=  nil | false | true | Numeral | LiteralString | ‘...’ | functiondef | 
		 prefixexp | tableconstructor | exp binop exp | unop exp 

	prefixexp ::= var | functioncall | ‘(’ exp ‘)’

	functioncall ::=  prefixexp args | prefixexp ‘:’ Name args 

	args ::=  ‘(’ [explist] ‘)’ | tableconstructor | LiteralString 

	functiondef ::= function funcbody

	funcbody ::= ‘(’ [parlist] ‘)’ block end

	parlist ::= namelist [‘,’ ‘...’] | ‘...’

	tableconstructor ::= ‘{’ [fieldlist] ‘}’

	fieldlist ::= field {fieldsep field} [fieldsep]

	field ::= ‘[’ exp ‘]’ ‘=’ exp | Name ‘=’ exp | exp

	fieldsep ::= ‘,’ | ‘;’

	binop ::=  ‘+’ | ‘-’ | ‘*’ | ‘/’ | ‘//’ | ‘^’ | ‘%’ | 
		 ‘&’ | ‘~’ | ‘|’ | ‘>>’ | ‘<<’ | ‘..’ | 
		 ‘<’ | ‘<=’ | ‘>’ | ‘>=’ | ‘==’ | ‘~=’ | 
		 and | or

	unop ::= ‘-’ | not | ‘#’ | ‘~

================================================ FILE: Tests/ApiExplorer/lua/doc/readme.html ================================================ Lua 5.3 readme

Lua Welcome to Lua 5.3

About Lua

Lua is a powerful, fast, lightweight, embeddable scripting language developed by a team at PUC-Rio, the Pontifical Catholic University of Rio de Janeiro in Brazil. Lua is free software used in many products and projects around the world.

Lua's official web site provides complete information about Lua, including an executive summary and updated documentation, especially the reference manual, which may differ slightly from the local copy distributed in this package.

Installing Lua

Lua is distributed in source form. You need to build it before using it. Building Lua should be straightforward because Lua is implemented in pure ANSI C and compiles unmodified in all known platforms that have an ANSI C compiler. Lua also compiles unmodified as C++. The instructions given below for building Lua are for Unix-like platforms. See also instructions for other systems and customization options.

If you don't have the time or the inclination to compile Lua yourself, get a binary from LuaBinaries. Try also LuaDist, a multi-platform distribution of Lua that includes batteries.

Building Lua

In most Unix-like platforms, simply do "make" with a suitable target. Here are the details.

  1. Open a terminal window and move to the top-level directory, which is named lua-5.3.5. The Makefile there controls both the build process and the installation process.

  2. Do "make" and see if your platform is listed. The platforms currently supported are:

    aix bsd c89 freebsd generic linux macosx mingw posix solaris

    If your platform is listed, just do "make xxx", where xxx is your platform name.

    If your platform is not listed, try the closest one or posix, generic, c89, in this order.

  3. The compilation takes only a few moments and produces three files in the src directory: lua (the interpreter), luac (the compiler), and liblua.a (the library).

  4. To check that Lua has been built correctly, do "make test" after building Lua. This will run the interpreter and print its version.

If you're running Linux and get compilation errors, make sure you have installed the readline development package (which is probably named libreadline-dev or readline-devel). If you get link errors after that, then try "make linux MYLIBS=-ltermcap".

Installing Lua

Once you have built Lua, you may want to install it in an official place in your system. In this case, do "make install". The official place and the way to install files are defined in the Makefile. You'll probably need the right permissions to install files.

To build and install Lua in one step, do "make xxx install", where xxx is your platform name.

To install Lua locally, do "make local". This will create a directory install with subdirectories bin, include, lib, man, share, and install Lua as listed below. To install Lua locally, but in some other directory, do "make install INSTALL_TOP=xxx", where xxx is your chosen directory. The installation starts in the src and doc directories, so take care if INSTALL_TOP is not an absolute path.

bin:
lua luac
include:
lua.h luaconf.h lualib.h lauxlib.h lua.hpp
lib:
liblua.a
man/man1:
lua.1 luac.1

These are the only directories you need for development. If you only want to run Lua programs, you only need the files in bin and man. The files in include and lib are needed for embedding Lua in C or C++ programs.

Customization

Three kinds of things can be customized by editing a file:

  • Where and how to install Lua — edit Makefile.
  • How to build Lua — edit src/Makefile.
  • Lua features — edit src/luaconf.h.

You don't actually need to edit the Makefiles because you may set the relevant variables in the command line when invoking make. Nevertheless, it's probably best to edit and save the Makefiles to record the changes you've made.

On the other hand, if you need to customize some Lua features, you'll need to edit src/luaconf.h before building and installing Lua. The edited file will be the one installed, and it will be used by any Lua clients that you build, to ensure consistency. Further customization is available to experts by editing the Lua sources.

Building Lua on other systems

If you're not using the usual Unix tools, then the instructions for building Lua depend on the compiler you use. You'll need to create projects (or whatever your compiler uses) for building the library, the interpreter, and the compiler, as follows:

library:
lapi.c lcode.c lctype.c ldebug.c ldo.c ldump.c lfunc.c lgc.c llex.c lmem.c lobject.c lopcodes.c lparser.c lstate.c lstring.c ltable.c ltm.c lundump.c lvm.c lzio.c lauxlib.c lbaselib.c lbitlib.c lcorolib.c ldblib.c liolib.c lmathlib.c loslib.c lstrlib.c ltablib.c lutf8lib.c loadlib.c linit.c
interpreter:
library, lua.c
compiler:
library, luac.c

To use Lua as a library in your own programs you'll need to know how to create and use libraries with your compiler. Moreover, to dynamically load C libraries for Lua you'll need to know how to create dynamic libraries and you'll need to make sure that the Lua API functions are accessible to those dynamic libraries — but don't link the Lua library into each dynamic library. For Unix, we recommend that the Lua library be linked statically into the host program and its symbols exported for dynamic linking; src/Makefile does this for the Lua interpreter. For Windows, we recommend that the Lua library be a DLL. In all cases, the compiler luac should be linked statically.

As mentioned above, you may edit src/luaconf.h to customize some features before building Lua.

Changes since Lua 5.2

Here are the main changes introduced in Lua 5.3. The reference manual lists the incompatibilities that had to be introduced.

Main changes

  • integers (64-bit by default)
  • official support for 32-bit numbers
  • bitwise operators
  • basic utf-8 support
  • functions for packing and unpacking values
Here are the other changes introduced in Lua 5.3:

Language

  • userdata can have any Lua value as uservalue
  • floor division
  • more flexible rules for some metamethods

Libraries

  • ipairs and the table library respect metamethods
  • strip option in string.dump
  • table library respects metamethods
  • new function table.move
  • new function string.pack
  • new function string.unpack
  • new function string.packsize

C API

  • simpler API for continuation functions in C
  • lua_gettable and similar functions return type of resulted value
  • strip option in lua_dump
  • new function: lua_geti
  • new function: lua_seti
  • new function: lua_isyieldable
  • new function: lua_numbertointeger
  • new function: lua_rotate
  • new function: lua_stringtonumber

Lua standalone interpreter

  • can be used as calculator; no need to prefix with '='
  • arg table available to all code

License

[osi certified] Lua is free software distributed under the terms of the MIT license reproduced below; it may be used for any purpose, including commercial purposes, at absolutely no cost without having to ask us. The only requirement is that if you do use Lua, then you should give us credit by including the appropriate copyright notice somewhere in your product or its documentation. For details, see this.

Copyright © 1994–2017 Lua.org, PUC-Rio.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

================================================ FILE: Tests/ApiExplorer/lua/src/Makefile ================================================ # Makefile for building Lua # See ../doc/readme.html for installation and customization instructions. # == CHANGE THE SETTINGS BELOW TO SUIT YOUR ENVIRONMENT ======================= # Your platform. See PLATS for possible values. PLAT= none CC= gcc -std=gnu99 CFLAGS= -O2 -Wall -Wextra -DLUA_COMPAT_5_2 $(SYSCFLAGS) $(MYCFLAGS) LDFLAGS= $(SYSLDFLAGS) $(MYLDFLAGS) LIBS= -lm $(SYSLIBS) $(MYLIBS) AR= ar rcu RANLIB= ranlib RM= rm -f SYSCFLAGS= SYSLDFLAGS= SYSLIBS= MYCFLAGS= MYLDFLAGS= MYLIBS= MYOBJS= # == END OF USER SETTINGS -- NO NEED TO CHANGE ANYTHING BELOW THIS LINE ======= PLATS= aix bsd c89 freebsd generic linux macosx mingw posix solaris LUA_A= liblua.a CORE_O= lapi.o lcode.o lctype.o ldebug.o ldo.o ldump.o lfunc.o lgc.o llex.o \ lmem.o lobject.o lopcodes.o lparser.o lstate.o lstring.o ltable.o \ ltm.o lundump.o lvm.o lzio.o LIB_O= lauxlib.o lbaselib.o lbitlib.o lcorolib.o ldblib.o liolib.o \ lmathlib.o loslib.o lstrlib.o ltablib.o lutf8lib.o loadlib.o linit.o BASE_O= $(CORE_O) $(LIB_O) $(MYOBJS) LUA_T= lua LUA_O= lua.o LUAC_T= luac LUAC_O= luac.o ALL_O= $(BASE_O) $(LUA_O) $(LUAC_O) ALL_T= $(LUA_A) $(LUA_T) $(LUAC_T) ALL_A= $(LUA_A) # Targets start here. default: $(PLAT) all: $(ALL_T) o: $(ALL_O) a: $(ALL_A) $(LUA_A): $(BASE_O) $(AR) $@ $(BASE_O) $(RANLIB) $@ $(LUA_T): $(LUA_O) $(LUA_A) $(CC) -o $@ $(LDFLAGS) $(LUA_O) $(LUA_A) $(LIBS) $(LUAC_T): $(LUAC_O) $(LUA_A) $(CC) -o $@ $(LDFLAGS) $(LUAC_O) $(LUA_A) $(LIBS) clean: $(RM) $(ALL_T) $(ALL_O) depend: @$(CC) $(CFLAGS) -MM l*.c echo: @echo "PLAT= $(PLAT)" @echo "CC= $(CC)" @echo "CFLAGS= $(CFLAGS)" @echo "LDFLAGS= $(SYSLDFLAGS)" @echo "LIBS= $(LIBS)" @echo "AR= $(AR)" @echo "RANLIB= $(RANLIB)" @echo "RM= $(RM)" # Convenience targets for popular platforms ALL= all none: @echo "Please do 'make PLATFORM' where PLATFORM is one of these:" @echo " $(PLATS)" aix: $(MAKE) $(ALL) CC="xlc" CFLAGS="-O2 -DLUA_USE_POSIX -DLUA_USE_DLOPEN" SYSLIBS="-ldl" SYSLDFLAGS="-brtl -bexpall" bsd: $(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_POSIX -DLUA_USE_DLOPEN" SYSLIBS="-Wl,-E" c89: $(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_C89" CC="gcc -std=c89" @echo '' @echo '*** C89 does not guarantee 64-bit integers for Lua.' @echo '' freebsd: $(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_LINUX -DLUA_USE_READLINE -I/usr/include/edit" SYSLIBS="-Wl,-E -ledit" CC="cc" generic: $(ALL) linux: $(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_LINUX" SYSLIBS="-Wl,-E -ldl -lreadline" macosx: $(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_MACOSX" SYSLIBS="-lreadline" mingw: $(MAKE) "LUA_A=lua53.dll" "LUA_T=lua.exe" \ "AR=$(CC) -shared -o" "RANLIB=strip --strip-unneeded" \ "SYSCFLAGS=-DLUA_BUILD_AS_DLL" "SYSLIBS=" "SYSLDFLAGS=-s" lua.exe $(MAKE) "LUAC_T=luac.exe" luac.exe posix: $(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_POSIX" solaris: $(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_POSIX -DLUA_USE_DLOPEN -D_REENTRANT" SYSLIBS="-ldl" # list targets that do not create files (but not all makes understand .PHONY) .PHONY: all $(PLATS) default o a clean depend echo none # DO NOT DELETE lapi.o: lapi.c lprefix.h lua.h luaconf.h lapi.h llimits.h lstate.h \ lobject.h ltm.h lzio.h lmem.h ldebug.h ldo.h lfunc.h lgc.h lstring.h \ ltable.h lundump.h lvm.h lauxlib.o: lauxlib.c lprefix.h lua.h luaconf.h lauxlib.h lbaselib.o: lbaselib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h lbitlib.o: lbitlib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h lcode.o: lcode.c lprefix.h lua.h luaconf.h lcode.h llex.h lobject.h \ llimits.h lzio.h lmem.h lopcodes.h lparser.h ldebug.h lstate.h ltm.h \ ldo.h lgc.h lstring.h ltable.h lvm.h lcorolib.o: lcorolib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h lctype.o: lctype.c lprefix.h lctype.h lua.h luaconf.h llimits.h ldblib.o: ldblib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h ldebug.o: ldebug.c lprefix.h lua.h luaconf.h lapi.h llimits.h lstate.h \ lobject.h ltm.h lzio.h lmem.h lcode.h llex.h lopcodes.h lparser.h \ ldebug.h ldo.h lfunc.h lstring.h lgc.h ltable.h lvm.h ldo.o: ldo.c lprefix.h lua.h luaconf.h lapi.h llimits.h lstate.h \ lobject.h ltm.h lzio.h lmem.h ldebug.h ldo.h lfunc.h lgc.h lopcodes.h \ lparser.h lstring.h ltable.h lundump.h lvm.h ldump.o: ldump.c lprefix.h lua.h luaconf.h lobject.h llimits.h lstate.h \ ltm.h lzio.h lmem.h lundump.h lfunc.o: lfunc.c lprefix.h lua.h luaconf.h lfunc.h lobject.h llimits.h \ lgc.h lstate.h ltm.h lzio.h lmem.h lgc.o: lgc.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \ llimits.h ltm.h lzio.h lmem.h ldo.h lfunc.h lgc.h lstring.h ltable.h linit.o: linit.c lprefix.h lua.h luaconf.h lualib.h lauxlib.h liolib.o: liolib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h llex.o: llex.c lprefix.h lua.h luaconf.h lctype.h llimits.h ldebug.h \ lstate.h lobject.h ltm.h lzio.h lmem.h ldo.h lgc.h llex.h lparser.h \ lstring.h ltable.h lmathlib.o: lmathlib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h lmem.o: lmem.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \ llimits.h ltm.h lzio.h lmem.h ldo.h lgc.h loadlib.o: loadlib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h lobject.o: lobject.c lprefix.h lua.h luaconf.h lctype.h llimits.h \ ldebug.h lstate.h lobject.h ltm.h lzio.h lmem.h ldo.h lstring.h lgc.h \ lvm.h lopcodes.o: lopcodes.c lprefix.h lopcodes.h llimits.h lua.h luaconf.h loslib.o: loslib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h lparser.o: lparser.c lprefix.h lua.h luaconf.h lcode.h llex.h lobject.h \ llimits.h lzio.h lmem.h lopcodes.h lparser.h ldebug.h lstate.h ltm.h \ ldo.h lfunc.h lstring.h lgc.h ltable.h lstate.o: lstate.c lprefix.h lua.h luaconf.h lapi.h llimits.h lstate.h \ lobject.h ltm.h lzio.h lmem.h ldebug.h ldo.h lfunc.h lgc.h llex.h \ lstring.h ltable.h lstring.o: lstring.c lprefix.h lua.h luaconf.h ldebug.h lstate.h \ lobject.h llimits.h ltm.h lzio.h lmem.h ldo.h lstring.h lgc.h lstrlib.o: lstrlib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h ltable.o: ltable.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \ llimits.h ltm.h lzio.h lmem.h ldo.h lgc.h lstring.h ltable.h lvm.h ltablib.o: ltablib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h ltm.o: ltm.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \ llimits.h ltm.h lzio.h lmem.h ldo.h lstring.h lgc.h ltable.h lvm.h lua.o: lua.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h luac.o: luac.c lprefix.h lua.h luaconf.h lauxlib.h lobject.h llimits.h \ lstate.h ltm.h lzio.h lmem.h lundump.h ldebug.h lopcodes.h lundump.o: lundump.c lprefix.h lua.h luaconf.h ldebug.h lstate.h \ lobject.h llimits.h ltm.h lzio.h lmem.h ldo.h lfunc.h lstring.h lgc.h \ lundump.h lutf8lib.o: lutf8lib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h lvm.o: lvm.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \ llimits.h ltm.h lzio.h lmem.h ldo.h lfunc.h lgc.h lopcodes.h lstring.h \ ltable.h lvm.h lzio.o: lzio.c lprefix.h lua.h luaconf.h llimits.h lmem.h lstate.h \ lobject.h ltm.h lzio.h # (end of Makefile) ================================================ FILE: Tests/ApiExplorer/lua/src/lapi.c ================================================ /* ** $Id: lapi.c,v 2.259.1.2 2017/12/06 18:35:12 roberto Exp $ ** Lua API ** See Copyright Notice in lua.h */ #pragma warning(disable: 6011) #pragma warning(disable: 4244) #define lapi_c #define LUA_CORE #include "lprefix.h" #include #include #include "lua.h" #include "lapi.h" #include "ldebug.h" #include "ldo.h" #include "lfunc.h" #include "lgc.h" #include "lmem.h" #include "lobject.h" #include "lstate.h" #include "lstring.h" #include "ltable.h" #include "ltm.h" #include "lundump.h" #include "lvm.h" const char lua_ident[] = "$LuaVersion: " LUA_COPYRIGHT " $" "$LuaAuthors: " LUA_AUTHORS " $"; /* value at a non-valid index */ #define NONVALIDVALUE cast(TValue *, luaO_nilobject) /* corresponding test */ #define isvalid(o) ((o) != luaO_nilobject) /* test for pseudo index */ #define ispseudo(i) ((i) <= LUA_REGISTRYINDEX) /* test for upvalue */ #define isupvalue(i) ((i) < LUA_REGISTRYINDEX) /* test for valid but not pseudo index */ #define isstackindex(i, o) (isvalid(o) && !ispseudo(i)) #define api_checkvalidindex(l,o) api_check(l, isvalid(o), "invalid index") #define api_checkstackindex(l, i, o) \ api_check(l, isstackindex(i, o), "index not in the stack") static TValue *index2addr (lua_State *L, int idx) { CallInfo *ci = L->ci; if (idx > 0) { TValue *o = ci->func + idx; api_check(L, idx <= ci->top - (ci->func + 1), "unacceptable index"); if (o >= L->top) return NONVALIDVALUE; else return o; } else if (!ispseudo(idx)) { /* negative index */ api_check(L, idx != 0 && -idx <= L->top - (ci->func + 1), "invalid index"); return L->top + idx; } else if (idx == LUA_REGISTRYINDEX) return &G(L)->l_registry; else { /* upvalues */ idx = LUA_REGISTRYINDEX - idx; api_check(L, idx <= MAXUPVAL + 1, "upvalue index too large"); if (ttislcf(ci->func)) /* light C function? */ return NONVALIDVALUE; /* it has no upvalues */ else { CClosure *func = clCvalue(ci->func); return (idx <= func->nupvalues) ? &func->upvalue[idx-1] : NONVALIDVALUE; } } } /* ** to be called by 'lua_checkstack' in protected mode, to grow stack ** capturing memory errors */ static void growstack (lua_State *L, void *ud) { int size = *(int *)ud; luaD_growstack(L, size); } LUA_API int lua_checkstack (lua_State *L, int n) { int res; CallInfo *ci = L->ci; lua_lock(L); api_check(L, n >= 0, "negative 'n'"); if (L->stack_last - L->top > n) /* stack large enough? */ res = 1; /* yes; check is OK */ else { /* no; need to grow stack */ int inuse = cast_int(L->top - L->stack) + EXTRA_STACK; if (inuse > LUAI_MAXSTACK - n) /* can grow without overflow? */ res = 0; /* no */ else /* try to grow stack */ res = (luaD_rawrunprotected(L, &growstack, &n) == LUA_OK); } if (res && ci->top < L->top + n) ci->top = L->top + n; /* adjust frame top */ lua_unlock(L); return res; } LUA_API void lua_xmove (lua_State *from, lua_State *to, int n) { int i; if (from == to) return; lua_lock(to); api_checknelems(from, n); api_check(from, G(from) == G(to), "moving among independent states"); api_check(from, to->ci->top - to->top >= n, "stack overflow"); from->top -= n; for (i = 0; i < n; i++) { setobj2s(to, to->top, from->top + i); to->top++; /* stack already checked by previous 'api_check' */ } lua_unlock(to); } LUA_API lua_CFunction lua_atpanic (lua_State *L, lua_CFunction panicf) { lua_CFunction old; lua_lock(L); old = G(L)->panic; G(L)->panic = panicf; lua_unlock(L); return old; } LUA_API const lua_Number *lua_version (lua_State *L) { static const lua_Number version = LUA_VERSION_NUM; if (L == NULL) return &version; else return G(L)->version; } /* ** basic stack manipulation */ /* ** convert an acceptable stack index into an absolute index */ LUA_API int lua_absindex (lua_State *L, int idx) { return (idx > 0 || ispseudo(idx)) ? idx : cast_int(L->top - L->ci->func) + idx; } LUA_API int lua_gettop (lua_State *L) { return cast_int(L->top - (L->ci->func + 1)); } LUA_API void lua_settop (lua_State *L, int idx) { StkId func = L->ci->func; lua_lock(L); if (idx >= 0) { api_check(L, idx <= L->stack_last - (func + 1), "new top too large"); while (L->top < (func + 1) + idx) setnilvalue(L->top++); L->top = (func + 1) + idx; } else { api_check(L, -(idx+1) <= (L->top - (func + 1)), "invalid new top"); L->top += idx+1; /* 'subtract' index (index is negative) */ } lua_unlock(L); } /* ** Reverse the stack segment from 'from' to 'to' ** (auxiliary to 'lua_rotate') */ static void reverse (lua_State *L, StkId from, StkId to) { for (; from < to; from++, to--) { TValue temp; setobj(L, &temp, from); setobjs2s(L, from, to); setobj2s(L, to, &temp); } } /* ** Let x = AB, where A is a prefix of length 'n'. Then, ** rotate x n == BA. But BA == (A^r . B^r)^r. */ LUA_API void lua_rotate (lua_State *L, int idx, int n) { StkId p, t, m; lua_lock(L); t = L->top - 1; /* end of stack segment being rotated */ p = index2addr(L, idx); /* start of segment */ api_checkstackindex(L, idx, p); api_check(L, (n >= 0 ? n : -n) <= (t - p + 1), "invalid 'n'"); m = (n >= 0 ? t - n : p - n - 1); /* end of prefix */ reverse(L, p, m); /* reverse the prefix with length 'n' */ reverse(L, m + 1, t); /* reverse the suffix */ reverse(L, p, t); /* reverse the entire segment */ lua_unlock(L); } LUA_API void lua_copy (lua_State *L, int fromidx, int toidx) { TValue *fr, *to; lua_lock(L); fr = index2addr(L, fromidx); to = index2addr(L, toidx); api_checkvalidindex(L, to); setobj(L, to, fr); if (isupvalue(toidx)) /* function upvalue? */ luaC_barrier(L, clCvalue(L->ci->func), fr); /* LUA_REGISTRYINDEX does not need gc barrier (collector revisits it before finishing collection) */ lua_unlock(L); } LUA_API void lua_pushvalue (lua_State *L, int idx) { lua_lock(L); setobj2s(L, L->top, index2addr(L, idx)); api_incr_top(L); lua_unlock(L); } /* ** access functions (stack -> C) */ LUA_API int lua_type (lua_State *L, int idx) { StkId o = index2addr(L, idx); return (isvalid(o) ? ttnov(o) : LUA_TNONE); } LUA_API const char *lua_typename (lua_State *L, int t) { UNUSED(L); api_check(L, LUA_TNONE <= t && t < LUA_NUMTAGS, "invalid tag"); return ttypename(t); } LUA_API int lua_iscfunction (lua_State *L, int idx) { StkId o = index2addr(L, idx); return (ttislcf(o) || (ttisCclosure(o))); } LUA_API int lua_isinteger (lua_State *L, int idx) { StkId o = index2addr(L, idx); return ttisinteger(o); } LUA_API int lua_isnumber (lua_State *L, int idx) { lua_Number n; const TValue *o = index2addr(L, idx); return tonumber(o, &n); } LUA_API int lua_isstring (lua_State *L, int idx) { const TValue *o = index2addr(L, idx); return (ttisstring(o) || cvt2str(o)); } LUA_API int lua_isuserdata (lua_State *L, int idx) { const TValue *o = index2addr(L, idx); return (ttisfulluserdata(o) || ttislightuserdata(o)); } LUA_API int lua_rawequal (lua_State *L, int index1, int index2) { StkId o1 = index2addr(L, index1); StkId o2 = index2addr(L, index2); return (isvalid(o1) && isvalid(o2)) ? luaV_rawequalobj(o1, o2) : 0; } LUA_API void lua_arith (lua_State *L, int op) { lua_lock(L); if (op != LUA_OPUNM && op != LUA_OPBNOT) api_checknelems(L, 2); /* all other operations expect two operands */ else { /* for unary operations, add fake 2nd operand */ api_checknelems(L, 1); setobjs2s(L, L->top, L->top - 1); api_incr_top(L); } /* first operand at top - 2, second at top - 1; result go to top - 2 */ luaO_arith(L, op, L->top - 2, L->top - 1, L->top - 2); L->top--; /* remove second operand */ lua_unlock(L); } LUA_API int lua_compare (lua_State *L, int index1, int index2, int op) { StkId o1, o2; int i = 0; lua_lock(L); /* may call tag method */ o1 = index2addr(L, index1); o2 = index2addr(L, index2); if (isvalid(o1) && isvalid(o2)) { switch (op) { case LUA_OPEQ: i = luaV_equalobj(L, o1, o2); break; case LUA_OPLT: i = luaV_lessthan(L, o1, o2); break; case LUA_OPLE: i = luaV_lessequal(L, o1, o2); break; default: api_check(L, 0, "invalid option"); } } lua_unlock(L); return i; } LUA_API size_t lua_stringtonumber (lua_State *L, const char *s) { size_t sz = luaO_str2num(s, L->top); if (sz != 0) api_incr_top(L); return sz; } LUA_API lua_Number lua_tonumberx (lua_State *L, int idx, int *pisnum) { lua_Number n; const TValue *o = index2addr(L, idx); int isnum = tonumber(o, &n); if (!isnum) n = 0; /* call to 'tonumber' may change 'n' even if it fails */ if (pisnum) *pisnum = isnum; return n; } LUA_API lua_Integer lua_tointegerx (lua_State *L, int idx, int *pisnum) { lua_Integer res; const TValue *o = index2addr(L, idx); int isnum = tointeger(o, &res); if (!isnum) res = 0; /* call to 'tointeger' may change 'n' even if it fails */ if (pisnum) *pisnum = isnum; return res; } LUA_API int lua_toboolean (lua_State *L, int idx) { const TValue *o = index2addr(L, idx); return !l_isfalse(o); } LUA_API const char *lua_tolstring (lua_State *L, int idx, size_t *len) { StkId o = index2addr(L, idx); if (!ttisstring(o)) { if (!cvt2str(o)) { /* not convertible? */ if (len != NULL) *len = 0; return NULL; } lua_lock(L); /* 'luaO_tostring' may create a new string */ luaO_tostring(L, o); luaC_checkGC(L); o = index2addr(L, idx); /* previous call may reallocate the stack */ lua_unlock(L); } if (len != NULL) *len = vslen(o); return svalue(o); } LUA_API size_t lua_rawlen (lua_State *L, int idx) { StkId o = index2addr(L, idx); switch (ttype(o)) { case LUA_TSHRSTR: return tsvalue(o)->shrlen; case LUA_TLNGSTR: return tsvalue(o)->u.lnglen; case LUA_TUSERDATA: return uvalue(o)->len; case LUA_TTABLE: return (size_t)luaH_getn(hvalue(o)); default: return 0; } } LUA_API lua_CFunction lua_tocfunction (lua_State *L, int idx) { StkId o = index2addr(L, idx); if (ttislcf(o)) return fvalue(o); else if (ttisCclosure(o)) return clCvalue(o)->f; else return NULL; /* not a C function */ } LUA_API void *lua_touserdata (lua_State *L, int idx) { StkId o = index2addr(L, idx); switch (ttnov(o)) { case LUA_TUSERDATA: return getudatamem(uvalue(o)); case LUA_TLIGHTUSERDATA: return pvalue(o); default: return NULL; } } LUA_API lua_State *lua_tothread (lua_State *L, int idx) { StkId o = index2addr(L, idx); return (!ttisthread(o)) ? NULL : thvalue(o); } LUA_API const void *lua_topointer (lua_State *L, int idx) { StkId o = index2addr(L, idx); switch (ttype(o)) { case LUA_TTABLE: return hvalue(o); case LUA_TLCL: return clLvalue(o); case LUA_TCCL: return clCvalue(o); case LUA_TLCF: return cast(void *, cast(size_t, fvalue(o))); case LUA_TTHREAD: return thvalue(o); case LUA_TUSERDATA: return getudatamem(uvalue(o)); case LUA_TLIGHTUSERDATA: return pvalue(o); default: return NULL; } } /* ** push functions (C -> stack) */ LUA_API void lua_pushnil (lua_State *L) { lua_lock(L); setnilvalue(L->top); api_incr_top(L); lua_unlock(L); } LUA_API void lua_pushnumber (lua_State *L, lua_Number n) { lua_lock(L); setfltvalue(L->top, n); api_incr_top(L); lua_unlock(L); } LUA_API void lua_pushinteger (lua_State *L, lua_Integer n) { lua_lock(L); setivalue(L->top, n); api_incr_top(L); lua_unlock(L); } /* ** Pushes on the stack a string with given length. Avoid using 's' when ** 'len' == 0 (as 's' can be NULL in that case), due to later use of ** 'memcmp' and 'memcpy'. */ LUA_API const char *lua_pushlstring (lua_State *L, const char *s, size_t len) { TString *ts; lua_lock(L); ts = (len == 0) ? luaS_new(L, "") : luaS_newlstr(L, s, len); setsvalue2s(L, L->top, ts); api_incr_top(L); luaC_checkGC(L); lua_unlock(L); return getstr(ts); } LUA_API const char *lua_pushstring (lua_State *L, const char *s) { lua_lock(L); if (s == NULL) setnilvalue(L->top); else { TString *ts; ts = luaS_new(L, s); setsvalue2s(L, L->top, ts); s = getstr(ts); /* internal copy's address */ } api_incr_top(L); luaC_checkGC(L); lua_unlock(L); return s; } LUA_API const char *lua_pushvfstring (lua_State *L, const char *fmt, va_list argp) { const char *ret; lua_lock(L); ret = luaO_pushvfstring(L, fmt, argp); luaC_checkGC(L); lua_unlock(L); return ret; } LUA_API const char *lua_pushfstring (lua_State *L, const char *fmt, ...) { const char *ret; va_list argp; lua_lock(L); va_start(argp, fmt); ret = luaO_pushvfstring(L, fmt, argp); va_end(argp); luaC_checkGC(L); lua_unlock(L); return ret; } LUA_API void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n) { lua_lock(L); if (n == 0) { setfvalue(L->top, fn); api_incr_top(L); } else { CClosure *cl; api_checknelems(L, n); api_check(L, n <= MAXUPVAL, "upvalue index too large"); cl = luaF_newCclosure(L, n); cl->f = fn; L->top -= n; while (n--) { setobj2n(L, &cl->upvalue[n], L->top + n); /* does not need barrier because closure is white */ } setclCvalue(L, L->top, cl); api_incr_top(L); luaC_checkGC(L); } lua_unlock(L); } LUA_API void lua_pushboolean (lua_State *L, int b) { lua_lock(L); setbvalue(L->top, (b != 0)); /* ensure that true is 1 */ api_incr_top(L); lua_unlock(L); } LUA_API void lua_pushlightuserdata (lua_State *L, void *p) { lua_lock(L); setpvalue(L->top, p); api_incr_top(L); lua_unlock(L); } LUA_API int lua_pushthread (lua_State *L) { lua_lock(L); setthvalue(L, L->top, L); api_incr_top(L); lua_unlock(L); return (G(L)->mainthread == L); } /* ** get functions (Lua -> stack) */ static int auxgetstr (lua_State *L, const TValue *t, const char *k) { const TValue *slot; TString *str = luaS_new(L, k); if (luaV_fastget(L, t, str, slot, luaH_getstr)) { setobj2s(L, L->top, slot); api_incr_top(L); } else { setsvalue2s(L, L->top, str); api_incr_top(L); luaV_finishget(L, t, L->top - 1, L->top - 1, slot); } lua_unlock(L); return ttnov(L->top - 1); } LUA_API int lua_getglobal (lua_State *L, const char *name) { Table *reg = hvalue(&G(L)->l_registry); lua_lock(L); return auxgetstr(L, luaH_getint(reg, LUA_RIDX_GLOBALS), name); } LUA_API int lua_gettable (lua_State *L, int idx) { StkId t; lua_lock(L); t = index2addr(L, idx); luaV_gettable(L, t, L->top - 1, L->top - 1); lua_unlock(L); return ttnov(L->top - 1); } LUA_API int lua_getfield (lua_State *L, int idx, const char *k) { lua_lock(L); return auxgetstr(L, index2addr(L, idx), k); } LUA_API int lua_geti (lua_State *L, int idx, lua_Integer n) { StkId t; const TValue *slot; lua_lock(L); t = index2addr(L, idx); if (luaV_fastget(L, t, n, slot, luaH_getint)) { setobj2s(L, L->top, slot); api_incr_top(L); } else { setivalue(L->top, n); api_incr_top(L); luaV_finishget(L, t, L->top - 1, L->top - 1, slot); } lua_unlock(L); return ttnov(L->top - 1); } LUA_API int lua_rawget (lua_State *L, int idx) { StkId t; lua_lock(L); t = index2addr(L, idx); api_check(L, ttistable(t), "table expected"); setobj2s(L, L->top - 1, luaH_get(hvalue(t), L->top - 1)); lua_unlock(L); return ttnov(L->top - 1); } LUA_API int lua_rawgeti (lua_State *L, int idx, lua_Integer n) { StkId t; lua_lock(L); t = index2addr(L, idx); api_check(L, ttistable(t), "table expected"); setobj2s(L, L->top, luaH_getint(hvalue(t), n)); api_incr_top(L); lua_unlock(L); return ttnov(L->top - 1); } LUA_API int lua_rawgetp (lua_State *L, int idx, const void *p) { StkId t; TValue k; lua_lock(L); t = index2addr(L, idx); api_check(L, ttistable(t), "table expected"); setpvalue(&k, cast(void *, p)); setobj2s(L, L->top, luaH_get(hvalue(t), &k)); api_incr_top(L); lua_unlock(L); return ttnov(L->top - 1); } LUA_API void lua_createtable (lua_State *L, int narray, int nrec) { Table *t; lua_lock(L); t = luaH_new(L); sethvalue(L, L->top, t); api_incr_top(L); if (narray > 0 || nrec > 0) luaH_resize(L, t, narray, nrec); luaC_checkGC(L); lua_unlock(L); } LUA_API int lua_getmetatable (lua_State *L, int objindex) { const TValue *obj; Table *mt; int res = 0; lua_lock(L); obj = index2addr(L, objindex); switch (ttnov(obj)) { case LUA_TTABLE: mt = hvalue(obj)->metatable; break; case LUA_TUSERDATA: mt = uvalue(obj)->metatable; break; default: mt = G(L)->mt[ttnov(obj)]; break; } if (mt != NULL) { sethvalue(L, L->top, mt); api_incr_top(L); res = 1; } lua_unlock(L); return res; } LUA_API int lua_getuservalue (lua_State *L, int idx) { StkId o; lua_lock(L); o = index2addr(L, idx); api_check(L, ttisfulluserdata(o), "full userdata expected"); getuservalue(L, uvalue(o), L->top); api_incr_top(L); lua_unlock(L); return ttnov(L->top - 1); } /* ** set functions (stack -> Lua) */ /* ** t[k] = value at the top of the stack (where 'k' is a string) */ static void auxsetstr (lua_State *L, const TValue *t, const char *k) { const TValue *slot; TString *str = luaS_new(L, k); api_checknelems(L, 1); if (luaV_fastset(L, t, str, slot, luaH_getstr, L->top - 1)) L->top--; /* pop value */ else { setsvalue2s(L, L->top, str); /* push 'str' (to make it a TValue) */ api_incr_top(L); luaV_finishset(L, t, L->top - 1, L->top - 2, slot); L->top -= 2; /* pop value and key */ } lua_unlock(L); /* lock done by caller */ } LUA_API void lua_setglobal (lua_State *L, const char *name) { Table *reg = hvalue(&G(L)->l_registry); lua_lock(L); /* unlock done in 'auxsetstr' */ auxsetstr(L, luaH_getint(reg, LUA_RIDX_GLOBALS), name); } LUA_API void lua_settable (lua_State *L, int idx) { StkId t; lua_lock(L); api_checknelems(L, 2); t = index2addr(L, idx); luaV_settable(L, t, L->top - 2, L->top - 1); L->top -= 2; /* pop index and value */ lua_unlock(L); } LUA_API void lua_setfield (lua_State *L, int idx, const char *k) { lua_lock(L); /* unlock done in 'auxsetstr' */ auxsetstr(L, index2addr(L, idx), k); } LUA_API void lua_seti (lua_State *L, int idx, lua_Integer n) { StkId t; const TValue *slot; lua_lock(L); api_checknelems(L, 1); t = index2addr(L, idx); if (luaV_fastset(L, t, n, slot, luaH_getint, L->top - 1)) L->top--; /* pop value */ else { setivalue(L->top, n); api_incr_top(L); luaV_finishset(L, t, L->top - 1, L->top - 2, slot); L->top -= 2; /* pop value and key */ } lua_unlock(L); } LUA_API void lua_rawset (lua_State *L, int idx) { StkId o; TValue *slot; lua_lock(L); api_checknelems(L, 2); o = index2addr(L, idx); api_check(L, ttistable(o), "table expected"); slot = luaH_set(L, hvalue(o), L->top - 2); setobj2t(L, slot, L->top - 1); invalidateTMcache(hvalue(o)); luaC_barrierback(L, hvalue(o), L->top-1); L->top -= 2; lua_unlock(L); } LUA_API void lua_rawseti (lua_State *L, int idx, lua_Integer n) { StkId o; lua_lock(L); api_checknelems(L, 1); o = index2addr(L, idx); api_check(L, ttistable(o), "table expected"); luaH_setint(L, hvalue(o), n, L->top - 1); luaC_barrierback(L, hvalue(o), L->top-1); L->top--; lua_unlock(L); } LUA_API void lua_rawsetp (lua_State *L, int idx, const void *p) { StkId o; TValue k, *slot; lua_lock(L); api_checknelems(L, 1); o = index2addr(L, idx); api_check(L, ttistable(o), "table expected"); setpvalue(&k, cast(void *, p)); slot = luaH_set(L, hvalue(o), &k); setobj2t(L, slot, L->top - 1); luaC_barrierback(L, hvalue(o), L->top - 1); L->top--; lua_unlock(L); } LUA_API int lua_setmetatable (lua_State *L, int objindex) { TValue *obj; Table *mt; lua_lock(L); api_checknelems(L, 1); obj = index2addr(L, objindex); if (ttisnil(L->top - 1)) mt = NULL; else { api_check(L, ttistable(L->top - 1), "table expected"); mt = hvalue(L->top - 1); } switch (ttnov(obj)) { case LUA_TTABLE: { hvalue(obj)->metatable = mt; if (mt) { luaC_objbarrier(L, gcvalue(obj), mt); luaC_checkfinalizer(L, gcvalue(obj), mt); } break; } case LUA_TUSERDATA: { uvalue(obj)->metatable = mt; if (mt) { luaC_objbarrier(L, uvalue(obj), mt); luaC_checkfinalizer(L, gcvalue(obj), mt); } break; } default: { G(L)->mt[ttnov(obj)] = mt; break; } } L->top--; lua_unlock(L); return 1; } LUA_API void lua_setuservalue (lua_State *L, int idx) { StkId o; lua_lock(L); api_checknelems(L, 1); o = index2addr(L, idx); api_check(L, ttisfulluserdata(o), "full userdata expected"); setuservalue(L, uvalue(o), L->top - 1); luaC_barrier(L, gcvalue(o), L->top - 1); L->top--; lua_unlock(L); } /* ** 'load' and 'call' functions (run Lua code) */ #define checkresults(L,na,nr) \ api_check(L, (nr) == LUA_MULTRET || (L->ci->top - L->top >= (nr) - (na)), \ "results from function overflow current stack size") LUA_API void lua_callk (lua_State *L, int nargs, int nresults, lua_KContext ctx, lua_KFunction k) { StkId func; lua_lock(L); api_check(L, k == NULL || !isLua(L->ci), "cannot use continuations inside hooks"); api_checknelems(L, nargs+1); api_check(L, L->status == LUA_OK, "cannot do calls on non-normal thread"); checkresults(L, nargs, nresults); func = L->top - (nargs+1); if (k != NULL && L->nny == 0) { /* need to prepare continuation? */ L->ci->u.c.k = k; /* save continuation */ L->ci->u.c.ctx = ctx; /* save context */ luaD_call(L, func, nresults); /* do the call */ } else /* no continuation or no yieldable */ luaD_callnoyield(L, func, nresults); /* just do the call */ adjustresults(L, nresults); lua_unlock(L); } /* ** Execute a protected call. */ struct CallS { /* data to 'f_call' */ StkId func; int nresults; }; static void f_call (lua_State *L, void *ud) { struct CallS *c = cast(struct CallS *, ud); luaD_callnoyield(L, c->func, c->nresults); } LUA_API int lua_pcallk (lua_State *L, int nargs, int nresults, int errfunc, lua_KContext ctx, lua_KFunction k) { struct CallS c; int status; ptrdiff_t func; lua_lock(L); api_check(L, k == NULL || !isLua(L->ci), "cannot use continuations inside hooks"); api_checknelems(L, nargs+1); api_check(L, L->status == LUA_OK, "cannot do calls on non-normal thread"); checkresults(L, nargs, nresults); if (errfunc == 0) func = 0; else { StkId o = index2addr(L, errfunc); api_checkstackindex(L, errfunc, o); func = savestack(L, o); } c.func = L->top - (nargs+1); /* function to be called */ if (k == NULL || L->nny > 0) { /* no continuation or no yieldable? */ c.nresults = nresults; /* do a 'conventional' protected call */ status = luaD_pcall(L, f_call, &c, savestack(L, c.func), func); } else { /* prepare continuation (call is already protected by 'resume') */ CallInfo *ci = L->ci; ci->u.c.k = k; /* save continuation */ ci->u.c.ctx = ctx; /* save context */ /* save information for error recovery */ ci->extra = savestack(L, c.func); ci->u.c.old_errfunc = L->errfunc; L->errfunc = func; setoah(ci->callstatus, L->allowhook); /* save value of 'allowhook' */ ci->callstatus |= CIST_YPCALL; /* function can do error recovery */ luaD_call(L, c.func, nresults); /* do the call */ ci->callstatus &= ~CIST_YPCALL; L->errfunc = ci->u.c.old_errfunc; status = LUA_OK; /* if it is here, there were no errors */ } adjustresults(L, nresults); lua_unlock(L); return status; } LUA_API int lua_load (lua_State *L, lua_Reader reader, void *data, const char *chunkname, const char *mode) { ZIO z; int status; lua_lock(L); if (!chunkname) chunkname = "?"; luaZ_init(L, &z, reader, data); status = luaD_protectedparser(L, &z, chunkname, mode); if (status == LUA_OK) { /* no errors? */ LClosure *f = clLvalue(L->top - 1); /* get newly created function */ if (f->nupvalues >= 1) { /* does it have an upvalue? */ /* get global table from registry */ Table *reg = hvalue(&G(L)->l_registry); const TValue *gt = luaH_getint(reg, LUA_RIDX_GLOBALS); /* set global table as 1st upvalue of 'f' (may be LUA_ENV) */ setobj(L, f->upvals[0]->v, gt); luaC_upvalbarrier(L, f->upvals[0]); } } lua_unlock(L); return status; } LUA_API int lua_dump (lua_State *L, lua_Writer writer, void *data, int strip) { int status; TValue *o; lua_lock(L); api_checknelems(L, 1); o = L->top - 1; if (isLfunction(o)) status = luaU_dump(L, getproto(o), writer, data, strip); else status = 1; lua_unlock(L); return status; } LUA_API int lua_status (lua_State *L) { return L->status; } /* ** Garbage-collection function */ LUA_API int lua_gc (lua_State *L, int what, int data) { int res = 0; global_State *g; lua_lock(L); g = G(L); switch (what) { case LUA_GCSTOP: { g->gcrunning = 0; break; } case LUA_GCRESTART: { luaE_setdebt(g, 0); g->gcrunning = 1; break; } case LUA_GCCOLLECT: { luaC_fullgc(L, 0); break; } case LUA_GCCOUNT: { /* GC values are expressed in Kbytes: #bytes/2^10 */ res = cast_int(gettotalbytes(g) >> 10); break; } case LUA_GCCOUNTB: { res = cast_int(gettotalbytes(g) & 0x3ff); break; } case LUA_GCSTEP: { l_mem debt = 1; /* =1 to signal that it did an actual step */ lu_byte oldrunning = g->gcrunning; g->gcrunning = 1; /* allow GC to run */ if (data == 0) { luaE_setdebt(g, -GCSTEPSIZE); /* to do a "small" step */ luaC_step(L); } else { /* add 'data' to total debt */ debt = cast(l_mem, data) * 1024 + g->GCdebt; luaE_setdebt(g, debt); luaC_checkGC(L); } g->gcrunning = oldrunning; /* restore previous state */ if (debt > 0 && g->gcstate == GCSpause) /* end of cycle? */ res = 1; /* signal it */ break; } case LUA_GCSETPAUSE: { res = g->gcpause; g->gcpause = data; break; } case LUA_GCSETSTEPMUL: { res = g->gcstepmul; if (data < 40) data = 40; /* avoid ridiculous low values (and 0) */ g->gcstepmul = data; break; } case LUA_GCISRUNNING: { res = g->gcrunning; break; } default: res = -1; /* invalid option */ } lua_unlock(L); return res; } /* ** miscellaneous functions */ LUA_API int lua_error (lua_State *L) { lua_lock(L); api_checknelems(L, 1); luaG_errormsg(L); /* code unreachable; will unlock when control actually leaves the kernel */ return 0; /* to avoid warnings */ } LUA_API int lua_next (lua_State *L, int idx) { StkId t; int more; lua_lock(L); t = index2addr(L, idx); api_check(L, ttistable(t), "table expected"); more = luaH_next(L, hvalue(t), L->top - 1); if (more) { api_incr_top(L); } else /* no more elements */ L->top -= 1; /* remove key */ lua_unlock(L); return more; } LUA_API void lua_concat (lua_State *L, int n) { lua_lock(L); api_checknelems(L, n); if (n >= 2) { luaV_concat(L, n); } else if (n == 0) { /* push empty string */ setsvalue2s(L, L->top, luaS_newlstr(L, "", 0)); api_incr_top(L); } /* else n == 1; nothing to do */ luaC_checkGC(L); lua_unlock(L); } LUA_API void lua_len (lua_State *L, int idx) { StkId t; lua_lock(L); t = index2addr(L, idx); luaV_objlen(L, L->top, t); api_incr_top(L); lua_unlock(L); } LUA_API lua_Alloc lua_getallocf (lua_State *L, void **ud) { lua_Alloc f; lua_lock(L); if (ud) *ud = G(L)->ud; f = G(L)->frealloc; lua_unlock(L); return f; } LUA_API void lua_setallocf (lua_State *L, lua_Alloc f, void *ud) { lua_lock(L); G(L)->ud = ud; G(L)->frealloc = f; lua_unlock(L); } LUA_API void *lua_newuserdata (lua_State *L, size_t size) { Udata *u; lua_lock(L); u = luaS_newudata(L, size); setuvalue(L, L->top, u); api_incr_top(L); luaC_checkGC(L); lua_unlock(L); return getudatamem(u); } static const char *aux_upvalue (StkId fi, int n, TValue **val, CClosure **owner, UpVal **uv) { switch (ttype(fi)) { case LUA_TCCL: { /* C closure */ CClosure *f = clCvalue(fi); if (!(1 <= n && n <= f->nupvalues)) return NULL; *val = &f->upvalue[n-1]; if (owner) *owner = f; return ""; } case LUA_TLCL: { /* Lua closure */ LClosure *f = clLvalue(fi); TString *name; Proto *p = f->p; if (!(1 <= n && n <= p->sizeupvalues)) return NULL; *val = f->upvals[n-1]->v; if (uv) *uv = f->upvals[n - 1]; name = p->upvalues[n-1].name; return (name == NULL) ? "(*no name)" : getstr(name); } default: return NULL; /* not a closure */ } } LUA_API const char *lua_getupvalue (lua_State *L, int funcindex, int n) { const char *name; TValue *val = NULL; /* to avoid warnings */ lua_lock(L); name = aux_upvalue(index2addr(L, funcindex), n, &val, NULL, NULL); if (name) { setobj2s(L, L->top, val); api_incr_top(L); } lua_unlock(L); return name; } LUA_API const char *lua_setupvalue (lua_State *L, int funcindex, int n) { const char *name; TValue *val = NULL; /* to avoid warnings */ CClosure *owner = NULL; UpVal *uv = NULL; StkId fi; lua_lock(L); fi = index2addr(L, funcindex); api_checknelems(L, 1); name = aux_upvalue(fi, n, &val, &owner, &uv); if (name) { L->top--; setobj(L, val, L->top); if (owner) { luaC_barrier(L, owner, L->top); } else if (uv) { luaC_upvalbarrier(L, uv); } } lua_unlock(L); return name; } static UpVal **getupvalref (lua_State *L, int fidx, int n, LClosure **pf) { LClosure *f; StkId fi = index2addr(L, fidx); api_check(L, ttisLclosure(fi), "Lua function expected"); f = clLvalue(fi); api_check(L, (1 <= n && n <= f->p->sizeupvalues), "invalid upvalue index"); if (pf) *pf = f; return &f->upvals[n - 1]; /* get its upvalue pointer */ } LUA_API void *lua_upvalueid (lua_State *L, int fidx, int n) { StkId fi = index2addr(L, fidx); switch (ttype(fi)) { case LUA_TLCL: { /* lua closure */ return *getupvalref(L, fidx, n, NULL); } case LUA_TCCL: { /* C closure */ CClosure *f = clCvalue(fi); api_check(L, 1 <= n && n <= f->nupvalues, "invalid upvalue index"); return &f->upvalue[n - 1]; } default: { api_check(L, 0, "closure expected"); return NULL; } } } LUA_API void lua_upvaluejoin (lua_State *L, int fidx1, int n1, int fidx2, int n2) { LClosure *f1; UpVal **up1 = getupvalref(L, fidx1, n1, &f1); UpVal **up2 = getupvalref(L, fidx2, n2, NULL); luaC_upvdeccount(L, *up1); *up1 = *up2; (*up1)->refcount++; if (upisopen(*up1)) (*up1)->u.open.touched = 1; luaC_upvalbarrier(L, *up1); } ================================================ FILE: Tests/ApiExplorer/lua/src/lapi.h ================================================ /* ** $Id: lapi.h,v 2.9.1.1 2017/04/19 17:20:42 roberto Exp $ ** Auxiliary functions from Lua API ** See Copyright Notice in lua.h */ #ifndef lapi_h #define lapi_h #include "llimits.h" #include "lstate.h" #define api_incr_top(L) {L->top++; api_check(L, L->top <= L->ci->top, \ "stack overflow");} #define adjustresults(L,nres) \ { if ((nres) == LUA_MULTRET && L->ci->top < L->top) L->ci->top = L->top; } #define api_checknelems(L,n) api_check(L, (n) < (L->top - L->ci->func), \ "not enough elements in the stack") #endif ================================================ FILE: Tests/ApiExplorer/lua/src/lauxlib.c ================================================ /* ** $Id: lauxlib.c,v 1.289.1.1 2017/04/19 17:20:42 roberto Exp $ ** Auxiliary functions for building Lua libraries ** See Copyright Notice in lua.h */ #pragma warning(disable: 4100) #pragma warning(disable: 4244) #define lauxlib_c #define LUA_LIB #include "lprefix.h" #include #include #include #include #include /* ** This file uses only the official API of Lua. ** Any function declared here could be written as an application function. */ #include "lua.h" #include "lauxlib.h" /* ** {====================================================== ** Traceback ** ======================================================= */ #define LEVELS1 10 /* size of the first part of the stack */ #define LEVELS2 11 /* size of the second part of the stack */ /* ** search for 'objidx' in table at index -1. ** return 1 + string at top if find a good name. */ static int findfield (lua_State *L, int objidx, int level) { if (level == 0 || !lua_istable(L, -1)) return 0; /* not found */ lua_pushnil(L); /* start 'next' loop */ while (lua_next(L, -2)) { /* for each pair in table */ if (lua_type(L, -2) == LUA_TSTRING) { /* ignore non-string keys */ if (lua_rawequal(L, objidx, -1)) { /* found object? */ lua_pop(L, 1); /* remove value (but keep name) */ return 1; } else if (findfield(L, objidx, level - 1)) { /* try recursively */ lua_remove(L, -2); /* remove table (but keep name) */ lua_pushliteral(L, "."); lua_insert(L, -2); /* place '.' between the two names */ lua_concat(L, 3); return 1; } } lua_pop(L, 1); /* remove value */ } return 0; /* not found */ } /* ** Search for a name for a function in all loaded modules */ static int pushglobalfuncname (lua_State *L, lua_Debug *ar) { int top = lua_gettop(L); lua_getinfo(L, "f", ar); /* push function */ lua_getfield(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE); if (findfield(L, top + 1, 2)) { const char *name = lua_tostring(L, -1); if (strncmp(name, "_G.", 3) == 0) { /* name start with '_G.'? */ lua_pushstring(L, name + 3); /* push name without prefix */ lua_remove(L, -2); /* remove original name */ } lua_copy(L, -1, top + 1); /* move name to proper place */ lua_pop(L, 2); /* remove pushed values */ return 1; } else { lua_settop(L, top); /* remove function and global table */ return 0; } } static void pushfuncname (lua_State *L, lua_Debug *ar) { if (pushglobalfuncname(L, ar)) { /* try first a global name */ lua_pushfstring(L, "function '%s'", lua_tostring(L, -1)); lua_remove(L, -2); /* remove name */ } else if (*ar->namewhat != '\0') /* is there a name from code? */ lua_pushfstring(L, "%s '%s'", ar->namewhat, ar->name); /* use it */ else if (*ar->what == 'm') /* main? */ lua_pushliteral(L, "main chunk"); else if (*ar->what != 'C') /* for Lua functions, use */ lua_pushfstring(L, "function <%s:%d>", ar->short_src, ar->linedefined); else /* nothing left... */ lua_pushliteral(L, "?"); } static int lastlevel (lua_State *L) { lua_Debug ar; int li = 1, le = 1; /* find an upper bound */ while (lua_getstack(L, le, &ar)) { li = le; le *= 2; } /* do a binary search */ while (li < le) { int m = (li + le)/2; if (lua_getstack(L, m, &ar)) li = m + 1; else le = m; } return le - 1; } LUALIB_API void luaL_traceback (lua_State *L, lua_State *L1, const char *msg, int level) { lua_Debug ar; int top = lua_gettop(L); int last = lastlevel(L1); int n1 = (last - level > LEVELS1 + LEVELS2) ? LEVELS1 : -1; if (msg) lua_pushfstring(L, "%s\n", msg); luaL_checkstack(L, 10, NULL); lua_pushliteral(L, "stack traceback:"); while (lua_getstack(L1, level++, &ar)) { if (n1-- == 0) { /* too many levels? */ lua_pushliteral(L, "\n\t..."); /* add a '...' */ level = last - LEVELS2 + 1; /* and skip to last ones */ } else { lua_getinfo(L1, "Slnt", &ar); lua_pushfstring(L, "\n\t%s:", ar.short_src); if (ar.currentline > 0) lua_pushfstring(L, "%d:", ar.currentline); lua_pushliteral(L, " in "); pushfuncname(L, &ar); if (ar.istailcall) lua_pushliteral(L, "\n\t(...tail calls...)"); lua_concat(L, lua_gettop(L) - top); } } lua_concat(L, lua_gettop(L) - top); } /* }====================================================== */ /* ** {====================================================== ** Error-report functions ** ======================================================= */ LUALIB_API int luaL_argerror (lua_State *L, int arg, const char *extramsg) { lua_Debug ar; if (!lua_getstack(L, 0, &ar)) /* no stack frame? */ return luaL_error(L, "bad argument #%d (%s)", arg, extramsg); lua_getinfo(L, "n", &ar); if (strcmp(ar.namewhat, "method") == 0) { arg--; /* do not count 'self' */ if (arg == 0) /* error is in the self argument itself? */ return luaL_error(L, "calling '%s' on bad self (%s)", ar.name, extramsg); } if (ar.name == NULL) ar.name = (pushglobalfuncname(L, &ar)) ? lua_tostring(L, -1) : "?"; return luaL_error(L, "bad argument #%d to '%s' (%s)", arg, ar.name, extramsg); } static int typeerror (lua_State *L, int arg, const char *tname) { const char *msg; const char *typearg; /* name for the type of the actual argument */ if (luaL_getmetafield(L, arg, "__name") == LUA_TSTRING) typearg = lua_tostring(L, -1); /* use the given type name */ else if (lua_type(L, arg) == LUA_TLIGHTUSERDATA) typearg = "light userdata"; /* special name for messages */ else typearg = luaL_typename(L, arg); /* standard name */ msg = lua_pushfstring(L, "%s expected, got %s", tname, typearg); return luaL_argerror(L, arg, msg); } static void tag_error (lua_State *L, int arg, int tag) { typeerror(L, arg, lua_typename(L, tag)); } /* ** The use of 'lua_pushfstring' ensures this function does not ** need reserved stack space when called. */ LUALIB_API void luaL_where (lua_State *L, int level) { lua_Debug ar; if (lua_getstack(L, level, &ar)) { /* check function at level */ lua_getinfo(L, "Sl", &ar); /* get info about it */ if (ar.currentline > 0) { /* is there info? */ lua_pushfstring(L, "%s:%d: ", ar.short_src, ar.currentline); return; } } lua_pushfstring(L, ""); /* else, no information available... */ } /* ** Again, the use of 'lua_pushvfstring' ensures this function does ** not need reserved stack space when called. (At worst, it generates ** an error with "stack overflow" instead of the given message.) */ LUALIB_API int luaL_error (lua_State *L, const char *fmt, ...) { va_list argp; va_start(argp, fmt); luaL_where(L, 1); lua_pushvfstring(L, fmt, argp); va_end(argp); lua_concat(L, 2); return lua_error(L); } LUALIB_API int luaL_fileresult (lua_State *L, int stat, const char *fname) { int en = errno; /* calls to Lua API may change this value */ if (stat) { lua_pushboolean(L, 1); return 1; } else { lua_pushnil(L); if (fname) lua_pushfstring(L, "%s: %s", fname, strerror(en)); else lua_pushstring(L, strerror(en)); lua_pushinteger(L, en); return 3; } } #if !defined(l_inspectstat) /* { */ #if defined(LUA_USE_POSIX) #include /* ** use appropriate macros to interpret 'pclose' return status */ #define l_inspectstat(stat,what) \ if (WIFEXITED(stat)) { stat = WEXITSTATUS(stat); } \ else if (WIFSIGNALED(stat)) { stat = WTERMSIG(stat); what = "signal"; } #else #define l_inspectstat(stat,what) /* no op */ #endif #endif /* } */ LUALIB_API int luaL_execresult (lua_State *L, int stat) { const char *what = "exit"; /* type of termination */ if (stat == -1) /* error? */ return luaL_fileresult(L, 0, NULL); else { l_inspectstat(stat, what); /* interpret result */ if (*what == 'e' && stat == 0) /* successful termination? */ lua_pushboolean(L, 1); else lua_pushnil(L); lua_pushstring(L, what); lua_pushinteger(L, stat); return 3; /* return true/nil,what,code */ } } /* }====================================================== */ /* ** {====================================================== ** Userdata's metatable manipulation ** ======================================================= */ LUALIB_API int luaL_newmetatable (lua_State *L, const char *tname) { if (luaL_getmetatable(L, tname) != LUA_TNIL) /* name already in use? */ return 0; /* leave previous value on top, but return 0 */ lua_pop(L, 1); lua_createtable(L, 0, 2); /* create metatable */ lua_pushstring(L, tname); lua_setfield(L, -2, "__name"); /* metatable.__name = tname */ lua_pushvalue(L, -1); lua_setfield(L, LUA_REGISTRYINDEX, tname); /* registry.name = metatable */ return 1; } LUALIB_API void luaL_setmetatable (lua_State *L, const char *tname) { luaL_getmetatable(L, tname); lua_setmetatable(L, -2); } LUALIB_API void *luaL_testudata (lua_State *L, int ud, const char *tname) { void *p = lua_touserdata(L, ud); if (p != NULL) { /* value is a userdata? */ if (lua_getmetatable(L, ud)) { /* does it have a metatable? */ luaL_getmetatable(L, tname); /* get correct metatable */ if (!lua_rawequal(L, -1, -2)) /* not the same? */ p = NULL; /* value is a userdata with wrong metatable */ lua_pop(L, 2); /* remove both metatables */ return p; } } return NULL; /* value is not a userdata with a metatable */ } LUALIB_API void *luaL_checkudata (lua_State *L, int ud, const char *tname) { void *p = luaL_testudata(L, ud, tname); if (p == NULL) typeerror(L, ud, tname); return p; } /* }====================================================== */ /* ** {====================================================== ** Argument check functions ** ======================================================= */ LUALIB_API int luaL_checkoption (lua_State *L, int arg, const char *def, const char *const lst[]) { const char *name = (def) ? luaL_optstring(L, arg, def) : luaL_checkstring(L, arg); int i; for (i=0; lst[i]; i++) if (strcmp(lst[i], name) == 0) return i; return luaL_argerror(L, arg, lua_pushfstring(L, "invalid option '%s'", name)); } /* ** Ensures the stack has at least 'space' extra slots, raising an error ** if it cannot fulfill the request. (The error handling needs a few ** extra slots to format the error message. In case of an error without ** this extra space, Lua will generate the same 'stack overflow' error, ** but without 'msg'.) */ LUALIB_API void luaL_checkstack (lua_State *L, int space, const char *msg) { if (!lua_checkstack(L, space)) { if (msg) luaL_error(L, "stack overflow (%s)", msg); else luaL_error(L, "stack overflow"); } } LUALIB_API void luaL_checktype (lua_State *L, int arg, int t) { if (lua_type(L, arg) != t) tag_error(L, arg, t); } LUALIB_API void luaL_checkany (lua_State *L, int arg) { if (lua_type(L, arg) == LUA_TNONE) luaL_argerror(L, arg, "value expected"); } LUALIB_API const char *luaL_checklstring (lua_State *L, int arg, size_t *len) { const char *s = lua_tolstring(L, arg, len); if (!s) tag_error(L, arg, LUA_TSTRING); return s; } LUALIB_API const char *luaL_optlstring (lua_State *L, int arg, const char *def, size_t *len) { if (lua_isnoneornil(L, arg)) { if (len) *len = (def ? strlen(def) : 0); return def; } else return luaL_checklstring(L, arg, len); } LUALIB_API lua_Number luaL_checknumber (lua_State *L, int arg) { int isnum; lua_Number d = lua_tonumberx(L, arg, &isnum); if (!isnum) tag_error(L, arg, LUA_TNUMBER); return d; } LUALIB_API lua_Number luaL_optnumber (lua_State *L, int arg, lua_Number def) { return luaL_opt(L, luaL_checknumber, arg, def); } static void interror (lua_State *L, int arg) { if (lua_isnumber(L, arg)) luaL_argerror(L, arg, "number has no integer representation"); else tag_error(L, arg, LUA_TNUMBER); } LUALIB_API lua_Integer luaL_checkinteger (lua_State *L, int arg) { int isnum; lua_Integer d = lua_tointegerx(L, arg, &isnum); if (!isnum) { interror(L, arg); } return d; } LUALIB_API lua_Integer luaL_optinteger (lua_State *L, int arg, lua_Integer def) { return luaL_opt(L, luaL_checkinteger, arg, def); } /* }====================================================== */ /* ** {====================================================== ** Generic Buffer manipulation ** ======================================================= */ /* userdata to box arbitrary data */ typedef struct UBox { void *box; size_t bsize; } UBox; static void *resizebox (lua_State *L, int idx, size_t newsize) { void *ud; lua_Alloc allocf = lua_getallocf(L, &ud); UBox *box = (UBox *)lua_touserdata(L, idx); void *temp = allocf(ud, box->box, box->bsize, newsize); if (temp == NULL && newsize > 0) { /* allocation error? */ resizebox(L, idx, 0); /* free buffer */ luaL_error(L, "not enough memory for buffer allocation"); } box->box = temp; box->bsize = newsize; return temp; } static int boxgc (lua_State *L) { resizebox(L, 1, 0); return 0; } static void *newbox (lua_State *L, size_t newsize) { UBox *box = (UBox *)lua_newuserdata(L, sizeof(UBox)); box->box = NULL; box->bsize = 0; if (luaL_newmetatable(L, "LUABOX")) { /* creating metatable? */ lua_pushcfunction(L, boxgc); lua_setfield(L, -2, "__gc"); /* metatable.__gc = boxgc */ } lua_setmetatable(L, -2); return resizebox(L, -1, newsize); } /* ** check whether buffer is using a userdata on the stack as a temporary ** buffer */ #define buffonstack(B) ((B)->b != (B)->initb) /* ** returns a pointer to a free area with at least 'sz' bytes */ LUALIB_API char *luaL_prepbuffsize (luaL_Buffer *B, size_t sz) { lua_State *L = B->L; if (B->size - B->n < sz) { /* not enough space? */ char *newbuff; size_t newsize = B->size * 2; /* double buffer size */ if (newsize - B->n < sz) /* not big enough? */ newsize = B->n + sz; if (newsize < B->n || newsize - B->n < sz) luaL_error(L, "buffer too large"); /* create larger buffer */ if (buffonstack(B)) newbuff = (char *)resizebox(L, -1, newsize); else { /* no buffer yet */ newbuff = (char *)newbox(L, newsize); memcpy(newbuff, B->b, B->n * sizeof(char)); /* copy original content */ } B->b = newbuff; B->size = newsize; } return &B->b[B->n]; } LUALIB_API void luaL_addlstring (luaL_Buffer *B, const char *s, size_t l) { if (l > 0) { /* avoid 'memcpy' when 's' can be NULL */ char *b = luaL_prepbuffsize(B, l); memcpy(b, s, l * sizeof(char)); luaL_addsize(B, l); } } LUALIB_API void luaL_addstring (luaL_Buffer *B, const char *s) { luaL_addlstring(B, s, strlen(s)); } LUALIB_API void luaL_pushresult (luaL_Buffer *B) { lua_State *L = B->L; lua_pushlstring(L, B->b, B->n); if (buffonstack(B)) { resizebox(L, -2, 0); /* delete old buffer */ lua_remove(L, -2); /* remove its header from the stack */ } } LUALIB_API void luaL_pushresultsize (luaL_Buffer *B, size_t sz) { luaL_addsize(B, sz); luaL_pushresult(B); } LUALIB_API void luaL_addvalue (luaL_Buffer *B) { lua_State *L = B->L; size_t l; const char *s = lua_tolstring(L, -1, &l); if (buffonstack(B)) lua_insert(L, -2); /* put value below buffer */ luaL_addlstring(B, s, l); lua_remove(L, (buffonstack(B)) ? -2 : -1); /* remove value */ } LUALIB_API void luaL_buffinit (lua_State *L, luaL_Buffer *B) { B->L = L; B->b = B->initb; B->n = 0; B->size = LUAL_BUFFERSIZE; } LUALIB_API char *luaL_buffinitsize (lua_State *L, luaL_Buffer *B, size_t sz) { luaL_buffinit(L, B); return luaL_prepbuffsize(B, sz); } /* }====================================================== */ /* ** {====================================================== ** Reference system ** ======================================================= */ /* index of free-list header */ #define freelist 0 LUALIB_API int luaL_ref (lua_State *L, int t) { int ref; if (lua_isnil(L, -1)) { lua_pop(L, 1); /* remove from stack */ return LUA_REFNIL; /* 'nil' has a unique fixed reference */ } t = lua_absindex(L, t); lua_rawgeti(L, t, freelist); /* get first free element */ ref = (int)lua_tointeger(L, -1); /* ref = t[freelist] */ lua_pop(L, 1); /* remove it from stack */ if (ref != 0) { /* any free element? */ lua_rawgeti(L, t, ref); /* remove it from list */ lua_rawseti(L, t, freelist); /* (t[freelist] = t[ref]) */ } else /* no free elements */ ref = (int)lua_rawlen(L, t) + 1; /* get a new reference */ lua_rawseti(L, t, ref); return ref; } LUALIB_API void luaL_unref (lua_State *L, int t, int ref) { if (ref >= 0) { t = lua_absindex(L, t); lua_rawgeti(L, t, freelist); lua_rawseti(L, t, ref); /* t[ref] = t[freelist] */ lua_pushinteger(L, ref); lua_rawseti(L, t, freelist); /* t[freelist] = ref */ } } /* }====================================================== */ /* ** {====================================================== ** Load functions ** ======================================================= */ typedef struct LoadF { int n; /* number of pre-read characters */ FILE *f; /* file being read */ char buff[BUFSIZ]; /* area for reading file */ } LoadF; static const char *getF (lua_State *L, void *ud, size_t *size) { LoadF *lf = (LoadF *)ud; (void)L; /* not used */ if (lf->n > 0) { /* are there pre-read characters to be read? */ *size = lf->n; /* return them (chars already in buffer) */ lf->n = 0; /* no more pre-read characters */ } else { /* read a block from file */ /* 'fread' can return > 0 *and* set the EOF flag. If next call to 'getF' called 'fread', it might still wait for user input. The next check avoids this problem. */ if (feof(lf->f)) return NULL; *size = fread(lf->buff, 1, sizeof(lf->buff), lf->f); /* read block */ } return lf->buff; } static int errfile (lua_State *L, const char *what, int fnameindex) { const char *serr = strerror(errno); const char *filename = lua_tostring(L, fnameindex) + 1; lua_pushfstring(L, "cannot %s %s: %s", what, filename, serr); lua_remove(L, fnameindex); return LUA_ERRFILE; } static int skipBOM (LoadF *lf) { const char *p = "\xEF\xBB\xBF"; /* UTF-8 BOM mark */ int c; lf->n = 0; do { c = getc(lf->f); if (c == EOF || c != *(const unsigned char *)p++) return c; lf->buff[lf->n++] = c; /* to be read by the parser */ } while (*p != '\0'); lf->n = 0; /* prefix matched; discard it */ return getc(lf->f); /* return next character */ } /* ** reads the first character of file 'f' and skips an optional BOM mark ** in its beginning plus its first line if it starts with '#'. Returns ** true if it skipped the first line. In any case, '*cp' has the ** first "valid" character of the file (after the optional BOM and ** a first-line comment). */ static int skipcomment (LoadF *lf, int *cp) { int c = *cp = skipBOM(lf); if (c == '#') { /* first line is a comment (Unix exec. file)? */ do { /* skip first line */ c = getc(lf->f); } while (c != EOF && c != '\n'); *cp = getc(lf->f); /* skip end-of-line, if present */ return 1; /* there was a comment */ } else return 0; /* no comment */ } LUALIB_API int luaL_loadfilex (lua_State *L, const char *filename, const char *mode) { LoadF lf; int status, readstatus; int c; int fnameindex = lua_gettop(L) + 1; /* index of filename on the stack */ if (filename == NULL) { lua_pushliteral(L, "=stdin"); lf.f = stdin; } else { lua_pushfstring(L, "@%s", filename); lf.f = fopen(filename, "r"); if (lf.f == NULL) return errfile(L, "open", fnameindex); } if (skipcomment(&lf, &c)) /* read initial portion */ lf.buff[lf.n++] = '\n'; /* add line to correct line numbers */ if (c == LUA_SIGNATURE[0] && filename) { /* binary file? */ lf.f = freopen(filename, "rb", lf.f); /* reopen in binary mode */ if (lf.f == NULL) return errfile(L, "reopen", fnameindex); skipcomment(&lf, &c); /* re-read initial portion */ } if (c != EOF) lf.buff[lf.n++] = c; /* 'c' is the first character of the stream */ status = lua_load(L, getF, &lf, lua_tostring(L, -1), mode); readstatus = ferror(lf.f); if (filename) fclose(lf.f); /* close file (even in case of errors) */ if (readstatus) { lua_settop(L, fnameindex); /* ignore results from 'lua_load' */ return errfile(L, "read", fnameindex); } lua_remove(L, fnameindex); return status; } typedef struct LoadS { const char *s; size_t size; } LoadS; static const char *getS (lua_State *L, void *ud, size_t *size) { LoadS *ls = (LoadS *)ud; (void)L; /* not used */ if (ls->size == 0) return NULL; *size = ls->size; ls->size = 0; return ls->s; } LUALIB_API int luaL_loadbufferx (lua_State *L, const char *buff, size_t size, const char *name, const char *mode) { LoadS ls; ls.s = buff; ls.size = size; return lua_load(L, getS, &ls, name, mode); } LUALIB_API int luaL_loadstring (lua_State *L, const char *s) { return luaL_loadbuffer(L, s, strlen(s), s); } /* }====================================================== */ LUALIB_API int luaL_getmetafield (lua_State *L, int obj, const char *event) { if (!lua_getmetatable(L, obj)) /* no metatable? */ return LUA_TNIL; else { int tt; lua_pushstring(L, event); tt = lua_rawget(L, -2); if (tt == LUA_TNIL) /* is metafield nil? */ lua_pop(L, 2); /* remove metatable and metafield */ else lua_remove(L, -2); /* remove only metatable */ return tt; /* return metafield type */ } } LUALIB_API int luaL_callmeta (lua_State *L, int obj, const char *event) { obj = lua_absindex(L, obj); if (luaL_getmetafield(L, obj, event) == LUA_TNIL) /* no metafield? */ return 0; lua_pushvalue(L, obj); lua_call(L, 1, 1); return 1; } LUALIB_API lua_Integer luaL_len (lua_State *L, int idx) { lua_Integer l; int isnum; lua_len(L, idx); l = lua_tointegerx(L, -1, &isnum); if (!isnum) luaL_error(L, "object length is not an integer"); lua_pop(L, 1); /* remove object */ return l; } LUALIB_API const char *luaL_tolstring (lua_State *L, int idx, size_t *len) { if (luaL_callmeta(L, idx, "__tostring")) { /* metafield? */ if (!lua_isstring(L, -1)) luaL_error(L, "'__tostring' must return a string"); } else { switch (lua_type(L, idx)) { case LUA_TNUMBER: { if (lua_isinteger(L, idx)) lua_pushfstring(L, "%I", (LUAI_UACINT)lua_tointeger(L, idx)); else lua_pushfstring(L, "%f", (LUAI_UACNUMBER)lua_tonumber(L, idx)); break; } case LUA_TSTRING: lua_pushvalue(L, idx); break; case LUA_TBOOLEAN: lua_pushstring(L, (lua_toboolean(L, idx) ? "true" : "false")); break; case LUA_TNIL: lua_pushliteral(L, "nil"); break; default: { int tt = luaL_getmetafield(L, idx, "__name"); /* try name */ const char *kind = (tt == LUA_TSTRING) ? lua_tostring(L, -1) : luaL_typename(L, idx); lua_pushfstring(L, "%s: %p", kind, lua_topointer(L, idx)); if (tt != LUA_TNIL) lua_remove(L, -2); /* remove '__name' */ break; } } } return lua_tolstring(L, -1, len); } /* ** {====================================================== ** Compatibility with 5.1 module functions ** ======================================================= */ #if defined(LUA_COMPAT_MODULE) static const char *luaL_findtable (lua_State *L, int idx, const char *fname, int szhint) { const char *e; if (idx) lua_pushvalue(L, idx); do { e = strchr(fname, '.'); if (e == NULL) e = fname + strlen(fname); lua_pushlstring(L, fname, e - fname); if (lua_rawget(L, -2) == LUA_TNIL) { /* no such field? */ lua_pop(L, 1); /* remove this nil */ lua_createtable(L, 0, (*e == '.' ? 1 : szhint)); /* new table for field */ lua_pushlstring(L, fname, e - fname); lua_pushvalue(L, -2); lua_settable(L, -4); /* set new table into field */ } else if (!lua_istable(L, -1)) { /* field has a non-table value? */ lua_pop(L, 2); /* remove table and value */ return fname; /* return problematic part of the name */ } lua_remove(L, -2); /* remove previous table */ fname = e + 1; } while (*e == '.'); return NULL; } /* ** Count number of elements in a luaL_Reg list. */ static int libsize (const luaL_Reg *l) { int size = 0; for (; l && l->name; l++) size++; return size; } /* ** Find or create a module table with a given name. The function ** first looks at the LOADED table and, if that fails, try a ** global variable with that name. In any case, leaves on the stack ** the module table. */ LUALIB_API void luaL_pushmodule (lua_State *L, const char *modname, int sizehint) { luaL_findtable(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE, 1); if (lua_getfield(L, -1, modname) != LUA_TTABLE) { /* no LOADED[modname]? */ lua_pop(L, 1); /* remove previous result */ /* try global variable (and create one if it does not exist) */ lua_pushglobaltable(L); if (luaL_findtable(L, 0, modname, sizehint) != NULL) luaL_error(L, "name conflict for module '%s'", modname); lua_pushvalue(L, -1); lua_setfield(L, -3, modname); /* LOADED[modname] = new table */ } lua_remove(L, -2); /* remove LOADED table */ } LUALIB_API void luaL_openlib (lua_State *L, const char *libname, const luaL_Reg *l, int nup) { luaL_checkversion(L); if (libname) { luaL_pushmodule(L, libname, libsize(l)); /* get/create library table */ lua_insert(L, -(nup + 1)); /* move library table to below upvalues */ } if (l) luaL_setfuncs(L, l, nup); else lua_pop(L, nup); /* remove upvalues */ } #endif /* }====================================================== */ /* ** set functions from list 'l' into table at top - 'nup'; each ** function gets the 'nup' elements at the top as upvalues. ** Returns with only the table at the stack. */ LUALIB_API void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) { luaL_checkstack(L, nup, "too many upvalues"); for (; l->name != NULL; l++) { /* fill the table with given functions */ int i; for (i = 0; i < nup; i++) /* copy upvalues to the top */ lua_pushvalue(L, -nup); lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */ lua_setfield(L, -(nup + 2), l->name); } lua_pop(L, nup); /* remove upvalues */ } /* ** ensure that stack[idx][fname] has a table and push that table ** into the stack */ LUALIB_API int luaL_getsubtable (lua_State *L, int idx, const char *fname) { if (lua_getfield(L, idx, fname) == LUA_TTABLE) return 1; /* table already there */ else { lua_pop(L, 1); /* remove previous result */ idx = lua_absindex(L, idx); lua_newtable(L); lua_pushvalue(L, -1); /* copy to be left at top */ lua_setfield(L, idx, fname); /* assign new table to field */ return 0; /* false, because did not find table there */ } } /* ** Stripped-down 'require': After checking "loaded" table, calls 'openf' ** to open a module, registers the result in 'package.loaded' table and, ** if 'glb' is true, also registers the result in the global table. ** Leaves resulting module on the top. */ LUALIB_API void luaL_requiref (lua_State *L, const char *modname, lua_CFunction openf, int glb) { luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE); lua_getfield(L, -1, modname); /* LOADED[modname] */ if (!lua_toboolean(L, -1)) { /* package not already loaded? */ lua_pop(L, 1); /* remove field */ lua_pushcfunction(L, openf); lua_pushstring(L, modname); /* argument to open function */ lua_call(L, 1, 1); /* call 'openf' to open module */ lua_pushvalue(L, -1); /* make copy of module (call result) */ lua_setfield(L, -3, modname); /* LOADED[modname] = module */ } lua_remove(L, -2); /* remove LOADED table */ if (glb) { lua_pushvalue(L, -1); /* copy of module */ lua_setglobal(L, modname); /* _G[modname] = module */ } } LUALIB_API const char *luaL_gsub (lua_State *L, const char *s, const char *p, const char *r) { const char *wild; size_t l = strlen(p); luaL_Buffer b; luaL_buffinit(L, &b); while ((wild = strstr(s, p)) != NULL) { luaL_addlstring(&b, s, wild - s); /* push prefix */ luaL_addstring(&b, r); /* push replacement in place of pattern */ s = wild + l; /* continue after 'p' */ } luaL_addstring(&b, s); /* push last suffix */ luaL_pushresult(&b); return lua_tostring(L, -1); } static void *l_alloc (void *ud, void *ptr, size_t osize, size_t nsize) { (void)ud; (void)osize; /* not used */ if (nsize == 0) { free(ptr); return NULL; } else return realloc(ptr, nsize); } static int panic (lua_State *L) { //lua_writestringerror("PANIC: unprotected error in call to Lua API (%s)\n", lua_tostring(L, -1)); lua_writestringerror("PANIC: unprotected error in call to Lua API\n"); return 0; /* return to Lua to abort */ } LUALIB_API lua_State *luaL_newstate (void) { lua_State *L = lua_newstate(l_alloc, NULL); if (L) lua_atpanic(L, &panic); return L; } LUALIB_API void luaL_checkversion_ (lua_State *L, lua_Number ver, size_t sz) { const lua_Number *v = lua_version(L); if (sz != LUAL_NUMSIZES) /* check numeric types */ luaL_error(L, "core and library have incompatible numeric types"); if (v != lua_version(NULL)) luaL_error(L, "multiple Lua VMs detected"); else if (*v != ver) luaL_error(L, "version mismatch: app. needs %f, Lua core provides %f", (LUAI_UACNUMBER)ver, (LUAI_UACNUMBER)*v); } ================================================ FILE: Tests/ApiExplorer/lua/src/lauxlib.h ================================================ /* ** $Id: lauxlib.h,v 1.131.1.1 2017/04/19 17:20:42 roberto Exp $ ** Auxiliary functions for building Lua libraries ** See Copyright Notice in lua.h */ #ifndef lauxlib_h #define lauxlib_h #include #include #include "lua.h" /* extra error code for 'luaL_loadfilex' */ #define LUA_ERRFILE (LUA_ERRERR+1) /* key, in the registry, for table of loaded modules */ #define LUA_LOADED_TABLE "_LOADED" /* key, in the registry, for table of preloaded loaders */ #define LUA_PRELOAD_TABLE "_PRELOAD" typedef struct luaL_Reg { const char *name; lua_CFunction func; } luaL_Reg; #define LUAL_NUMSIZES (sizeof(lua_Integer)*16 + sizeof(lua_Number)) LUALIB_API void (luaL_checkversion_) (lua_State *L, lua_Number ver, size_t sz); #define luaL_checkversion(L) \ luaL_checkversion_(L, LUA_VERSION_NUM, LUAL_NUMSIZES) LUALIB_API int (luaL_getmetafield) (lua_State *L, int obj, const char *e); LUALIB_API int (luaL_callmeta) (lua_State *L, int obj, const char *e); LUALIB_API const char *(luaL_tolstring) (lua_State *L, int idx, size_t *len); LUALIB_API int (luaL_argerror) (lua_State *L, int arg, const char *extramsg); LUALIB_API const char *(luaL_checklstring) (lua_State *L, int arg, size_t *l); LUALIB_API const char *(luaL_optlstring) (lua_State *L, int arg, const char *def, size_t *l); LUALIB_API lua_Number (luaL_checknumber) (lua_State *L, int arg); LUALIB_API lua_Number (luaL_optnumber) (lua_State *L, int arg, lua_Number def); LUALIB_API lua_Integer (luaL_checkinteger) (lua_State *L, int arg); LUALIB_API lua_Integer (luaL_optinteger) (lua_State *L, int arg, lua_Integer def); LUALIB_API void (luaL_checkstack) (lua_State *L, int sz, const char *msg); LUALIB_API void (luaL_checktype) (lua_State *L, int arg, int t); LUALIB_API void (luaL_checkany) (lua_State *L, int arg); LUALIB_API int (luaL_newmetatable) (lua_State *L, const char *tname); LUALIB_API void (luaL_setmetatable) (lua_State *L, const char *tname); LUALIB_API void *(luaL_testudata) (lua_State *L, int ud, const char *tname); LUALIB_API void *(luaL_checkudata) (lua_State *L, int ud, const char *tname); LUALIB_API void (luaL_where) (lua_State *L, int lvl); LUALIB_API int (luaL_error) (lua_State *L, const char *fmt, ...); LUALIB_API int (luaL_checkoption) (lua_State *L, int arg, const char *def, const char *const lst[]); LUALIB_API int (luaL_fileresult) (lua_State *L, int stat, const char *fname); LUALIB_API int (luaL_execresult) (lua_State *L, int stat); /* predefined references */ #define LUA_NOREF (-2) #define LUA_REFNIL (-1) LUALIB_API int (luaL_ref) (lua_State *L, int t); LUALIB_API void (luaL_unref) (lua_State *L, int t, int ref); LUALIB_API int (luaL_loadfilex) (lua_State *L, const char *filename, const char *mode); #define luaL_loadfile(L,f) luaL_loadfilex(L,f,NULL) LUALIB_API int (luaL_loadbufferx) (lua_State *L, const char *buff, size_t sz, const char *name, const char *mode); LUALIB_API int (luaL_loadstring) (lua_State *L, const char *s); LUALIB_API lua_State *(luaL_newstate) (void); LUALIB_API lua_Integer (luaL_len) (lua_State *L, int idx); LUALIB_API const char *(luaL_gsub) (lua_State *L, const char *s, const char *p, const char *r); LUALIB_API void (luaL_setfuncs) (lua_State *L, const luaL_Reg *l, int nup); LUALIB_API int (luaL_getsubtable) (lua_State *L, int idx, const char *fname); LUALIB_API void (luaL_traceback) (lua_State *L, lua_State *L1, const char *msg, int level); LUALIB_API void (luaL_requiref) (lua_State *L, const char *modname, lua_CFunction openf, int glb); /* ** =============================================================== ** some useful macros ** =============================================================== */ #define luaL_newlibtable(L,l) \ lua_createtable(L, 0, sizeof(l)/sizeof((l)[0]) - 1) #define luaL_newlib(L,l) \ (luaL_checkversion(L), luaL_newlibtable(L,l), luaL_setfuncs(L,l,0)) #define luaL_argcheck(L, cond,arg,extramsg) \ ((void)((cond) || luaL_argerror(L, (arg), (extramsg)))) #define luaL_checkstring(L,n) (luaL_checklstring(L, (n), NULL)) #define luaL_optstring(L,n,d) (luaL_optlstring(L, (n), (d), NULL)) #define luaL_typename(L,i) lua_typename(L, lua_type(L,(i))) #define luaL_dofile(L, fn) \ (luaL_loadfile(L, fn) || lua_pcall(L, 0, LUA_MULTRET, 0)) #define luaL_dostring(L, s) \ (luaL_loadstring(L, s) || lua_pcall(L, 0, LUA_MULTRET, 0)) #define luaL_getmetatable(L,n) (lua_getfield(L, LUA_REGISTRYINDEX, (n))) #define luaL_opt(L,f,n,d) (lua_isnoneornil(L,(n)) ? (d) : f(L,(n))) #define luaL_loadbuffer(L,s,sz,n) luaL_loadbufferx(L,s,sz,n,NULL) /* ** {====================================================== ** Generic Buffer manipulation ** ======================================================= */ typedef struct luaL_Buffer { char *b; /* buffer address */ size_t size; /* buffer size */ size_t n; /* number of characters in buffer */ lua_State *L; char initb[LUAL_BUFFERSIZE]; /* initial buffer */ } luaL_Buffer; #define luaL_addchar(B,c) \ ((void)((B)->n < (B)->size || luaL_prepbuffsize((B), 1)), \ ((B)->b[(B)->n++] = (c))) #define luaL_addsize(B,s) ((B)->n += (s)) LUALIB_API void (luaL_buffinit) (lua_State *L, luaL_Buffer *B); LUALIB_API char *(luaL_prepbuffsize) (luaL_Buffer *B, size_t sz); LUALIB_API void (luaL_addlstring) (luaL_Buffer *B, const char *s, size_t l); LUALIB_API void (luaL_addstring) (luaL_Buffer *B, const char *s); LUALIB_API void (luaL_addvalue) (luaL_Buffer *B); LUALIB_API void (luaL_pushresult) (luaL_Buffer *B); LUALIB_API void (luaL_pushresultsize) (luaL_Buffer *B, size_t sz); LUALIB_API char *(luaL_buffinitsize) (lua_State *L, luaL_Buffer *B, size_t sz); #define luaL_prepbuffer(B) luaL_prepbuffsize(B, LUAL_BUFFERSIZE) /* }====================================================== */ /* ** {====================================================== ** File handles for IO library ** ======================================================= */ /* ** A file handle is a userdata with metatable 'LUA_FILEHANDLE' and ** initial structure 'luaL_Stream' (it may contain other fields ** after that initial structure). */ #define LUA_FILEHANDLE "FILE*" typedef struct luaL_Stream { FILE *f; /* stream (NULL for incompletely created streams) */ lua_CFunction closef; /* to close stream (NULL for closed streams) */ } luaL_Stream; /* }====================================================== */ /* compatibility with old module system */ #if defined(LUA_COMPAT_MODULE) LUALIB_API void (luaL_pushmodule) (lua_State *L, const char *modname, int sizehint); LUALIB_API void (luaL_openlib) (lua_State *L, const char *libname, const luaL_Reg *l, int nup); #define luaL_register(L,n,l) (luaL_openlib(L,(n),(l),0)) #endif /* ** {================================================================== ** "Abstraction Layer" for basic report of messages and errors ** =================================================================== */ /* print a string */ #if !defined(lua_writestring) //#define lua_writestring(s,l) fwrite((s), sizeof(char), (l), stdout) #define lua_writestring(s,l) LogCat(true, s); #endif /* print a newline and flush the output */ #if !defined(lua_writeline) #define lua_writeline() LogCat(true, "\n"); // (lua_writestring("\n", 1), fflush(stdout)) #endif /* print an error message */ #if !defined(lua_writestringerror) //#define lua_writestringerror(s,p) (fprintf(stderr, (s), (p)), fflush(stderr)) #define lua_writestringerror(s) LogCat(true, s); #endif /* }================================================================== */ /* ** {============================================================ ** Compatibility with deprecated conversions ** ============================================================= */ #if defined(LUA_COMPAT_APIINTCASTS) #define luaL_checkunsigned(L,a) ((lua_Unsigned)luaL_checkinteger(L,a)) #define luaL_optunsigned(L,a,d) \ ((lua_Unsigned)luaL_optinteger(L,a,(lua_Integer)(d))) #define luaL_checkint(L,n) ((int)luaL_checkinteger(L, (n))) #define luaL_optint(L,n,d) ((int)luaL_optinteger(L, (n), (d))) #define luaL_checklong(L,n) ((long)luaL_checkinteger(L, (n))) #define luaL_optlong(L,n,d) ((long)luaL_optinteger(L, (n), (d))) #endif /* }============================================================ */ #endif ================================================ FILE: Tests/ApiExplorer/lua/src/lbaselib.c ================================================ /* ** $Id: lbaselib.c,v 1.314.1.1 2017/04/19 17:39:34 roberto Exp $ ** Basic library ** See Copyright Notice in lua.h */ #define lbaselib_c #define LUA_LIB #include "lprefix.h" #include #include #include #include #include "lua.h" #include "lauxlib.h" #include "lualib.h" static int luaB_print (lua_State *L) { int n = lua_gettop(L); /* number of arguments */ int i; lua_getglobal(L, "tostring"); for (i=1; i<=n; i++) { const char *s; size_t l; lua_pushvalue(L, -1); /* function to be called */ lua_pushvalue(L, i); /* value to print */ lua_call(L, 1, 1); s = lua_tolstring(L, -1, &l); /* get result */ if (s == NULL) return luaL_error(L, "'tostring' must return a string to 'print'"); if (i>1) lua_writestring("\t", 1); lua_writestring(s, l); lua_pop(L, 1); /* pop result */ } lua_writeline(); return 0; } #define SPACECHARS " \f\n\r\t\v" static const char *b_str2int (const char *s, int base, lua_Integer *pn) { lua_Unsigned n = 0; int neg = 0; s += strspn(s, SPACECHARS); /* skip initial spaces */ if (*s == '-') { s++; neg = 1; } /* handle signal */ else if (*s == '+') s++; if (!isalnum((unsigned char)*s)) /* no digit? */ return NULL; do { int digit = (isdigit((unsigned char)*s)) ? *s - '0' : (toupper((unsigned char)*s) - 'A') + 10; if (digit >= base) return NULL; /* invalid numeral */ n = n * base + digit; s++; } while (isalnum((unsigned char)*s)); s += strspn(s, SPACECHARS); /* skip trailing spaces */ *pn = (lua_Integer)((neg) ? (0u - n) : n); return s; } static int luaB_tonumber (lua_State *L) { if (lua_isnoneornil(L, 2)) { /* standard conversion? */ luaL_checkany(L, 1); if (lua_type(L, 1) == LUA_TNUMBER) { /* already a number? */ lua_settop(L, 1); /* yes; return it */ return 1; } else { size_t l; const char *s = lua_tolstring(L, 1, &l); if (s != NULL && lua_stringtonumber(L, s) == l + 1) return 1; /* successful conversion to number */ /* else not a number */ } } else { size_t l; const char *s; lua_Integer n = 0; /* to avoid warnings */ lua_Integer base = luaL_checkinteger(L, 2); luaL_checktype(L, 1, LUA_TSTRING); /* no numbers as strings */ s = lua_tolstring(L, 1, &l); luaL_argcheck(L, 2 <= base && base <= 36, 2, "base out of range"); if (b_str2int(s, (int)base, &n) == s + l) { lua_pushinteger(L, n); return 1; } /* else not a number */ } /* else not a number */ lua_pushnil(L); /* not a number */ return 1; } static int luaB_error (lua_State *L) { int level = (int)luaL_optinteger(L, 2, 1); lua_settop(L, 1); if (lua_type(L, 1) == LUA_TSTRING && level > 0) { luaL_where(L, level); /* add extra information */ lua_pushvalue(L, 1); lua_concat(L, 2); } return lua_error(L); } static int luaB_getmetatable (lua_State *L) { luaL_checkany(L, 1); if (!lua_getmetatable(L, 1)) { lua_pushnil(L); return 1; /* no metatable */ } luaL_getmetafield(L, 1, "__metatable"); return 1; /* returns either __metatable field (if present) or metatable */ } static int luaB_setmetatable (lua_State *L) { int t = lua_type(L, 2); luaL_checktype(L, 1, LUA_TTABLE); luaL_argcheck(L, t == LUA_TNIL || t == LUA_TTABLE, 2, "nil or table expected"); if (luaL_getmetafield(L, 1, "__metatable") != LUA_TNIL) return luaL_error(L, "cannot change a protected metatable"); lua_settop(L, 2); lua_setmetatable(L, 1); return 1; } static int luaB_rawequal (lua_State *L) { luaL_checkany(L, 1); luaL_checkany(L, 2); lua_pushboolean(L, lua_rawequal(L, 1, 2)); return 1; } static int luaB_rawlen (lua_State *L) { int t = lua_type(L, 1); luaL_argcheck(L, t == LUA_TTABLE || t == LUA_TSTRING, 1, "table or string expected"); lua_pushinteger(L, lua_rawlen(L, 1)); return 1; } static int luaB_rawget (lua_State *L) { luaL_checktype(L, 1, LUA_TTABLE); luaL_checkany(L, 2); lua_settop(L, 2); lua_rawget(L, 1); return 1; } static int luaB_rawset (lua_State *L) { luaL_checktype(L, 1, LUA_TTABLE); luaL_checkany(L, 2); luaL_checkany(L, 3); lua_settop(L, 3); lua_rawset(L, 1); return 1; } static int luaB_collectgarbage (lua_State *L) { static const char *const opts[] = {"stop", "restart", "collect", "count", "step", "setpause", "setstepmul", "isrunning", NULL}; static const int optsnum[] = {LUA_GCSTOP, LUA_GCRESTART, LUA_GCCOLLECT, LUA_GCCOUNT, LUA_GCSTEP, LUA_GCSETPAUSE, LUA_GCSETSTEPMUL, LUA_GCISRUNNING}; int o = optsnum[luaL_checkoption(L, 1, "collect", opts)]; int ex = (int)luaL_optinteger(L, 2, 0); int res = lua_gc(L, o, ex); switch (o) { case LUA_GCCOUNT: { int b = lua_gc(L, LUA_GCCOUNTB, 0); lua_pushnumber(L, (lua_Number)res + ((lua_Number)b/1024)); return 1; } case LUA_GCSTEP: case LUA_GCISRUNNING: { lua_pushboolean(L, res); return 1; } default: { lua_pushinteger(L, res); return 1; } } } static int luaB_type (lua_State *L) { int t = lua_type(L, 1); luaL_argcheck(L, t != LUA_TNONE, 1, "value expected"); lua_pushstring(L, lua_typename(L, t)); return 1; } static int pairsmeta (lua_State *L, const char *method, int iszero, lua_CFunction iter) { luaL_checkany(L, 1); if (luaL_getmetafield(L, 1, method) == LUA_TNIL) { /* no metamethod? */ lua_pushcfunction(L, iter); /* will return generator, */ lua_pushvalue(L, 1); /* state, */ if (iszero) lua_pushinteger(L, 0); /* and initial value */ else lua_pushnil(L); } else { lua_pushvalue(L, 1); /* argument 'self' to metamethod */ lua_call(L, 1, 3); /* get 3 values from metamethod */ } return 3; } static int luaB_next (lua_State *L) { luaL_checktype(L, 1, LUA_TTABLE); lua_settop(L, 2); /* create a 2nd argument if there isn't one */ if (lua_next(L, 1)) return 2; else { lua_pushnil(L); return 1; } } static int luaB_pairs (lua_State *L) { return pairsmeta(L, "__pairs", 0, luaB_next); } /* ** Traversal function for 'ipairs' */ static int ipairsaux (lua_State *L) { lua_Integer i = luaL_checkinteger(L, 2) + 1; lua_pushinteger(L, i); return (lua_geti(L, 1, i) == LUA_TNIL) ? 1 : 2; } /* ** 'ipairs' function. Returns 'ipairsaux', given "table", 0. ** (The given "table" may not be a table.) */ static int luaB_ipairs (lua_State *L) { #if defined(LUA_COMPAT_IPAIRS) return pairsmeta(L, "__ipairs", 1, ipairsaux); #else luaL_checkany(L, 1); lua_pushcfunction(L, ipairsaux); /* iteration function */ lua_pushvalue(L, 1); /* state */ lua_pushinteger(L, 0); /* initial value */ return 3; #endif } static int load_aux (lua_State *L, int status, int envidx) { if (status == LUA_OK) { if (envidx != 0) { /* 'env' parameter? */ lua_pushvalue(L, envidx); /* environment for loaded function */ if (!lua_setupvalue(L, -2, 1)) /* set it as 1st upvalue */ lua_pop(L, 1); /* remove 'env' if not used by previous call */ } return 1; } else { /* error (message is on top of the stack) */ lua_pushnil(L); lua_insert(L, -2); /* put before error message */ return 2; /* return nil plus error message */ } } static int luaB_loadfile (lua_State *L) { const char *fname = luaL_optstring(L, 1, NULL); const char *mode = luaL_optstring(L, 2, NULL); int env = (!lua_isnone(L, 3) ? 3 : 0); /* 'env' index or 0 if no 'env' */ int status = luaL_loadfilex(L, fname, mode); return load_aux(L, status, env); } /* ** {====================================================== ** Generic Read function ** ======================================================= */ /* ** reserved slot, above all arguments, to hold a copy of the returned ** string to avoid it being collected while parsed. 'load' has four ** optional arguments (chunk, source name, mode, and environment). */ #define RESERVEDSLOT 5 /* ** Reader for generic 'load' function: 'lua_load' uses the ** stack for internal stuff, so the reader cannot change the ** stack top. Instead, it keeps its resulting string in a ** reserved slot inside the stack. */ static const char *generic_reader (lua_State *L, void *ud, size_t *size) { (void)(ud); /* not used */ luaL_checkstack(L, 2, "too many nested functions"); lua_pushvalue(L, 1); /* get function */ lua_call(L, 0, 1); /* call it */ if (lua_isnil(L, -1)) { lua_pop(L, 1); /* pop result */ *size = 0; return NULL; } else if (!lua_isstring(L, -1)) luaL_error(L, "reader function must return a string"); lua_replace(L, RESERVEDSLOT); /* save string in reserved slot */ return lua_tolstring(L, RESERVEDSLOT, size); } static int luaB_load (lua_State *L) { int status; size_t l; const char *s = lua_tolstring(L, 1, &l); const char *mode = luaL_optstring(L, 3, "bt"); int env = (!lua_isnone(L, 4) ? 4 : 0); /* 'env' index or 0 if no 'env' */ if (s != NULL) { /* loading a string? */ const char *chunkname = luaL_optstring(L, 2, s); status = luaL_loadbufferx(L, s, l, chunkname, mode); } else { /* loading from a reader function */ const char *chunkname = luaL_optstring(L, 2, "=(load)"); luaL_checktype(L, 1, LUA_TFUNCTION); lua_settop(L, RESERVEDSLOT); /* create reserved slot */ status = lua_load(L, generic_reader, NULL, chunkname, mode); } return load_aux(L, status, env); } /* }====================================================== */ static int dofilecont (lua_State *L, int d1, lua_KContext d2) { (void)d1; (void)d2; /* only to match 'lua_Kfunction' prototype */ return lua_gettop(L) - 1; } static int luaB_dofile (lua_State *L) { const char *fname = luaL_optstring(L, 1, NULL); lua_settop(L, 1); if (luaL_loadfile(L, fname) != LUA_OK) return lua_error(L); lua_callk(L, 0, LUA_MULTRET, 0, dofilecont); return dofilecont(L, 0, 0); } static int luaB_assert (lua_State *L) { if (lua_toboolean(L, 1)) /* condition is true? */ return lua_gettop(L); /* return all arguments */ else { /* error */ luaL_checkany(L, 1); /* there must be a condition */ lua_remove(L, 1); /* remove it */ lua_pushliteral(L, "assertion failed!"); /* default message */ lua_settop(L, 1); /* leave only message (default if no other one) */ return luaB_error(L); /* call 'error' */ } } static int luaB_select (lua_State *L) { int n = lua_gettop(L); if (lua_type(L, 1) == LUA_TSTRING && *lua_tostring(L, 1) == '#') { lua_pushinteger(L, n-1); return 1; } else { lua_Integer i = luaL_checkinteger(L, 1); if (i < 0) i = n + i; else if (i > n) i = n; luaL_argcheck(L, 1 <= i, 1, "index out of range"); return n - (int)i; } } /* ** Continuation function for 'pcall' and 'xpcall'. Both functions ** already pushed a 'true' before doing the call, so in case of success ** 'finishpcall' only has to return everything in the stack minus ** 'extra' values (where 'extra' is exactly the number of items to be ** ignored). */ static int finishpcall (lua_State *L, int status, lua_KContext extra) { if (status != LUA_OK && status != LUA_YIELD) { /* error? */ lua_pushboolean(L, 0); /* first result (false) */ lua_pushvalue(L, -2); /* error message */ return 2; /* return false, msg */ } else return lua_gettop(L) - (int)extra; /* return all results */ } static int luaB_pcall (lua_State *L) { int status; luaL_checkany(L, 1); lua_pushboolean(L, 1); /* first result if no errors */ lua_insert(L, 1); /* put it in place */ status = lua_pcallk(L, lua_gettop(L) - 2, LUA_MULTRET, 0, 0, finishpcall); return finishpcall(L, status, 0); } /* ** Do a protected call with error handling. After 'lua_rotate', the ** stack will have ; so, the function passes ** 2 to 'finishpcall' to skip the 2 first values when returning results. */ static int luaB_xpcall (lua_State *L) { int status; int n = lua_gettop(L); luaL_checktype(L, 2, LUA_TFUNCTION); /* check error function */ lua_pushboolean(L, 1); /* first result */ lua_pushvalue(L, 1); /* function */ lua_rotate(L, 3, 2); /* move them below function's arguments */ status = lua_pcallk(L, n - 2, LUA_MULTRET, 2, 2, finishpcall); return finishpcall(L, status, 2); } static int luaB_tostring (lua_State *L) { luaL_checkany(L, 1); luaL_tolstring(L, 1, NULL); return 1; } static const luaL_Reg base_funcs[] = { {"assert", luaB_assert}, {"collectgarbage", luaB_collectgarbage}, {"dofile", luaB_dofile}, {"error", luaB_error}, {"getmetatable", luaB_getmetatable}, {"ipairs", luaB_ipairs}, {"loadfile", luaB_loadfile}, {"load", luaB_load}, #if defined(LUA_COMPAT_LOADSTRING) {"loadstring", luaB_load}, #endif {"next", luaB_next}, {"pairs", luaB_pairs}, {"pcall", luaB_pcall}, {"print", luaB_print}, {"rawequal", luaB_rawequal}, {"rawlen", luaB_rawlen}, {"rawget", luaB_rawget}, {"rawset", luaB_rawset}, {"select", luaB_select}, {"setmetatable", luaB_setmetatable}, {"tonumber", luaB_tonumber}, {"tostring", luaB_tostring}, {"type", luaB_type}, {"xpcall", luaB_xpcall}, /* placeholders */ {"_G", NULL}, {"_VERSION", NULL}, {NULL, NULL} }; LUAMOD_API int luaopen_base (lua_State *L) { /* open lib into global table */ lua_pushglobaltable(L); luaL_setfuncs(L, base_funcs, 0); /* set global _G */ lua_pushvalue(L, -1); lua_setfield(L, -2, "_G"); /* set global _VERSION */ lua_pushliteral(L, LUA_VERSION); lua_setfield(L, -2, "_VERSION"); return 1; } ================================================ FILE: Tests/ApiExplorer/lua/src/lbitlib.c ================================================ /* ** $Id: lbitlib.c,v 1.30.1.1 2017/04/19 17:20:42 roberto Exp $ ** Standard library for bitwise operations ** See Copyright Notice in lua.h */ #define lbitlib_c #define LUA_LIB #include "lprefix.h" #include "lua.h" #include "lauxlib.h" #include "lualib.h" #if defined(LUA_COMPAT_BITLIB) /* { */ #define pushunsigned(L,n) lua_pushinteger(L, (lua_Integer)(n)) #define checkunsigned(L,i) ((lua_Unsigned)luaL_checkinteger(L,i)) /* number of bits to consider in a number */ #if !defined(LUA_NBITS) #define LUA_NBITS 32 #endif /* ** a lua_Unsigned with its first LUA_NBITS bits equal to 1. (Shift must ** be made in two parts to avoid problems when LUA_NBITS is equal to the ** number of bits in a lua_Unsigned.) */ #define ALLONES (~(((~(lua_Unsigned)0) << (LUA_NBITS - 1)) << 1)) /* macro to trim extra bits */ #define trim(x) ((x) & ALLONES) /* builds a number with 'n' ones (1 <= n <= LUA_NBITS) */ #define mask(n) (~((ALLONES << 1) << ((n) - 1))) static lua_Unsigned andaux (lua_State *L) { int i, n = lua_gettop(L); lua_Unsigned r = ~(lua_Unsigned)0; for (i = 1; i <= n; i++) r &= checkunsigned(L, i); return trim(r); } static int b_and (lua_State *L) { lua_Unsigned r = andaux(L); pushunsigned(L, r); return 1; } static int b_test (lua_State *L) { lua_Unsigned r = andaux(L); lua_pushboolean(L, r != 0); return 1; } static int b_or (lua_State *L) { int i, n = lua_gettop(L); lua_Unsigned r = 0; for (i = 1; i <= n; i++) r |= checkunsigned(L, i); pushunsigned(L, trim(r)); return 1; } static int b_xor (lua_State *L) { int i, n = lua_gettop(L); lua_Unsigned r = 0; for (i = 1; i <= n; i++) r ^= checkunsigned(L, i); pushunsigned(L, trim(r)); return 1; } static int b_not (lua_State *L) { lua_Unsigned r = ~checkunsigned(L, 1); pushunsigned(L, trim(r)); return 1; } static int b_shift (lua_State *L, lua_Unsigned r, lua_Integer i) { if (i < 0) { /* shift right? */ i = -i; r = trim(r); if (i >= LUA_NBITS) r = 0; else r >>= i; } else { /* shift left */ if (i >= LUA_NBITS) r = 0; else r <<= i; r = trim(r); } pushunsigned(L, r); return 1; } static int b_lshift (lua_State *L) { return b_shift(L, checkunsigned(L, 1), luaL_checkinteger(L, 2)); } static int b_rshift (lua_State *L) { return b_shift(L, checkunsigned(L, 1), -luaL_checkinteger(L, 2)); } static int b_arshift (lua_State *L) { lua_Unsigned r = checkunsigned(L, 1); lua_Integer i = luaL_checkinteger(L, 2); if (i < 0 || !(r & ((lua_Unsigned)1 << (LUA_NBITS - 1)))) return b_shift(L, r, -i); else { /* arithmetic shift for 'negative' number */ if (i >= LUA_NBITS) r = ALLONES; else r = trim((r >> i) | ~(trim(~(lua_Unsigned)0) >> i)); /* add signal bit */ pushunsigned(L, r); return 1; } } static int b_rot (lua_State *L, lua_Integer d) { lua_Unsigned r = checkunsigned(L, 1); int i = d & (LUA_NBITS - 1); /* i = d % NBITS */ r = trim(r); if (i != 0) /* avoid undefined shift of LUA_NBITS when i == 0 */ r = (r << i) | (r >> (LUA_NBITS - i)); pushunsigned(L, trim(r)); return 1; } static int b_lrot (lua_State *L) { return b_rot(L, luaL_checkinteger(L, 2)); } static int b_rrot (lua_State *L) { return b_rot(L, -luaL_checkinteger(L, 2)); } /* ** get field and width arguments for field-manipulation functions, ** checking whether they are valid. ** ('luaL_error' called without 'return' to avoid later warnings about ** 'width' being used uninitialized.) */ static int fieldargs (lua_State *L, int farg, int *width) { lua_Integer f = luaL_checkinteger(L, farg); lua_Integer w = luaL_optinteger(L, farg + 1, 1); luaL_argcheck(L, 0 <= f, farg, "field cannot be negative"); luaL_argcheck(L, 0 < w, farg + 1, "width must be positive"); if (f + w > LUA_NBITS) luaL_error(L, "trying to access non-existent bits"); *width = (int)w; return (int)f; } static int b_extract (lua_State *L) { int w; lua_Unsigned r = trim(checkunsigned(L, 1)); int f = fieldargs(L, 2, &w); r = (r >> f) & mask(w); pushunsigned(L, r); return 1; } static int b_replace (lua_State *L) { int w; lua_Unsigned r = trim(checkunsigned(L, 1)); lua_Unsigned v = trim(checkunsigned(L, 2)); int f = fieldargs(L, 3, &w); lua_Unsigned m = mask(w); r = (r & ~(m << f)) | ((v & m) << f); pushunsigned(L, r); return 1; } static const luaL_Reg bitlib[] = { {"arshift", b_arshift}, {"band", b_and}, {"bnot", b_not}, {"bor", b_or}, {"bxor", b_xor}, {"btest", b_test}, {"extract", b_extract}, {"lrotate", b_lrot}, {"lshift", b_lshift}, {"replace", b_replace}, {"rrotate", b_rrot}, {"rshift", b_rshift}, {NULL, NULL} }; LUAMOD_API int luaopen_bit32 (lua_State *L) { luaL_newlib(L, bitlib); return 1; } #else /* }{ */ LUAMOD_API int luaopen_bit32 (lua_State *L) { return luaL_error(L, "library 'bit32' has been deprecated"); } #endif /* } */ ================================================ FILE: Tests/ApiExplorer/lua/src/lcode.c ================================================ /* ** $Id: lcode.c,v 2.112.1.1 2017/04/19 17:20:42 roberto Exp $ ** Code generator for Lua ** See Copyright Notice in lua.h */ #pragma warning(disable: 4244) #define lcode_c #define LUA_CORE #include "lprefix.h" #include #include #include "lua.h" #include "lcode.h" #include "ldebug.h" #include "ldo.h" #include "lgc.h" #include "llex.h" #include "lmem.h" #include "lobject.h" #include "lopcodes.h" #include "lparser.h" #include "lstring.h" #include "ltable.h" #include "lvm.h" /* Maximum number of registers in a Lua function (must fit in 8 bits) */ #define MAXREGS 255 #define hasjumps(e) ((e)->t != (e)->f) /* ** If expression is a numeric constant, fills 'v' with its value ** and returns 1. Otherwise, returns 0. */ static int tonumeral(const expdesc *e, TValue *v) { if (hasjumps(e)) return 0; /* not a numeral */ switch (e->k) { case VKINT: if (v) setivalue(v, e->u.ival); return 1; case VKFLT: if (v) setfltvalue(v, e->u.nval); return 1; default: return 0; } } /* ** Create a OP_LOADNIL instruction, but try to optimize: if the previous ** instruction is also OP_LOADNIL and ranges are compatible, adjust ** range of previous instruction instead of emitting a new one. (For ** instance, 'local a; local b' will generate a single opcode.) */ void luaK_nil (FuncState *fs, int from, int n) { Instruction *previous; int l = from + n - 1; /* last register to set nil */ if (fs->pc > fs->lasttarget) { /* no jumps to current position? */ previous = &fs->f->code[fs->pc-1]; if (GET_OPCODE(*previous) == OP_LOADNIL) { /* previous is LOADNIL? */ int pfrom = GETARG_A(*previous); /* get previous range */ int pl = pfrom + GETARG_B(*previous); if ((pfrom <= from && from <= pl + 1) || (from <= pfrom && pfrom <= l + 1)) { /* can connect both? */ if (pfrom < from) from = pfrom; /* from = min(from, pfrom) */ if (pl > l) l = pl; /* l = max(l, pl) */ SETARG_A(*previous, from); SETARG_B(*previous, l - from); return; } } /* else go through */ } luaK_codeABC(fs, OP_LOADNIL, from, n - 1, 0); /* else no optimization */ } /* ** Gets the destination address of a jump instruction. Used to traverse ** a list of jumps. */ static int getjump (FuncState *fs, int pc) { int offset = GETARG_sBx(fs->f->code[pc]); if (offset == NO_JUMP) /* point to itself represents end of list */ return NO_JUMP; /* end of list */ else return (pc+1)+offset; /* turn offset into absolute position */ } /* ** Fix jump instruction at position 'pc' to jump to 'dest'. ** (Jump addresses are relative in Lua) */ static void fixjump (FuncState *fs, int pc, int dest) { Instruction *jmp = &fs->f->code[pc]; int offset = dest - (pc + 1); lua_assert(dest != NO_JUMP); if (abs(offset) > MAXARG_sBx) luaX_syntaxerror(fs->ls, "control structure too long"); SETARG_sBx(*jmp, offset); } /* ** Concatenate jump-list 'l2' into jump-list 'l1' */ void luaK_concat (FuncState *fs, int *l1, int l2) { if (l2 == NO_JUMP) return; /* nothing to concatenate? */ else if (*l1 == NO_JUMP) /* no original list? */ *l1 = l2; /* 'l1' points to 'l2' */ else { int list = *l1; int next; while ((next = getjump(fs, list)) != NO_JUMP) /* find last element */ list = next; fixjump(fs, list, l2); /* last element links to 'l2' */ } } /* ** Create a jump instruction and return its position, so its destination ** can be fixed later (with 'fixjump'). If there are jumps to ** this position (kept in 'jpc'), link them all together so that ** 'patchlistaux' will fix all them directly to the final destination. */ int luaK_jump (FuncState *fs) { int jpc = fs->jpc; /* save list of jumps to here */ int j; fs->jpc = NO_JUMP; /* no more jumps to here */ j = luaK_codeAsBx(fs, OP_JMP, 0, NO_JUMP); luaK_concat(fs, &j, jpc); /* keep them on hold */ return j; } /* ** Code a 'return' instruction */ void luaK_ret (FuncState *fs, int first, int nret) { luaK_codeABC(fs, OP_RETURN, first, nret+1, 0); } /* ** Code a "conditional jump", that is, a test or comparison opcode ** followed by a jump. Return jump position. */ static int condjump (FuncState *fs, OpCode op, int A, int B, int C) { luaK_codeABC(fs, op, A, B, C); return luaK_jump(fs); } /* ** returns current 'pc' and marks it as a jump target (to avoid wrong ** optimizations with consecutive instructions not in the same basic block). */ int luaK_getlabel (FuncState *fs) { fs->lasttarget = fs->pc; return fs->pc; } /* ** Returns the position of the instruction "controlling" a given ** jump (that is, its condition), or the jump itself if it is ** unconditional. */ static Instruction *getjumpcontrol (FuncState *fs, int pc) { Instruction *pi = &fs->f->code[pc]; if (pc >= 1 && testTMode(GET_OPCODE(*(pi-1)))) return pi-1; else return pi; } /* ** Patch destination register for a TESTSET instruction. ** If instruction in position 'node' is not a TESTSET, return 0 ("fails"). ** Otherwise, if 'reg' is not 'NO_REG', set it as the destination ** register. Otherwise, change instruction to a simple 'TEST' (produces ** no register value) */ static int patchtestreg (FuncState *fs, int node, int reg) { Instruction *i = getjumpcontrol(fs, node); if (GET_OPCODE(*i) != OP_TESTSET) return 0; /* cannot patch other instructions */ if (reg != NO_REG && reg != GETARG_B(*i)) SETARG_A(*i, reg); else { /* no register to put value or register already has the value; change instruction to simple test */ *i = CREATE_ABC(OP_TEST, GETARG_B(*i), 0, GETARG_C(*i)); } return 1; } /* ** Traverse a list of tests ensuring no one produces a value */ static void removevalues (FuncState *fs, int list) { for (; list != NO_JUMP; list = getjump(fs, list)) patchtestreg(fs, list, NO_REG); } /* ** Traverse a list of tests, patching their destination address and ** registers: tests producing values jump to 'vtarget' (and put their ** values in 'reg'), other tests jump to 'dtarget'. */ static void patchlistaux (FuncState *fs, int list, int vtarget, int reg, int dtarget) { while (list != NO_JUMP) { int next = getjump(fs, list); if (patchtestreg(fs, list, reg)) fixjump(fs, list, vtarget); else fixjump(fs, list, dtarget); /* jump to default target */ list = next; } } /* ** Ensure all pending jumps to current position are fixed (jumping ** to current position with no values) and reset list of pending ** jumps */ static void dischargejpc (FuncState *fs) { patchlistaux(fs, fs->jpc, fs->pc, NO_REG, fs->pc); fs->jpc = NO_JUMP; } /* ** Add elements in 'list' to list of pending jumps to "here" ** (current position) */ void luaK_patchtohere (FuncState *fs, int list) { luaK_getlabel(fs); /* mark "here" as a jump target */ luaK_concat(fs, &fs->jpc, list); } /* ** Path all jumps in 'list' to jump to 'target'. ** (The assert means that we cannot fix a jump to a forward address ** because we only know addresses once code is generated.) */ void luaK_patchlist (FuncState *fs, int list, int target) { if (target == fs->pc) /* 'target' is current position? */ luaK_patchtohere(fs, list); /* add list to pending jumps */ else { lua_assert(target < fs->pc); patchlistaux(fs, list, target, NO_REG, target); } } /* ** Path all jumps in 'list' to close upvalues up to given 'level' ** (The assertion checks that jumps either were closing nothing ** or were closing higher levels, from inner blocks.) */ void luaK_patchclose (FuncState *fs, int list, int level) { level++; /* argument is +1 to reserve 0 as non-op */ for (; list != NO_JUMP; list = getjump(fs, list)) { lua_assert(GET_OPCODE(fs->f->code[list]) == OP_JMP && (GETARG_A(fs->f->code[list]) == 0 || GETARG_A(fs->f->code[list]) >= level)); SETARG_A(fs->f->code[list], level); } } /* ** Emit instruction 'i', checking for array sizes and saving also its ** line information. Return 'i' position. */ static int luaK_code (FuncState *fs, Instruction i) { Proto *f = fs->f; dischargejpc(fs); /* 'pc' will change */ /* put new instruction in code array */ luaM_growvector(fs->ls->L, f->code, fs->pc, f->sizecode, Instruction, MAX_INT, "opcodes"); f->code[fs->pc] = i; /* save corresponding line information */ luaM_growvector(fs->ls->L, f->lineinfo, fs->pc, f->sizelineinfo, int, MAX_INT, "opcodes"); f->lineinfo[fs->pc] = fs->ls->lastline; return fs->pc++; } /* ** Format and emit an 'iABC' instruction. (Assertions check consistency ** of parameters versus opcode.) */ int luaK_codeABC (FuncState *fs, OpCode o, int a, int b, int c) { lua_assert(getOpMode(o) == iABC); lua_assert(getBMode(o) != OpArgN || b == 0); lua_assert(getCMode(o) != OpArgN || c == 0); lua_assert(a <= MAXARG_A && b <= MAXARG_B && c <= MAXARG_C); return luaK_code(fs, CREATE_ABC(o, a, b, c)); } /* ** Format and emit an 'iABx' instruction. */ int luaK_codeABx (FuncState *fs, OpCode o, int a, unsigned int bc) { lua_assert(getOpMode(o) == iABx || getOpMode(o) == iAsBx); lua_assert(getCMode(o) == OpArgN); lua_assert(a <= MAXARG_A && bc <= MAXARG_Bx); return luaK_code(fs, CREATE_ABx(o, a, bc)); } /* ** Emit an "extra argument" instruction (format 'iAx') */ static int codeextraarg (FuncState *fs, int a) { lua_assert(a <= MAXARG_Ax); return luaK_code(fs, CREATE_Ax(OP_EXTRAARG, a)); } /* ** Emit a "load constant" instruction, using either 'OP_LOADK' ** (if constant index 'k' fits in 18 bits) or an 'OP_LOADKX' ** instruction with "extra argument". */ int luaK_codek (FuncState *fs, int reg, int k) { if (k <= MAXARG_Bx) return luaK_codeABx(fs, OP_LOADK, reg, k); else { int p = luaK_codeABx(fs, OP_LOADKX, reg, 0); codeextraarg(fs, k); return p; } } /* ** Check register-stack level, keeping track of its maximum size ** in field 'maxstacksize' */ void luaK_checkstack (FuncState *fs, int n) { int newstack = fs->freereg + n; if (newstack > fs->f->maxstacksize) { if (newstack >= MAXREGS) luaX_syntaxerror(fs->ls, "function or expression needs too many registers"); fs->f->maxstacksize = cast_byte(newstack); } } /* ** Reserve 'n' registers in register stack */ void luaK_reserveregs (FuncState *fs, int n) { luaK_checkstack(fs, n); fs->freereg += n; } /* ** Free register 'reg', if it is neither a constant index nor ** a local variable. ) */ static void freereg (FuncState *fs, int reg) { if (!ISK(reg) && reg >= fs->nactvar) { fs->freereg--; lua_assert(reg == fs->freereg); } } /* ** Free register used by expression 'e' (if any) */ static void freeexp (FuncState *fs, expdesc *e) { if (e->k == VNONRELOC) freereg(fs, e->u.info); } /* ** Free registers used by expressions 'e1' and 'e2' (if any) in proper ** order. */ static void freeexps (FuncState *fs, expdesc *e1, expdesc *e2) { int r1 = (e1->k == VNONRELOC) ? e1->u.info : -1; int r2 = (e2->k == VNONRELOC) ? e2->u.info : -1; if (r1 > r2) { freereg(fs, r1); freereg(fs, r2); } else { freereg(fs, r2); freereg(fs, r1); } } /* ** Add constant 'v' to prototype's list of constants (field 'k'). ** Use scanner's table to cache position of constants in constant list ** and try to reuse constants. Because some values should not be used ** as keys (nil cannot be a key, integer keys can collapse with float ** keys), the caller must provide a useful 'key' for indexing the cache. */ static int addk (FuncState *fs, TValue *key, TValue *v) { lua_State *L = fs->ls->L; Proto *f = fs->f; TValue *idx = luaH_set(L, fs->ls->h, key); /* index scanner table */ int k, oldsize; if (ttisinteger(idx)) { /* is there an index there? */ k = cast_int(ivalue(idx)); /* correct value? (warning: must distinguish floats from integers!) */ if (k < fs->nk && ttype(&f->k[k]) == ttype(v) && luaV_rawequalobj(&f->k[k], v)) return k; /* reuse index */ } /* constant not found; create a new entry */ oldsize = f->sizek; k = fs->nk; /* numerical value does not need GC barrier; table has no metatable, so it does not need to invalidate cache */ setivalue(idx, k); luaM_growvector(L, f->k, k, f->sizek, TValue, MAXARG_Ax, "constants"); while (oldsize < f->sizek) setnilvalue(&f->k[oldsize++]); setobj(L, &f->k[k], v); fs->nk++; luaC_barrier(L, f, v); return k; } /* ** Add a string to list of constants and return its index. */ int luaK_stringK (FuncState *fs, TString *s) { TValue o; setsvalue(fs->ls->L, &o, s); return addk(fs, &o, &o); /* use string itself as key */ } /* ** Add an integer to list of constants and return its index. ** Integers use userdata as keys to avoid collision with floats with ** same value; conversion to 'void*' is used only for hashing, so there ** are no "precision" problems. */ int luaK_intK (FuncState *fs, lua_Integer n) { TValue k, o; setpvalue(&k, cast(void*, cast(size_t, n))); setivalue(&o, n); return addk(fs, &k, &o); } /* ** Add a float to list of constants and return its index. */ static int luaK_numberK (FuncState *fs, lua_Number r) { TValue o; setfltvalue(&o, r); return addk(fs, &o, &o); /* use number itself as key */ } /* ** Add a boolean to list of constants and return its index. */ static int boolK (FuncState *fs, int b) { TValue o; setbvalue(&o, b); return addk(fs, &o, &o); /* use boolean itself as key */ } /* ** Add nil to list of constants and return its index. */ static int nilK (FuncState *fs) { TValue k, v; setnilvalue(&v); /* cannot use nil as key; instead use table itself to represent nil */ sethvalue(fs->ls->L, &k, fs->ls->h); return addk(fs, &k, &v); } /* ** Fix an expression to return the number of results 'nresults'. ** Either 'e' is a multi-ret expression (function call or vararg) ** or 'nresults' is LUA_MULTRET (as any expression can satisfy that). */ void luaK_setreturns (FuncState *fs, expdesc *e, int nresults) { if (e->k == VCALL) { /* expression is an open function call? */ SETARG_C(getinstruction(fs, e), nresults + 1); } else if (e->k == VVARARG) { Instruction *pc = &getinstruction(fs, e); SETARG_B(*pc, nresults + 1); SETARG_A(*pc, fs->freereg); luaK_reserveregs(fs, 1); } else lua_assert(nresults == LUA_MULTRET); } /* ** Fix an expression to return one result. ** If expression is not a multi-ret expression (function call or ** vararg), it already returns one result, so nothing needs to be done. ** Function calls become VNONRELOC expressions (as its result comes ** fixed in the base register of the call), while vararg expressions ** become VRELOCABLE (as OP_VARARG puts its results where it wants). ** (Calls are created returning one result, so that does not need ** to be fixed.) */ void luaK_setoneret (FuncState *fs, expdesc *e) { if (e->k == VCALL) { /* expression is an open function call? */ /* already returns 1 value */ lua_assert(GETARG_C(getinstruction(fs, e)) == 2); e->k = VNONRELOC; /* result has fixed position */ e->u.info = GETARG_A(getinstruction(fs, e)); } else if (e->k == VVARARG) { SETARG_B(getinstruction(fs, e), 2); e->k = VRELOCABLE; /* can relocate its simple result */ } } /* ** Ensure that expression 'e' is not a variable. */ void luaK_dischargevars (FuncState *fs, expdesc *e) { switch (e->k) { case VLOCAL: { /* already in a register */ e->k = VNONRELOC; /* becomes a non-relocatable value */ break; } case VUPVAL: { /* move value to some (pending) register */ e->u.info = luaK_codeABC(fs, OP_GETUPVAL, 0, e->u.info, 0); e->k = VRELOCABLE; break; } case VINDEXED: { OpCode op; freereg(fs, e->u.ind.idx); if (e->u.ind.vt == VLOCAL) { /* is 't' in a register? */ freereg(fs, e->u.ind.t); op = OP_GETTABLE; } else { lua_assert(e->u.ind.vt == VUPVAL); op = OP_GETTABUP; /* 't' is in an upvalue */ } e->u.info = luaK_codeABC(fs, op, 0, e->u.ind.t, e->u.ind.idx); e->k = VRELOCABLE; break; } case VVARARG: case VCALL: { luaK_setoneret(fs, e); break; } default: break; /* there is one value available (somewhere) */ } } /* ** Ensures expression value is in register 'reg' (and therefore ** 'e' will become a non-relocatable expression). */ static void discharge2reg (FuncState *fs, expdesc *e, int reg) { luaK_dischargevars(fs, e); switch (e->k) { case VNIL: { luaK_nil(fs, reg, 1); break; } case VFALSE: case VTRUE: { luaK_codeABC(fs, OP_LOADBOOL, reg, e->k == VTRUE, 0); break; } case VK: { luaK_codek(fs, reg, e->u.info); break; } case VKFLT: { luaK_codek(fs, reg, luaK_numberK(fs, e->u.nval)); break; } case VKINT: { luaK_codek(fs, reg, luaK_intK(fs, e->u.ival)); break; } case VRELOCABLE: { Instruction *pc = &getinstruction(fs, e); SETARG_A(*pc, reg); /* instruction will put result in 'reg' */ break; } case VNONRELOC: { if (reg != e->u.info) luaK_codeABC(fs, OP_MOVE, reg, e->u.info, 0); break; } default: { lua_assert(e->k == VJMP); return; /* nothing to do... */ } } e->u.info = reg; e->k = VNONRELOC; } /* ** Ensures expression value is in any register. */ static void discharge2anyreg (FuncState *fs, expdesc *e) { if (e->k != VNONRELOC) { /* no fixed register yet? */ luaK_reserveregs(fs, 1); /* get a register */ discharge2reg(fs, e, fs->freereg-1); /* put value there */ } } static int code_loadbool (FuncState *fs, int A, int b, int jump) { luaK_getlabel(fs); /* those instructions may be jump targets */ return luaK_codeABC(fs, OP_LOADBOOL, A, b, jump); } /* ** check whether list has any jump that do not produce a value ** or produce an inverted value */ static int need_value (FuncState *fs, int list) { for (; list != NO_JUMP; list = getjump(fs, list)) { Instruction i = *getjumpcontrol(fs, list); if (GET_OPCODE(i) != OP_TESTSET) return 1; } return 0; /* not found */ } /* ** Ensures final expression result (including results from its jump ** lists) is in register 'reg'. ** If expression has jumps, need to patch these jumps either to ** its final position or to "load" instructions (for those tests ** that do not produce values). */ static void exp2reg (FuncState *fs, expdesc *e, int reg) { discharge2reg(fs, e, reg); if (e->k == VJMP) /* expression itself is a test? */ luaK_concat(fs, &e->t, e->u.info); /* put this jump in 't' list */ if (hasjumps(e)) { int final; /* position after whole expression */ int p_f = NO_JUMP; /* position of an eventual LOAD false */ int p_t = NO_JUMP; /* position of an eventual LOAD true */ if (need_value(fs, e->t) || need_value(fs, e->f)) { int fj = (e->k == VJMP) ? NO_JUMP : luaK_jump(fs); p_f = code_loadbool(fs, reg, 0, 1); p_t = code_loadbool(fs, reg, 1, 0); luaK_patchtohere(fs, fj); } final = luaK_getlabel(fs); patchlistaux(fs, e->f, final, reg, p_f); patchlistaux(fs, e->t, final, reg, p_t); } e->f = e->t = NO_JUMP; e->u.info = reg; e->k = VNONRELOC; } /* ** Ensures final expression result (including results from its jump ** lists) is in next available register. */ void luaK_exp2nextreg (FuncState *fs, expdesc *e) { luaK_dischargevars(fs, e); freeexp(fs, e); luaK_reserveregs(fs, 1); exp2reg(fs, e, fs->freereg - 1); } /* ** Ensures final expression result (including results from its jump ** lists) is in some (any) register and return that register. */ int luaK_exp2anyreg (FuncState *fs, expdesc *e) { luaK_dischargevars(fs, e); if (e->k == VNONRELOC) { /* expression already has a register? */ if (!hasjumps(e)) /* no jumps? */ return e->u.info; /* result is already in a register */ if (e->u.info >= fs->nactvar) { /* reg. is not a local? */ exp2reg(fs, e, e->u.info); /* put final result in it */ return e->u.info; } } luaK_exp2nextreg(fs, e); /* otherwise, use next available register */ return e->u.info; } /* ** Ensures final expression result is either in a register or in an ** upvalue. */ void luaK_exp2anyregup (FuncState *fs, expdesc *e) { if (e->k != VUPVAL || hasjumps(e)) luaK_exp2anyreg(fs, e); } /* ** Ensures final expression result is either in a register or it is ** a constant. */ void luaK_exp2val (FuncState *fs, expdesc *e) { if (hasjumps(e)) luaK_exp2anyreg(fs, e); else luaK_dischargevars(fs, e); } /* ** Ensures final expression result is in a valid R/K index ** (that is, it is either in a register or in 'k' with an index ** in the range of R/K indices). ** Returns R/K index. */ int luaK_exp2RK (FuncState *fs, expdesc *e) { luaK_exp2val(fs, e); switch (e->k) { /* move constants to 'k' */ case VTRUE: e->u.info = boolK(fs, 1); goto vk; case VFALSE: e->u.info = boolK(fs, 0); goto vk; case VNIL: e->u.info = nilK(fs); goto vk; case VKINT: e->u.info = luaK_intK(fs, e->u.ival); goto vk; case VKFLT: e->u.info = luaK_numberK(fs, e->u.nval); goto vk; case VK: vk: e->k = VK; if (e->u.info <= MAXINDEXRK) /* constant fits in 'argC'? */ return RKASK(e->u.info); else break; default: break; } /* not a constant in the right range: put it in a register */ return luaK_exp2anyreg(fs, e); } /* ** Generate code to store result of expression 'ex' into variable 'var'. */ void luaK_storevar (FuncState *fs, expdesc *var, expdesc *ex) { switch (var->k) { case VLOCAL: { freeexp(fs, ex); exp2reg(fs, ex, var->u.info); /* compute 'ex' into proper place */ return; } case VUPVAL: { int e = luaK_exp2anyreg(fs, ex); luaK_codeABC(fs, OP_SETUPVAL, e, var->u.info, 0); break; } case VINDEXED: { OpCode op = (var->u.ind.vt == VLOCAL) ? OP_SETTABLE : OP_SETTABUP; int e = luaK_exp2RK(fs, ex); luaK_codeABC(fs, op, var->u.ind.t, var->u.ind.idx, e); break; } default: lua_assert(0); /* invalid var kind to store */ } freeexp(fs, ex); } /* ** Emit SELF instruction (convert expression 'e' into 'e:key(e,'). */ void luaK_self (FuncState *fs, expdesc *e, expdesc *key) { int ereg; luaK_exp2anyreg(fs, e); ereg = e->u.info; /* register where 'e' was placed */ freeexp(fs, e); e->u.info = fs->freereg; /* base register for op_self */ e->k = VNONRELOC; /* self expression has a fixed register */ luaK_reserveregs(fs, 2); /* function and 'self' produced by op_self */ luaK_codeABC(fs, OP_SELF, e->u.info, ereg, luaK_exp2RK(fs, key)); freeexp(fs, key); } /* ** Negate condition 'e' (where 'e' is a comparison). */ static void negatecondition (FuncState *fs, expdesc *e) { Instruction *pc = getjumpcontrol(fs, e->u.info); lua_assert(testTMode(GET_OPCODE(*pc)) && GET_OPCODE(*pc) != OP_TESTSET && GET_OPCODE(*pc) != OP_TEST); SETARG_A(*pc, !(GETARG_A(*pc))); } /* ** Emit instruction to jump if 'e' is 'cond' (that is, if 'cond' ** is true, code will jump if 'e' is true.) Return jump position. ** Optimize when 'e' is 'not' something, inverting the condition ** and removing the 'not'. */ static int jumponcond (FuncState *fs, expdesc *e, int cond) { if (e->k == VRELOCABLE) { Instruction ie = getinstruction(fs, e); if (GET_OPCODE(ie) == OP_NOT) { fs->pc--; /* remove previous OP_NOT */ return condjump(fs, OP_TEST, GETARG_B(ie), 0, !cond); } /* else go through */ } discharge2anyreg(fs, e); freeexp(fs, e); return condjump(fs, OP_TESTSET, NO_REG, e->u.info, cond); } /* ** Emit code to go through if 'e' is true, jump otherwise. */ void luaK_goiftrue (FuncState *fs, expdesc *e) { int pc; /* pc of new jump */ luaK_dischargevars(fs, e); switch (e->k) { case VJMP: { /* condition? */ negatecondition(fs, e); /* jump when it is false */ pc = e->u.info; /* save jump position */ break; } case VK: case VKFLT: case VKINT: case VTRUE: { pc = NO_JUMP; /* always true; do nothing */ break; } default: { pc = jumponcond(fs, e, 0); /* jump when false */ break; } } luaK_concat(fs, &e->f, pc); /* insert new jump in false list */ luaK_patchtohere(fs, e->t); /* true list jumps to here (to go through) */ e->t = NO_JUMP; } /* ** Emit code to go through if 'e' is false, jump otherwise. */ void luaK_goiffalse (FuncState *fs, expdesc *e) { int pc; /* pc of new jump */ luaK_dischargevars(fs, e); switch (e->k) { case VJMP: { pc = e->u.info; /* already jump if true */ break; } case VNIL: case VFALSE: { pc = NO_JUMP; /* always false; do nothing */ break; } default: { pc = jumponcond(fs, e, 1); /* jump if true */ break; } } luaK_concat(fs, &e->t, pc); /* insert new jump in 't' list */ luaK_patchtohere(fs, e->f); /* false list jumps to here (to go through) */ e->f = NO_JUMP; } /* ** Code 'not e', doing constant folding. */ static void codenot (FuncState *fs, expdesc *e) { luaK_dischargevars(fs, e); switch (e->k) { case VNIL: case VFALSE: { e->k = VTRUE; /* true == not nil == not false */ break; } case VK: case VKFLT: case VKINT: case VTRUE: { e->k = VFALSE; /* false == not "x" == not 0.5 == not 1 == not true */ break; } case VJMP: { negatecondition(fs, e); break; } case VRELOCABLE: case VNONRELOC: { discharge2anyreg(fs, e); freeexp(fs, e); e->u.info = luaK_codeABC(fs, OP_NOT, 0, e->u.info, 0); e->k = VRELOCABLE; break; } default: lua_assert(0); /* cannot happen */ } /* interchange true and false lists */ { int temp = e->f; e->f = e->t; e->t = temp; } removevalues(fs, e->f); /* values are useless when negated */ removevalues(fs, e->t); } /* ** Create expression 't[k]'. 't' must have its final result already in a ** register or upvalue. */ void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k) { lua_assert(!hasjumps(t) && (vkisinreg(t->k) || t->k == VUPVAL)); t->u.ind.t = t->u.info; /* register or upvalue index */ t->u.ind.idx = luaK_exp2RK(fs, k); /* R/K index for key */ t->u.ind.vt = (t->k == VUPVAL) ? VUPVAL : VLOCAL; t->k = VINDEXED; } /* ** Return false if folding can raise an error. ** Bitwise operations need operands convertible to integers; division ** operations cannot have 0 as divisor. */ static int validop (int op, TValue *v1, TValue *v2) { switch (op) { case LUA_OPBAND: case LUA_OPBOR: case LUA_OPBXOR: case LUA_OPSHL: case LUA_OPSHR: case LUA_OPBNOT: { /* conversion errors */ lua_Integer i; return (tointeger(v1, &i) && tointeger(v2, &i)); } case LUA_OPDIV: case LUA_OPIDIV: case LUA_OPMOD: /* division by 0 */ return (nvalue(v2) != 0); default: return 1; /* everything else is valid */ } } /* ** Try to "constant-fold" an operation; return 1 iff successful. ** (In this case, 'e1' has the final result.) */ static int constfolding (FuncState *fs, int op, expdesc *e1, const expdesc *e2) { TValue v1, v2, res; if (!tonumeral(e1, &v1) || !tonumeral(e2, &v2) || !validop(op, &v1, &v2)) return 0; /* non-numeric operands or not safe to fold */ luaO_arith(fs->ls->L, op, &v1, &v2, &res); /* does operation */ if (ttisinteger(&res)) { e1->k = VKINT; e1->u.ival = ivalue(&res); } else { /* folds neither NaN nor 0.0 (to avoid problems with -0.0) */ lua_Number n = fltvalue(&res); if (luai_numisnan(n) || n == 0) return 0; e1->k = VKFLT; e1->u.nval = n; } return 1; } /* ** Emit code for unary expressions that "produce values" ** (everything but 'not'). ** Expression to produce final result will be encoded in 'e'. */ static void codeunexpval (FuncState *fs, OpCode op, expdesc *e, int line) { int r = luaK_exp2anyreg(fs, e); /* opcodes operate only on registers */ freeexp(fs, e); e->u.info = luaK_codeABC(fs, op, 0, r, 0); /* generate opcode */ e->k = VRELOCABLE; /* all those operations are relocatable */ luaK_fixline(fs, line); } /* ** Emit code for binary expressions that "produce values" ** (everything but logical operators 'and'/'or' and comparison ** operators). ** Expression to produce final result will be encoded in 'e1'. ** Because 'luaK_exp2RK' can free registers, its calls must be ** in "stack order" (that is, first on 'e2', which may have more ** recent registers to be released). */ static void codebinexpval (FuncState *fs, OpCode op, expdesc *e1, expdesc *e2, int line) { int rk2 = luaK_exp2RK(fs, e2); /* both operands are "RK" */ int rk1 = luaK_exp2RK(fs, e1); freeexps(fs, e1, e2); e1->u.info = luaK_codeABC(fs, op, 0, rk1, rk2); /* generate opcode */ e1->k = VRELOCABLE; /* all those operations are relocatable */ luaK_fixline(fs, line); } /* ** Emit code for comparisons. ** 'e1' was already put in R/K form by 'luaK_infix'. */ static void codecomp (FuncState *fs, BinOpr opr, expdesc *e1, expdesc *e2) { int rk1 = (e1->k == VK) ? RKASK(e1->u.info) : check_exp(e1->k == VNONRELOC, e1->u.info); int rk2 = luaK_exp2RK(fs, e2); freeexps(fs, e1, e2); switch (opr) { case OPR_NE: { /* '(a ~= b)' ==> 'not (a == b)' */ e1->u.info = condjump(fs, OP_EQ, 0, rk1, rk2); break; } case OPR_GT: case OPR_GE: { /* '(a > b)' ==> '(b < a)'; '(a >= b)' ==> '(b <= a)' */ OpCode op = cast(OpCode, (opr - OPR_NE) + OP_EQ); e1->u.info = condjump(fs, op, 1, rk2, rk1); /* invert operands */ break; } default: { /* '==', '<', '<=' use their own opcodes */ OpCode op = cast(OpCode, (opr - OPR_EQ) + OP_EQ); e1->u.info = condjump(fs, op, 1, rk1, rk2); break; } } e1->k = VJMP; } /* ** Aplly prefix operation 'op' to expression 'e'. */ void luaK_prefix (FuncState *fs, UnOpr op, expdesc *e, int line) { static const expdesc ef = {VKINT, {0}, NO_JUMP, NO_JUMP}; switch (op) { case OPR_MINUS: case OPR_BNOT: /* use 'ef' as fake 2nd operand */ if (constfolding(fs, op + LUA_OPUNM, e, &ef)) break; /* FALLTHROUGH */ case OPR_LEN: codeunexpval(fs, cast(OpCode, op + OP_UNM), e, line); break; case OPR_NOT: codenot(fs, e); break; default: lua_assert(0); } } /* ** Process 1st operand 'v' of binary operation 'op' before reading ** 2nd operand. */ void luaK_infix (FuncState *fs, BinOpr op, expdesc *v) { switch (op) { case OPR_AND: { luaK_goiftrue(fs, v); /* go ahead only if 'v' is true */ break; } case OPR_OR: { luaK_goiffalse(fs, v); /* go ahead only if 'v' is false */ break; } case OPR_CONCAT: { luaK_exp2nextreg(fs, v); /* operand must be on the 'stack' */ break; } case OPR_ADD: case OPR_SUB: case OPR_MUL: case OPR_DIV: case OPR_IDIV: case OPR_MOD: case OPR_POW: case OPR_BAND: case OPR_BOR: case OPR_BXOR: case OPR_SHL: case OPR_SHR: { if (!tonumeral(v, NULL)) luaK_exp2RK(fs, v); /* else keep numeral, which may be folded with 2nd operand */ break; } default: { luaK_exp2RK(fs, v); break; } } } /* ** Finalize code for binary operation, after reading 2nd operand. ** For '(a .. b .. c)' (which is '(a .. (b .. c))', because ** concatenation is right associative), merge second CONCAT into first ** one. */ void luaK_posfix (FuncState *fs, BinOpr op, expdesc *e1, expdesc *e2, int line) { switch (op) { case OPR_AND: { lua_assert(e1->t == NO_JUMP); /* list closed by 'luK_infix' */ luaK_dischargevars(fs, e2); luaK_concat(fs, &e2->f, e1->f); *e1 = *e2; break; } case OPR_OR: { lua_assert(e1->f == NO_JUMP); /* list closed by 'luK_infix' */ luaK_dischargevars(fs, e2); luaK_concat(fs, &e2->t, e1->t); *e1 = *e2; break; } case OPR_CONCAT: { luaK_exp2val(fs, e2); if (e2->k == VRELOCABLE && GET_OPCODE(getinstruction(fs, e2)) == OP_CONCAT) { lua_assert(e1->u.info == GETARG_B(getinstruction(fs, e2))-1); freeexp(fs, e1); SETARG_B(getinstruction(fs, e2), e1->u.info); e1->k = VRELOCABLE; e1->u.info = e2->u.info; } else { luaK_exp2nextreg(fs, e2); /* operand must be on the 'stack' */ codebinexpval(fs, OP_CONCAT, e1, e2, line); } break; } case OPR_ADD: case OPR_SUB: case OPR_MUL: case OPR_DIV: case OPR_IDIV: case OPR_MOD: case OPR_POW: case OPR_BAND: case OPR_BOR: case OPR_BXOR: case OPR_SHL: case OPR_SHR: { if (!constfolding(fs, op + LUA_OPADD, e1, e2)) codebinexpval(fs, cast(OpCode, op + OP_ADD), e1, e2, line); break; } case OPR_EQ: case OPR_LT: case OPR_LE: case OPR_NE: case OPR_GT: case OPR_GE: { codecomp(fs, op, e1, e2); break; } default: lua_assert(0); } } /* ** Change line information associated with current position. */ void luaK_fixline (FuncState *fs, int line) { fs->f->lineinfo[fs->pc - 1] = line; } /* ** Emit a SETLIST instruction. ** 'base' is register that keeps table; ** 'nelems' is #table plus those to be stored now; ** 'tostore' is number of values (in registers 'base + 1',...) to add to ** table (or LUA_MULTRET to add up to stack top). */ void luaK_setlist (FuncState *fs, int base, int nelems, int tostore) { int c = (nelems - 1)/LFIELDS_PER_FLUSH + 1; int b = (tostore == LUA_MULTRET) ? 0 : tostore; lua_assert(tostore != 0 && tostore <= LFIELDS_PER_FLUSH); if (c <= MAXARG_C) luaK_codeABC(fs, OP_SETLIST, base, b, c); else if (c <= MAXARG_Ax) { luaK_codeABC(fs, OP_SETLIST, base, b, 0); codeextraarg(fs, c); } else luaX_syntaxerror(fs->ls, "constructor too long"); fs->freereg = base + 1; /* free registers with list values */ } ================================================ FILE: Tests/ApiExplorer/lua/src/lcode.h ================================================ /* ** $Id: lcode.h,v 1.64.1.1 2017/04/19 17:20:42 roberto Exp $ ** Code generator for Lua ** See Copyright Notice in lua.h */ #ifndef lcode_h #define lcode_h #include "llex.h" #include "lobject.h" #include "lopcodes.h" #include "lparser.h" /* ** Marks the end of a patch list. It is an invalid value both as an absolute ** address, and as a list link (would link an element to itself). */ #define NO_JUMP (-1) /* ** grep "ORDER OPR" if you change these enums (ORDER OP) */ typedef enum BinOpr { OPR_ADD, OPR_SUB, OPR_MUL, OPR_MOD, OPR_POW, OPR_DIV, OPR_IDIV, OPR_BAND, OPR_BOR, OPR_BXOR, OPR_SHL, OPR_SHR, OPR_CONCAT, OPR_EQ, OPR_LT, OPR_LE, OPR_NE, OPR_GT, OPR_GE, OPR_AND, OPR_OR, OPR_NOBINOPR } BinOpr; typedef enum UnOpr { OPR_MINUS, OPR_BNOT, OPR_NOT, OPR_LEN, OPR_NOUNOPR } UnOpr; /* get (pointer to) instruction of given 'expdesc' */ #define getinstruction(fs,e) ((fs)->f->code[(e)->u.info]) #define luaK_codeAsBx(fs,o,A,sBx) luaK_codeABx(fs,o,A,(sBx)+MAXARG_sBx) #define luaK_setmultret(fs,e) luaK_setreturns(fs, e, LUA_MULTRET) #define luaK_jumpto(fs,t) luaK_patchlist(fs, luaK_jump(fs), t) LUAI_FUNC int luaK_codeABx (FuncState *fs, OpCode o, int A, unsigned int Bx); LUAI_FUNC int luaK_codeABC (FuncState *fs, OpCode o, int A, int B, int C); LUAI_FUNC int luaK_codek (FuncState *fs, int reg, int k); LUAI_FUNC void luaK_fixline (FuncState *fs, int line); LUAI_FUNC void luaK_nil (FuncState *fs, int from, int n); LUAI_FUNC void luaK_reserveregs (FuncState *fs, int n); LUAI_FUNC void luaK_checkstack (FuncState *fs, int n); LUAI_FUNC int luaK_stringK (FuncState *fs, TString *s); LUAI_FUNC int luaK_intK (FuncState *fs, lua_Integer n); LUAI_FUNC void luaK_dischargevars (FuncState *fs, expdesc *e); LUAI_FUNC int luaK_exp2anyreg (FuncState *fs, expdesc *e); LUAI_FUNC void luaK_exp2anyregup (FuncState *fs, expdesc *e); LUAI_FUNC void luaK_exp2nextreg (FuncState *fs, expdesc *e); LUAI_FUNC void luaK_exp2val (FuncState *fs, expdesc *e); LUAI_FUNC int luaK_exp2RK (FuncState *fs, expdesc *e); LUAI_FUNC void luaK_self (FuncState *fs, expdesc *e, expdesc *key); LUAI_FUNC void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k); LUAI_FUNC void luaK_goiftrue (FuncState *fs, expdesc *e); LUAI_FUNC void luaK_goiffalse (FuncState *fs, expdesc *e); LUAI_FUNC void luaK_storevar (FuncState *fs, expdesc *var, expdesc *e); LUAI_FUNC void luaK_setreturns (FuncState *fs, expdesc *e, int nresults); LUAI_FUNC void luaK_setoneret (FuncState *fs, expdesc *e); LUAI_FUNC int luaK_jump (FuncState *fs); LUAI_FUNC void luaK_ret (FuncState *fs, int first, int nret); LUAI_FUNC void luaK_patchlist (FuncState *fs, int list, int target); LUAI_FUNC void luaK_patchtohere (FuncState *fs, int list); LUAI_FUNC void luaK_patchclose (FuncState *fs, int list, int level); LUAI_FUNC void luaK_concat (FuncState *fs, int *l1, int l2); LUAI_FUNC int luaK_getlabel (FuncState *fs); LUAI_FUNC void luaK_prefix (FuncState *fs, UnOpr op, expdesc *v, int line); LUAI_FUNC void luaK_infix (FuncState *fs, BinOpr op, expdesc *v); LUAI_FUNC void luaK_posfix (FuncState *fs, BinOpr op, expdesc *v1, expdesc *v2, int line); LUAI_FUNC void luaK_setlist (FuncState *fs, int base, int nelems, int tostore); #endif ================================================ FILE: Tests/ApiExplorer/lua/src/lcorolib.c ================================================ /* ** $Id: lcorolib.c,v 1.10.1.1 2017/04/19 17:20:42 roberto Exp $ ** Coroutine Library ** See Copyright Notice in lua.h */ #define lcorolib_c #define LUA_LIB #include "lprefix.h" #include #include "lua.h" #include "lauxlib.h" #include "lualib.h" static lua_State *getco (lua_State *L) { lua_State *co = lua_tothread(L, 1); luaL_argcheck(L, co, 1, "thread expected"); return co; } static int auxresume (lua_State *L, lua_State *co, int narg) { int status; if (!lua_checkstack(co, narg)) { lua_pushliteral(L, "too many arguments to resume"); return -1; /* error flag */ } if (lua_status(co) == LUA_OK && lua_gettop(co) == 0) { lua_pushliteral(L, "cannot resume dead coroutine"); return -1; /* error flag */ } lua_xmove(L, co, narg); status = lua_resume(co, L, narg); if (status == LUA_OK || status == LUA_YIELD) { int nres = lua_gettop(co); if (!lua_checkstack(L, nres + 1)) { lua_pop(co, nres); /* remove results anyway */ lua_pushliteral(L, "too many results to resume"); return -1; /* error flag */ } lua_xmove(co, L, nres); /* move yielded values */ return nres; } else { lua_xmove(co, L, 1); /* move error message */ return -1; /* error flag */ } } static int luaB_coresume (lua_State *L) { lua_State *co = getco(L); int r; r = auxresume(L, co, lua_gettop(L) - 1); if (r < 0) { lua_pushboolean(L, 0); lua_insert(L, -2); return 2; /* return false + error message */ } else { lua_pushboolean(L, 1); lua_insert(L, -(r + 1)); return r + 1; /* return true + 'resume' returns */ } } static int luaB_auxwrap (lua_State *L) { lua_State *co = lua_tothread(L, lua_upvalueindex(1)); int r = auxresume(L, co, lua_gettop(L)); if (r < 0) { if (lua_type(L, -1) == LUA_TSTRING) { /* error object is a string? */ luaL_where(L, 1); /* add extra info */ lua_insert(L, -2); lua_concat(L, 2); } return lua_error(L); /* propagate error */ } return r; } static int luaB_cocreate (lua_State *L) { lua_State *NL; luaL_checktype(L, 1, LUA_TFUNCTION); NL = lua_newthread(L); lua_pushvalue(L, 1); /* move function to top */ lua_xmove(L, NL, 1); /* move function from L to NL */ return 1; } static int luaB_cowrap (lua_State *L) { luaB_cocreate(L); lua_pushcclosure(L, luaB_auxwrap, 1); return 1; } static int luaB_yield (lua_State *L) { return lua_yield(L, lua_gettop(L)); } static int luaB_costatus (lua_State *L) { lua_State *co = getco(L); if (L == co) lua_pushliteral(L, "running"); else { switch (lua_status(co)) { case LUA_YIELD: lua_pushliteral(L, "suspended"); break; case LUA_OK: { lua_Debug ar; if (lua_getstack(co, 0, &ar) > 0) /* does it have frames? */ lua_pushliteral(L, "normal"); /* it is running */ else if (lua_gettop(co) == 0) lua_pushliteral(L, "dead"); else lua_pushliteral(L, "suspended"); /* initial state */ break; } default: /* some error occurred */ lua_pushliteral(L, "dead"); break; } } return 1; } static int luaB_yieldable (lua_State *L) { lua_pushboolean(L, lua_isyieldable(L)); return 1; } static int luaB_corunning (lua_State *L) { int ismain = lua_pushthread(L); lua_pushboolean(L, ismain); return 2; } static const luaL_Reg co_funcs[] = { {"create", luaB_cocreate}, {"resume", luaB_coresume}, {"running", luaB_corunning}, {"status", luaB_costatus}, {"wrap", luaB_cowrap}, {"yield", luaB_yield}, {"isyieldable", luaB_yieldable}, {NULL, NULL} }; LUAMOD_API int luaopen_coroutine (lua_State *L) { luaL_newlib(L, co_funcs); return 1; } ================================================ FILE: Tests/ApiExplorer/lua/src/lctype.c ================================================ /* ** $Id: lctype.c,v 1.12.1.1 2017/04/19 17:20:42 roberto Exp $ ** 'ctype' functions for Lua ** See Copyright Notice in lua.h */ #define lctype_c #define LUA_CORE #include "lprefix.h" #include "lctype.h" #if !LUA_USE_CTYPE /* { */ #include LUAI_DDEF const lu_byte luai_ctype_[UCHAR_MAX + 2] = { 0x00, /* EOZ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0. */ 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 1. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, /* 2. */ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, /* 3. */ 0x16, 0x16, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x05, /* 4. */ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, /* 5. */ 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x05, 0x04, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x05, /* 6. */ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, /* 7. */ 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 8. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 9. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* a. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* b. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* c. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* d. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* e. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* f. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; #endif /* } */ ================================================ FILE: Tests/ApiExplorer/lua/src/lctype.h ================================================ /* ** $Id: lctype.h,v 1.12.1.1 2013/04/12 18:48:47 roberto Exp $ ** 'ctype' functions for Lua ** See Copyright Notice in lua.h */ #ifndef lctype_h #define lctype_h #include "lua.h" /* ** WARNING: the functions defined here do not necessarily correspond ** to the similar functions in the standard C ctype.h. They are ** optimized for the specific needs of Lua */ #if !defined(LUA_USE_CTYPE) #if 'A' == 65 && '0' == 48 /* ASCII case: can use its own tables; faster and fixed */ #define LUA_USE_CTYPE 0 #else /* must use standard C ctype */ #define LUA_USE_CTYPE 1 #endif #endif #if !LUA_USE_CTYPE /* { */ #include #include "llimits.h" #define ALPHABIT 0 #define DIGITBIT 1 #define PRINTBIT 2 #define SPACEBIT 3 #define XDIGITBIT 4 #define MASK(B) (1 << (B)) /* ** add 1 to char to allow index -1 (EOZ) */ #define testprop(c,p) (luai_ctype_[(c)+1] & (p)) /* ** 'lalpha' (Lua alphabetic) and 'lalnum' (Lua alphanumeric) both include '_' */ #define lislalpha(c) testprop(c, MASK(ALPHABIT)) #define lislalnum(c) testprop(c, (MASK(ALPHABIT) | MASK(DIGITBIT))) #define lisdigit(c) testprop(c, MASK(DIGITBIT)) #define lisspace(c) testprop(c, MASK(SPACEBIT)) #define lisprint(c) testprop(c, MASK(PRINTBIT)) #define lisxdigit(c) testprop(c, MASK(XDIGITBIT)) /* ** this 'ltolower' only works for alphabetic characters */ #define ltolower(c) ((c) | ('A' ^ 'a')) /* two more entries for 0 and -1 (EOZ) */ LUAI_DDEC const lu_byte luai_ctype_[UCHAR_MAX + 2]; #else /* }{ */ /* ** use standard C ctypes */ #include #define lislalpha(c) (isalpha(c) || (c) == '_') #define lislalnum(c) (isalnum(c) || (c) == '_') #define lisdigit(c) (isdigit(c)) #define lisspace(c) (isspace(c)) #define lisprint(c) (isprint(c)) #define lisxdigit(c) (isxdigit(c)) #define ltolower(c) (tolower(c)) #endif /* } */ #endif ================================================ FILE: Tests/ApiExplorer/lua/src/ldblib.c ================================================ /* ** $Id: ldblib.c,v 1.151.1.1 2017/04/19 17:20:42 roberto Exp $ ** Interface from Lua to its debug API ** See Copyright Notice in lua.h */ #define ldblib_c #define LUA_LIB #include "lprefix.h" #include #include #include #include "lua.h" #include "lauxlib.h" #include "lualib.h" /* ** The hook table at registry[&HOOKKEY] maps threads to their current ** hook function. (We only need the unique address of 'HOOKKEY'.) */ static const int HOOKKEY = 0; /* ** If L1 != L, L1 can be in any state, and therefore there are no ** guarantees about its stack space; any push in L1 must be ** checked. */ static void checkstack (lua_State *L, lua_State *L1, int n) { if (L != L1 && !lua_checkstack(L1, n)) luaL_error(L, "stack overflow"); } static int db_getregistry (lua_State *L) { lua_pushvalue(L, LUA_REGISTRYINDEX); return 1; } static int db_getmetatable (lua_State *L) { luaL_checkany(L, 1); if (!lua_getmetatable(L, 1)) { lua_pushnil(L); /* no metatable */ } return 1; } static int db_setmetatable (lua_State *L) { int t = lua_type(L, 2); luaL_argcheck(L, t == LUA_TNIL || t == LUA_TTABLE, 2, "nil or table expected"); lua_settop(L, 2); lua_setmetatable(L, 1); return 1; /* return 1st argument */ } static int db_getuservalue (lua_State *L) { if (lua_type(L, 1) != LUA_TUSERDATA) lua_pushnil(L); else lua_getuservalue(L, 1); return 1; } static int db_setuservalue (lua_State *L) { luaL_checktype(L, 1, LUA_TUSERDATA); luaL_checkany(L, 2); lua_settop(L, 2); lua_setuservalue(L, 1); return 1; } /* ** Auxiliary function used by several library functions: check for ** an optional thread as function's first argument and set 'arg' with ** 1 if this argument is present (so that functions can skip it to ** access their other arguments) */ static lua_State *getthread (lua_State *L, int *arg) { if (lua_isthread(L, 1)) { *arg = 1; return lua_tothread(L, 1); } else { *arg = 0; return L; /* function will operate over current thread */ } } /* ** Variations of 'lua_settable', used by 'db_getinfo' to put results ** from 'lua_getinfo' into result table. Key is always a string; ** value can be a string, an int, or a boolean. */ static void settabss (lua_State *L, const char *k, const char *v) { lua_pushstring(L, v); lua_setfield(L, -2, k); } static void settabsi (lua_State *L, const char *k, int v) { lua_pushinteger(L, v); lua_setfield(L, -2, k); } static void settabsb (lua_State *L, const char *k, int v) { lua_pushboolean(L, v); lua_setfield(L, -2, k); } /* ** In function 'db_getinfo', the call to 'lua_getinfo' may push ** results on the stack; later it creates the result table to put ** these objects. Function 'treatstackoption' puts the result from ** 'lua_getinfo' on top of the result table so that it can call ** 'lua_setfield'. */ static void treatstackoption (lua_State *L, lua_State *L1, const char *fname) { if (L == L1) lua_rotate(L, -2, 1); /* exchange object and table */ else lua_xmove(L1, L, 1); /* move object to the "main" stack */ lua_setfield(L, -2, fname); /* put object into table */ } /* ** Calls 'lua_getinfo' and collects all results in a new table. ** L1 needs stack space for an optional input (function) plus ** two optional outputs (function and line table) from function ** 'lua_getinfo'. */ static int db_getinfo (lua_State *L) { lua_Debug ar; int arg; lua_State *L1 = getthread(L, &arg); const char *options = luaL_optstring(L, arg+2, "flnStu"); checkstack(L, L1, 3); if (lua_isfunction(L, arg + 1)) { /* info about a function? */ options = lua_pushfstring(L, ">%s", options); /* add '>' to 'options' */ lua_pushvalue(L, arg + 1); /* move function to 'L1' stack */ lua_xmove(L, L1, 1); } else { /* stack level */ if (!lua_getstack(L1, (int)luaL_checkinteger(L, arg + 1), &ar)) { lua_pushnil(L); /* level out of range */ return 1; } } if (!lua_getinfo(L1, options, &ar)) return luaL_argerror(L, arg+2, "invalid option"); lua_newtable(L); /* table to collect results */ if (strchr(options, 'S')) { settabss(L, "source", ar.source); settabss(L, "short_src", ar.short_src); settabsi(L, "linedefined", ar.linedefined); settabsi(L, "lastlinedefined", ar.lastlinedefined); settabss(L, "what", ar.what); } if (strchr(options, 'l')) settabsi(L, "currentline", ar.currentline); if (strchr(options, 'u')) { settabsi(L, "nups", ar.nups); settabsi(L, "nparams", ar.nparams); settabsb(L, "isvararg", ar.isvararg); } if (strchr(options, 'n')) { settabss(L, "name", ar.name); settabss(L, "namewhat", ar.namewhat); } if (strchr(options, 't')) settabsb(L, "istailcall", ar.istailcall); if (strchr(options, 'L')) treatstackoption(L, L1, "activelines"); if (strchr(options, 'f')) treatstackoption(L, L1, "func"); return 1; /* return table */ } static int db_getlocal (lua_State *L) { int arg; lua_State *L1 = getthread(L, &arg); lua_Debug ar; const char *name; int nvar = (int)luaL_checkinteger(L, arg + 2); /* local-variable index */ if (lua_isfunction(L, arg + 1)) { /* function argument? */ lua_pushvalue(L, arg + 1); /* push function */ lua_pushstring(L, lua_getlocal(L, NULL, nvar)); /* push local name */ return 1; /* return only name (there is no value) */ } else { /* stack-level argument */ int level = (int)luaL_checkinteger(L, arg + 1); if (!lua_getstack(L1, level, &ar)) /* out of range? */ return luaL_argerror(L, arg+1, "level out of range"); checkstack(L, L1, 1); name = lua_getlocal(L1, &ar, nvar); if (name) { lua_xmove(L1, L, 1); /* move local value */ lua_pushstring(L, name); /* push name */ lua_rotate(L, -2, 1); /* re-order */ return 2; } else { lua_pushnil(L); /* no name (nor value) */ return 1; } } } static int db_setlocal (lua_State *L) { int arg; const char *name; lua_State *L1 = getthread(L, &arg); lua_Debug ar; int level = (int)luaL_checkinteger(L, arg + 1); int nvar = (int)luaL_checkinteger(L, arg + 2); if (!lua_getstack(L1, level, &ar)) /* out of range? */ return luaL_argerror(L, arg+1, "level out of range"); luaL_checkany(L, arg+3); lua_settop(L, arg+3); checkstack(L, L1, 1); lua_xmove(L, L1, 1); name = lua_setlocal(L1, &ar, nvar); if (name == NULL) lua_pop(L1, 1); /* pop value (if not popped by 'lua_setlocal') */ lua_pushstring(L, name); return 1; } /* ** get (if 'get' is true) or set an upvalue from a closure */ static int auxupvalue (lua_State *L, int get) { const char *name; int n = (int)luaL_checkinteger(L, 2); /* upvalue index */ luaL_checktype(L, 1, LUA_TFUNCTION); /* closure */ name = get ? lua_getupvalue(L, 1, n) : lua_setupvalue(L, 1, n); if (name == NULL) return 0; lua_pushstring(L, name); lua_insert(L, -(get+1)); /* no-op if get is false */ return get + 1; } static int db_getupvalue (lua_State *L) { return auxupvalue(L, 1); } static int db_setupvalue (lua_State *L) { luaL_checkany(L, 3); return auxupvalue(L, 0); } /* ** Check whether a given upvalue from a given closure exists and ** returns its index */ static int checkupval (lua_State *L, int argf, int argnup) { int nup = (int)luaL_checkinteger(L, argnup); /* upvalue index */ luaL_checktype(L, argf, LUA_TFUNCTION); /* closure */ luaL_argcheck(L, (lua_getupvalue(L, argf, nup) != NULL), argnup, "invalid upvalue index"); return nup; } static int db_upvalueid (lua_State *L) { int n = checkupval(L, 1, 2); lua_pushlightuserdata(L, lua_upvalueid(L, 1, n)); return 1; } static int db_upvaluejoin (lua_State *L) { int n1 = checkupval(L, 1, 2); int n2 = checkupval(L, 3, 4); luaL_argcheck(L, !lua_iscfunction(L, 1), 1, "Lua function expected"); luaL_argcheck(L, !lua_iscfunction(L, 3), 3, "Lua function expected"); lua_upvaluejoin(L, 1, n1, 3, n2); return 0; } /* ** Call hook function registered at hook table for the current ** thread (if there is one) */ static void hookf (lua_State *L, lua_Debug *ar) { static const char *const hooknames[] = {"call", "return", "line", "count", "tail call"}; lua_rawgetp(L, LUA_REGISTRYINDEX, &HOOKKEY); lua_pushthread(L); if (lua_rawget(L, -2) == LUA_TFUNCTION) { /* is there a hook function? */ lua_pushstring(L, hooknames[(int)ar->event]); /* push event name */ if (ar->currentline >= 0) lua_pushinteger(L, ar->currentline); /* push current line */ else lua_pushnil(L); lua_assert(lua_getinfo(L, "lS", ar)); lua_call(L, 2, 0); /* call hook function */ } } /* ** Convert a string mask (for 'sethook') into a bit mask */ static int makemask (const char *smask, int count) { int mask = 0; if (strchr(smask, 'c')) mask |= LUA_MASKCALL; if (strchr(smask, 'r')) mask |= LUA_MASKRET; if (strchr(smask, 'l')) mask |= LUA_MASKLINE; if (count > 0) mask |= LUA_MASKCOUNT; return mask; } /* ** Convert a bit mask (for 'gethook') into a string mask */ static char *unmakemask (int mask, char *smask) { int i = 0; if (mask & LUA_MASKCALL) smask[i++] = 'c'; if (mask & LUA_MASKRET) smask[i++] = 'r'; if (mask & LUA_MASKLINE) smask[i++] = 'l'; smask[i] = '\0'; return smask; } static int db_sethook (lua_State *L) { int arg, mask, count; lua_Hook func; lua_State *L1 = getthread(L, &arg); if (lua_isnoneornil(L, arg+1)) { /* no hook? */ lua_settop(L, arg+1); func = NULL; mask = 0; count = 0; /* turn off hooks */ } else { const char *smask = luaL_checkstring(L, arg+2); luaL_checktype(L, arg+1, LUA_TFUNCTION); count = (int)luaL_optinteger(L, arg + 3, 0); func = hookf; mask = makemask(smask, count); } if (lua_rawgetp(L, LUA_REGISTRYINDEX, &HOOKKEY) == LUA_TNIL) { lua_createtable(L, 0, 2); /* create a hook table */ lua_pushvalue(L, -1); lua_rawsetp(L, LUA_REGISTRYINDEX, &HOOKKEY); /* set it in position */ lua_pushstring(L, "k"); lua_setfield(L, -2, "__mode"); /** hooktable.__mode = "k" */ lua_pushvalue(L, -1); lua_setmetatable(L, -2); /* setmetatable(hooktable) = hooktable */ } checkstack(L, L1, 1); lua_pushthread(L1); lua_xmove(L1, L, 1); /* key (thread) */ lua_pushvalue(L, arg + 1); /* value (hook function) */ lua_rawset(L, -3); /* hooktable[L1] = new Lua hook */ lua_sethook(L1, func, mask, count); return 0; } static int db_gethook (lua_State *L) { int arg; lua_State *L1 = getthread(L, &arg); char buff[5]; int mask = lua_gethookmask(L1); lua_Hook hook = lua_gethook(L1); if (hook == NULL) /* no hook? */ lua_pushnil(L); else if (hook != hookf) /* external hook? */ lua_pushliteral(L, "external hook"); else { /* hook table must exist */ lua_rawgetp(L, LUA_REGISTRYINDEX, &HOOKKEY); checkstack(L, L1, 1); lua_pushthread(L1); lua_xmove(L1, L, 1); lua_rawget(L, -2); /* 1st result = hooktable[L1] */ lua_remove(L, -2); /* remove hook table */ } lua_pushstring(L, unmakemask(mask, buff)); /* 2nd result = mask */ lua_pushinteger(L, lua_gethookcount(L1)); /* 3rd result = count */ return 3; } static int db_debug (lua_State *L) { for (;;) { char buffer[250]; //lua_writestringerror("%s", "lua_debug> "); if (fgets(buffer, sizeof(buffer), stdin) == 0 || strcmp(buffer, "cont\n") == 0) return 0; if (luaL_loadbuffer(L, buffer, strlen(buffer), "=(debug command)") || lua_pcall(L, 0, 0, 0)) //lua_writestringerror("%s\n", lua_tostring(L, -1)); lua_settop(L, 0); /* remove eventual returns */ } } static int db_traceback (lua_State *L) { int arg; lua_State *L1 = getthread(L, &arg); const char *msg = lua_tostring(L, arg + 1); if (msg == NULL && !lua_isnoneornil(L, arg + 1)) /* non-string 'msg'? */ lua_pushvalue(L, arg + 1); /* return it untouched */ else { int level = (int)luaL_optinteger(L, arg + 2, (L == L1) ? 1 : 0); luaL_traceback(L, L1, msg, level); } return 1; } static const luaL_Reg dblib[] = { {"debug", db_debug}, {"getuservalue", db_getuservalue}, {"gethook", db_gethook}, {"getinfo", db_getinfo}, {"getlocal", db_getlocal}, {"getregistry", db_getregistry}, {"getmetatable", db_getmetatable}, {"getupvalue", db_getupvalue}, {"upvaluejoin", db_upvaluejoin}, {"upvalueid", db_upvalueid}, {"setuservalue", db_setuservalue}, {"sethook", db_sethook}, {"setlocal", db_setlocal}, {"setmetatable", db_setmetatable}, {"setupvalue", db_setupvalue}, {"traceback", db_traceback}, {NULL, NULL} }; LUAMOD_API int luaopen_debug (lua_State *L) { luaL_newlib(L, dblib); return 1; } ================================================ FILE: Tests/ApiExplorer/lua/src/ldebug.c ================================================ /* ** $Id: ldebug.c,v 2.121.1.2 2017/07/10 17:21:50 roberto Exp $ ** Debug Interface ** See Copyright Notice in lua.h */ #pragma warning(disable: 6011) #define ldebug_c #define LUA_CORE #include "lprefix.h" #include #include #include #include "lua.h" #include "lapi.h" #include "lcode.h" #include "ldebug.h" #include "ldo.h" #include "lfunc.h" #include "lobject.h" #include "lopcodes.h" #include "lstate.h" #include "lstring.h" #include "ltable.h" #include "ltm.h" #include "lvm.h" #define noLuaClosure(f) ((f) == NULL || (f)->c.tt == LUA_TCCL) /* Active Lua function (given call info) */ #define ci_func(ci) (clLvalue((ci)->func)) static const char *funcnamefromcode (lua_State *L, CallInfo *ci, const char **name); static int currentpc (CallInfo *ci) { lua_assert(isLua(ci)); return pcRel(ci->u.l.savedpc, ci_func(ci)->p); } static int currentline (CallInfo *ci) { return getfuncline(ci_func(ci)->p, currentpc(ci)); } /* ** If function yielded, its 'func' can be in the 'extra' field. The ** next function restores 'func' to its correct value for debugging ** purposes. (It exchanges 'func' and 'extra'; so, when called again, ** after debugging, it also "re-restores" ** 'func' to its altered value. */ static void swapextra (lua_State *L) { if (L->status == LUA_YIELD) { CallInfo *ci = L->ci; /* get function that yielded */ StkId temp = ci->func; /* exchange its 'func' and 'extra' values */ ci->func = restorestack(L, ci->extra); ci->extra = savestack(L, temp); } } /* ** This function can be called asynchronously (e.g. during a signal). ** Fields 'oldpc', 'basehookcount', and 'hookcount' (set by ** 'resethookcount') are for debug only, and it is no problem if they ** get arbitrary values (causes at most one wrong hook call). 'hookmask' ** is an atomic value. We assume that pointers are atomic too (e.g., gcc ** ensures that for all platforms where it runs). Moreover, 'hook' is ** always checked before being called (see 'luaD_hook'). */ LUA_API void lua_sethook (lua_State *L, lua_Hook func, int mask, int count) { if (func == NULL || mask == 0) { /* turn off hooks? */ mask = 0; func = NULL; } if (isLua(L->ci)) L->oldpc = L->ci->u.l.savedpc; L->hook = func; L->basehookcount = count; resethookcount(L); L->hookmask = cast_byte(mask); } LUA_API lua_Hook lua_gethook (lua_State *L) { return L->hook; } LUA_API int lua_gethookmask (lua_State *L) { return L->hookmask; } LUA_API int lua_gethookcount (lua_State *L) { return L->basehookcount; } LUA_API int lua_getstack (lua_State *L, int level, lua_Debug *ar) { int status; CallInfo *ci; if (level < 0) return 0; /* invalid (negative) level */ lua_lock(L); for (ci = L->ci; level > 0 && ci != &L->base_ci; ci = ci->previous) level--; if (level == 0 && ci != &L->base_ci) { /* level found? */ status = 1; ar->i_ci = ci; } else status = 0; /* no such level */ lua_unlock(L); return status; } static const char *upvalname (Proto *p, int uv) { TString *s = check_exp(uv < p->sizeupvalues, p->upvalues[uv].name); if (s == NULL) return "?"; else return getstr(s); } static const char *findvararg (CallInfo *ci, int n, StkId *pos) { int nparams = clLvalue(ci->func)->p->numparams; if (n >= cast_int(ci->u.l.base - ci->func) - nparams) return NULL; /* no such vararg */ else { *pos = ci->func + nparams + n; return "(*vararg)"; /* generic name for any vararg */ } } static const char *findlocal (lua_State *L, CallInfo *ci, int n, StkId *pos) { const char *name = NULL; StkId base; if (isLua(ci)) { if (n < 0) /* access to vararg values? */ return findvararg(ci, -n, pos); else { base = ci->u.l.base; name = luaF_getlocalname(ci_func(ci)->p, n, currentpc(ci)); } } else base = ci->func + 1; if (name == NULL) { /* no 'standard' name? */ StkId limit = (ci == L->ci) ? L->top : ci->next->func; if (limit - base >= n && n > 0) /* is 'n' inside 'ci' stack? */ name = "(*temporary)"; /* generic name for any valid slot */ else return NULL; /* no name */ } *pos = base + (n - 1); return name; } LUA_API const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n) { const char *name; lua_lock(L); swapextra(L); if (ar == NULL) { /* information about non-active function? */ if (!isLfunction(L->top - 1)) /* not a Lua function? */ name = NULL; else /* consider live variables at function start (parameters) */ name = luaF_getlocalname(clLvalue(L->top - 1)->p, n, 0); } else { /* active function; get information through 'ar' */ StkId pos = NULL; /* to avoid warnings */ name = findlocal(L, ar->i_ci, n, &pos); if (name) { setobj2s(L, L->top, pos); api_incr_top(L); } } swapextra(L); lua_unlock(L); return name; } LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n) { StkId pos = NULL; /* to avoid warnings */ const char *name; lua_lock(L); swapextra(L); name = findlocal(L, ar->i_ci, n, &pos); if (name) { setobjs2s(L, pos, L->top - 1); L->top--; /* pop value */ } swapextra(L); lua_unlock(L); return name; } static void funcinfo (lua_Debug *ar, Closure *cl) { if (noLuaClosure(cl)) { ar->source = "=[C]"; ar->linedefined = -1; ar->lastlinedefined = -1; ar->what = "C"; } else { Proto *p = cl->l.p; ar->source = p->source ? getstr(p->source) : "=?"; ar->linedefined = p->linedefined; ar->lastlinedefined = p->lastlinedefined; ar->what = (ar->linedefined == 0) ? "main" : "Lua"; } luaO_chunkid(ar->short_src, ar->source, LUA_IDSIZE); } static void collectvalidlines (lua_State *L, Closure *f) { if (noLuaClosure(f)) { setnilvalue(L->top); api_incr_top(L); } else { int i; TValue v; int *lineinfo = f->l.p->lineinfo; Table *t = luaH_new(L); /* new table to store active lines */ sethvalue(L, L->top, t); /* push it on stack */ api_incr_top(L); setbvalue(&v, 1); /* boolean 'true' to be the value of all indices */ for (i = 0; i < f->l.p->sizelineinfo; i++) /* for all lines with code */ luaH_setint(L, t, lineinfo[i], &v); /* table[line] = true */ } } static const char *getfuncname (lua_State *L, CallInfo *ci, const char **name) { if (ci == NULL) /* no 'ci'? */ return NULL; /* no info */ else if (ci->callstatus & CIST_FIN) { /* is this a finalizer? */ *name = "__gc"; return "metamethod"; /* report it as such */ } /* calling function is a known Lua function? */ else if (!(ci->callstatus & CIST_TAIL) && isLua(ci->previous)) return funcnamefromcode(L, ci->previous, name); else return NULL; /* no way to find a name */ } static int auxgetinfo (lua_State *L, const char *what, lua_Debug *ar, Closure *f, CallInfo *ci) { int status = 1; for (; *what; what++) { switch (*what) { case 'S': { funcinfo(ar, f); break; } case 'l': { ar->currentline = (ci && isLua(ci)) ? currentline(ci) : -1; break; } case 'u': { ar->nups = (f == NULL) ? 0 : f->c.nupvalues; if (noLuaClosure(f)) { ar->isvararg = 1; ar->nparams = 0; } else { ar->isvararg = f->l.p->is_vararg; ar->nparams = f->l.p->numparams; } break; } case 't': { ar->istailcall = (ci) ? ci->callstatus & CIST_TAIL : 0; break; } case 'n': { ar->namewhat = getfuncname(L, ci, &ar->name); if (ar->namewhat == NULL) { ar->namewhat = ""; /* not found */ ar->name = NULL; } break; } case 'L': case 'f': /* handled by lua_getinfo */ break; default: status = 0; /* invalid option */ } } return status; } LUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar) { int status; Closure *cl; CallInfo *ci; StkId func; lua_lock(L); swapextra(L); if (*what == '>') { ci = NULL; func = L->top - 1; api_check(L, ttisfunction(func), "function expected"); what++; /* skip the '>' */ L->top--; /* pop function */ } else { ci = ar->i_ci; func = ci->func; lua_assert(ttisfunction(ci->func)); } cl = ttisclosure(func) ? clvalue(func) : NULL; status = auxgetinfo(L, what, ar, cl, ci); if (strchr(what, 'f')) { setobjs2s(L, L->top, func); api_incr_top(L); } swapextra(L); /* correct before option 'L', which can raise a mem. error */ if (strchr(what, 'L')) collectvalidlines(L, cl); lua_unlock(L); return status; } /* ** {====================================================== ** Symbolic Execution ** ======================================================= */ static const char *getobjname (Proto *p, int lastpc, int reg, const char **name); /* ** find a "name" for the RK value 'c' */ static void kname (Proto *p, int pc, int c, const char **name) { if (ISK(c)) { /* is 'c' a constant? */ TValue *kvalue = &p->k[INDEXK(c)]; if (ttisstring(kvalue)) { /* literal constant? */ *name = svalue(kvalue); /* it is its own name */ return; } /* else no reasonable name found */ } else { /* 'c' is a register */ const char *what = getobjname(p, pc, c, name); /* search for 'c' */ if (what && *what == 'c') { /* found a constant name? */ return; /* 'name' already filled */ } /* else no reasonable name found */ } *name = "?"; /* no reasonable name found */ } static int filterpc (int pc, int jmptarget) { if (pc < jmptarget) /* is code conditional (inside a jump)? */ return -1; /* cannot know who sets that register */ else return pc; /* current position sets that register */ } /* ** try to find last instruction before 'lastpc' that modified register 'reg' */ static int findsetreg (Proto *p, int lastpc, int reg) { int pc; int setreg = -1; /* keep last instruction that changed 'reg' */ int jmptarget = 0; /* any code before this address is conditional */ for (pc = 0; pc < lastpc; pc++) { Instruction i = p->code[pc]; OpCode op = GET_OPCODE(i); int a = GETARG_A(i); switch (op) { case OP_LOADNIL: { int b = GETARG_B(i); if (a <= reg && reg <= a + b) /* set registers from 'a' to 'a+b' */ setreg = filterpc(pc, jmptarget); break; } case OP_TFORCALL: { if (reg >= a + 2) /* affect all regs above its base */ setreg = filterpc(pc, jmptarget); break; } case OP_CALL: case OP_TAILCALL: { if (reg >= a) /* affect all registers above base */ setreg = filterpc(pc, jmptarget); break; } case OP_JMP: { int b = GETARG_sBx(i); int dest = pc + 1 + b; /* jump is forward and do not skip 'lastpc'? */ if (pc < dest && dest <= lastpc) { if (dest > jmptarget) jmptarget = dest; /* update 'jmptarget' */ } break; } default: if (testAMode(op) && reg == a) /* any instruction that set A */ setreg = filterpc(pc, jmptarget); break; } } return setreg; } static const char *getobjname (Proto *p, int lastpc, int reg, const char **name) { int pc; *name = luaF_getlocalname(p, reg + 1, lastpc); if (*name) /* is a local? */ return "local"; /* else try symbolic execution */ pc = findsetreg(p, lastpc, reg); if (pc != -1) { /* could find instruction? */ Instruction i = p->code[pc]; OpCode op = GET_OPCODE(i); switch (op) { case OP_MOVE: { int b = GETARG_B(i); /* move from 'b' to 'a' */ if (b < GETARG_A(i)) return getobjname(p, pc, b, name); /* get name for 'b' */ break; } case OP_GETTABUP: case OP_GETTABLE: { int k = GETARG_C(i); /* key index */ int t = GETARG_B(i); /* table index */ const char *vn = (op == OP_GETTABLE) /* name of indexed variable */ ? luaF_getlocalname(p, t + 1, pc) : upvalname(p, t); kname(p, pc, k, name); return (vn && strcmp(vn, LUA_ENV) == 0) ? "global" : "field"; } case OP_GETUPVAL: { *name = upvalname(p, GETARG_B(i)); return "upvalue"; } case OP_LOADK: case OP_LOADKX: { int b = (op == OP_LOADK) ? GETARG_Bx(i) : GETARG_Ax(p->code[pc + 1]); if (ttisstring(&p->k[b])) { *name = svalue(&p->k[b]); return "constant"; } break; } case OP_SELF: { int k = GETARG_C(i); /* key index */ kname(p, pc, k, name); return "method"; } default: break; /* go through to return NULL */ } } return NULL; /* could not find reasonable name */ } /* ** Try to find a name for a function based on the code that called it. ** (Only works when function was called by a Lua function.) ** Returns what the name is (e.g., "for iterator", "method", ** "metamethod") and sets '*name' to point to the name. */ static const char *funcnamefromcode (lua_State *L, CallInfo *ci, const char **name) { TMS tm = (TMS)0; /* (initial value avoids warnings) */ Proto *p = ci_func(ci)->p; /* calling function */ int pc = currentpc(ci); /* calling instruction index */ Instruction i = p->code[pc]; /* calling instruction */ if (ci->callstatus & CIST_HOOKED) { /* was it called inside a hook? */ *name = "?"; return "hook"; } switch (GET_OPCODE(i)) { case OP_CALL: case OP_TAILCALL: return getobjname(p, pc, GETARG_A(i), name); /* get function name */ case OP_TFORCALL: { /* for iterator */ *name = "for iterator"; return "for iterator"; } /* other instructions can do calls through metamethods */ case OP_SELF: case OP_GETTABUP: case OP_GETTABLE: tm = TM_INDEX; break; case OP_SETTABUP: case OP_SETTABLE: tm = TM_NEWINDEX; break; case OP_ADD: case OP_SUB: case OP_MUL: case OP_MOD: case OP_POW: case OP_DIV: case OP_IDIV: case OP_BAND: case OP_BOR: case OP_BXOR: case OP_SHL: case OP_SHR: { int offset = cast_int(GET_OPCODE(i)) - cast_int(OP_ADD); /* ORDER OP */ tm = cast(TMS, offset + cast_int(TM_ADD)); /* ORDER TM */ break; } case OP_UNM: tm = TM_UNM; break; case OP_BNOT: tm = TM_BNOT; break; case OP_LEN: tm = TM_LEN; break; case OP_CONCAT: tm = TM_CONCAT; break; case OP_EQ: tm = TM_EQ; break; case OP_LT: tm = TM_LT; break; case OP_LE: tm = TM_LE; break; default: return NULL; /* cannot find a reasonable name */ } *name = getstr(G(L)->tmname[tm]); return "metamethod"; } /* }====================================================== */ /* ** The subtraction of two potentially unrelated pointers is ** not ISO C, but it should not crash a program; the subsequent ** checks are ISO C and ensure a correct result. */ static int isinstack (CallInfo *ci, const TValue *o) { ptrdiff_t i = o - ci->u.l.base; return (0 <= i && i < (ci->top - ci->u.l.base) && ci->u.l.base + i == o); } /* ** Checks whether value 'o' came from an upvalue. (That can only happen ** with instructions OP_GETTABUP/OP_SETTABUP, which operate directly on ** upvalues.) */ static const char *getupvalname (CallInfo *ci, const TValue *o, const char **name) { LClosure *c = ci_func(ci); int i; for (i = 0; i < c->nupvalues; i++) { if (c->upvals[i]->v == o) { *name = upvalname(c->p, i); return "upvalue"; } } return NULL; } static const char *varinfo (lua_State *L, const TValue *o) { const char *name = NULL; /* to avoid warnings */ CallInfo *ci = L->ci; const char *kind = NULL; if (isLua(ci)) { kind = getupvalname(ci, o, &name); /* check whether 'o' is an upvalue */ if (!kind && isinstack(ci, o)) /* no? try a register */ kind = getobjname(ci_func(ci)->p, currentpc(ci), cast_int(o - ci->u.l.base), &name); } return (kind) ? luaO_pushfstring(L, " (%s '%s')", kind, name) : ""; } l_noret luaG_typeerror (lua_State *L, const TValue *o, const char *op) { const char *t = luaT_objtypename(L, o); luaG_runerror(L, "attempt to %s a %s value%s", op, t, varinfo(L, o)); } l_noret luaG_concaterror (lua_State *L, const TValue *p1, const TValue *p2) { if (ttisstring(p1) || cvt2str(p1)) p1 = p2; luaG_typeerror(L, p1, "concatenate"); } l_noret luaG_opinterror (lua_State *L, const TValue *p1, const TValue *p2, const char *msg) { lua_Number temp; if (!tonumber(p1, &temp)) /* first operand is wrong? */ p2 = p1; /* now second is wrong */ luaG_typeerror(L, p2, msg); } /* ** Error when both values are convertible to numbers, but not to integers */ l_noret luaG_tointerror (lua_State *L, const TValue *p1, const TValue *p2) { lua_Integer temp; if (!tointeger(p1, &temp)) p2 = p1; luaG_runerror(L, "number%s has no integer representation", varinfo(L, p2)); } l_noret luaG_ordererror (lua_State *L, const TValue *p1, const TValue *p2) { const char *t1 = luaT_objtypename(L, p1); const char *t2 = luaT_objtypename(L, p2); if (strcmp(t1, t2) == 0) luaG_runerror(L, "attempt to compare two %s values", t1); else luaG_runerror(L, "attempt to compare %s with %s", t1, t2); } /* add src:line information to 'msg' */ const char *luaG_addinfo (lua_State *L, const char *msg, TString *src, int line) { char buff[LUA_IDSIZE]; if (src) luaO_chunkid(buff, getstr(src), LUA_IDSIZE); else { /* no source available; use "?" instead */ buff[0] = '?'; buff[1] = '\0'; } return luaO_pushfstring(L, "%s:%d: %s", buff, line, msg); } l_noret luaG_errormsg (lua_State *L) { if (L->errfunc != 0) { /* is there an error handling function? */ StkId errfunc = restorestack(L, L->errfunc); setobjs2s(L, L->top, L->top - 1); /* move argument */ setobjs2s(L, L->top - 1, errfunc); /* push function */ L->top++; /* assume EXTRA_STACK */ luaD_callnoyield(L, L->top - 2, 1); /* call it */ } luaD_throw(L, LUA_ERRRUN); } l_noret luaG_runerror (lua_State *L, const char *fmt, ...) { CallInfo *ci = L->ci; const char *msg; va_list argp; luaC_checkGC(L); /* error message uses memory */ va_start(argp, fmt); msg = luaO_pushvfstring(L, fmt, argp); /* format message */ va_end(argp); if (isLua(ci)) /* if Lua function, add source:line information */ luaG_addinfo(L, msg, ci_func(ci)->p->source, currentline(ci)); luaG_errormsg(L); } void luaG_traceexec (lua_State *L) { CallInfo *ci = L->ci; lu_byte mask = L->hookmask; int counthook = (--L->hookcount == 0 && (mask & LUA_MASKCOUNT)); if (counthook) resethookcount(L); /* reset count */ else if (!(mask & LUA_MASKLINE)) return; /* no line hook and count != 0; nothing to be done */ if (ci->callstatus & CIST_HOOKYIELD) { /* called hook last time? */ ci->callstatus &= ~CIST_HOOKYIELD; /* erase mark */ return; /* do not call hook again (VM yielded, so it did not move) */ } if (counthook) luaD_hook(L, LUA_HOOKCOUNT, -1); /* call count hook */ if (mask & LUA_MASKLINE) { Proto *p = ci_func(ci)->p; int npc = pcRel(ci->u.l.savedpc, p); int newline = getfuncline(p, npc); if (npc == 0 || /* call linehook when enter a new function, */ ci->u.l.savedpc <= L->oldpc || /* when jump back (loop), or when */ newline != getfuncline(p, pcRel(L->oldpc, p))) /* enter a new line */ luaD_hook(L, LUA_HOOKLINE, newline); /* call line hook */ } L->oldpc = ci->u.l.savedpc; if (L->status == LUA_YIELD) { /* did hook yield? */ if (counthook) L->hookcount = 1; /* undo decrement to zero */ ci->u.l.savedpc--; /* undo increment (resume will increment it again) */ ci->callstatus |= CIST_HOOKYIELD; /* mark that it yielded */ ci->func = L->top - 1; /* protect stack below results */ luaD_throw(L, LUA_YIELD); } } ================================================ FILE: Tests/ApiExplorer/lua/src/ldebug.h ================================================ /* ** $Id: ldebug.h,v 2.14.1.1 2017/04/19 17:20:42 roberto Exp $ ** Auxiliary functions from Debug Interface module ** See Copyright Notice in lua.h */ #ifndef ldebug_h #define ldebug_h #include "lstate.h" #define pcRel(pc, p) (cast(int, (pc) - (p)->code) - 1) #define getfuncline(f,pc) (((f)->lineinfo) ? (f)->lineinfo[pc] : -1) #define resethookcount(L) (L->hookcount = L->basehookcount) LUAI_FUNC l_noret luaG_typeerror (lua_State *L, const TValue *o, const char *opname); LUAI_FUNC l_noret luaG_concaterror (lua_State *L, const TValue *p1, const TValue *p2); LUAI_FUNC l_noret luaG_opinterror (lua_State *L, const TValue *p1, const TValue *p2, const char *msg); LUAI_FUNC l_noret luaG_tointerror (lua_State *L, const TValue *p1, const TValue *p2); LUAI_FUNC l_noret luaG_ordererror (lua_State *L, const TValue *p1, const TValue *p2); LUAI_FUNC l_noret luaG_runerror (lua_State *L, const char *fmt, ...); LUAI_FUNC const char *luaG_addinfo (lua_State *L, const char *msg, TString *src, int line); LUAI_FUNC l_noret luaG_errormsg (lua_State *L); LUAI_FUNC void luaG_traceexec (lua_State *L); #endif ================================================ FILE: Tests/ApiExplorer/lua/src/ldo.c ================================================ /* ** $Id: ldo.c,v 2.157.1.1 2017/04/19 17:20:42 roberto Exp $ ** Stack and Call structure of Lua ** See Copyright Notice in lua.h */ #pragma warning(disable: 6011) #pragma warning(disable: 4244) #define ldo_c #define LUA_CORE #include "lprefix.h" #include #include #include #include "lua.h" #include "lapi.h" #include "ldebug.h" #include "ldo.h" #include "lfunc.h" #include "lgc.h" #include "lmem.h" #include "lobject.h" #include "lopcodes.h" #include "lparser.h" #include "lstate.h" #include "lstring.h" #include "ltable.h" #include "ltm.h" #include "lundump.h" #include "lvm.h" #include "lzio.h" #define errorstatus(s) ((s) > LUA_YIELD) /* ** {====================================================== ** Error-recovery functions ** ======================================================= */ /* ** LUAI_THROW/LUAI_TRY define how Lua does exception handling. By ** default, Lua handles errors with exceptions when compiling as ** C++ code, with _longjmp/_setjmp when asked to use them, and with ** longjmp/setjmp otherwise. */ #if !defined(LUAI_THROW) /* { */ #if defined(__cplusplus) && !defined(LUA_USE_LONGJMP) /* { */ /* C++ exceptions */ #define LUAI_THROW(L,c) throw(c) #define LUAI_TRY(L,c,a) \ try { a } catch(...) { if ((c)->status == 0) (c)->status = -1; } #define luai_jmpbuf int /* dummy variable */ #elif defined(LUA_USE_POSIX) /* }{ */ /* in POSIX, try _longjmp/_setjmp (more efficient) */ #define LUAI_THROW(L,c) _longjmp((c)->b, 1) #define LUAI_TRY(L,c,a) if (_setjmp((c)->b) == 0) { a } #define luai_jmpbuf jmp_buf #else /* }{ */ /* ISO C handling with long jumps */ #define LUAI_THROW(L,c) longjmp((c)->b, 1) #define LUAI_TRY(L,c,a) if (setjmp((c)->b) == 0) { a } #define luai_jmpbuf jmp_buf #endif /* } */ #endif /* } */ /* chain list of long jump buffers */ struct lua_longjmp { struct lua_longjmp *previous; luai_jmpbuf b; volatile int status; /* error code */ }; static void seterrorobj (lua_State *L, int errcode, StkId oldtop) { switch (errcode) { case LUA_ERRMEM: { /* memory error? */ setsvalue2s(L, oldtop, G(L)->memerrmsg); /* reuse preregistered msg. */ break; } case LUA_ERRERR: { setsvalue2s(L, oldtop, luaS_newliteral(L, "error in error handling")); break; } default: { setobjs2s(L, oldtop, L->top - 1); /* error message on current top */ break; } } L->top = oldtop + 1; } l_noret luaD_throw (lua_State *L, int errcode) { if (L->errorJmp) { /* thread has an error handler? */ L->errorJmp->status = errcode; /* set status */ LUAI_THROW(L, L->errorJmp); /* jump to it */ } else { /* thread has no error handler */ global_State *g = G(L); L->status = cast_byte(errcode); /* mark it as dead */ if (g->mainthread->errorJmp) { /* main thread has a handler? */ setobjs2s(L, g->mainthread->top++, L->top - 1); /* copy error obj. */ luaD_throw(g->mainthread, errcode); /* re-throw in main thread */ } else { /* no handler at all; abort */ if (g->panic) { /* panic function? */ seterrorobj(L, errcode, L->top); /* assume EXTRA_STACK */ if (L->ci->top < L->top) L->ci->top = L->top; /* pushing msg. can break this invariant */ lua_unlock(L); g->panic(L); /* call panic function (last chance to jump out) */ } abort(); } } } int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) { unsigned short oldnCcalls = L->nCcalls; struct lua_longjmp lj; lj.status = LUA_OK; lj.previous = L->errorJmp; /* chain new error handler */ L->errorJmp = &lj; LUAI_TRY(L, &lj, (*f)(L, ud); ); L->errorJmp = lj.previous; /* restore old error handler */ L->nCcalls = oldnCcalls; return lj.status; } /* }====================================================== */ /* ** {================================================================== ** Stack reallocation ** =================================================================== */ static void correctstack (lua_State *L, TValue *oldstack) { CallInfo *ci; UpVal *up; L->top = (L->top - oldstack) + L->stack; for (up = L->openupval; up != NULL; up = up->u.open.next) up->v = (up->v - oldstack) + L->stack; for (ci = L->ci; ci != NULL; ci = ci->previous) { ci->top = (ci->top - oldstack) + L->stack; ci->func = (ci->func - oldstack) + L->stack; if (isLua(ci)) ci->u.l.base = (ci->u.l.base - oldstack) + L->stack; } } /* some space for error handling */ #define ERRORSTACKSIZE (LUAI_MAXSTACK + 200) void luaD_reallocstack (lua_State *L, int newsize) { TValue *oldstack = L->stack; int lim = L->stacksize; lua_assert(newsize <= LUAI_MAXSTACK || newsize == ERRORSTACKSIZE); lua_assert(L->stack_last - L->stack == L->stacksize - EXTRA_STACK); luaM_reallocvector(L, L->stack, L->stacksize, newsize, TValue); for (; lim < newsize; lim++) setnilvalue(L->stack + lim); /* erase new segment */ L->stacksize = newsize; L->stack_last = L->stack + newsize - EXTRA_STACK; correctstack(L, oldstack); } void luaD_growstack (lua_State *L, int n) { int size = L->stacksize; if (size > LUAI_MAXSTACK) /* error after extra size? */ luaD_throw(L, LUA_ERRERR); else { int needed = cast_int(L->top - L->stack) + n + EXTRA_STACK; int newsize = 2 * size; if (newsize > LUAI_MAXSTACK) newsize = LUAI_MAXSTACK; if (newsize < needed) newsize = needed; if (newsize > LUAI_MAXSTACK) { /* stack overflow? */ luaD_reallocstack(L, ERRORSTACKSIZE); luaG_runerror(L, "stack overflow"); } else luaD_reallocstack(L, newsize); } } static int stackinuse (lua_State *L) { CallInfo *ci; StkId lim = L->top; for (ci = L->ci; ci != NULL; ci = ci->previous) { if (lim < ci->top) lim = ci->top; } lua_assert(lim <= L->stack_last); return cast_int(lim - L->stack) + 1; /* part of stack in use */ } void luaD_shrinkstack (lua_State *L) { int inuse = stackinuse(L); int goodsize = inuse + (inuse / 8) + 2*EXTRA_STACK; if (goodsize > LUAI_MAXSTACK) goodsize = LUAI_MAXSTACK; /* respect stack limit */ if (L->stacksize > LUAI_MAXSTACK) /* had been handling stack overflow? */ luaE_freeCI(L); /* free all CIs (list grew because of an error) */ else luaE_shrinkCI(L); /* shrink list */ /* if thread is currently not handling a stack overflow and its good size is smaller than current size, shrink its stack */ if (inuse <= (LUAI_MAXSTACK - EXTRA_STACK) && goodsize < L->stacksize) luaD_reallocstack(L, goodsize); else /* don't change stack */ condmovestack(L,{},{}); /* (change only for debugging) */ } void luaD_inctop (lua_State *L) { luaD_checkstack(L, 1); L->top++; } /* }================================================================== */ /* ** Call a hook for the given event. Make sure there is a hook to be ** called. (Both 'L->hook' and 'L->hookmask', which triggers this ** function, can be changed asynchronously by signals.) */ void luaD_hook (lua_State *L, int event, int line) { lua_Hook hook = L->hook; if (hook && L->allowhook) { /* make sure there is a hook */ CallInfo *ci = L->ci; ptrdiff_t top = savestack(L, L->top); ptrdiff_t ci_top = savestack(L, ci->top); lua_Debug ar; ar.event = event; ar.currentline = line; ar.i_ci = ci; luaD_checkstack(L, LUA_MINSTACK); /* ensure minimum stack size */ ci->top = L->top + LUA_MINSTACK; lua_assert(ci->top <= L->stack_last); L->allowhook = 0; /* cannot call hooks inside a hook */ ci->callstatus |= CIST_HOOKED; lua_unlock(L); (*hook)(L, &ar); lua_lock(L); lua_assert(!L->allowhook); L->allowhook = 1; ci->top = restorestack(L, ci_top); L->top = restorestack(L, top); ci->callstatus &= ~CIST_HOOKED; } } static void callhook (lua_State *L, CallInfo *ci) { int hook = LUA_HOOKCALL; ci->u.l.savedpc++; /* hooks assume 'pc' is already incremented */ if (isLua(ci->previous) && GET_OPCODE(*(ci->previous->u.l.savedpc - 1)) == OP_TAILCALL) { ci->callstatus |= CIST_TAIL; hook = LUA_HOOKTAILCALL; } luaD_hook(L, hook, -1); ci->u.l.savedpc--; /* correct 'pc' */ } static StkId adjust_varargs (lua_State *L, Proto *p, int actual) { int i; int nfixargs = p->numparams; StkId base, fixed; /* move fixed parameters to final position */ fixed = L->top - actual; /* first fixed argument */ base = L->top; /* final position of first argument */ for (i = 0; i < nfixargs && i < actual; i++) { setobjs2s(L, L->top++, fixed + i); setnilvalue(fixed + i); /* erase original copy (for GC) */ } for (; i < nfixargs; i++) setnilvalue(L->top++); /* complete missing arguments */ return base; } /* ** Check whether __call metafield of 'func' is a function. If so, put ** it in stack below original 'func' so that 'luaD_precall' can call ** it. Raise an error if __call metafield is not a function. */ static void tryfuncTM (lua_State *L, StkId func) { const TValue *tm = luaT_gettmbyobj(L, func, TM_CALL); StkId p; if (!ttisfunction(tm)) luaG_typeerror(L, func, "call"); /* Open a hole inside the stack at 'func' */ for (p = L->top; p > func; p--) setobjs2s(L, p, p-1); L->top++; /* slot ensured by caller */ setobj2s(L, func, tm); /* tag method is the new function to be called */ } /* ** Given 'nres' results at 'firstResult', move 'wanted' of them to 'res'. ** Handle most typical cases (zero results for commands, one result for ** expressions, multiple results for tail calls/single parameters) ** separated. */ static int moveresults (lua_State *L, const TValue *firstResult, StkId res, int nres, int wanted) { switch (wanted) { /* handle typical cases separately */ case 0: break; /* nothing to move */ case 1: { /* one result needed */ if (nres == 0) /* no results? */ firstResult = luaO_nilobject; /* adjust with nil */ setobjs2s(L, res, firstResult); /* move it to proper place */ break; } case LUA_MULTRET: { int i; for (i = 0; i < nres; i++) /* move all results to correct place */ setobjs2s(L, res + i, firstResult + i); L->top = res + nres; return 0; /* wanted == LUA_MULTRET */ } default: { int i; if (wanted <= nres) { /* enough results? */ for (i = 0; i < wanted; i++) /* move wanted results to correct place */ setobjs2s(L, res + i, firstResult + i); } else { /* not enough results; use all of them plus nils */ for (i = 0; i < nres; i++) /* move all results to correct place */ setobjs2s(L, res + i, firstResult + i); for (; i < wanted; i++) /* complete wanted number of results */ setnilvalue(res + i); } break; } } L->top = res + wanted; /* top points after the last result */ return 1; } /* ** Finishes a function call: calls hook if necessary, removes CallInfo, ** moves current number of results to proper place; returns 0 iff call ** wanted multiple (variable number of) results. */ int luaD_poscall (lua_State *L, CallInfo *ci, StkId firstResult, int nres) { StkId res; int wanted = ci->nresults; if (L->hookmask & (LUA_MASKRET | LUA_MASKLINE)) { if (L->hookmask & LUA_MASKRET) { ptrdiff_t fr = savestack(L, firstResult); /* hook may change stack */ luaD_hook(L, LUA_HOOKRET, -1); firstResult = restorestack(L, fr); } L->oldpc = ci->previous->u.l.savedpc; /* 'oldpc' for caller function */ } res = ci->func; /* res == final position of 1st result */ L->ci = ci->previous; /* back to caller */ /* move results to proper place */ return moveresults(L, firstResult, res, nres, wanted); } #define next_ci(L) (L->ci = (L->ci->next ? L->ci->next : luaE_extendCI(L))) /* macro to check stack size, preserving 'p' */ #define checkstackp(L,n,p) \ luaD_checkstackaux(L, n, \ ptrdiff_t t__ = savestack(L, p); /* save 'p' */ \ luaC_checkGC(L), /* stack grow uses memory */ \ p = restorestack(L, t__)) /* 'pos' part: restore 'p' */ /* ** Prepares a function call: checks the stack, creates a new CallInfo ** entry, fills in the relevant information, calls hook if needed. ** If function is a C function, does the call, too. (Otherwise, leave ** the execution ('luaV_execute') to the caller, to allow stackless ** calls.) Returns true iff function has been executed (C function). */ int luaD_precall (lua_State *L, StkId func, int nresults) { lua_CFunction f; CallInfo *ci; switch (ttype(func)) { case LUA_TCCL: /* C closure */ f = clCvalue(func)->f; goto Cfunc; case LUA_TLCF: /* light C function */ f = fvalue(func); Cfunc: { int n; /* number of returns */ checkstackp(L, LUA_MINSTACK, func); /* ensure minimum stack size */ ci = next_ci(L); /* now 'enter' new function */ ci->nresults = nresults; ci->func = func; ci->top = L->top + LUA_MINSTACK; lua_assert(ci->top <= L->stack_last); ci->callstatus = 0; if (L->hookmask & LUA_MASKCALL) luaD_hook(L, LUA_HOOKCALL, -1); lua_unlock(L); n = (*f)(L); /* do the actual call */ lua_lock(L); api_checknelems(L, n); luaD_poscall(L, ci, L->top - n, n); return 1; } case LUA_TLCL: { /* Lua function: prepare its call */ StkId base; Proto *p = clLvalue(func)->p; int n = cast_int(L->top - func) - 1; /* number of real arguments */ int fsize = p->maxstacksize; /* frame size */ checkstackp(L, fsize, func); if (p->is_vararg) base = adjust_varargs(L, p, n); else { /* non vararg function */ for (; n < p->numparams; n++) setnilvalue(L->top++); /* complete missing arguments */ base = func + 1; } ci = next_ci(L); /* now 'enter' new function */ ci->nresults = nresults; ci->func = func; ci->u.l.base = base; L->top = ci->top = base + fsize; lua_assert(ci->top <= L->stack_last); ci->u.l.savedpc = p->code; /* starting point */ ci->callstatus = CIST_LUA; if (L->hookmask & LUA_MASKCALL) callhook(L, ci); return 0; } default: { /* not a function */ checkstackp(L, 1, func); /* ensure space for metamethod */ tryfuncTM(L, func); /* try to get '__call' metamethod */ return luaD_precall(L, func, nresults); /* now it must be a function */ } } } /* ** Check appropriate error for stack overflow ("regular" overflow or ** overflow while handling stack overflow). If 'nCalls' is larger than ** LUAI_MAXCCALLS (which means it is handling a "regular" overflow) but ** smaller than 9/8 of LUAI_MAXCCALLS, does not report an error (to ** allow overflow handling to work) */ static void stackerror (lua_State *L) { if (L->nCcalls == LUAI_MAXCCALLS) luaG_runerror(L, "C stack overflow"); else if (L->nCcalls >= (LUAI_MAXCCALLS + (LUAI_MAXCCALLS>>3))) luaD_throw(L, LUA_ERRERR); /* error while handing stack error */ } /* ** Call a function (C or Lua). The function to be called is at *func. ** The arguments are on the stack, right after the function. ** When returns, all the results are on the stack, starting at the original ** function position. */ void luaD_call (lua_State *L, StkId func, int nResults) { if (++L->nCcalls >= LUAI_MAXCCALLS) stackerror(L); if (!luaD_precall(L, func, nResults)) /* is a Lua function? */ luaV_execute(L); /* call it */ L->nCcalls--; } /* ** Similar to 'luaD_call', but does not allow yields during the call */ void luaD_callnoyield (lua_State *L, StkId func, int nResults) { L->nny++; luaD_call(L, func, nResults); L->nny--; } /* ** Completes the execution of an interrupted C function, calling its ** continuation function. */ static void finishCcall (lua_State *L, int status) { CallInfo *ci = L->ci; int n; /* must have a continuation and must be able to call it */ lua_assert(ci->u.c.k != NULL && L->nny == 0); /* error status can only happen in a protected call */ lua_assert((ci->callstatus & CIST_YPCALL) || status == LUA_YIELD); if (ci->callstatus & CIST_YPCALL) { /* was inside a pcall? */ ci->callstatus &= ~CIST_YPCALL; /* continuation is also inside it */ L->errfunc = ci->u.c.old_errfunc; /* with the same error function */ } /* finish 'lua_callk'/'lua_pcall'; CIST_YPCALL and 'errfunc' already handled */ adjustresults(L, ci->nresults); lua_unlock(L); n = (*ci->u.c.k)(L, status, ci->u.c.ctx); /* call continuation function */ lua_lock(L); api_checknelems(L, n); luaD_poscall(L, ci, L->top - n, n); /* finish 'luaD_precall' */ } /* ** Executes "full continuation" (everything in the stack) of a ** previously interrupted coroutine until the stack is empty (or another ** interruption long-jumps out of the loop). If the coroutine is ** recovering from an error, 'ud' points to the error status, which must ** be passed to the first continuation function (otherwise the default ** status is LUA_YIELD). */ static void unroll (lua_State *L, void *ud) { if (ud != NULL) /* error status? */ finishCcall(L, *(int *)ud); /* finish 'lua_pcallk' callee */ while (L->ci != &L->base_ci) { /* something in the stack */ if (!isLua(L->ci)) /* C function? */ finishCcall(L, LUA_YIELD); /* complete its execution */ else { /* Lua function */ luaV_finishOp(L); /* finish interrupted instruction */ luaV_execute(L); /* execute down to higher C 'boundary' */ } } } /* ** Try to find a suspended protected call (a "recover point") for the ** given thread. */ static CallInfo *findpcall (lua_State *L) { CallInfo *ci; for (ci = L->ci; ci != NULL; ci = ci->previous) { /* search for a pcall */ if (ci->callstatus & CIST_YPCALL) return ci; } return NULL; /* no pending pcall */ } /* ** Recovers from an error in a coroutine. Finds a recover point (if ** there is one) and completes the execution of the interrupted ** 'luaD_pcall'. If there is no recover point, returns zero. */ static int recover (lua_State *L, int status) { StkId oldtop; CallInfo *ci = findpcall(L); if (ci == NULL) return 0; /* no recovery point */ /* "finish" luaD_pcall */ oldtop = restorestack(L, ci->extra); luaF_close(L, oldtop); seterrorobj(L, status, oldtop); L->ci = ci; L->allowhook = getoah(ci->callstatus); /* restore original 'allowhook' */ L->nny = 0; /* should be zero to be yieldable */ luaD_shrinkstack(L); L->errfunc = ci->u.c.old_errfunc; return 1; /* continue running the coroutine */ } /* ** Signal an error in the call to 'lua_resume', not in the execution ** of the coroutine itself. (Such errors should not be handled by any ** coroutine error handler and should not kill the coroutine.) */ static int resume_error (lua_State *L, const char *msg, int narg) { L->top -= narg; /* remove args from the stack */ setsvalue2s(L, L->top, luaS_new(L, msg)); /* push error message */ api_incr_top(L); lua_unlock(L); return LUA_ERRRUN; } /* ** Do the work for 'lua_resume' in protected mode. Most of the work ** depends on the status of the coroutine: initial state, suspended ** inside a hook, or regularly suspended (optionally with a continuation ** function), plus erroneous cases: non-suspended coroutine or dead ** coroutine. */ static void resume (lua_State *L, void *ud) { int n = *(cast(int*, ud)); /* number of arguments */ StkId firstArg = L->top - n; /* first argument */ CallInfo *ci = L->ci; if (L->status == LUA_OK) { /* starting a coroutine? */ if (!luaD_precall(L, firstArg - 1, LUA_MULTRET)) /* Lua function? */ luaV_execute(L); /* call it */ } else { /* resuming from previous yield */ lua_assert(L->status == LUA_YIELD); L->status = LUA_OK; /* mark that it is running (again) */ ci->func = restorestack(L, ci->extra); if (isLua(ci)) /* yielded inside a hook? */ luaV_execute(L); /* just continue running Lua code */ else { /* 'common' yield */ if (ci->u.c.k != NULL) { /* does it have a continuation function? */ lua_unlock(L); n = (*ci->u.c.k)(L, LUA_YIELD, ci->u.c.ctx); /* call continuation */ lua_lock(L); api_checknelems(L, n); firstArg = L->top - n; /* yield results come from continuation */ } luaD_poscall(L, ci, firstArg, n); /* finish 'luaD_precall' */ } unroll(L, NULL); /* run continuation */ } } LUA_API int lua_resume (lua_State *L, lua_State *from, int nargs) { int status; unsigned short oldnny = L->nny; /* save "number of non-yieldable" calls */ lua_lock(L); if (L->status == LUA_OK) { /* may be starting a coroutine */ if (L->ci != &L->base_ci) /* not in base level? */ return resume_error(L, "cannot resume non-suspended coroutine", nargs); } else if (L->status != LUA_YIELD) return resume_error(L, "cannot resume dead coroutine", nargs); L->nCcalls = (from) ? from->nCcalls + 1 : 1; if (L->nCcalls >= LUAI_MAXCCALLS) return resume_error(L, "C stack overflow", nargs); luai_userstateresume(L, nargs); L->nny = 0; /* allow yields */ api_checknelems(L, (L->status == LUA_OK) ? nargs + 1 : nargs); status = luaD_rawrunprotected(L, resume, &nargs); if (status == -1) /* error calling 'lua_resume'? */ status = LUA_ERRRUN; else { /* continue running after recoverable errors */ while (errorstatus(status) && recover(L, status)) { /* unroll continuation */ status = luaD_rawrunprotected(L, unroll, &status); } if (errorstatus(status)) { /* unrecoverable error? */ L->status = cast_byte(status); /* mark thread as 'dead' */ seterrorobj(L, status, L->top); /* push error message */ L->ci->top = L->top; } else lua_assert(status == L->status); /* normal end or yield */ } L->nny = oldnny; /* restore 'nny' */ L->nCcalls--; lua_assert(L->nCcalls == ((from) ? from->nCcalls : 0)); lua_unlock(L); return status; } LUA_API int lua_isyieldable (lua_State *L) { return (L->nny == 0); } LUA_API int lua_yieldk (lua_State *L, int nresults, lua_KContext ctx, lua_KFunction k) { CallInfo *ci = L->ci; luai_userstateyield(L, nresults); lua_lock(L); api_checknelems(L, nresults); if (L->nny > 0) { if (L != G(L)->mainthread) luaG_runerror(L, "attempt to yield across a C-call boundary"); else luaG_runerror(L, "attempt to yield from outside a coroutine"); } L->status = LUA_YIELD; ci->extra = savestack(L, ci->func); /* save current 'func' */ if (isLua(ci)) { /* inside a hook? */ api_check(L, k == NULL, "hooks cannot continue after yielding"); } else { if ((ci->u.c.k = k) != NULL) /* is there a continuation? */ ci->u.c.ctx = ctx; /* save context */ ci->func = L->top - nresults - 1; /* protect stack below results */ luaD_throw(L, LUA_YIELD); } lua_assert(ci->callstatus & CIST_HOOKED); /* must be inside a hook */ lua_unlock(L); return 0; /* return to 'luaD_hook' */ } int luaD_pcall (lua_State *L, Pfunc func, void *u, ptrdiff_t old_top, ptrdiff_t ef) { int status; CallInfo *old_ci = L->ci; lu_byte old_allowhooks = L->allowhook; unsigned short old_nny = L->nny; ptrdiff_t old_errfunc = L->errfunc; L->errfunc = ef; status = luaD_rawrunprotected(L, func, u); if (status != LUA_OK) { /* an error occurred? */ StkId oldtop = restorestack(L, old_top); luaF_close(L, oldtop); /* close possible pending closures */ seterrorobj(L, status, oldtop); L->ci = old_ci; L->allowhook = old_allowhooks; L->nny = old_nny; luaD_shrinkstack(L); } L->errfunc = old_errfunc; return status; } /* ** Execute a protected parser. */ struct SParser { /* data to 'f_parser' */ ZIO *z; Mbuffer buff; /* dynamic structure used by the scanner */ Dyndata dyd; /* dynamic structures used by the parser */ const char *mode; const char *name; }; static void checkmode (lua_State *L, const char *mode, const char *x) { if (mode && strchr(mode, x[0]) == NULL) { luaO_pushfstring(L, "attempt to load a %s chunk (mode is '%s')", x, mode); luaD_throw(L, LUA_ERRSYNTAX); } } static void f_parser (lua_State *L, void *ud) { LClosure *cl; struct SParser *p = cast(struct SParser *, ud); int c = zgetc(p->z); /* read first character */ if (c == LUA_SIGNATURE[0]) { checkmode(L, p->mode, "binary"); cl = luaU_undump(L, p->z, p->name); } else { checkmode(L, p->mode, "text"); cl = luaY_parser(L, p->z, &p->buff, &p->dyd, p->name, c); } lua_assert(cl->nupvalues == cl->p->sizeupvalues); luaF_initupvals(L, cl); } int luaD_protectedparser (lua_State *L, ZIO *z, const char *name, const char *mode) { struct SParser p; int status; L->nny++; /* cannot yield during parsing */ p.z = z; p.name = name; p.mode = mode; p.dyd.actvar.arr = NULL; p.dyd.actvar.size = 0; p.dyd.gt.arr = NULL; p.dyd.gt.size = 0; p.dyd.label.arr = NULL; p.dyd.label.size = 0; luaZ_initbuffer(L, &p.buff); status = luaD_pcall(L, f_parser, &p, savestack(L, L->top), L->errfunc); luaZ_freebuffer(L, &p.buff); luaM_freearray(L, p.dyd.actvar.arr, p.dyd.actvar.size); luaM_freearray(L, p.dyd.gt.arr, p.dyd.gt.size); luaM_freearray(L, p.dyd.label.arr, p.dyd.label.size); L->nny--; return status; } ================================================ FILE: Tests/ApiExplorer/lua/src/ldo.h ================================================ /* ** $Id: ldo.h,v 2.29.1.1 2017/04/19 17:20:42 roberto Exp $ ** Stack and Call structure of Lua ** See Copyright Notice in lua.h */ #ifndef ldo_h #define ldo_h #include "lobject.h" #include "lstate.h" #include "lzio.h" /* ** Macro to check stack size and grow stack if needed. Parameters ** 'pre'/'pos' allow the macro to preserve a pointer into the ** stack across reallocations, doing the work only when needed. ** 'condmovestack' is used in heavy tests to force a stack reallocation ** at every check. */ #define luaD_checkstackaux(L,n,pre,pos) \ if (L->stack_last - L->top <= (n)) \ { pre; luaD_growstack(L, n); pos; } else { condmovestack(L,pre,pos); } /* In general, 'pre'/'pos' are empty (nothing to save) */ #define luaD_checkstack(L,n) luaD_checkstackaux(L,n,(void)0,(void)0) #define savestack(L,p) ((char *)(p) - (char *)L->stack) #define restorestack(L,n) ((TValue *)((char *)L->stack + (n))) /* type of protected functions, to be ran by 'runprotected' */ typedef void (*Pfunc) (lua_State *L, void *ud); LUAI_FUNC int luaD_protectedparser (lua_State *L, ZIO *z, const char *name, const char *mode); LUAI_FUNC void luaD_hook (lua_State *L, int event, int line); LUAI_FUNC int luaD_precall (lua_State *L, StkId func, int nresults); LUAI_FUNC void luaD_call (lua_State *L, StkId func, int nResults); LUAI_FUNC void luaD_callnoyield (lua_State *L, StkId func, int nResults); LUAI_FUNC int luaD_pcall (lua_State *L, Pfunc func, void *u, ptrdiff_t oldtop, ptrdiff_t ef); LUAI_FUNC int luaD_poscall (lua_State *L, CallInfo *ci, StkId firstResult, int nres); LUAI_FUNC void luaD_reallocstack (lua_State *L, int newsize); LUAI_FUNC void luaD_growstack (lua_State *L, int n); LUAI_FUNC void luaD_shrinkstack (lua_State *L); LUAI_FUNC void luaD_inctop (lua_State *L); LUAI_FUNC l_noret luaD_throw (lua_State *L, int errcode); LUAI_FUNC int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud); #endif ================================================ FILE: Tests/ApiExplorer/lua/src/ldump.c ================================================ /* ** $Id: ldump.c,v 2.37.1.1 2017/04/19 17:20:42 roberto Exp $ ** save precompiled Lua chunks ** See Copyright Notice in lua.h */ #define ldump_c #define LUA_CORE #include "lprefix.h" #include #include "lua.h" #include "lobject.h" #include "lstate.h" #include "lundump.h" typedef struct { lua_State *L; lua_Writer writer; void *data; int strip; int status; } DumpState; /* ** All high-level dumps go through DumpVector; you can change it to ** change the endianness of the result */ #define DumpVector(v,n,D) DumpBlock(v,(n)*sizeof((v)[0]),D) #define DumpLiteral(s,D) DumpBlock(s, sizeof(s) - sizeof(char), D) static void DumpBlock (const void *b, size_t size, DumpState *D) { if (D->status == 0 && size > 0) { lua_unlock(D->L); D->status = (*D->writer)(D->L, b, size, D->data); lua_lock(D->L); } } #define DumpVar(x,D) DumpVector(&x,1,D) static void DumpByte (int y, DumpState *D) { lu_byte x = (lu_byte)y; DumpVar(x, D); } static void DumpInt (int x, DumpState *D) { DumpVar(x, D); } static void DumpNumber (lua_Number x, DumpState *D) { DumpVar(x, D); } static void DumpInteger (lua_Integer x, DumpState *D) { DumpVar(x, D); } static void DumpString (const TString *s, DumpState *D) { if (s == NULL) DumpByte(0, D); else { size_t size = tsslen(s) + 1; /* include trailing '\0' */ const char *str = getstr(s); if (size < 0xFF) DumpByte(cast_int(size), D); else { DumpByte(0xFF, D); DumpVar(size, D); } DumpVector(str, size - 1, D); /* no need to save '\0' */ } } static void DumpCode (const Proto *f, DumpState *D) { DumpInt(f->sizecode, D); DumpVector(f->code, f->sizecode, D); } static void DumpFunction(const Proto *f, TString *psource, DumpState *D); static void DumpConstants (const Proto *f, DumpState *D) { int i; int n = f->sizek; DumpInt(n, D); for (i = 0; i < n; i++) { const TValue *o = &f->k[i]; DumpByte(ttype(o), D); switch (ttype(o)) { case LUA_TNIL: break; case LUA_TBOOLEAN: DumpByte(bvalue(o), D); break; case LUA_TNUMFLT: DumpNumber(fltvalue(o), D); break; case LUA_TNUMINT: DumpInteger(ivalue(o), D); break; case LUA_TSHRSTR: case LUA_TLNGSTR: DumpString(tsvalue(o), D); break; default: lua_assert(0); } } } static void DumpProtos (const Proto *f, DumpState *D) { int i; int n = f->sizep; DumpInt(n, D); for (i = 0; i < n; i++) DumpFunction(f->p[i], f->source, D); } static void DumpUpvalues (const Proto *f, DumpState *D) { int i, n = f->sizeupvalues; DumpInt(n, D); for (i = 0; i < n; i++) { DumpByte(f->upvalues[i].instack, D); DumpByte(f->upvalues[i].idx, D); } } static void DumpDebug (const Proto *f, DumpState *D) { int i, n; n = (D->strip) ? 0 : f->sizelineinfo; DumpInt(n, D); DumpVector(f->lineinfo, n, D); n = (D->strip) ? 0 : f->sizelocvars; DumpInt(n, D); for (i = 0; i < n; i++) { DumpString(f->locvars[i].varname, D); DumpInt(f->locvars[i].startpc, D); DumpInt(f->locvars[i].endpc, D); } n = (D->strip) ? 0 : f->sizeupvalues; DumpInt(n, D); for (i = 0; i < n; i++) DumpString(f->upvalues[i].name, D); } static void DumpFunction (const Proto *f, TString *psource, DumpState *D) { if (D->strip || f->source == psource) DumpString(NULL, D); /* no debug info or same source as its parent */ else DumpString(f->source, D); DumpInt(f->linedefined, D); DumpInt(f->lastlinedefined, D); DumpByte(f->numparams, D); DumpByte(f->is_vararg, D); DumpByte(f->maxstacksize, D); DumpCode(f, D); DumpConstants(f, D); DumpUpvalues(f, D); DumpProtos(f, D); DumpDebug(f, D); } static void DumpHeader (DumpState *D) { DumpLiteral(LUA_SIGNATURE, D); DumpByte(LUAC_VERSION, D); DumpByte(LUAC_FORMAT, D); DumpLiteral(LUAC_DATA, D); DumpByte(sizeof(int), D); DumpByte(sizeof(size_t), D); DumpByte(sizeof(Instruction), D); DumpByte(sizeof(lua_Integer), D); DumpByte(sizeof(lua_Number), D); DumpInteger(LUAC_INT, D); DumpNumber(LUAC_NUM, D); } /* ** dump Lua function as precompiled chunk */ int luaU_dump(lua_State *L, const Proto *f, lua_Writer w, void *data, int strip) { DumpState D; D.L = L; D.writer = w; D.data = data; D.strip = strip; D.status = 0; DumpHeader(&D); DumpByte(f->sizeupvalues, &D); DumpFunction(f, NULL, &D); return D.status; } ================================================ FILE: Tests/ApiExplorer/lua/src/lfunc.c ================================================ /* ** $Id: lfunc.c,v 2.45.1.1 2017/04/19 17:39:34 roberto Exp $ ** Auxiliary functions to manipulate prototypes and closures ** See Copyright Notice in lua.h */ #define lfunc_c #define LUA_CORE #include "lprefix.h" #include #include "lua.h" #include "lfunc.h" #include "lgc.h" #include "lmem.h" #include "lobject.h" #include "lstate.h" CClosure *luaF_newCclosure (lua_State *L, int n) { GCObject *o = luaC_newobj(L, LUA_TCCL, sizeCclosure(n)); CClosure *c = gco2ccl(o); c->nupvalues = cast_byte(n); return c; } LClosure *luaF_newLclosure (lua_State *L, int n) { GCObject *o = luaC_newobj(L, LUA_TLCL, sizeLclosure(n)); LClosure *c = gco2lcl(o); c->p = NULL; c->nupvalues = cast_byte(n); while (n--) c->upvals[n] = NULL; return c; } /* ** fill a closure with new closed upvalues */ void luaF_initupvals (lua_State *L, LClosure *cl) { int i; for (i = 0; i < cl->nupvalues; i++) { UpVal *uv = luaM_new(L, UpVal); uv->refcount = 1; uv->v = &uv->u.value; /* make it closed */ setnilvalue(uv->v); cl->upvals[i] = uv; } } UpVal *luaF_findupval (lua_State *L, StkId level) { UpVal **pp = &L->openupval; UpVal *p; UpVal *uv; lua_assert(isintwups(L) || L->openupval == NULL); while (*pp != NULL && (p = *pp)->v >= level) { lua_assert(upisopen(p)); if (p->v == level) /* found a corresponding upvalue? */ return p; /* return it */ pp = &p->u.open.next; } /* not found: create a new upvalue */ uv = luaM_new(L, UpVal); uv->refcount = 0; uv->u.open.next = *pp; /* link it to list of open upvalues */ uv->u.open.touched = 1; *pp = uv; uv->v = level; /* current value lives in the stack */ if (!isintwups(L)) { /* thread not in list of threads with upvalues? */ L->twups = G(L)->twups; /* link it to the list */ G(L)->twups = L; } return uv; } void luaF_close (lua_State *L, StkId level) { UpVal *uv; while (L->openupval != NULL && (uv = L->openupval)->v >= level) { lua_assert(upisopen(uv)); L->openupval = uv->u.open.next; /* remove from 'open' list */ if (uv->refcount == 0) /* no references? */ luaM_free(L, uv); /* free upvalue */ else { setobj(L, &uv->u.value, uv->v); /* move value to upvalue slot */ uv->v = &uv->u.value; /* now current value lives here */ luaC_upvalbarrier(L, uv); } } } Proto *luaF_newproto (lua_State *L) { GCObject *o = luaC_newobj(L, LUA_TPROTO, sizeof(Proto)); Proto *f = gco2p(o); f->k = NULL; f->sizek = 0; f->p = NULL; f->sizep = 0; f->code = NULL; f->cache = NULL; f->sizecode = 0; f->lineinfo = NULL; f->sizelineinfo = 0; f->upvalues = NULL; f->sizeupvalues = 0; f->numparams = 0; f->is_vararg = 0; f->maxstacksize = 0; f->locvars = NULL; f->sizelocvars = 0; f->linedefined = 0; f->lastlinedefined = 0; f->source = NULL; return f; } void luaF_freeproto (lua_State *L, Proto *f) { luaM_freearray(L, f->code, f->sizecode); luaM_freearray(L, f->p, f->sizep); luaM_freearray(L, f->k, f->sizek); luaM_freearray(L, f->lineinfo, f->sizelineinfo); luaM_freearray(L, f->locvars, f->sizelocvars); luaM_freearray(L, f->upvalues, f->sizeupvalues); luaM_free(L, f); } /* ** Look for n-th local variable at line 'line' in function 'func'. ** Returns NULL if not found. */ const char *luaF_getlocalname (const Proto *f, int local_number, int pc) { int i; for (i = 0; isizelocvars && f->locvars[i].startpc <= pc; i++) { if (pc < f->locvars[i].endpc) { /* is variable active? */ local_number--; if (local_number == 0) return getstr(f->locvars[i].varname); } } return NULL; /* not found */ } ================================================ FILE: Tests/ApiExplorer/lua/src/lfunc.h ================================================ /* ** $Id: lfunc.h,v 2.15.1.1 2017/04/19 17:39:34 roberto Exp $ ** Auxiliary functions to manipulate prototypes and closures ** See Copyright Notice in lua.h */ #ifndef lfunc_h #define lfunc_h #include "lobject.h" #define sizeCclosure(n) (cast(int, sizeof(CClosure)) + \ cast(int, sizeof(TValue)*((n)-1))) #define sizeLclosure(n) (cast(int, sizeof(LClosure)) + \ cast(int, sizeof(TValue *)*((n)-1))) /* test whether thread is in 'twups' list */ #define isintwups(L) (L->twups != L) /* ** maximum number of upvalues in a closure (both C and Lua). (Value ** must fit in a VM register.) */ #define MAXUPVAL 255 /* ** Upvalues for Lua closures */ struct UpVal { TValue *v; /* points to stack or to its own value */ lu_mem refcount; /* reference counter */ union { struct { /* (when open) */ UpVal *next; /* linked list */ int touched; /* mark to avoid cycles with dead threads */ } open; TValue value; /* the value (when closed) */ } u; }; #define upisopen(up) ((up)->v != &(up)->u.value) LUAI_FUNC Proto *luaF_newproto (lua_State *L); LUAI_FUNC CClosure *luaF_newCclosure (lua_State *L, int nelems); LUAI_FUNC LClosure *luaF_newLclosure (lua_State *L, int nelems); LUAI_FUNC void luaF_initupvals (lua_State *L, LClosure *cl); LUAI_FUNC UpVal *luaF_findupval (lua_State *L, StkId level); LUAI_FUNC void luaF_close (lua_State *L, StkId level); LUAI_FUNC void luaF_freeproto (lua_State *L, Proto *f); LUAI_FUNC const char *luaF_getlocalname (const Proto *func, int local_number, int pc); #endif ================================================ FILE: Tests/ApiExplorer/lua/src/lgc.c ================================================ /* ** $Id: lgc.c,v 2.215.1.2 2017/08/31 16:15:27 roberto Exp $ ** Garbage Collector ** See Copyright Notice in lua.h */ #pragma warning(disable: 6297) #define lgc_c #define LUA_CORE #include "lprefix.h" #include #include "lua.h" #include "ldebug.h" #include "ldo.h" #include "lfunc.h" #include "lgc.h" #include "lmem.h" #include "lobject.h" #include "lstate.h" #include "lstring.h" #include "ltable.h" #include "ltm.h" /* ** internal state for collector while inside the atomic phase. The ** collector should never be in this state while running regular code. */ #define GCSinsideatomic (GCSpause + 1) /* ** cost of sweeping one element (the size of a small object divided ** by some adjust for the sweep speed) */ #define GCSWEEPCOST ((sizeof(TString) + 4) / 4) /* maximum number of elements to sweep in each single step */ #define GCSWEEPMAX (cast_int((GCSTEPSIZE / GCSWEEPCOST) / 4)) /* cost of calling one finalizer */ #define GCFINALIZECOST GCSWEEPCOST /* ** macro to adjust 'stepmul': 'stepmul' is actually used like ** 'stepmul / STEPMULADJ' (value chosen by tests) */ #define STEPMULADJ 200 /* ** macro to adjust 'pause': 'pause' is actually used like ** 'pause / PAUSEADJ' (value chosen by tests) */ #define PAUSEADJ 100 /* ** 'makewhite' erases all color bits then sets only the current white ** bit */ #define maskcolors (~(bitmask(BLACKBIT) | WHITEBITS)) #define makewhite(g,x) \ (x->marked = cast_byte((x->marked & maskcolors) | luaC_white(g))) #define white2gray(x) resetbits(x->marked, WHITEBITS) #define black2gray(x) resetbit(x->marked, BLACKBIT) #define valiswhite(x) (iscollectable(x) && iswhite(gcvalue(x))) #define checkdeadkey(n) lua_assert(!ttisdeadkey(gkey(n)) || ttisnil(gval(n))) #define checkconsistency(obj) \ lua_longassert(!iscollectable(obj) || righttt(obj)) #define markvalue(g,o) { checkconsistency(o); \ if (valiswhite(o)) reallymarkobject(g,gcvalue(o)); } #define markobject(g,t) { if (iswhite(t)) reallymarkobject(g, obj2gco(t)); } /* ** mark an object that can be NULL (either because it is really optional, ** or it was stripped as debug info, or inside an uncompleted structure) */ #define markobjectN(g,t) { if (t) markobject(g,t); } static void reallymarkobject (global_State *g, GCObject *o); /* ** {====================================================== ** Generic functions ** ======================================================= */ /* ** one after last element in a hash array */ #define gnodelast(h) gnode(h, cast(size_t, sizenode(h))) /* ** link collectable object 'o' into list pointed by 'p' */ #define linkgclist(o,p) ((o)->gclist = (p), (p) = obj2gco(o)) /* ** If key is not marked, mark its entry as dead. This allows key to be ** collected, but keeps its entry in the table. A dead node is needed ** when Lua looks up for a key (it may be part of a chain) and when ** traversing a weak table (key might be removed from the table during ** traversal). Other places never manipulate dead keys, because its ** associated nil value is enough to signal that the entry is logically ** empty. */ static void removeentry (Node *n) { lua_assert(ttisnil(gval(n))); if (valiswhite(gkey(n))) setdeadvalue(wgkey(n)); /* unused and unmarked key; remove it */ } /* ** tells whether a key or value can be cleared from a weak ** table. Non-collectable objects are never removed from weak ** tables. Strings behave as 'values', so are never removed too. for ** other objects: if really collected, cannot keep them; for objects ** being finalized, keep them in keys, but not in values */ static int iscleared (global_State *g, const TValue *o) { if (!iscollectable(o)) return 0; else if (ttisstring(o)) { markobject(g, tsvalue(o)); /* strings are 'values', so are never weak */ return 0; } else return iswhite(gcvalue(o)); } /* ** barrier that moves collector forward, that is, mark the white object ** being pointed by a black object. (If in sweep phase, clear the black ** object to white [sweep it] to avoid other barrier calls for this ** same object.) */ void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v) { global_State *g = G(L); lua_assert(isblack(o) && iswhite(v) && !isdead(g, v) && !isdead(g, o)); if (keepinvariant(g)) /* must keep invariant? */ reallymarkobject(g, v); /* restore invariant */ else { /* sweep phase */ lua_assert(issweepphase(g)); makewhite(g, o); /* mark main obj. as white to avoid other barriers */ } } /* ** barrier that moves collector backward, that is, mark the black object ** pointing to a white object as gray again. */ void luaC_barrierback_ (lua_State *L, Table *t) { global_State *g = G(L); lua_assert(isblack(t) && !isdead(g, t)); black2gray(t); /* make table gray (again) */ linkgclist(t, g->grayagain); } /* ** barrier for assignments to closed upvalues. Because upvalues are ** shared among closures, it is impossible to know the color of all ** closures pointing to it. So, we assume that the object being assigned ** must be marked. */ void luaC_upvalbarrier_ (lua_State *L, UpVal *uv) { global_State *g = G(L); GCObject *o = gcvalue(uv->v); lua_assert(!upisopen(uv)); /* ensured by macro luaC_upvalbarrier */ if (keepinvariant(g)) markobject(g, o); } void luaC_fix (lua_State *L, GCObject *o) { global_State *g = G(L); lua_assert(g->allgc == o); /* object must be 1st in 'allgc' list! */ white2gray(o); /* they will be gray forever */ g->allgc = o->next; /* remove object from 'allgc' list */ o->next = g->fixedgc; /* link it to 'fixedgc' list */ g->fixedgc = o; } /* ** create a new collectable object (with given type and size) and link ** it to 'allgc' list. */ GCObject *luaC_newobj (lua_State *L, int tt, size_t sz) { global_State *g = G(L); GCObject *o = cast(GCObject *, luaM_newobject(L, novariant(tt), sz)); o->marked = luaC_white(g); o->tt = tt; o->next = g->allgc; g->allgc = o; return o; } /* }====================================================== */ /* ** {====================================================== ** Mark functions ** ======================================================= */ /* ** mark an object. Userdata, strings, and closed upvalues are visited ** and turned black here. Other objects are marked gray and added ** to appropriate list to be visited (and turned black) later. (Open ** upvalues are already linked in 'headuv' list.) */ static void reallymarkobject (global_State *g, GCObject *o) { reentry: white2gray(o); switch (o->tt) { case LUA_TSHRSTR: { gray2black(o); g->GCmemtrav += sizelstring(gco2ts(o)->shrlen); break; } case LUA_TLNGSTR: { gray2black(o); g->GCmemtrav += sizelstring(gco2ts(o)->u.lnglen); break; } case LUA_TUSERDATA: { TValue uvalue; markobjectN(g, gco2u(o)->metatable); /* mark its metatable */ gray2black(o); g->GCmemtrav += sizeudata(gco2u(o)); getuservalue(g->mainthread, gco2u(o), &uvalue); if (valiswhite(&uvalue)) { /* markvalue(g, &uvalue); */ o = gcvalue(&uvalue); goto reentry; } break; } case LUA_TLCL: { linkgclist(gco2lcl(o), g->gray); break; } case LUA_TCCL: { linkgclist(gco2ccl(o), g->gray); break; } case LUA_TTABLE: { linkgclist(gco2t(o), g->gray); break; } case LUA_TTHREAD: { linkgclist(gco2th(o), g->gray); break; } case LUA_TPROTO: { linkgclist(gco2p(o), g->gray); break; } default: lua_assert(0); break; } } /* ** mark metamethods for basic types */ static void markmt (global_State *g) { int i; for (i=0; i < LUA_NUMTAGS; i++) markobjectN(g, g->mt[i]); } /* ** mark all objects in list of being-finalized */ static void markbeingfnz (global_State *g) { GCObject *o; for (o = g->tobefnz; o != NULL; o = o->next) markobject(g, o); } /* ** Mark all values stored in marked open upvalues from non-marked threads. ** (Values from marked threads were already marked when traversing the ** thread.) Remove from the list threads that no longer have upvalues and ** not-marked threads. */ static void remarkupvals (global_State *g) { lua_State *thread; lua_State **p = &g->twups; while ((thread = *p) != NULL) { lua_assert(!isblack(thread)); /* threads are never black */ if (isgray(thread) && thread->openupval != NULL) p = &thread->twups; /* keep marked thread with upvalues in the list */ else { /* thread is not marked or without upvalues */ UpVal *uv; *p = thread->twups; /* remove thread from the list */ thread->twups = thread; /* mark that it is out of list */ for (uv = thread->openupval; uv != NULL; uv = uv->u.open.next) { if (uv->u.open.touched) { markvalue(g, uv->v); /* remark upvalue's value */ uv->u.open.touched = 0; } } } } } /* ** mark root set and reset all gray lists, to start a new collection */ static void restartcollection (global_State *g) { g->gray = g->grayagain = NULL; g->weak = g->allweak = g->ephemeron = NULL; markobject(g, g->mainthread); markvalue(g, &g->l_registry); markmt(g); markbeingfnz(g); /* mark any finalizing object left from previous cycle */ } /* }====================================================== */ /* ** {====================================================== ** Traverse functions ** ======================================================= */ /* ** Traverse a table with weak values and link it to proper list. During ** propagate phase, keep it in 'grayagain' list, to be revisited in the ** atomic phase. In the atomic phase, if table has any white value, ** put it in 'weak' list, to be cleared. */ static void traverseweakvalue (global_State *g, Table *h) { Node *n, *limit = gnodelast(h); /* if there is array part, assume it may have white values (it is not worth traversing it now just to check) */ int hasclears = (h->sizearray > 0); for (n = gnode(h, 0); n < limit; n++) { /* traverse hash part */ checkdeadkey(n); if (ttisnil(gval(n))) /* entry is empty? */ removeentry(n); /* remove it */ else { lua_assert(!ttisnil(gkey(n))); markvalue(g, gkey(n)); /* mark key */ if (!hasclears && iscleared(g, gval(n))) /* is there a white value? */ hasclears = 1; /* table will have to be cleared */ } } if (g->gcstate == GCSpropagate) linkgclist(h, g->grayagain); /* must retraverse it in atomic phase */ else if (hasclears) linkgclist(h, g->weak); /* has to be cleared later */ } /* ** Traverse an ephemeron table and link it to proper list. Returns true ** iff any object was marked during this traversal (which implies that ** convergence has to continue). During propagation phase, keep table ** in 'grayagain' list, to be visited again in the atomic phase. In ** the atomic phase, if table has any white->white entry, it has to ** be revisited during ephemeron convergence (as that key may turn ** black). Otherwise, if it has any white key, table has to be cleared ** (in the atomic phase). */ static int traverseephemeron (global_State *g, Table *h) { int marked = 0; /* true if an object is marked in this traversal */ int hasclears = 0; /* true if table has white keys */ int hasww = 0; /* true if table has entry "white-key -> white-value" */ Node *n, *limit = gnodelast(h); unsigned int i; /* traverse array part */ for (i = 0; i < h->sizearray; i++) { if (valiswhite(&h->array[i])) { marked = 1; reallymarkobject(g, gcvalue(&h->array[i])); } } /* traverse hash part */ for (n = gnode(h, 0); n < limit; n++) { checkdeadkey(n); if (ttisnil(gval(n))) /* entry is empty? */ removeentry(n); /* remove it */ else if (iscleared(g, gkey(n))) { /* key is not marked (yet)? */ hasclears = 1; /* table must be cleared */ if (valiswhite(gval(n))) /* value not marked yet? */ hasww = 1; /* white-white entry */ } else if (valiswhite(gval(n))) { /* value not marked yet? */ marked = 1; reallymarkobject(g, gcvalue(gval(n))); /* mark it now */ } } /* link table into proper list */ if (g->gcstate == GCSpropagate) linkgclist(h, g->grayagain); /* must retraverse it in atomic phase */ else if (hasww) /* table has white->white entries? */ linkgclist(h, g->ephemeron); /* have to propagate again */ else if (hasclears) /* table has white keys? */ linkgclist(h, g->allweak); /* may have to clean white keys */ return marked; } static void traversestrongtable (global_State *g, Table *h) { Node *n, *limit = gnodelast(h); unsigned int i; for (i = 0; i < h->sizearray; i++) /* traverse array part */ markvalue(g, &h->array[i]); for (n = gnode(h, 0); n < limit; n++) { /* traverse hash part */ checkdeadkey(n); if (ttisnil(gval(n))) /* entry is empty? */ removeentry(n); /* remove it */ else { lua_assert(!ttisnil(gkey(n))); markvalue(g, gkey(n)); /* mark key */ markvalue(g, gval(n)); /* mark value */ } } } static lu_mem traversetable (global_State *g, Table *h) { const char *weakkey, *weakvalue; const TValue *mode = gfasttm(g, h->metatable, TM_MODE); markobjectN(g, h->metatable); if (mode && ttisstring(mode) && /* is there a weak mode? */ ((weakkey = strchr(svalue(mode), 'k')), (weakvalue = strchr(svalue(mode), 'v')), (weakkey || weakvalue))) { /* is really weak? */ black2gray(h); /* keep table gray */ if (!weakkey) /* strong keys? */ traverseweakvalue(g, h); else if (!weakvalue) /* strong values? */ traverseephemeron(g, h); else /* all weak */ linkgclist(h, g->allweak); /* nothing to traverse now */ } else /* not weak */ traversestrongtable(g, h); return sizeof(Table) + sizeof(TValue) * h->sizearray + sizeof(Node) * cast(size_t, allocsizenode(h)); } /* ** Traverse a prototype. (While a prototype is being build, its ** arrays can be larger than needed; the extra slots are filled with ** NULL, so the use of 'markobjectN') */ static int traverseproto (global_State *g, Proto *f) { int i; if (f->cache && iswhite(f->cache)) f->cache = NULL; /* allow cache to be collected */ markobjectN(g, f->source); for (i = 0; i < f->sizek; i++) /* mark literals */ markvalue(g, &f->k[i]); for (i = 0; i < f->sizeupvalues; i++) /* mark upvalue names */ markobjectN(g, f->upvalues[i].name); for (i = 0; i < f->sizep; i++) /* mark nested protos */ markobjectN(g, f->p[i]); for (i = 0; i < f->sizelocvars; i++) /* mark local-variable names */ markobjectN(g, f->locvars[i].varname); return sizeof(Proto) + sizeof(Instruction) * f->sizecode + sizeof(Proto *) * f->sizep + sizeof(TValue) * f->sizek + sizeof(int) * f->sizelineinfo + sizeof(LocVar) * f->sizelocvars + sizeof(Upvaldesc) * f->sizeupvalues; } static lu_mem traverseCclosure (global_State *g, CClosure *cl) { int i; for (i = 0; i < cl->nupvalues; i++) /* mark its upvalues */ markvalue(g, &cl->upvalue[i]); return sizeCclosure(cl->nupvalues); } /* ** open upvalues point to values in a thread, so those values should ** be marked when the thread is traversed except in the atomic phase ** (because then the value cannot be changed by the thread and the ** thread may not be traversed again) */ static lu_mem traverseLclosure (global_State *g, LClosure *cl) { int i; markobjectN(g, cl->p); /* mark its prototype */ for (i = 0; i < cl->nupvalues; i++) { /* mark its upvalues */ UpVal *uv = cl->upvals[i]; if (uv != NULL) { if (upisopen(uv) && g->gcstate != GCSinsideatomic) uv->u.open.touched = 1; /* can be marked in 'remarkupvals' */ else markvalue(g, uv->v); } } return sizeLclosure(cl->nupvalues); } static lu_mem traversethread (global_State *g, lua_State *th) { StkId o = th->stack; if (o == NULL) return 1; /* stack not completely built yet */ lua_assert(g->gcstate == GCSinsideatomic || th->openupval == NULL || isintwups(th)); for (; o < th->top; o++) /* mark live elements in the stack */ markvalue(g, o); if (g->gcstate == GCSinsideatomic) { /* final traversal? */ StkId lim = th->stack + th->stacksize; /* real end of stack */ for (; o < lim; o++) /* clear not-marked stack slice */ setnilvalue(o); /* 'remarkupvals' may have removed thread from 'twups' list */ if (!isintwups(th) && th->openupval != NULL) { th->twups = g->twups; /* link it back to the list */ g->twups = th; } } else if (g->gckind != KGC_EMERGENCY) luaD_shrinkstack(th); /* do not change stack in emergency cycle */ return (sizeof(lua_State) + sizeof(TValue) * th->stacksize + sizeof(CallInfo) * th->nci); } /* ** traverse one gray object, turning it to black (except for threads, ** which are always gray). */ static void propagatemark (global_State *g) { lu_mem size; GCObject *o = g->gray; lua_assert(isgray(o)); gray2black(o); switch (o->tt) { case LUA_TTABLE: { Table *h = gco2t(o); g->gray = h->gclist; /* remove from 'gray' list */ size = traversetable(g, h); break; } case LUA_TLCL: { LClosure *cl = gco2lcl(o); g->gray = cl->gclist; /* remove from 'gray' list */ size = traverseLclosure(g, cl); break; } case LUA_TCCL: { CClosure *cl = gco2ccl(o); g->gray = cl->gclist; /* remove from 'gray' list */ size = traverseCclosure(g, cl); break; } case LUA_TTHREAD: { lua_State *th = gco2th(o); g->gray = th->gclist; /* remove from 'gray' list */ linkgclist(th, g->grayagain); /* insert into 'grayagain' list */ black2gray(o); size = traversethread(g, th); break; } case LUA_TPROTO: { Proto *p = gco2p(o); g->gray = p->gclist; /* remove from 'gray' list */ size = traverseproto(g, p); break; } default: lua_assert(0); return; } g->GCmemtrav += size; } static void propagateall (global_State *g) { while (g->gray) propagatemark(g); } static void convergeephemerons (global_State *g) { int changed; do { GCObject *w; GCObject *next = g->ephemeron; /* get ephemeron list */ g->ephemeron = NULL; /* tables may return to this list when traversed */ changed = 0; while ((w = next) != NULL) { next = gco2t(w)->gclist; if (traverseephemeron(g, gco2t(w))) { /* traverse marked some value? */ propagateall(g); /* propagate changes */ changed = 1; /* will have to revisit all ephemeron tables */ } } } while (changed); } /* }====================================================== */ /* ** {====================================================== ** Sweep Functions ** ======================================================= */ /* ** clear entries with unmarked keys from all weaktables in list 'l' up ** to element 'f' */ static void clearkeys (global_State *g, GCObject *l, GCObject *f) { for (; l != f; l = gco2t(l)->gclist) { Table *h = gco2t(l); Node *n, *limit = gnodelast(h); for (n = gnode(h, 0); n < limit; n++) { if (!ttisnil(gval(n)) && (iscleared(g, gkey(n)))) { setnilvalue(gval(n)); /* remove value ... */ } if (ttisnil(gval(n))) /* is entry empty? */ removeentry(n); /* remove entry from table */ } } } /* ** clear entries with unmarked values from all weaktables in list 'l' up ** to element 'f' */ static void clearvalues (global_State *g, GCObject *l, GCObject *f) { for (; l != f; l = gco2t(l)->gclist) { Table *h = gco2t(l); Node *n, *limit = gnodelast(h); unsigned int i; for (i = 0; i < h->sizearray; i++) { TValue *o = &h->array[i]; if (iscleared(g, o)) /* value was collected? */ setnilvalue(o); /* remove value */ } for (n = gnode(h, 0); n < limit; n++) { if (!ttisnil(gval(n)) && iscleared(g, gval(n))) { setnilvalue(gval(n)); /* remove value ... */ removeentry(n); /* and remove entry from table */ } } } } void luaC_upvdeccount (lua_State *L, UpVal *uv) { lua_assert(uv->refcount > 0); uv->refcount--; if (uv->refcount == 0 && !upisopen(uv)) luaM_free(L, uv); } static void freeLclosure (lua_State *L, LClosure *cl) { int i; for (i = 0; i < cl->nupvalues; i++) { UpVal *uv = cl->upvals[i]; if (uv) luaC_upvdeccount(L, uv); } luaM_freemem(L, cl, sizeLclosure(cl->nupvalues)); } static void freeobj (lua_State *L, GCObject *o) { switch (o->tt) { case LUA_TPROTO: luaF_freeproto(L, gco2p(o)); break; case LUA_TLCL: { freeLclosure(L, gco2lcl(o)); break; } case LUA_TCCL: { luaM_freemem(L, o, sizeCclosure(gco2ccl(o)->nupvalues)); break; } case LUA_TTABLE: luaH_free(L, gco2t(o)); break; case LUA_TTHREAD: luaE_freethread(L, gco2th(o)); break; case LUA_TUSERDATA: luaM_freemem(L, o, sizeudata(gco2u(o))); break; case LUA_TSHRSTR: luaS_remove(L, gco2ts(o)); /* remove it from hash table */ luaM_freemem(L, o, sizelstring(gco2ts(o)->shrlen)); break; case LUA_TLNGSTR: { luaM_freemem(L, o, sizelstring(gco2ts(o)->u.lnglen)); break; } default: lua_assert(0); } } #define sweepwholelist(L,p) sweeplist(L,p,MAX_LUMEM) static GCObject **sweeplist (lua_State *L, GCObject **p, lu_mem count); /* ** sweep at most 'count' elements from a list of GCObjects erasing dead ** objects, where a dead object is one marked with the old (non current) ** white; change all non-dead objects back to white, preparing for next ** collection cycle. Return where to continue the traversal or NULL if ** list is finished. */ static GCObject **sweeplist (lua_State *L, GCObject **p, lu_mem count) { global_State *g = G(L); int ow = otherwhite(g); int white = luaC_white(g); /* current white */ while (*p != NULL && count-- > 0) { GCObject *curr = *p; int marked = curr->marked; if (isdeadm(ow, marked)) { /* is 'curr' dead? */ *p = curr->next; /* remove 'curr' from list */ freeobj(L, curr); /* erase 'curr' */ } else { /* change mark to 'white' */ curr->marked = cast_byte((marked & maskcolors) | white); p = &curr->next; /* go to next element */ } } return (*p == NULL) ? NULL : p; } /* ** sweep a list until a live object (or end of list) */ static GCObject **sweeptolive (lua_State *L, GCObject **p) { GCObject **old = p; do { p = sweeplist(L, p, 1); } while (p == old); return p; } /* }====================================================== */ /* ** {====================================================== ** Finalization ** ======================================================= */ /* ** If possible, shrink string table */ static void checkSizes (lua_State *L, global_State *g) { if (g->gckind != KGC_EMERGENCY) { l_mem olddebt = g->GCdebt; if (g->strt.nuse < g->strt.size / 4) /* string table too big? */ luaS_resize(L, g->strt.size / 2); /* shrink it a little */ g->GCestimate += g->GCdebt - olddebt; /* update estimate */ } } static GCObject *udata2finalize (global_State *g) { GCObject *o = g->tobefnz; /* get first element */ lua_assert(tofinalize(o)); g->tobefnz = o->next; /* remove it from 'tobefnz' list */ o->next = g->allgc; /* return it to 'allgc' list */ g->allgc = o; resetbit(o->marked, FINALIZEDBIT); /* object is "normal" again */ if (issweepphase(g)) makewhite(g, o); /* "sweep" object */ return o; } static void dothecall (lua_State *L, void *ud) { UNUSED(ud); luaD_callnoyield(L, L->top - 2, 0); } static void GCTM (lua_State *L, int propagateerrors) { global_State *g = G(L); const TValue *tm; TValue v; setgcovalue(L, &v, udata2finalize(g)); tm = luaT_gettmbyobj(L, &v, TM_GC); if (tm != NULL && ttisfunction(tm)) { /* is there a finalizer? */ int status; lu_byte oldah = L->allowhook; int running = g->gcrunning; L->allowhook = 0; /* stop debug hooks during GC metamethod */ g->gcrunning = 0; /* avoid GC steps */ setobj2s(L, L->top, tm); /* push finalizer... */ setobj2s(L, L->top + 1, &v); /* ... and its argument */ L->top += 2; /* and (next line) call the finalizer */ L->ci->callstatus |= CIST_FIN; /* will run a finalizer */ status = luaD_pcall(L, dothecall, NULL, savestack(L, L->top - 2), 0); L->ci->callstatus &= ~CIST_FIN; /* not running a finalizer anymore */ L->allowhook = oldah; /* restore hooks */ g->gcrunning = running; /* restore state */ if (status != LUA_OK && propagateerrors) { /* error while running __gc? */ if (status == LUA_ERRRUN) { /* is there an error object? */ const char *msg = (ttisstring(L->top - 1)) ? svalue(L->top - 1) : "no message"; luaO_pushfstring(L, "error in __gc metamethod (%s)", msg); status = LUA_ERRGCMM; /* error in __gc metamethod */ } luaD_throw(L, status); /* re-throw error */ } } } /* ** call a few (up to 'g->gcfinnum') finalizers */ static int runafewfinalizers (lua_State *L) { global_State *g = G(L); unsigned int i; lua_assert(!g->tobefnz || g->gcfinnum > 0); for (i = 0; g->tobefnz && i < g->gcfinnum; i++) GCTM(L, 1); /* call one finalizer */ g->gcfinnum = (!g->tobefnz) ? 0 /* nothing more to finalize? */ : g->gcfinnum * 2; /* else call a few more next time */ return i; } /* ** call all pending finalizers */ static void callallpendingfinalizers (lua_State *L) { global_State *g = G(L); while (g->tobefnz) GCTM(L, 0); } /* ** find last 'next' field in list 'p' list (to add elements in its end) */ static GCObject **findlast (GCObject **p) { while (*p != NULL) p = &(*p)->next; return p; } /* ** move all unreachable objects (or 'all' objects) that need ** finalization from list 'finobj' to list 'tobefnz' (to be finalized) */ static void separatetobefnz (global_State *g, int all) { GCObject *curr; GCObject **p = &g->finobj; GCObject **lastnext = findlast(&g->tobefnz); while ((curr = *p) != NULL) { /* traverse all finalizable objects */ lua_assert(tofinalize(curr)); if (!(iswhite(curr) || all)) /* not being collected? */ p = &curr->next; /* don't bother with it */ else { *p = curr->next; /* remove 'curr' from 'finobj' list */ curr->next = *lastnext; /* link at the end of 'tobefnz' list */ *lastnext = curr; lastnext = &curr->next; } } } /* ** if object 'o' has a finalizer, remove it from 'allgc' list (must ** search the list to find it) and link it in 'finobj' list. */ void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt) { global_State *g = G(L); if (tofinalize(o) || /* obj. is already marked... */ gfasttm(g, mt, TM_GC) == NULL) /* or has no finalizer? */ return; /* nothing to be done */ else { /* move 'o' to 'finobj' list */ GCObject **p; if (issweepphase(g)) { makewhite(g, o); /* "sweep" object 'o' */ if (g->sweepgc == &o->next) /* should not remove 'sweepgc' object */ g->sweepgc = sweeptolive(L, g->sweepgc); /* change 'sweepgc' */ } /* search for pointer pointing to 'o' */ for (p = &g->allgc; *p != o; p = &(*p)->next) { /* empty */ } *p = o->next; /* remove 'o' from 'allgc' list */ o->next = g->finobj; /* link it in 'finobj' list */ g->finobj = o; l_setbit(o->marked, FINALIZEDBIT); /* mark it as such */ } } /* }====================================================== */ /* ** {====================================================== ** GC control ** ======================================================= */ /* ** Set a reasonable "time" to wait before starting a new GC cycle; cycle ** will start when memory use hits threshold. (Division by 'estimate' ** should be OK: it cannot be zero (because Lua cannot even start with ** less than PAUSEADJ bytes). */ static void setpause (global_State *g) { l_mem threshold, debt; l_mem estimate = g->GCestimate / PAUSEADJ; /* adjust 'estimate' */ lua_assert(estimate > 0); threshold = (g->gcpause < MAX_LMEM / estimate) /* overflow? */ ? estimate * g->gcpause /* no overflow */ : MAX_LMEM; /* overflow; truncate to maximum */ debt = gettotalbytes(g) - threshold; luaE_setdebt(g, debt); } /* ** Enter first sweep phase. ** The call to 'sweeplist' tries to make pointer point to an object ** inside the list (instead of to the header), so that the real sweep do ** not need to skip objects created between "now" and the start of the ** real sweep. */ static void entersweep (lua_State *L) { global_State *g = G(L); g->gcstate = GCSswpallgc; lua_assert(g->sweepgc == NULL); g->sweepgc = sweeplist(L, &g->allgc, 1); } void luaC_freeallobjects (lua_State *L) { global_State *g = G(L); separatetobefnz(g, 1); /* separate all objects with finalizers */ lua_assert(g->finobj == NULL); callallpendingfinalizers(L); lua_assert(g->tobefnz == NULL); g->currentwhite = WHITEBITS; /* this "white" makes all objects look dead */ g->gckind = KGC_NORMAL; sweepwholelist(L, &g->finobj); sweepwholelist(L, &g->allgc); sweepwholelist(L, &g->fixedgc); /* collect fixed objects */ lua_assert(g->strt.nuse == 0); } static l_mem atomic (lua_State *L) { global_State *g = G(L); l_mem work; GCObject *origweak, *origall; GCObject *grayagain = g->grayagain; /* save original list */ lua_assert(g->ephemeron == NULL && g->weak == NULL); lua_assert(!iswhite(g->mainthread)); g->gcstate = GCSinsideatomic; g->GCmemtrav = 0; /* start counting work */ markobject(g, L); /* mark running thread */ /* registry and global metatables may be changed by API */ markvalue(g, &g->l_registry); markmt(g); /* mark global metatables */ /* remark occasional upvalues of (maybe) dead threads */ remarkupvals(g); propagateall(g); /* propagate changes */ work = g->GCmemtrav; /* stop counting (do not recount 'grayagain') */ g->gray = grayagain; propagateall(g); /* traverse 'grayagain' list */ g->GCmemtrav = 0; /* restart counting */ convergeephemerons(g); /* at this point, all strongly accessible objects are marked. */ /* Clear values from weak tables, before checking finalizers */ clearvalues(g, g->weak, NULL); clearvalues(g, g->allweak, NULL); origweak = g->weak; origall = g->allweak; work += g->GCmemtrav; /* stop counting (objects being finalized) */ separatetobefnz(g, 0); /* separate objects to be finalized */ g->gcfinnum = 1; /* there may be objects to be finalized */ markbeingfnz(g); /* mark objects that will be finalized */ propagateall(g); /* remark, to propagate 'resurrection' */ g->GCmemtrav = 0; /* restart counting */ convergeephemerons(g); /* at this point, all resurrected objects are marked. */ /* remove dead objects from weak tables */ clearkeys(g, g->ephemeron, NULL); /* clear keys from all ephemeron tables */ clearkeys(g, g->allweak, NULL); /* clear keys from all 'allweak' tables */ /* clear values from resurrected weak tables */ clearvalues(g, g->weak, origweak); clearvalues(g, g->allweak, origall); luaS_clearcache(g); g->currentwhite = cast_byte(otherwhite(g)); /* flip current white */ work += g->GCmemtrav; /* complete counting */ return work; /* estimate of memory marked by 'atomic' */ } static lu_mem sweepstep (lua_State *L, global_State *g, int nextstate, GCObject **nextlist) { if (g->sweepgc) { l_mem olddebt = g->GCdebt; g->sweepgc = sweeplist(L, g->sweepgc, GCSWEEPMAX); g->GCestimate += g->GCdebt - olddebt; /* update estimate */ if (g->sweepgc) /* is there still something to sweep? */ return (GCSWEEPMAX * GCSWEEPCOST); } /* else enter next state */ g->gcstate = nextstate; g->sweepgc = nextlist; return 0; } static lu_mem singlestep (lua_State *L) { global_State *g = G(L); switch (g->gcstate) { case GCSpause: { g->GCmemtrav = g->strt.size * sizeof(GCObject*); restartcollection(g); g->gcstate = GCSpropagate; return g->GCmemtrav; } case GCSpropagate: { g->GCmemtrav = 0; lua_assert(g->gray); propagatemark(g); if (g->gray == NULL) /* no more gray objects? */ g->gcstate = GCSatomic; /* finish propagate phase */ return g->GCmemtrav; /* memory traversed in this step */ } case GCSatomic: { lu_mem work; propagateall(g); /* make sure gray list is empty */ work = atomic(L); /* work is what was traversed by 'atomic' */ entersweep(L); g->GCestimate = gettotalbytes(g); /* first estimate */; return work; } case GCSswpallgc: { /* sweep "regular" objects */ return sweepstep(L, g, GCSswpfinobj, &g->finobj); } case GCSswpfinobj: { /* sweep objects with finalizers */ return sweepstep(L, g, GCSswptobefnz, &g->tobefnz); } case GCSswptobefnz: { /* sweep objects to be finalized */ return sweepstep(L, g, GCSswpend, NULL); } case GCSswpend: { /* finish sweeps */ makewhite(g, g->mainthread); /* sweep main thread */ checkSizes(L, g); g->gcstate = GCScallfin; return 0; } case GCScallfin: { /* call remaining finalizers */ if (g->tobefnz && g->gckind != KGC_EMERGENCY) { int n = runafewfinalizers(L); return (n * GCFINALIZECOST); } else { /* emergency mode or no more finalizers */ g->gcstate = GCSpause; /* finish collection */ return 0; } } default: lua_assert(0); return 0; } } /* ** advances the garbage collector until it reaches a state allowed ** by 'statemask' */ void luaC_runtilstate (lua_State *L, int statesmask) { global_State *g = G(L); while (!testbit(statesmask, g->gcstate)) singlestep(L); } /* ** get GC debt and convert it from Kb to 'work units' (avoid zero debt ** and overflows) */ static l_mem getdebt (global_State *g) { l_mem debt = g->GCdebt; int stepmul = g->gcstepmul; if (debt <= 0) return 0; /* minimal debt */ else { debt = (debt / STEPMULADJ) + 1; debt = (debt < MAX_LMEM / stepmul) ? debt * stepmul : MAX_LMEM; return debt; } } /* ** performs a basic GC step when collector is running */ void luaC_step (lua_State *L) { global_State *g = G(L); l_mem debt = getdebt(g); /* GC deficit (be paid now) */ if (!g->gcrunning) { /* not running? */ luaE_setdebt(g, -GCSTEPSIZE * 10); /* avoid being called too often */ return; } do { /* repeat until pause or enough "credit" (negative debt) */ lu_mem work = singlestep(L); /* perform one single step */ debt -= work; } while (debt > -GCSTEPSIZE && g->gcstate != GCSpause); if (g->gcstate == GCSpause) setpause(g); /* pause until next cycle */ else { debt = (debt / g->gcstepmul) * STEPMULADJ; /* convert 'work units' to Kb */ luaE_setdebt(g, debt); runafewfinalizers(L); } } /* ** Performs a full GC cycle; if 'isemergency', set a flag to avoid ** some operations which could change the interpreter state in some ** unexpected ways (running finalizers and shrinking some structures). ** Before running the collection, check 'keepinvariant'; if it is true, ** there may be some objects marked as black, so the collector has ** to sweep all objects to turn them back to white (as white has not ** changed, nothing will be collected). */ void luaC_fullgc (lua_State *L, int isemergency) { global_State *g = G(L); lua_assert(g->gckind == KGC_NORMAL); if (isemergency) g->gckind = KGC_EMERGENCY; /* set flag */ if (keepinvariant(g)) { /* black objects? */ entersweep(L); /* sweep everything to turn them back to white */ } /* finish any pending sweep phase to start a new cycle */ luaC_runtilstate(L, bitmask(GCSpause)); luaC_runtilstate(L, ~bitmask(GCSpause)); /* start new collection */ luaC_runtilstate(L, bitmask(GCScallfin)); /* run up to finalizers */ /* estimate must be correct after a full GC cycle */ lua_assert(g->GCestimate == gettotalbytes(g)); luaC_runtilstate(L, bitmask(GCSpause)); /* finish collection */ g->gckind = KGC_NORMAL; setpause(g); } /* }====================================================== */ ================================================ FILE: Tests/ApiExplorer/lua/src/lgc.h ================================================ /* ** $Id: lgc.h,v 2.91.1.1 2017/04/19 17:39:34 roberto Exp $ ** Garbage Collector ** See Copyright Notice in lua.h */ #ifndef lgc_h #define lgc_h #include "lobject.h" #include "lstate.h" /* ** Collectable objects may have one of three colors: white, which ** means the object is not marked; gray, which means the ** object is marked, but its references may be not marked; and ** black, which means that the object and all its references are marked. ** The main invariant of the garbage collector, while marking objects, ** is that a black object can never point to a white one. Moreover, ** any gray object must be in a "gray list" (gray, grayagain, weak, ** allweak, ephemeron) so that it can be visited again before finishing ** the collection cycle. These lists have no meaning when the invariant ** is not being enforced (e.g., sweep phase). */ /* how much to allocate before next GC step */ #if !defined(GCSTEPSIZE) /* ~100 small strings */ #define GCSTEPSIZE (cast_int(100 * sizeof(TString))) #endif /* ** Possible states of the Garbage Collector */ #define GCSpropagate 0 #define GCSatomic 1 #define GCSswpallgc 2 #define GCSswpfinobj 3 #define GCSswptobefnz 4 #define GCSswpend 5 #define GCScallfin 6 #define GCSpause 7 #define issweepphase(g) \ (GCSswpallgc <= (g)->gcstate && (g)->gcstate <= GCSswpend) /* ** macro to tell when main invariant (white objects cannot point to black ** ones) must be kept. During a collection, the sweep ** phase may break the invariant, as objects turned white may point to ** still-black objects. The invariant is restored when sweep ends and ** all objects are white again. */ #define keepinvariant(g) ((g)->gcstate <= GCSatomic) /* ** some useful bit tricks */ #define resetbits(x,m) ((x) &= cast(lu_byte, ~(m))) #define setbits(x,m) ((x) |= (m)) #define testbits(x,m) ((x) & (m)) #define bitmask(b) (1<<(b)) #define bit2mask(b1,b2) (bitmask(b1) | bitmask(b2)) #define l_setbit(x,b) setbits(x, bitmask(b)) #define resetbit(x,b) resetbits(x, bitmask(b)) #define testbit(x,b) testbits(x, bitmask(b)) /* Layout for bit use in 'marked' field: */ #define WHITE0BIT 0 /* object is white (type 0) */ #define WHITE1BIT 1 /* object is white (type 1) */ #define BLACKBIT 2 /* object is black */ #define FINALIZEDBIT 3 /* object has been marked for finalization */ /* bit 7 is currently used by tests (luaL_checkmemory) */ #define WHITEBITS bit2mask(WHITE0BIT, WHITE1BIT) #define iswhite(x) testbits((x)->marked, WHITEBITS) #define isblack(x) testbit((x)->marked, BLACKBIT) #define isgray(x) /* neither white nor black */ \ (!testbits((x)->marked, WHITEBITS | bitmask(BLACKBIT))) #define tofinalize(x) testbit((x)->marked, FINALIZEDBIT) #define otherwhite(g) ((g)->currentwhite ^ WHITEBITS) #define isdeadm(ow,m) (!(((m) ^ WHITEBITS) & (ow))) #define isdead(g,v) isdeadm(otherwhite(g), (v)->marked) #define changewhite(x) ((x)->marked ^= WHITEBITS) #define gray2black(x) l_setbit((x)->marked, BLACKBIT) #define luaC_white(g) cast(lu_byte, (g)->currentwhite & WHITEBITS) /* ** Does one step of collection when debt becomes positive. 'pre'/'pos' ** allows some adjustments to be done only when needed. macro ** 'condchangemem' is used only for heavy tests (forcing a full ** GC cycle on every opportunity) */ #define luaC_condGC(L,pre,pos) \ { if (G(L)->GCdebt > 0) { pre; luaC_step(L); pos;}; \ condchangemem(L,pre,pos); } /* more often than not, 'pre'/'pos' are empty */ #define luaC_checkGC(L) luaC_condGC(L,(void)0,(void)0) #define luaC_barrier(L,p,v) ( \ (iscollectable(v) && isblack(p) && iswhite(gcvalue(v))) ? \ luaC_barrier_(L,obj2gco(p),gcvalue(v)) : cast_void(0)) #define luaC_barrierback(L,p,v) ( \ (iscollectable(v) && isblack(p) && iswhite(gcvalue(v))) ? \ luaC_barrierback_(L,p) : cast_void(0)) #define luaC_objbarrier(L,p,o) ( \ (isblack(p) && iswhite(o)) ? \ luaC_barrier_(L,obj2gco(p),obj2gco(o)) : cast_void(0)) #define luaC_upvalbarrier(L,uv) ( \ (iscollectable((uv)->v) && !upisopen(uv)) ? \ luaC_upvalbarrier_(L,uv) : cast_void(0)) LUAI_FUNC void luaC_fix (lua_State *L, GCObject *o); LUAI_FUNC void luaC_freeallobjects (lua_State *L); LUAI_FUNC void luaC_step (lua_State *L); LUAI_FUNC void luaC_runtilstate (lua_State *L, int statesmask); LUAI_FUNC void luaC_fullgc (lua_State *L, int isemergency); LUAI_FUNC GCObject *luaC_newobj (lua_State *L, int tt, size_t sz); LUAI_FUNC void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v); LUAI_FUNC void luaC_barrierback_ (lua_State *L, Table *o); LUAI_FUNC void luaC_upvalbarrier_ (lua_State *L, UpVal *uv); LUAI_FUNC void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt); LUAI_FUNC void luaC_upvdeccount (lua_State *L, UpVal *uv); #endif ================================================ FILE: Tests/ApiExplorer/lua/src/linit.c ================================================ /* ** $Id: linit.c,v 1.39.1.1 2017/04/19 17:20:42 roberto Exp $ ** Initialization of libraries for lua.c and other clients ** See Copyright Notice in lua.h */ #define linit_c #define LUA_LIB /* ** If you embed Lua in your program and need to open the standard ** libraries, call luaL_openlibs in your program. If you need a ** different set of libraries, copy this file to your project and edit ** it to suit your needs. ** ** You can also *preload* libraries, so that a later 'require' can ** open the library, which is already linked to the application. ** For that, do the following code: ** ** luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_PRELOAD_TABLE); ** lua_pushcfunction(L, luaopen_modname); ** lua_setfield(L, -2, modname); ** lua_pop(L, 1); // remove PRELOAD table */ #include "lprefix.h" #include #include "lua.h" #include "lualib.h" #include "lauxlib.h" /* ** these libs are loaded by lua.c and are readily available to any Lua ** program */ static const luaL_Reg loadedlibs[] = { {"_G", luaopen_base}, {LUA_LOADLIBNAME, luaopen_package}, {LUA_COLIBNAME, luaopen_coroutine}, {LUA_TABLIBNAME, luaopen_table}, {LUA_IOLIBNAME, luaopen_io}, {LUA_OSLIBNAME, luaopen_os}, {LUA_STRLIBNAME, luaopen_string}, {LUA_MATHLIBNAME, luaopen_math}, {LUA_UTF8LIBNAME, luaopen_utf8}, {LUA_DBLIBNAME, luaopen_debug}, #if defined(LUA_COMPAT_BITLIB) {LUA_BITLIBNAME, luaopen_bit32}, #endif {NULL, NULL} }; LUALIB_API void luaL_openlibs (lua_State *L) { const luaL_Reg *lib; /* "require" functions from 'loadedlibs' and set results to global table */ for (lib = loadedlibs; lib->func; lib++) { luaL_requiref(L, lib->name, lib->func, 1); lua_pop(L, 1); /* remove lib */ } } ================================================ FILE: Tests/ApiExplorer/lua/src/liolib.c ================================================ /* ** $Id: liolib.c,v 2.151.1.1 2017/04/19 17:29:57 roberto Exp $ ** Standard I/O (and system) library ** See Copyright Notice in lua.h */ #pragma warning(disable: 4244) #define liolib_c #define LUA_LIB #include "lprefix.h" #include #include #include #include #include #include #include "lua.h" #include "lauxlib.h" #include "lualib.h" /* ** Change this macro to accept other modes for 'fopen' besides ** the standard ones. */ #if !defined(l_checkmode) /* accepted extensions to 'mode' in 'fopen' */ #if !defined(L_MODEEXT) #define L_MODEEXT "b" #endif /* Check whether 'mode' matches '[rwa]%+?[L_MODEEXT]*' */ static int l_checkmode (const char *mode) { return (*mode != '\0' && strchr("rwa", *(mode++)) != NULL && (*mode != '+' || (++mode, 1)) && /* skip if char is '+' */ (strspn(mode, L_MODEEXT) == strlen(mode))); /* check extensions */ } #endif /* ** {====================================================== ** l_popen spawns a new process connected to the current ** one through the file streams. ** ======================================================= */ #if !defined(l_popen) /* { */ #if defined(LUA_USE_POSIX) /* { */ #define l_popen(L,c,m) (fflush(NULL), popen(c,m)) #define l_pclose(L,file) (pclose(file)) #if !APIEXPLORER_UWP #elif defined(LUA_USE_WINDOWS) /* }{ */ #define l_popen(L,c,m) (_popen(c,m)) #define l_pclose(L,file) (_pclose(file)) #endif #else /* }{ */ /* ISO C definitions */ #define l_popen(L,c,m) \ ((void)((void)c, m), \ luaL_error(L, "'popen' not supported"), \ (FILE*)0) #define l_pclose(L,file) ((void)L, (void)file, -1) #endif /* } */ #endif /* } */ /* }====================================================== */ #if !defined(l_getc) /* { */ #if defined(LUA_USE_POSIX) #define l_getc(f) getc_unlocked(f) #define l_lockfile(f) flockfile(f) #define l_unlockfile(f) funlockfile(f) #else #define l_getc(f) getc(f) #define l_lockfile(f) ((void)0) #define l_unlockfile(f) ((void)0) #endif #endif /* } */ /* ** {====================================================== ** l_fseek: configuration for longer offsets ** ======================================================= */ #if !defined(l_fseek) /* { */ #if defined(LUA_USE_POSIX) /* { */ #include #define l_fseek(f,o,w) fseeko(f,o,w) #define l_ftell(f) ftello(f) #define l_seeknum off_t #elif defined(LUA_USE_WINDOWS) && !defined(_CRTIMP_TYPEINFO) \ && defined(_MSC_VER) && (_MSC_VER >= 1400) /* }{ */ /* Windows (but not DDK) and Visual C++ 2005 or higher */ #define l_fseek(f,o,w) _fseeki64(f,o,w) #define l_ftell(f) _ftelli64(f) #define l_seeknum __int64 #else /* }{ */ /* ISO C definitions */ #define l_fseek(f,o,w) fseek(f,o,w) #define l_ftell(f) ftell(f) #define l_seeknum long #endif /* } */ #endif /* } */ /* }====================================================== */ #define IO_PREFIX "_IO_" #define IOPREF_LEN (sizeof(IO_PREFIX)/sizeof(char) - 1) #define IO_INPUT (IO_PREFIX "input") #define IO_OUTPUT (IO_PREFIX "output") typedef luaL_Stream LStream; #define tolstream(L) ((LStream *)luaL_checkudata(L, 1, LUA_FILEHANDLE)) #define isclosed(p) ((p)->closef == NULL) static int io_type (lua_State *L) { LStream *p; luaL_checkany(L, 1); p = (LStream *)luaL_testudata(L, 1, LUA_FILEHANDLE); if (p == NULL) lua_pushnil(L); /* not a file */ else if (isclosed(p)) lua_pushliteral(L, "closed file"); else lua_pushliteral(L, "file"); return 1; } static int f_tostring (lua_State *L) { LStream *p = tolstream(L); if (isclosed(p)) lua_pushliteral(L, "file (closed)"); else lua_pushfstring(L, "file (%p)", p->f); return 1; } static FILE *tofile (lua_State *L) { LStream *p = tolstream(L); if (isclosed(p)) luaL_error(L, "attempt to use a closed file"); lua_assert(p->f); return p->f; } /* ** When creating file handles, always creates a 'closed' file handle ** before opening the actual file; so, if there is a memory error, the ** handle is in a consistent state. */ static LStream *newprefile (lua_State *L) { LStream *p = (LStream *)lua_newuserdata(L, sizeof(LStream)); p->closef = NULL; /* mark file handle as 'closed' */ luaL_setmetatable(L, LUA_FILEHANDLE); return p; } /* ** Calls the 'close' function from a file handle. The 'volatile' avoids ** a bug in some versions of the Clang compiler (e.g., clang 3.0 for ** 32 bits). */ static int aux_close (lua_State *L) { LStream *p = tolstream(L); volatile lua_CFunction cf = p->closef; p->closef = NULL; /* mark stream as closed */ return (*cf)(L); /* close it */ } static int f_close (lua_State *L) { tofile(L); /* make sure argument is an open stream */ return aux_close(L); } static int io_close (lua_State *L) { if (lua_isnone(L, 1)) /* no argument? */ lua_getfield(L, LUA_REGISTRYINDEX, IO_OUTPUT); /* use standard output */ return f_close(L); } static int f_gc (lua_State *L) { LStream *p = tolstream(L); if (!isclosed(p) && p->f != NULL) aux_close(L); /* ignore closed and incompletely open files */ return 0; } /* ** function to close regular files */ static int io_fclose (lua_State *L) { LStream *p = tolstream(L); int res = fclose(p->f); return luaL_fileresult(L, (res == 0), NULL); } static LStream *newfile (lua_State *L) { LStream *p = newprefile(L); p->f = NULL; p->closef = &io_fclose; return p; } static void opencheck (lua_State *L, const char *fname, const char *mode) { LStream *p = newfile(L); p->f = fopen(fname, mode); if (p->f == NULL) luaL_error(L, "cannot open file '%s' (%s)", fname, strerror(errno)); } static int io_open (lua_State *L) { const char *filename = luaL_checkstring(L, 1); const char *mode = luaL_optstring(L, 2, "r"); LStream *p = newfile(L); const char *md = mode; /* to traverse/check mode */ luaL_argcheck(L, l_checkmode(md), 2, "invalid mode"); p->f = fopen(filename, mode); return (p->f == NULL) ? luaL_fileresult(L, 0, filename) : 1; } /* ** function to close 'popen' files */ static int io_pclose (lua_State *L) { LStream *p = tolstream(L); return luaL_execresult(L, l_pclose(L, p->f)); } static int io_popen (lua_State *L) { const char *filename = luaL_checkstring(L, 1); const char *mode = luaL_optstring(L, 2, "r"); LStream *p = newprefile(L); p->f = l_popen(L, filename, mode); p->closef = &io_pclose; return (p->f == NULL) ? luaL_fileresult(L, 0, filename) : 1; } static int io_tmpfile (lua_State *L) { LStream *p = newfile(L); p->f = tmpfile(); return (p->f == NULL) ? luaL_fileresult(L, 0, NULL) : 1; } static FILE *getiofile (lua_State *L, const char *findex) { LStream *p; lua_getfield(L, LUA_REGISTRYINDEX, findex); p = (LStream *)lua_touserdata(L, -1); if (isclosed(p)) luaL_error(L, "standard %s file is closed", findex + IOPREF_LEN); return p->f; } static int g_iofile (lua_State *L, const char *f, const char *mode) { if (!lua_isnoneornil(L, 1)) { const char *filename = lua_tostring(L, 1); if (filename) opencheck(L, filename, mode); else { tofile(L); /* check that it's a valid file handle */ lua_pushvalue(L, 1); } lua_setfield(L, LUA_REGISTRYINDEX, f); } /* return current value */ lua_getfield(L, LUA_REGISTRYINDEX, f); return 1; } static int io_input (lua_State *L) { return g_iofile(L, IO_INPUT, "r"); } static int io_output (lua_State *L) { return g_iofile(L, IO_OUTPUT, "w"); } static int io_readline (lua_State *L); /* ** maximum number of arguments to 'f:lines'/'io.lines' (it + 3 must fit ** in the limit for upvalues of a closure) */ #define MAXARGLINE 250 static void aux_lines (lua_State *L, int toclose) { int n = lua_gettop(L) - 1; /* number of arguments to read */ luaL_argcheck(L, n <= MAXARGLINE, MAXARGLINE + 2, "too many arguments"); lua_pushinteger(L, n); /* number of arguments to read */ lua_pushboolean(L, toclose); /* close/not close file when finished */ lua_rotate(L, 2, 2); /* move 'n' and 'toclose' to their positions */ lua_pushcclosure(L, io_readline, 3 + n); } static int f_lines (lua_State *L) { tofile(L); /* check that it's a valid file handle */ aux_lines(L, 0); return 1; } static int io_lines (lua_State *L) { int toclose; if (lua_isnone(L, 1)) lua_pushnil(L); /* at least one argument */ if (lua_isnil(L, 1)) { /* no file name? */ lua_getfield(L, LUA_REGISTRYINDEX, IO_INPUT); /* get default input */ lua_replace(L, 1); /* put it at index 1 */ tofile(L); /* check that it's a valid file handle */ toclose = 0; /* do not close it after iteration */ } else { /* open a new file */ const char *filename = luaL_checkstring(L, 1); opencheck(L, filename, "r"); lua_replace(L, 1); /* put file at index 1 */ toclose = 1; /* close it after iteration */ } aux_lines(L, toclose); return 1; } /* ** {====================================================== ** READ ** ======================================================= */ /* maximum length of a numeral */ #if !defined (L_MAXLENNUM) #define L_MAXLENNUM 200 #endif /* auxiliary structure used by 'read_number' */ typedef struct { FILE *f; /* file being read */ int c; /* current character (look ahead) */ int n; /* number of elements in buffer 'buff' */ char buff[L_MAXLENNUM + 1]; /* +1 for ending '\0' */ } RN; /* ** Add current char to buffer (if not out of space) and read next one */ static int nextc (RN *rn) { if (rn->n >= L_MAXLENNUM) { /* buffer overflow? */ rn->buff[0] = '\0'; /* invalidate result */ return 0; /* fail */ } else { rn->buff[rn->n++] = rn->c; /* save current char */ rn->c = l_getc(rn->f); /* read next one */ return 1; } } /* ** Accept current char if it is in 'set' (of size 2) */ static int test2 (RN *rn, const char *set) { if (rn->c == set[0] || rn->c == set[1]) return nextc(rn); else return 0; } /* ** Read a sequence of (hex)digits */ static int readdigits (RN *rn, int hex) { int count = 0; while ((hex ? isxdigit(rn->c) : isdigit(rn->c)) && nextc(rn)) count++; return count; } /* ** Read a number: first reads a valid prefix of a numeral into a buffer. ** Then it calls 'lua_stringtonumber' to check whether the format is ** correct and to convert it to a Lua number */ static int read_number (lua_State *L, FILE *f) { RN rn; int count = 0; int hex = 0; char decp[2]; rn.f = f; rn.n = 0; decp[0] = lua_getlocaledecpoint(); /* get decimal point from locale */ decp[1] = '.'; /* always accept a dot */ l_lockfile(rn.f); do { rn.c = l_getc(rn.f); } while (isspace(rn.c)); /* skip spaces */ test2(&rn, "-+"); /* optional signal */ if (test2(&rn, "00")) { if (test2(&rn, "xX")) hex = 1; /* numeral is hexadecimal */ else count = 1; /* count initial '0' as a valid digit */ } count += readdigits(&rn, hex); /* integral part */ if (test2(&rn, decp)) /* decimal point? */ count += readdigits(&rn, hex); /* fractional part */ if (count > 0 && test2(&rn, (hex ? "pP" : "eE"))) { /* exponent mark? */ test2(&rn, "-+"); /* exponent signal */ readdigits(&rn, 0); /* exponent digits */ } ungetc(rn.c, rn.f); /* unread look-ahead char */ l_unlockfile(rn.f); rn.buff[rn.n] = '\0'; /* finish string */ if (lua_stringtonumber(L, rn.buff)) /* is this a valid number? */ return 1; /* ok */ else { /* invalid format */ lua_pushnil(L); /* "result" to be removed */ return 0; /* read fails */ } } static int test_eof (lua_State *L, FILE *f) { int c = getc(f); ungetc(c, f); /* no-op when c == EOF */ lua_pushliteral(L, ""); return (c != EOF); } static int read_line (lua_State *L, FILE *f, int chop) { luaL_Buffer b; int c = '\0'; luaL_buffinit(L, &b); while (c != EOF && c != '\n') { /* repeat until end of line */ char *buff = luaL_prepbuffer(&b); /* preallocate buffer */ int i = 0; l_lockfile(f); /* no memory errors can happen inside the lock */ while (i < LUAL_BUFFERSIZE && (c = l_getc(f)) != EOF && c != '\n') buff[i++] = c; l_unlockfile(f); luaL_addsize(&b, i); } if (!chop && c == '\n') /* want a newline and have one? */ luaL_addchar(&b, c); /* add ending newline to result */ luaL_pushresult(&b); /* close buffer */ /* return ok if read something (either a newline or something else) */ return (c == '\n' || lua_rawlen(L, -1) > 0); } static void read_all (lua_State *L, FILE *f) { size_t nr; luaL_Buffer b; luaL_buffinit(L, &b); do { /* read file in chunks of LUAL_BUFFERSIZE bytes */ char *p = luaL_prepbuffer(&b); nr = fread(p, sizeof(char), LUAL_BUFFERSIZE, f); luaL_addsize(&b, nr); } while (nr == LUAL_BUFFERSIZE); luaL_pushresult(&b); /* close buffer */ } static int read_chars (lua_State *L, FILE *f, size_t n) { size_t nr; /* number of chars actually read */ char *p; luaL_Buffer b; luaL_buffinit(L, &b); p = luaL_prepbuffsize(&b, n); /* prepare buffer to read whole block */ nr = fread(p, sizeof(char), n, f); /* try to read 'n' chars */ luaL_addsize(&b, nr); luaL_pushresult(&b); /* close buffer */ return (nr > 0); /* true iff read something */ } static int g_read (lua_State *L, FILE *f, int first) { int nargs = lua_gettop(L) - 1; int success; int n; clearerr(f); if (nargs == 0) { /* no arguments? */ success = read_line(L, f, 1); n = first+1; /* to return 1 result */ } else { /* ensure stack space for all results and for auxlib's buffer */ luaL_checkstack(L, nargs+LUA_MINSTACK, "too many arguments"); success = 1; for (n = first; nargs-- && success; n++) { if (lua_type(L, n) == LUA_TNUMBER) { size_t l = (size_t)luaL_checkinteger(L, n); success = (l == 0) ? test_eof(L, f) : read_chars(L, f, l); } else { const char *p = luaL_checkstring(L, n); if (*p == '*') p++; /* skip optional '*' (for compatibility) */ switch (*p) { case 'n': /* number */ success = read_number(L, f); break; case 'l': /* line */ success = read_line(L, f, 1); break; case 'L': /* line with end-of-line */ success = read_line(L, f, 0); break; case 'a': /* file */ read_all(L, f); /* read entire file */ success = 1; /* always success */ break; default: return luaL_argerror(L, n, "invalid format"); } } } } if (ferror(f)) return luaL_fileresult(L, 0, NULL); if (!success) { lua_pop(L, 1); /* remove last result */ lua_pushnil(L); /* push nil instead */ } return n - first; } static int io_read (lua_State *L) { return g_read(L, getiofile(L, IO_INPUT), 1); } static int f_read (lua_State *L) { return g_read(L, tofile(L), 2); } static int io_readline (lua_State *L) { LStream *p = (LStream *)lua_touserdata(L, lua_upvalueindex(1)); int i; int n = (int)lua_tointeger(L, lua_upvalueindex(2)); if (isclosed(p)) /* file is already closed? */ return luaL_error(L, "file is already closed"); lua_settop(L , 1); luaL_checkstack(L, n, "too many arguments"); for (i = 1; i <= n; i++) /* push arguments to 'g_read' */ lua_pushvalue(L, lua_upvalueindex(3 + i)); n = g_read(L, p->f, 2); /* 'n' is number of results */ lua_assert(n > 0); /* should return at least a nil */ if (lua_toboolean(L, -n)) /* read at least one value? */ return n; /* return them */ else { /* first result is nil: EOF or error */ if (n > 1) { /* is there error information? */ /* 2nd result is error message */ return luaL_error(L, "%s", lua_tostring(L, -n + 1)); } if (lua_toboolean(L, lua_upvalueindex(3))) { /* generator created file? */ lua_settop(L, 0); lua_pushvalue(L, lua_upvalueindex(1)); aux_close(L); /* close it */ } return 0; } } /* }====================================================== */ static int g_write (lua_State *L, FILE *f, int arg) { int nargs = lua_gettop(L) - arg; int status = 1; for (; nargs--; arg++) { if (lua_type(L, arg) == LUA_TNUMBER) { /* optimization: could be done exactly as for strings */ int len = lua_isinteger(L, arg) ? fprintf(f, LUA_INTEGER_FMT, (LUAI_UACINT)lua_tointeger(L, arg)) : fprintf(f, LUA_NUMBER_FMT, (LUAI_UACNUMBER)lua_tonumber(L, arg)); status = status && (len > 0); } else { size_t l; const char *s = luaL_checklstring(L, arg, &l); status = status && (fwrite(s, sizeof(char), l, f) == l); } } if (status) return 1; /* file handle already on stack top */ else return luaL_fileresult(L, status, NULL); } static int io_write (lua_State *L) { return g_write(L, getiofile(L, IO_OUTPUT), 1); } static int f_write (lua_State *L) { FILE *f = tofile(L); lua_pushvalue(L, 1); /* push file at the stack top (to be returned) */ return g_write(L, f, 2); } static int f_seek (lua_State *L) { static const int mode[] = {SEEK_SET, SEEK_CUR, SEEK_END}; static const char *const modenames[] = {"set", "cur", "end", NULL}; FILE *f = tofile(L); int op = luaL_checkoption(L, 2, "cur", modenames); lua_Integer p3 = luaL_optinteger(L, 3, 0); l_seeknum offset = (l_seeknum)p3; luaL_argcheck(L, (lua_Integer)offset == p3, 3, "not an integer in proper range"); op = l_fseek(f, offset, mode[op]); if (op) return luaL_fileresult(L, 0, NULL); /* error */ else { lua_pushinteger(L, (lua_Integer)l_ftell(f)); return 1; } } static int f_setvbuf (lua_State *L) { static const int mode[] = {_IONBF, _IOFBF, _IOLBF}; static const char *const modenames[] = {"no", "full", "line", NULL}; FILE *f = tofile(L); int op = luaL_checkoption(L, 2, NULL, modenames); lua_Integer sz = luaL_optinteger(L, 3, LUAL_BUFFERSIZE); int res = setvbuf(f, NULL, mode[op], (size_t)sz); return luaL_fileresult(L, res == 0, NULL); } static int io_flush (lua_State *L) { return luaL_fileresult(L, fflush(getiofile(L, IO_OUTPUT)) == 0, NULL); } static int f_flush (lua_State *L) { return luaL_fileresult(L, fflush(tofile(L)) == 0, NULL); } /* ** functions for 'io' library */ static const luaL_Reg iolib[] = { {"close", io_close}, {"flush", io_flush}, {"input", io_input}, {"lines", io_lines}, {"open", io_open}, {"output", io_output}, {"popen", io_popen}, {"read", io_read}, {"tmpfile", io_tmpfile}, {"type", io_type}, {"write", io_write}, {NULL, NULL} }; /* ** methods for file handles */ static const luaL_Reg flib[] = { {"close", f_close}, {"flush", f_flush}, {"lines", f_lines}, {"read", f_read}, {"seek", f_seek}, {"setvbuf", f_setvbuf}, {"write", f_write}, {"__gc", f_gc}, {"__tostring", f_tostring}, {NULL, NULL} }; static void createmeta (lua_State *L) { luaL_newmetatable(L, LUA_FILEHANDLE); /* create metatable for file handles */ lua_pushvalue(L, -1); /* push metatable */ lua_setfield(L, -2, "__index"); /* metatable.__index = metatable */ luaL_setfuncs(L, flib, 0); /* add file methods to new metatable */ lua_pop(L, 1); /* pop new metatable */ } /* ** function to (not) close the standard files stdin, stdout, and stderr */ static int io_noclose (lua_State *L) { LStream *p = tolstream(L); p->closef = &io_noclose; /* keep file opened */ lua_pushnil(L); lua_pushliteral(L, "cannot close standard file"); return 2; } static void createstdfile (lua_State *L, FILE *f, const char *k, const char *fname) { LStream *p = newprefile(L); p->f = f; p->closef = &io_noclose; if (k != NULL) { lua_pushvalue(L, -1); lua_setfield(L, LUA_REGISTRYINDEX, k); /* add file to registry */ } lua_setfield(L, -2, fname); /* add file to module */ } LUAMOD_API int luaopen_io (lua_State *L) { luaL_newlib(L, iolib); /* new module */ createmeta(L); /* create (and set) default files */ createstdfile(L, stdin, IO_INPUT, "stdin"); createstdfile(L, stdout, IO_OUTPUT, "stdout"); createstdfile(L, stderr, NULL, "stderr"); return 1; } ================================================ FILE: Tests/ApiExplorer/lua/src/llex.c ================================================ /* ** $Id: llex.c,v 2.96.1.1 2017/04/19 17:20:42 roberto Exp $ ** Lexical Analyzer ** See Copyright Notice in lua.h */ #define llex_c #define LUA_CORE #include "lprefix.h" #include #include #include "lua.h" #include "lctype.h" #include "ldebug.h" #include "ldo.h" #include "lgc.h" #include "llex.h" #include "lobject.h" #include "lparser.h" #include "lstate.h" #include "lstring.h" #include "ltable.h" #include "lzio.h" #define next(ls) (ls->current = zgetc(ls->z)) #define currIsNewline(ls) (ls->current == '\n' || ls->current == '\r') /* ORDER RESERVED */ static const char *const luaX_tokens [] = { "and", "break", "do", "else", "elseif", "end", "false", "for", "function", "goto", "if", "in", "local", "nil", "not", "or", "repeat", "return", "then", "true", "until", "while", "//", "..", "...", "==", ">=", "<=", "~=", "<<", ">>", "::", "", "", "", "", "" }; #define save_and_next(ls) (save(ls, ls->current), next(ls)) static l_noret lexerror (LexState *ls, const char *msg, int token); static void save (LexState *ls, int c) { Mbuffer *b = ls->buff; if (luaZ_bufflen(b) + 1 > luaZ_sizebuffer(b)) { size_t newsize; if (luaZ_sizebuffer(b) >= MAX_SIZE/2) lexerror(ls, "lexical element too long", 0); newsize = luaZ_sizebuffer(b) * 2; luaZ_resizebuffer(ls->L, b, newsize); } b->buffer[luaZ_bufflen(b)++] = cast(char, c); } void luaX_init (lua_State *L) { int i; TString *e = luaS_newliteral(L, LUA_ENV); /* create env name */ luaC_fix(L, obj2gco(e)); /* never collect this name */ for (i=0; iextra = cast_byte(i+1); /* reserved word */ } } const char *luaX_token2str (LexState *ls, int token) { if (token < FIRST_RESERVED) { /* single-byte symbols? */ lua_assert(token == cast_uchar(token)); return luaO_pushfstring(ls->L, "'%c'", token); } else { const char *s = luaX_tokens[token - FIRST_RESERVED]; if (token < TK_EOS) /* fixed format (symbols and reserved words)? */ return luaO_pushfstring(ls->L, "'%s'", s); else /* names, strings, and numerals */ return s; } } static const char *txtToken (LexState *ls, int token) { switch (token) { case TK_NAME: case TK_STRING: case TK_FLT: case TK_INT: save(ls, '\0'); return luaO_pushfstring(ls->L, "'%s'", luaZ_buffer(ls->buff)); default: return luaX_token2str(ls, token); } } static l_noret lexerror (LexState *ls, const char *msg, int token) { msg = luaG_addinfo(ls->L, msg, ls->source, ls->linenumber); if (token) luaO_pushfstring(ls->L, "%s near %s", msg, txtToken(ls, token)); luaD_throw(ls->L, LUA_ERRSYNTAX); } l_noret luaX_syntaxerror (LexState *ls, const char *msg) { lexerror(ls, msg, ls->t.token); } /* ** creates a new string and anchors it in scanner's table so that ** it will not be collected until the end of the compilation ** (by that time it should be anchored somewhere) */ TString *luaX_newstring (LexState *ls, const char *str, size_t l) { lua_State *L = ls->L; TValue *o; /* entry for 'str' */ TString *ts = luaS_newlstr(L, str, l); /* create new string */ setsvalue2s(L, L->top++, ts); /* temporarily anchor it in stack */ o = luaH_set(L, ls->h, L->top - 1); if (ttisnil(o)) { /* not in use yet? */ /* boolean value does not need GC barrier; table has no metatable, so it does not need to invalidate cache */ setbvalue(o, 1); /* t[string] = true */ luaC_checkGC(L); } else { /* string already present */ ts = tsvalue(keyfromval(o)); /* re-use value previously stored */ } L->top--; /* remove string from stack */ return ts; } /* ** increment line number and skips newline sequence (any of ** \n, \r, \n\r, or \r\n) */ static void inclinenumber (LexState *ls) { int old = ls->current; lua_assert(currIsNewline(ls)); next(ls); /* skip '\n' or '\r' */ if (currIsNewline(ls) && ls->current != old) next(ls); /* skip '\n\r' or '\r\n' */ if (++ls->linenumber >= MAX_INT) lexerror(ls, "chunk has too many lines", 0); } void luaX_setinput (lua_State *L, LexState *ls, ZIO *z, TString *source, int firstchar) { ls->t.token = 0; ls->L = L; ls->current = firstchar; ls->lookahead.token = TK_EOS; /* no look-ahead token */ ls->z = z; ls->fs = NULL; ls->linenumber = 1; ls->lastline = 1; ls->source = source; ls->envn = luaS_newliteral(L, LUA_ENV); /* get env name */ luaZ_resizebuffer(ls->L, ls->buff, LUA_MINBUFFER); /* initialize buffer */ } /* ** ======================================================= ** LEXICAL ANALYZER ** ======================================================= */ static int check_next1 (LexState *ls, int c) { if (ls->current == c) { next(ls); return 1; } else return 0; } /* ** Check whether current char is in set 'set' (with two chars) and ** saves it */ static int check_next2 (LexState *ls, const char *set) { lua_assert(set[2] == '\0'); if (ls->current == set[0] || ls->current == set[1]) { save_and_next(ls); return 1; } else return 0; } /* LUA_NUMBER */ /* ** this function is quite liberal in what it accepts, as 'luaO_str2num' ** will reject ill-formed numerals. */ static int read_numeral (LexState *ls, SemInfo *seminfo) { TValue obj; const char *expo = "Ee"; int first = ls->current; lua_assert(lisdigit(ls->current)); save_and_next(ls); if (first == '0' && check_next2(ls, "xX")) /* hexadecimal? */ expo = "Pp"; for (;;) { if (check_next2(ls, expo)) /* exponent part? */ check_next2(ls, "-+"); /* optional exponent sign */ if (lisxdigit(ls->current)) save_and_next(ls); else if (ls->current == '.') save_and_next(ls); else break; } save(ls, '\0'); if (luaO_str2num(luaZ_buffer(ls->buff), &obj) == 0) /* format error? */ lexerror(ls, "malformed number", TK_FLT); if (ttisinteger(&obj)) { seminfo->i = ivalue(&obj); return TK_INT; } else { lua_assert(ttisfloat(&obj)); seminfo->r = fltvalue(&obj); return TK_FLT; } } /* ** skip a sequence '[=*[' or ']=*]'; if sequence is well formed, return ** its number of '='s; otherwise, return a negative number (-1 iff there ** are no '='s after initial bracket) */ static int skip_sep (LexState *ls) { int count = 0; int s = ls->current; lua_assert(s == '[' || s == ']'); save_and_next(ls); while (ls->current == '=') { save_and_next(ls); count++; } return (ls->current == s) ? count : (-count) - 1; } static void read_long_string (LexState *ls, SemInfo *seminfo, int sep) { int line = ls->linenumber; /* initial line (for error message) */ save_and_next(ls); /* skip 2nd '[' */ if (currIsNewline(ls)) /* string starts with a newline? */ inclinenumber(ls); /* skip it */ for (;;) { switch (ls->current) { case EOZ: { /* error */ const char *what = (seminfo ? "string" : "comment"); const char *msg = luaO_pushfstring(ls->L, "unfinished long %s (starting at line %d)", what, line); lexerror(ls, msg, TK_EOS); break; /* to avoid warnings */ } case ']': { if (skip_sep(ls) == sep) { save_and_next(ls); /* skip 2nd ']' */ goto endloop; } break; } case '\n': case '\r': { save(ls, '\n'); inclinenumber(ls); if (!seminfo) luaZ_resetbuffer(ls->buff); /* avoid wasting space */ break; } default: { if (seminfo) save_and_next(ls); else next(ls); } } } endloop: if (seminfo) seminfo->ts = luaX_newstring(ls, luaZ_buffer(ls->buff) + (2 + sep), luaZ_bufflen(ls->buff) - 2*(2 + sep)); } static void esccheck (LexState *ls, int c, const char *msg) { if (!c) { if (ls->current != EOZ) save_and_next(ls); /* add current to buffer for error message */ lexerror(ls, msg, TK_STRING); } } static int gethexa (LexState *ls) { save_and_next(ls); esccheck (ls, lisxdigit(ls->current), "hexadecimal digit expected"); return luaO_hexavalue(ls->current); } static int readhexaesc (LexState *ls) { int r = gethexa(ls); r = (r << 4) + gethexa(ls); luaZ_buffremove(ls->buff, 2); /* remove saved chars from buffer */ return r; } static unsigned long readutf8esc (LexState *ls) { unsigned long r; int i = 4; /* chars to be removed: '\', 'u', '{', and first digit */ save_and_next(ls); /* skip 'u' */ esccheck(ls, ls->current == '{', "missing '{'"); r = gethexa(ls); /* must have at least one digit */ while ((save_and_next(ls), lisxdigit(ls->current))) { i++; r = (r << 4) + luaO_hexavalue(ls->current); esccheck(ls, r <= 0x10FFFF, "UTF-8 value too large"); } esccheck(ls, ls->current == '}', "missing '}'"); next(ls); /* skip '}' */ luaZ_buffremove(ls->buff, i); /* remove saved chars from buffer */ return r; } static void utf8esc (LexState *ls) { char buff[UTF8BUFFSZ]; int n = luaO_utf8esc(buff, readutf8esc(ls)); for (; n > 0; n--) /* add 'buff' to string */ save(ls, buff[UTF8BUFFSZ - n]); } static int readdecesc (LexState *ls) { int i; int r = 0; /* result accumulator */ for (i = 0; i < 3 && lisdigit(ls->current); i++) { /* read up to 3 digits */ r = 10*r + ls->current - '0'; save_and_next(ls); } esccheck(ls, r <= UCHAR_MAX, "decimal escape too large"); luaZ_buffremove(ls->buff, i); /* remove read digits from buffer */ return r; } static void read_string (LexState *ls, int del, SemInfo *seminfo) { save_and_next(ls); /* keep delimiter (for error messages) */ while (ls->current != del) { switch (ls->current) { case EOZ: lexerror(ls, "unfinished string", TK_EOS); break; /* to avoid warnings */ case '\n': case '\r': lexerror(ls, "unfinished string", TK_STRING); break; /* to avoid warnings */ case '\\': { /* escape sequences */ int c; /* final character to be saved */ save_and_next(ls); /* keep '\\' for error messages */ switch (ls->current) { case 'a': c = '\a'; goto read_save; case 'b': c = '\b'; goto read_save; case 'f': c = '\f'; goto read_save; case 'n': c = '\n'; goto read_save; case 'r': c = '\r'; goto read_save; case 't': c = '\t'; goto read_save; case 'v': c = '\v'; goto read_save; case 'x': c = readhexaesc(ls); goto read_save; case 'u': utf8esc(ls); goto no_save; case '\n': case '\r': inclinenumber(ls); c = '\n'; goto only_save; case '\\': case '\"': case '\'': c = ls->current; goto read_save; case EOZ: goto no_save; /* will raise an error next loop */ case 'z': { /* zap following span of spaces */ luaZ_buffremove(ls->buff, 1); /* remove '\\' */ next(ls); /* skip the 'z' */ while (lisspace(ls->current)) { if (currIsNewline(ls)) inclinenumber(ls); else next(ls); } goto no_save; } default: { esccheck(ls, lisdigit(ls->current), "invalid escape sequence"); c = readdecesc(ls); /* digital escape '\ddd' */ goto only_save; } } read_save: next(ls); /* go through */ only_save: luaZ_buffremove(ls->buff, 1); /* remove '\\' */ save(ls, c); /* go through */ no_save: break; } default: save_and_next(ls); } } save_and_next(ls); /* skip delimiter */ seminfo->ts = luaX_newstring(ls, luaZ_buffer(ls->buff) + 1, luaZ_bufflen(ls->buff) - 2); } static int llex (LexState *ls, SemInfo *seminfo) { luaZ_resetbuffer(ls->buff); for (;;) { switch (ls->current) { case '\n': case '\r': { /* line breaks */ inclinenumber(ls); break; } case ' ': case '\f': case '\t': case '\v': { /* spaces */ next(ls); break; } case '-': { /* '-' or '--' (comment) */ next(ls); if (ls->current != '-') return '-'; /* else is a comment */ next(ls); if (ls->current == '[') { /* long comment? */ int sep = skip_sep(ls); luaZ_resetbuffer(ls->buff); /* 'skip_sep' may dirty the buffer */ if (sep >= 0) { read_long_string(ls, NULL, sep); /* skip long comment */ luaZ_resetbuffer(ls->buff); /* previous call may dirty the buff. */ break; } } /* else short comment */ while (!currIsNewline(ls) && ls->current != EOZ) next(ls); /* skip until end of line (or end of file) */ break; } case '[': { /* long string or simply '[' */ int sep = skip_sep(ls); if (sep >= 0) { read_long_string(ls, seminfo, sep); return TK_STRING; } else if (sep != -1) /* '[=...' missing second bracket */ lexerror(ls, "invalid long string delimiter", TK_STRING); return '['; } case '=': { next(ls); if (check_next1(ls, '=')) return TK_EQ; else return '='; } case '<': { next(ls); if (check_next1(ls, '=')) return TK_LE; else if (check_next1(ls, '<')) return TK_SHL; else return '<'; } case '>': { next(ls); if (check_next1(ls, '=')) return TK_GE; else if (check_next1(ls, '>')) return TK_SHR; else return '>'; } case '/': { next(ls); if (check_next1(ls, '/')) return TK_IDIV; else return '/'; } case '~': { next(ls); if (check_next1(ls, '=')) return TK_NE; else return '~'; } case ':': { next(ls); if (check_next1(ls, ':')) return TK_DBCOLON; else return ':'; } case '"': case '\'': { /* short literal strings */ read_string(ls, ls->current, seminfo); return TK_STRING; } case '.': { /* '.', '..', '...', or number */ save_and_next(ls); if (check_next1(ls, '.')) { if (check_next1(ls, '.')) return TK_DOTS; /* '...' */ else return TK_CONCAT; /* '..' */ } else if (!lisdigit(ls->current)) return '.'; else return read_numeral(ls, seminfo); } case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { return read_numeral(ls, seminfo); } case EOZ: { return TK_EOS; } default: { if (lislalpha(ls->current)) { /* identifier or reserved word? */ TString *ts; do { save_and_next(ls); } while (lislalnum(ls->current)); ts = luaX_newstring(ls, luaZ_buffer(ls->buff), luaZ_bufflen(ls->buff)); seminfo->ts = ts; if (isreserved(ts)) /* reserved word? */ return ts->extra - 1 + FIRST_RESERVED; else { return TK_NAME; } } else { /* single-char tokens (+ - / ...) */ int c = ls->current; next(ls); return c; } } } } } void luaX_next (LexState *ls) { ls->lastline = ls->linenumber; if (ls->lookahead.token != TK_EOS) { /* is there a look-ahead token? */ ls->t = ls->lookahead; /* use this one */ ls->lookahead.token = TK_EOS; /* and discharge it */ } else ls->t.token = llex(ls, &ls->t.seminfo); /* read next token */ } int luaX_lookahead (LexState *ls) { lua_assert(ls->lookahead.token == TK_EOS); ls->lookahead.token = llex(ls, &ls->lookahead.seminfo); return ls->lookahead.token; } ================================================ FILE: Tests/ApiExplorer/lua/src/llex.h ================================================ /* ** $Id: llex.h,v 1.79.1.1 2017/04/19 17:20:42 roberto Exp $ ** Lexical Analyzer ** See Copyright Notice in lua.h */ #ifndef llex_h #define llex_h #include "lobject.h" #include "lzio.h" #define FIRST_RESERVED 257 #if !defined(LUA_ENV) #define LUA_ENV "_ENV" #endif /* * WARNING: if you change the order of this enumeration, * grep "ORDER RESERVED" */ enum RESERVED { /* terminal symbols denoted by reserved words */ TK_AND = FIRST_RESERVED, TK_BREAK, TK_DO, TK_ELSE, TK_ELSEIF, TK_END, TK_FALSE, TK_FOR, TK_FUNCTION, TK_GOTO, TK_IF, TK_IN, TK_LOCAL, TK_NIL, TK_NOT, TK_OR, TK_REPEAT, TK_RETURN, TK_THEN, TK_TRUE, TK_UNTIL, TK_WHILE, /* other terminal symbols */ TK_IDIV, TK_CONCAT, TK_DOTS, TK_EQ, TK_GE, TK_LE, TK_NE, TK_SHL, TK_SHR, TK_DBCOLON, TK_EOS, TK_FLT, TK_INT, TK_NAME, TK_STRING }; /* number of reserved words */ #define NUM_RESERVED (cast(int, TK_WHILE-FIRST_RESERVED+1)) typedef union { lua_Number r; lua_Integer i; TString *ts; } SemInfo; /* semantics information */ typedef struct Token { int token; SemInfo seminfo; } Token; /* state of the lexer plus state of the parser when shared by all functions */ typedef struct LexState { int current; /* current character (charint) */ int linenumber; /* input line counter */ int lastline; /* line of last token 'consumed' */ Token t; /* current token */ Token lookahead; /* look ahead token */ struct FuncState *fs; /* current function (parser) */ struct lua_State *L; ZIO *z; /* input stream */ Mbuffer *buff; /* buffer for tokens */ Table *h; /* to avoid collection/reuse strings */ struct Dyndata *dyd; /* dynamic structures used by the parser */ TString *source; /* current source name */ TString *envn; /* environment variable name */ } LexState; LUAI_FUNC void luaX_init (lua_State *L); LUAI_FUNC void luaX_setinput (lua_State *L, LexState *ls, ZIO *z, TString *source, int firstchar); LUAI_FUNC TString *luaX_newstring (LexState *ls, const char *str, size_t l); LUAI_FUNC void luaX_next (LexState *ls); LUAI_FUNC int luaX_lookahead (LexState *ls); LUAI_FUNC l_noret luaX_syntaxerror (LexState *ls, const char *s); LUAI_FUNC const char *luaX_token2str (LexState *ls, int token); #endif ================================================ FILE: Tests/ApiExplorer/lua/src/llimits.h ================================================ /* ** $Id: llimits.h,v 1.141.1.1 2017/04/19 17:20:42 roberto Exp $ ** Limits, basic types, and some other 'installation-dependent' definitions ** See Copyright Notice in lua.h */ #ifndef llimits_h #define llimits_h #include #include #include "lua.h" /* ** 'lu_mem' and 'l_mem' are unsigned/signed integers big enough to count ** the total memory used by Lua (in bytes). Usually, 'size_t' and ** 'ptrdiff_t' should work, but we use 'long' for 16-bit machines. */ #if defined(LUAI_MEM) /* { external definitions? */ typedef LUAI_UMEM lu_mem; typedef LUAI_MEM l_mem; #elif LUAI_BITSINT >= 32 /* }{ */ typedef size_t lu_mem; typedef ptrdiff_t l_mem; #else /* 16-bit ints */ /* }{ */ typedef unsigned long lu_mem; typedef long l_mem; #endif /* } */ /* chars used as small naturals (so that 'char' is reserved for characters) */ typedef unsigned char lu_byte; /* maximum value for size_t */ #define MAX_SIZET ((size_t)(~(size_t)0)) /* maximum size visible for Lua (must be representable in a lua_Integer */ #define MAX_SIZE (sizeof(size_t) < sizeof(lua_Integer) ? MAX_SIZET \ : (size_t)(LUA_MAXINTEGER)) #define MAX_LUMEM ((lu_mem)(~(lu_mem)0)) #define MAX_LMEM ((l_mem)(MAX_LUMEM >> 1)) #define MAX_INT INT_MAX /* maximum value of an int */ /* ** conversion of pointer to unsigned integer: ** this is for hashing only; there is no problem if the integer ** cannot hold the whole pointer value */ #define point2uint(p) ((unsigned int)((size_t)(p) & UINT_MAX)) /* type to ensure maximum alignment */ #if defined(LUAI_USER_ALIGNMENT_T) typedef LUAI_USER_ALIGNMENT_T L_Umaxalign; #else typedef union { lua_Number n; double u; void *s; lua_Integer i; long l; } L_Umaxalign; #endif /* types of 'usual argument conversions' for lua_Number and lua_Integer */ typedef LUAI_UACNUMBER l_uacNumber; typedef LUAI_UACINT l_uacInt; /* internal assertions for in-house debugging */ #if defined(lua_assert) #define check_exp(c,e) (lua_assert(c), (e)) /* to avoid problems with conditions too long */ #define lua_longassert(c) ((c) ? (void)0 : lua_assert(0)) #else #define lua_assert(c) ((void)0) #define check_exp(c,e) (e) #define lua_longassert(c) ((void)0) #endif /* ** assertion for checking API calls */ #if !defined(luai_apicheck) #define luai_apicheck(l,e) lua_assert(e) #endif #define api_check(l,e,msg) luai_apicheck(l,(e) && msg) /* macro to avoid warnings about unused variables */ #if !defined(UNUSED) #define UNUSED(x) ((void)(x)) #endif /* type casts (a macro highlights casts in the code) */ #define cast(t, exp) ((t)(exp)) #define cast_void(i) cast(void, (i)) #define cast_byte(i) cast(lu_byte, (i)) #define cast_num(i) cast(lua_Number, (i)) #define cast_int(i) cast(int, (i)) #define cast_uchar(i) cast(unsigned char, (i)) /* cast a signed lua_Integer to lua_Unsigned */ #if !defined(l_castS2U) #define l_castS2U(i) ((lua_Unsigned)(i)) #endif /* ** cast a lua_Unsigned to a signed lua_Integer; this cast is ** not strict ISO C, but two-complement architectures should ** work fine. */ #if !defined(l_castU2S) #define l_castU2S(i) ((lua_Integer)(i)) #endif /* ** non-return type */ #if defined(__GNUC__) #define l_noret void __attribute__((noreturn)) #elif defined(_MSC_VER) && _MSC_VER >= 1200 #define l_noret void __declspec(noreturn) #else #define l_noret void #endif /* ** maximum depth for nested C calls and syntactical nested non-terminals ** in a program. (Value must fit in an unsigned short int.) */ #if !defined(LUAI_MAXCCALLS) #define LUAI_MAXCCALLS 200 #endif /* ** type for virtual-machine instructions; ** must be an unsigned with (at least) 4 bytes (see details in lopcodes.h) */ #if LUAI_BITSINT >= 32 typedef unsigned int Instruction; #else typedef unsigned long Instruction; #endif /* ** Maximum length for short strings, that is, strings that are ** internalized. (Cannot be smaller than reserved words or tags for ** metamethods, as these strings must be internalized; ** #("function") = 8, #("__newindex") = 10.) */ #if !defined(LUAI_MAXSHORTLEN) #define LUAI_MAXSHORTLEN 40 #endif /* ** Initial size for the string table (must be power of 2). ** The Lua core alone registers ~50 strings (reserved words + ** metaevent keys + a few others). Libraries would typically add ** a few dozens more. */ #if !defined(MINSTRTABSIZE) #define MINSTRTABSIZE 128 #endif /* ** Size of cache for strings in the API. 'N' is the number of ** sets (better be a prime) and "M" is the size of each set (M == 1 ** makes a direct cache.) */ #if !defined(STRCACHE_N) #define STRCACHE_N 53 #define STRCACHE_M 2 #endif /* minimum size for string buffer */ #if !defined(LUA_MINBUFFER) #define LUA_MINBUFFER 32 #endif /* ** macros that are executed whenever program enters the Lua core ** ('lua_lock') and leaves the core ('lua_unlock') */ #if !defined(lua_lock) #define lua_lock(L) ((void) 0) #define lua_unlock(L) ((void) 0) #endif /* ** macro executed during Lua functions at points where the ** function can yield. */ #if !defined(luai_threadyield) #define luai_threadyield(L) {lua_unlock(L); lua_lock(L);} #endif /* ** these macros allow user-specific actions on threads when you defined ** LUAI_EXTRASPACE and need to do something extra when a thread is ** created/deleted/resumed/yielded. */ #if !defined(luai_userstateopen) #define luai_userstateopen(L) ((void)L) #endif #if !defined(luai_userstateclose) #define luai_userstateclose(L) ((void)L) #endif #if !defined(luai_userstatethread) #define luai_userstatethread(L,L1) ((void)L) #endif #if !defined(luai_userstatefree) #define luai_userstatefree(L,L1) ((void)L) #endif #if !defined(luai_userstateresume) #define luai_userstateresume(L,n) ((void)L) #endif #if !defined(luai_userstateyield) #define luai_userstateyield(L,n) ((void)L) #endif /* ** The luai_num* macros define the primitive operations over numbers. */ /* floor division (defined as 'floor(a/b)') */ #if !defined(luai_numidiv) #define luai_numidiv(L,a,b) ((void)L, l_floor(luai_numdiv(L,a,b))) #endif /* float division */ #if !defined(luai_numdiv) #define luai_numdiv(L,a,b) ((a)/(b)) #endif /* ** modulo: defined as 'a - floor(a/b)*b'; this definition gives NaN when ** 'b' is huge, but the result should be 'a'. 'fmod' gives the result of ** 'a - trunc(a/b)*b', and therefore must be corrected when 'trunc(a/b) ** ~= floor(a/b)'. That happens when the division has a non-integer ** negative result, which is equivalent to the test below. */ #if !defined(luai_nummod) #define luai_nummod(L,a,b,m) \ { (m) = l_mathop(fmod)(a,b); if ((m)*(b) < 0) (m) += (b); } #endif /* exponentiation */ #if !defined(luai_numpow) #define luai_numpow(L,a,b) ((void)L, l_mathop(pow)(a,b)) #endif /* the others are quite standard operations */ #if !defined(luai_numadd) #define luai_numadd(L,a,b) ((a)+(b)) #define luai_numsub(L,a,b) ((a)-(b)) #define luai_nummul(L,a,b) ((a)*(b)) #define luai_numunm(L,a) (-(a)) #define luai_numeq(a,b) ((a)==(b)) #define luai_numlt(a,b) ((a)<(b)) #define luai_numle(a,b) ((a)<=(b)) #define luai_numisnan(a) (!luai_numeq((a), (a))) #endif /* ** macro to control inclusion of some hard tests on stack reallocation */ #if !defined(HARDSTACKTESTS) #define condmovestack(L,pre,pos) ((void)0) #else /* realloc stack keeping its size */ #define condmovestack(L,pre,pos) \ { int sz_ = (L)->stacksize; pre; luaD_reallocstack((L), sz_); pos; } #endif #if !defined(HARDMEMTESTS) #define condchangemem(L,pre,pos) ((void)0) #else #define condchangemem(L,pre,pos) \ { if (G(L)->gcrunning) { pre; luaC_fullgc(L, 0); pos; } } #endif #endif ================================================ FILE: Tests/ApiExplorer/lua/src/lmathlib.c ================================================ /* ** $Id: lmathlib.c,v 1.119.1.1 2017/04/19 17:20:42 roberto Exp $ ** Standard mathematical library ** See Copyright Notice in lua.h */ #define lmathlib_c #define LUA_LIB #include "lprefix.h" #include #include #include "lua.h" #include "lauxlib.h" #include "lualib.h" #undef PI #define PI (l_mathop(3.141592653589793238462643383279502884)) #if !defined(l_rand) /* { */ #if defined(LUA_USE_POSIX) #define l_rand() random() #define l_srand(x) srandom(x) #define L_RANDMAX 2147483647 /* (2^31 - 1), following POSIX */ #else #define l_rand() rand() #define l_srand(x) srand(x) #define L_RANDMAX RAND_MAX #endif #endif /* } */ static int math_abs (lua_State *L) { if (lua_isinteger(L, 1)) { lua_Integer n = lua_tointeger(L, 1); if (n < 0) n = (lua_Integer)(0u - (lua_Unsigned)n); lua_pushinteger(L, n); } else lua_pushnumber(L, l_mathop(fabs)(luaL_checknumber(L, 1))); return 1; } static int math_sin (lua_State *L) { lua_pushnumber(L, l_mathop(sin)(luaL_checknumber(L, 1))); return 1; } static int math_cos (lua_State *L) { lua_pushnumber(L, l_mathop(cos)(luaL_checknumber(L, 1))); return 1; } static int math_tan (lua_State *L) { lua_pushnumber(L, l_mathop(tan)(luaL_checknumber(L, 1))); return 1; } static int math_asin (lua_State *L) { lua_pushnumber(L, l_mathop(asin)(luaL_checknumber(L, 1))); return 1; } static int math_acos (lua_State *L) { lua_pushnumber(L, l_mathop(acos)(luaL_checknumber(L, 1))); return 1; } static int math_atan (lua_State *L) { lua_Number y = luaL_checknumber(L, 1); lua_Number x = luaL_optnumber(L, 2, 1); lua_pushnumber(L, l_mathop(atan2)(y, x)); return 1; } static int math_toint (lua_State *L) { int valid; lua_Integer n = lua_tointegerx(L, 1, &valid); if (valid) lua_pushinteger(L, n); else { luaL_checkany(L, 1); lua_pushnil(L); /* value is not convertible to integer */ } return 1; } static void pushnumint (lua_State *L, lua_Number d) { lua_Integer n; if (lua_numbertointeger(d, &n)) /* does 'd' fit in an integer? */ lua_pushinteger(L, n); /* result is integer */ else lua_pushnumber(L, d); /* result is float */ } static int math_floor (lua_State *L) { if (lua_isinteger(L, 1)) lua_settop(L, 1); /* integer is its own floor */ else { lua_Number d = l_mathop(floor)(luaL_checknumber(L, 1)); pushnumint(L, d); } return 1; } static int math_ceil (lua_State *L) { if (lua_isinteger(L, 1)) lua_settop(L, 1); /* integer is its own ceil */ else { lua_Number d = l_mathop(ceil)(luaL_checknumber(L, 1)); pushnumint(L, d); } return 1; } static int math_fmod (lua_State *L) { if (lua_isinteger(L, 1) && lua_isinteger(L, 2)) { lua_Integer d = lua_tointeger(L, 2); if ((lua_Unsigned)d + 1u <= 1u) { /* special cases: -1 or 0 */ luaL_argcheck(L, d != 0, 2, "zero"); lua_pushinteger(L, 0); /* avoid overflow with 0x80000... / -1 */ } else lua_pushinteger(L, lua_tointeger(L, 1) % d); } else lua_pushnumber(L, l_mathop(fmod)(luaL_checknumber(L, 1), luaL_checknumber(L, 2))); return 1; } /* ** next function does not use 'modf', avoiding problems with 'double*' ** (which is not compatible with 'float*') when lua_Number is not ** 'double'. */ static int math_modf (lua_State *L) { if (lua_isinteger(L ,1)) { lua_settop(L, 1); /* number is its own integer part */ lua_pushnumber(L, 0); /* no fractional part */ } else { lua_Number n = luaL_checknumber(L, 1); /* integer part (rounds toward zero) */ lua_Number ip = (n < 0) ? l_mathop(ceil)(n) : l_mathop(floor)(n); pushnumint(L, ip); /* fractional part (test needed for inf/-inf) */ lua_pushnumber(L, (n == ip) ? l_mathop(0.0) : (n - ip)); } return 2; } static int math_sqrt (lua_State *L) { lua_pushnumber(L, l_mathop(sqrt)(luaL_checknumber(L, 1))); return 1; } static int math_ult (lua_State *L) { lua_Integer a = luaL_checkinteger(L, 1); lua_Integer b = luaL_checkinteger(L, 2); lua_pushboolean(L, (lua_Unsigned)a < (lua_Unsigned)b); return 1; } static int math_log (lua_State *L) { lua_Number x = luaL_checknumber(L, 1); lua_Number res; if (lua_isnoneornil(L, 2)) res = l_mathop(log)(x); else { lua_Number base = luaL_checknumber(L, 2); #if !defined(LUA_USE_C89) if (base == l_mathop(2.0)) res = l_mathop(log2)(x); else #endif if (base == l_mathop(10.0)) res = l_mathop(log10)(x); else res = l_mathop(log)(x)/l_mathop(log)(base); } lua_pushnumber(L, res); return 1; } static int math_exp (lua_State *L) { lua_pushnumber(L, l_mathop(exp)(luaL_checknumber(L, 1))); return 1; } static int math_deg (lua_State *L) { lua_pushnumber(L, luaL_checknumber(L, 1) * (l_mathop(180.0) / PI)); return 1; } static int math_rad (lua_State *L) { lua_pushnumber(L, luaL_checknumber(L, 1) * (PI / l_mathop(180.0))); return 1; } static int math_min (lua_State *L) { int n = lua_gettop(L); /* number of arguments */ int imin = 1; /* index of current minimum value */ int i; luaL_argcheck(L, n >= 1, 1, "value expected"); for (i = 2; i <= n; i++) { if (lua_compare(L, i, imin, LUA_OPLT)) imin = i; } lua_pushvalue(L, imin); return 1; } static int math_max (lua_State *L) { int n = lua_gettop(L); /* number of arguments */ int imax = 1; /* index of current maximum value */ int i; luaL_argcheck(L, n >= 1, 1, "value expected"); for (i = 2; i <= n; i++) { if (lua_compare(L, imax, i, LUA_OPLT)) imax = i; } lua_pushvalue(L, imax); return 1; } /* ** This function uses 'double' (instead of 'lua_Number') to ensure that ** all bits from 'l_rand' can be represented, and that 'RANDMAX + 1.0' ** will keep full precision (ensuring that 'r' is always less than 1.0.) */ static int math_random (lua_State *L) { lua_Integer low, up; double r = (double)l_rand() * (1.0 / ((double)L_RANDMAX + 1.0)); switch (lua_gettop(L)) { /* check number of arguments */ case 0: { /* no arguments */ lua_pushnumber(L, (lua_Number)r); /* Number between 0 and 1 */ return 1; } case 1: { /* only upper limit */ low = 1; up = luaL_checkinteger(L, 1); break; } case 2: { /* lower and upper limits */ low = luaL_checkinteger(L, 1); up = luaL_checkinteger(L, 2); break; } default: return luaL_error(L, "wrong number of arguments"); } /* random integer in the interval [low, up] */ luaL_argcheck(L, low <= up, 1, "interval is empty"); luaL_argcheck(L, low >= 0 || up <= LUA_MAXINTEGER + low, 1, "interval too large"); r *= (double)(up - low) + 1.0; lua_pushinteger(L, (lua_Integer)r + low); return 1; } static int math_randomseed (lua_State *L) { l_srand((unsigned int)(lua_Integer)luaL_checknumber(L, 1)); (void)l_rand(); /* discard first value to avoid undesirable correlations */ return 0; } static int math_type (lua_State *L) { if (lua_type(L, 1) == LUA_TNUMBER) { if (lua_isinteger(L, 1)) lua_pushliteral(L, "integer"); else lua_pushliteral(L, "float"); } else { luaL_checkany(L, 1); lua_pushnil(L); } return 1; } /* ** {================================================================== ** Deprecated functions (for compatibility only) ** =================================================================== */ #if defined(LUA_COMPAT_MATHLIB) static int math_cosh (lua_State *L) { lua_pushnumber(L, l_mathop(cosh)(luaL_checknumber(L, 1))); return 1; } static int math_sinh (lua_State *L) { lua_pushnumber(L, l_mathop(sinh)(luaL_checknumber(L, 1))); return 1; } static int math_tanh (lua_State *L) { lua_pushnumber(L, l_mathop(tanh)(luaL_checknumber(L, 1))); return 1; } static int math_pow (lua_State *L) { lua_Number x = luaL_checknumber(L, 1); lua_Number y = luaL_checknumber(L, 2); lua_pushnumber(L, l_mathop(pow)(x, y)); return 1; } static int math_frexp (lua_State *L) { int e; lua_pushnumber(L, l_mathop(frexp)(luaL_checknumber(L, 1), &e)); lua_pushinteger(L, e); return 2; } static int math_ldexp (lua_State *L) { lua_Number x = luaL_checknumber(L, 1); int ep = (int)luaL_checkinteger(L, 2); lua_pushnumber(L, l_mathop(ldexp)(x, ep)); return 1; } static int math_log10 (lua_State *L) { lua_pushnumber(L, l_mathop(log10)(luaL_checknumber(L, 1))); return 1; } #endif /* }================================================================== */ static const luaL_Reg mathlib[] = { {"abs", math_abs}, {"acos", math_acos}, {"asin", math_asin}, {"atan", math_atan}, {"ceil", math_ceil}, {"cos", math_cos}, {"deg", math_deg}, {"exp", math_exp}, {"tointeger", math_toint}, {"floor", math_floor}, {"fmod", math_fmod}, {"ult", math_ult}, {"log", math_log}, {"max", math_max}, {"min", math_min}, {"modf", math_modf}, {"rad", math_rad}, {"random", math_random}, {"randomseed", math_randomseed}, {"sin", math_sin}, {"sqrt", math_sqrt}, {"tan", math_tan}, {"type", math_type}, #if defined(LUA_COMPAT_MATHLIB) {"atan2", math_atan}, {"cosh", math_cosh}, {"sinh", math_sinh}, {"tanh", math_tanh}, {"pow", math_pow}, {"frexp", math_frexp}, {"ldexp", math_ldexp}, {"log10", math_log10}, #endif /* placeholders */ {"pi", NULL}, {"huge", NULL}, {"maxinteger", NULL}, {"mininteger", NULL}, {NULL, NULL} }; /* ** Open math library */ LUAMOD_API int luaopen_math (lua_State *L) { luaL_newlib(L, mathlib); lua_pushnumber(L, PI); lua_setfield(L, -2, "pi"); lua_pushnumber(L, (lua_Number)HUGE_VAL); lua_setfield(L, -2, "huge"); lua_pushinteger(L, LUA_MAXINTEGER); lua_setfield(L, -2, "maxinteger"); lua_pushinteger(L, LUA_MININTEGER); lua_setfield(L, -2, "mininteger"); return 1; } ================================================ FILE: Tests/ApiExplorer/lua/src/lmem.c ================================================ /* ** $Id: lmem.c,v 1.91.1.1 2017/04/19 17:20:42 roberto Exp $ ** Interface to Memory Manager ** See Copyright Notice in lua.h */ #define lmem_c #define LUA_CORE #include "lprefix.h" #include #include "lua.h" #include "ldebug.h" #include "ldo.h" #include "lgc.h" #include "lmem.h" #include "lobject.h" #include "lstate.h" /* ** About the realloc function: ** void * frealloc (void *ud, void *ptr, size_t osize, size_t nsize); ** ('osize' is the old size, 'nsize' is the new size) ** ** * frealloc(ud, NULL, x, s) creates a new block of size 's' (no ** matter 'x'). ** ** * frealloc(ud, p, x, 0) frees the block 'p' ** (in this specific case, frealloc must return NULL); ** particularly, frealloc(ud, NULL, 0, 0) does nothing ** (which is equivalent to free(NULL) in ISO C) ** ** frealloc returns NULL if it cannot create or reallocate the area ** (any reallocation to an equal or smaller size cannot fail!) */ #define MINSIZEARRAY 4 void *luaM_growaux_ (lua_State *L, void *block, int *size, size_t size_elems, int limit, const char *what) { void *newblock; int newsize; if (*size >= limit/2) { /* cannot double it? */ if (*size >= limit) /* cannot grow even a little? */ luaG_runerror(L, "too many %s (limit is %d)", what, limit); newsize = limit; /* still have at least one free place */ } else { newsize = (*size)*2; if (newsize < MINSIZEARRAY) newsize = MINSIZEARRAY; /* minimum size */ } newblock = luaM_reallocv(L, block, *size, newsize, size_elems); *size = newsize; /* update only when everything else is OK */ return newblock; } l_noret luaM_toobig (lua_State *L) { luaG_runerror(L, "memory allocation error: block too big"); } /* ** generic allocation routine. */ void *luaM_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize) { void *newblock; global_State *g = G(L); size_t realosize = (block) ? osize : 0; lua_assert((realosize == 0) == (block == NULL)); #if defined(HARDMEMTESTS) if (nsize > realosize && g->gcrunning) luaC_fullgc(L, 1); /* force a GC whenever possible */ #endif newblock = (*g->frealloc)(g->ud, block, osize, nsize); if (newblock == NULL && nsize > 0) { lua_assert(nsize > realosize); /* cannot fail when shrinking a block */ if (g->version) { /* is state fully built? */ luaC_fullgc(L, 1); /* try to free some memory... */ newblock = (*g->frealloc)(g->ud, block, osize, nsize); /* try again */ } if (newblock == NULL) luaD_throw(L, LUA_ERRMEM); } lua_assert((nsize == 0) == (newblock == NULL)); g->GCdebt = (g->GCdebt + nsize) - realosize; return newblock; } ================================================ FILE: Tests/ApiExplorer/lua/src/lmem.h ================================================ /* ** $Id: lmem.h,v 1.43.1.1 2017/04/19 17:20:42 roberto Exp $ ** Interface to Memory Manager ** See Copyright Notice in lua.h */ #ifndef lmem_h #define lmem_h #include #include "llimits.h" #include "lua.h" /* ** This macro reallocs a vector 'b' from 'on' to 'n' elements, where ** each element has size 'e'. In case of arithmetic overflow of the ** product 'n'*'e', it raises an error (calling 'luaM_toobig'). Because ** 'e' is always constant, it avoids the runtime division MAX_SIZET/(e). ** ** (The macro is somewhat complex to avoid warnings: The 'sizeof' ** comparison avoids a runtime comparison when overflow cannot occur. ** The compiler should be able to optimize the real test by itself, but ** when it does it, it may give a warning about "comparison is always ** false due to limited range of data type"; the +1 tricks the compiler, ** avoiding this warning but also this optimization.) */ #define luaM_reallocv(L,b,on,n,e) \ (((sizeof(n) >= sizeof(size_t) && cast(size_t, (n)) + 1 > MAX_SIZET/(e)) \ ? luaM_toobig(L) : cast_void(0)) , \ luaM_realloc_(L, (b), (on)*(e), (n)*(e))) /* ** Arrays of chars do not need any test */ #define luaM_reallocvchar(L,b,on,n) \ cast(char *, luaM_realloc_(L, (b), (on)*sizeof(char), (n)*sizeof(char))) #define luaM_freemem(L, b, s) luaM_realloc_(L, (b), (s), 0) #define luaM_free(L, b) luaM_realloc_(L, (b), sizeof(*(b)), 0) #define luaM_freearray(L, b, n) luaM_realloc_(L, (b), (n)*sizeof(*(b)), 0) #define luaM_malloc(L,s) luaM_realloc_(L, NULL, 0, (s)) #define luaM_new(L,t) cast(t *, luaM_malloc(L, sizeof(t))) #define luaM_newvector(L,n,t) \ cast(t *, luaM_reallocv(L, NULL, 0, n, sizeof(t))) #define luaM_newobject(L,tag,s) luaM_realloc_(L, NULL, tag, (s)) #define luaM_growvector(L,v,nelems,size,t,limit,e) \ if ((nelems)+1 > (size)) \ ((v)=cast(t *, luaM_growaux_(L,v,&(size),sizeof(t),limit,e))) #define luaM_reallocvector(L, v,oldn,n,t) \ ((v)=cast(t *, luaM_reallocv(L, v, oldn, n, sizeof(t)))) LUAI_FUNC l_noret luaM_toobig (lua_State *L); /* not to be called directly */ LUAI_FUNC void *luaM_realloc_ (lua_State *L, void *block, size_t oldsize, size_t size); LUAI_FUNC void *luaM_growaux_ (lua_State *L, void *block, int *size, size_t size_elem, int limit, const char *what); #endif ================================================ FILE: Tests/ApiExplorer/lua/src/loadlib.c ================================================ /* ** $Id: loadlib.c,v 1.130.1.1 2017/04/19 17:20:42 roberto Exp $ ** Dynamic library loader for Lua ** See Copyright Notice in lua.h ** ** This module contains an implementation of loadlib for Unix systems ** that have dlfcn, an implementation for Windows, and a stub for other ** systems. */ #define loadlib_c #define LUA_LIB #include "lprefix.h" #include #include #include #include "lua.h" #include "lauxlib.h" #include "lualib.h" /* ** LUA_IGMARK is a mark to ignore all before it when building the ** luaopen_ function name. */ #if !defined (LUA_IGMARK) #define LUA_IGMARK "-" #endif /* ** LUA_CSUBSEP is the character that replaces dots in submodule names ** when searching for a C loader. ** LUA_LSUBSEP is the character that replaces dots in submodule names ** when searching for a Lua loader. */ #if !defined(LUA_CSUBSEP) #define LUA_CSUBSEP LUA_DIRSEP #endif #if !defined(LUA_LSUBSEP) #define LUA_LSUBSEP LUA_DIRSEP #endif /* prefix for open functions in C libraries */ #define LUA_POF "luaopen_" /* separator for open functions in C libraries */ #define LUA_OFSEP "_" /* ** unique key for table in the registry that keeps handles ** for all loaded C libraries */ static const int CLIBS = 0; #define LIB_FAIL "open" #define setprogdir(L) ((void)0) /* ** system-dependent functions */ /* ** unload library 'lib' */ static void lsys_unloadlib (void *lib); /* ** load C library in file 'path'. If 'seeglb', load with all names in ** the library global. ** Returns the library; in case of error, returns NULL plus an ** error string in the stack. */ static void *lsys_load (lua_State *L, const char *path, int seeglb); /* ** Try to find a function named 'sym' in library 'lib'. ** Returns the function; in case of error, returns NULL plus an ** error string in the stack. */ static lua_CFunction lsys_sym (lua_State *L, void *lib, const char *sym); #if defined(LUA_USE_DLOPEN) /* { */ /* ** {======================================================================== ** This is an implementation of loadlib based on the dlfcn interface. ** The dlfcn interface is available in Linux, SunOS, Solaris, IRIX, FreeBSD, ** NetBSD, AIX 4.2, HPUX 11, and probably most other Unix flavors, at least ** as an emulation layer on top of native functions. ** ========================================================================= */ #include /* ** Macro to convert pointer-to-void* to pointer-to-function. This cast ** is undefined according to ISO C, but POSIX assumes that it works. ** (The '__extension__' in gnu compilers is only to avoid warnings.) */ #if defined(__GNUC__) #define cast_func(p) (__extension__ (lua_CFunction)(p)) #else #define cast_func(p) ((lua_CFunction)(p)) #endif static void lsys_unloadlib (void *lib) { dlclose(lib); } static void *lsys_load (lua_State *L, const char *path, int seeglb) { void *lib = dlopen(path, RTLD_NOW | (seeglb ? RTLD_GLOBAL : RTLD_LOCAL)); if (lib == NULL) lua_pushstring(L, dlerror()); return lib; } static lua_CFunction lsys_sym (lua_State *L, void *lib, const char *sym) { lua_CFunction f = cast_func(dlsym(lib, sym)); if (f == NULL) lua_pushstring(L, dlerror()); return f; } /* }====================================================== */ #elif defined(LUA_DL_DLL) /* }{ */ /* ** {====================================================================== ** This is an implementation of loadlib for Windows using native functions. ** ======================================================================= */ #include /* ** optional flags for LoadLibraryEx */ #if !defined(LUA_LLE_FLAGS) #define LUA_LLE_FLAGS 0 #endif #undef setprogdir /* ** Replace in the path (on the top of the stack) any occurrence ** of LUA_EXEC_DIR with the executable's path. */ static void setprogdir (lua_State *L) { char buff[MAX_PATH + 1]; char *lb; DWORD nsize = sizeof(buff)/sizeof(char); DWORD n = GetModuleFileNameA(NULL, buff, nsize); /* get exec. name */ if (n == 0 || n == nsize || (lb = strrchr(buff, '\\')) == NULL) luaL_error(L, "unable to get ModuleFileName"); else { *lb = '\0'; /* cut name on the last '\\' to get the path */ luaL_gsub(L, lua_tostring(L, -1), LUA_EXEC_DIR, buff); lua_remove(L, -2); /* remove original string */ } } static void pusherror (lua_State *L) { int error = GetLastError(); #if HC_PLATFORM != HC_PLATFORM_XDK char buffer[128]; if (FormatMessageA(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM, NULL, error, 0, buffer, sizeof(buffer)/sizeof(char), NULL)) lua_pushstring(L, buffer); else #endif lua_pushfstring(L, "system error %d\n", error); } static void lsys_unloadlib (void *lib) { FreeLibrary((HMODULE)lib); } static void *lsys_load (lua_State *L, const char *path, int seeglb) { #if !APIEXPLORER_UWP HMODULE lib = LoadLibraryExA(path, NULL, LUA_LLE_FLAGS); (void)(seeglb); /* not used: symbols are 'global' by default */ if (lib == NULL) pusherror(L); return lib; #else pusherror(L); return nullptr; #endif } static lua_CFunction lsys_sym (lua_State *L, void *lib, const char *sym) { lua_CFunction f = (lua_CFunction)GetProcAddress((HMODULE)lib, sym); if (f == NULL) pusherror(L); return f; } /* }====================================================== */ #else /* }{ */ /* ** {====================================================== ** Fallback for other systems ** ======================================================= */ #undef LIB_FAIL #define LIB_FAIL "absent" #define DLMSG "dynamic libraries not enabled; check your Lua installation" static void lsys_unloadlib (void *lib) { (void)(lib); /* not used */ } static void *lsys_load (lua_State *L, const char *path, int seeglb) { (void)(path); (void)(seeglb); /* not used */ lua_pushliteral(L, DLMSG); return NULL; } static lua_CFunction lsys_sym (lua_State *L, void *lib, const char *sym) { (void)(lib); (void)(sym); /* not used */ lua_pushliteral(L, DLMSG); return NULL; } /* }====================================================== */ #endif /* } */ /* ** {================================================================== ** Set Paths ** =================================================================== */ /* ** LUA_PATH_VAR and LUA_CPATH_VAR are the names of the environment ** variables that Lua check to set its paths. */ #if !defined(LUA_PATH_VAR) #define LUA_PATH_VAR "LUA_PATH" #endif #if !defined(LUA_CPATH_VAR) #define LUA_CPATH_VAR "LUA_CPATH" #endif #define AUXMARK "\1" /* auxiliary mark */ /* ** return registry.LUA_NOENV as a boolean */ static int noenv (lua_State *L) { int b; lua_getfield(L, LUA_REGISTRYINDEX, "LUA_NOENV"); b = lua_toboolean(L, -1); lua_pop(L, 1); /* remove value */ return b; } /* ** Set a path */ static void setpath (lua_State *L, const char *fieldname, const char *envname, const char *dft) { const char *nver = lua_pushfstring(L, "%s%s", envname, LUA_VERSUFFIX); #if HC_PLATFORM != HC_PLATFORM_XDK const char *path = getenv(nver); /* use versioned name */ if (path == NULL) /* no environment variable? */ path = getenv(envname); /* try unversioned name */ #else UNREFERENCED_PARAMETER(nver); const char *path = NULL; #endif if (path == NULL || noenv(L)) /* no environment variable? */ lua_pushstring(L, dft); /* use default */ else { /* replace ";;" by ";AUXMARK;" and then AUXMARK by default path */ path = luaL_gsub(L, path, LUA_PATH_SEP LUA_PATH_SEP, LUA_PATH_SEP AUXMARK LUA_PATH_SEP); luaL_gsub(L, path, AUXMARK, dft); lua_remove(L, -2); /* remove result from 1st 'gsub' */ } setprogdir(L); lua_setfield(L, -3, fieldname); /* package[fieldname] = path value */ lua_pop(L, 1); /* pop versioned variable name */ } /* }================================================================== */ /* ** return registry.CLIBS[path] */ static void *checkclib (lua_State *L, const char *path) { void *plib; lua_rawgetp(L, LUA_REGISTRYINDEX, &CLIBS); lua_getfield(L, -1, path); plib = lua_touserdata(L, -1); /* plib = CLIBS[path] */ lua_pop(L, 2); /* pop CLIBS table and 'plib' */ return plib; } /* ** registry.CLIBS[path] = plib -- for queries ** registry.CLIBS[#CLIBS + 1] = plib -- also keep a list of all libraries */ static void addtoclib (lua_State *L, const char *path, void *plib) { lua_rawgetp(L, LUA_REGISTRYINDEX, &CLIBS); lua_pushlightuserdata(L, plib); lua_pushvalue(L, -1); lua_setfield(L, -3, path); /* CLIBS[path] = plib */ lua_rawseti(L, -2, luaL_len(L, -2) + 1); /* CLIBS[#CLIBS + 1] = plib */ lua_pop(L, 1); /* pop CLIBS table */ } /* ** __gc tag method for CLIBS table: calls 'lsys_unloadlib' for all lib ** handles in list CLIBS */ static int gctm (lua_State *L) { lua_Integer n = luaL_len(L, 1); for (; n >= 1; n--) { /* for each handle, in reverse order */ lua_rawgeti(L, 1, n); /* get handle CLIBS[n] */ lsys_unloadlib(lua_touserdata(L, -1)); lua_pop(L, 1); /* pop handle */ } return 0; } /* error codes for 'lookforfunc' */ #define ERRLIB 1 #define ERRFUNC 2 /* ** Look for a C function named 'sym' in a dynamically loaded library ** 'path'. ** First, check whether the library is already loaded; if not, try ** to load it. ** Then, if 'sym' is '*', return true (as library has been loaded). ** Otherwise, look for symbol 'sym' in the library and push a ** C function with that symbol. ** Return 0 and 'true' or a function in the stack; in case of ** errors, return an error code and an error message in the stack. */ static int lookforfunc (lua_State *L, const char *path, const char *sym) { void *reg = checkclib(L, path); /* check loaded C libraries */ if (reg == NULL) { /* must load library? */ reg = lsys_load(L, path, *sym == '*'); /* global symbols if 'sym'=='*' */ if (reg == NULL) return ERRLIB; /* unable to load library */ addtoclib(L, path, reg); } if (*sym == '*') { /* loading only library (no function)? */ lua_pushboolean(L, 1); /* return 'true' */ return 0; /* no errors */ } else { lua_CFunction f = lsys_sym(L, reg, sym); if (f == NULL) return ERRFUNC; /* unable to find function */ lua_pushcfunction(L, f); /* else create new function */ return 0; /* no errors */ } } static int ll_loadlib (lua_State *L) { const char *path = luaL_checkstring(L, 1); const char *init = luaL_checkstring(L, 2); int stat = lookforfunc(L, path, init); if (stat == 0) /* no errors? */ return 1; /* return the loaded function */ else { /* error; error message is on stack top */ lua_pushnil(L); lua_insert(L, -2); lua_pushstring(L, (stat == ERRLIB) ? LIB_FAIL : "init"); return 3; /* return nil, error message, and where */ } } /* ** {====================================================== ** 'require' function ** ======================================================= */ static int readable (const char *filename) { FILE *f = fopen(filename, "r"); /* try to open file */ if (f == NULL) return 0; /* open failed */ fclose(f); return 1; } static const char *pushnexttemplate (lua_State *L, const char *path) { const char *l; while (*path == *LUA_PATH_SEP) path++; /* skip separators */ if (*path == '\0') return NULL; /* no more templates */ l = strchr(path, *LUA_PATH_SEP); /* find next separator */ if (l == NULL) l = path + strlen(path); lua_pushlstring(L, path, l - path); /* template */ return l; } static const char *searchpath (lua_State *L, const char *name, const char *path, const char *sep, const char *dirsep) { luaL_Buffer msg; /* to build error message */ luaL_buffinit(L, &msg); if (*sep != '\0') /* non-empty separator? */ name = luaL_gsub(L, name, sep, dirsep); /* replace it by 'dirsep' */ while ((path = pushnexttemplate(L, path)) != NULL) { const char *filename = luaL_gsub(L, lua_tostring(L, -1), LUA_PATH_MARK, name); lua_remove(L, -2); /* remove path template */ if (readable(filename)) /* does file exist and is readable? */ return filename; /* return that file name */ lua_pushfstring(L, "\n\tno file '%s'", filename); lua_remove(L, -2); /* remove file name */ luaL_addvalue(&msg); /* concatenate error msg. entry */ } luaL_pushresult(&msg); /* create error message */ return NULL; /* not found */ } static int ll_searchpath (lua_State *L) { const char *f = searchpath(L, luaL_checkstring(L, 1), luaL_checkstring(L, 2), luaL_optstring(L, 3, "."), luaL_optstring(L, 4, LUA_DIRSEP)); if (f != NULL) return 1; else { /* error message is on top of the stack */ lua_pushnil(L); lua_insert(L, -2); return 2; /* return nil + error message */ } } static const char *findfile (lua_State *L, const char *name, const char *pname, const char *dirsep) { const char *path; lua_getfield(L, lua_upvalueindex(1), pname); path = lua_tostring(L, -1); if (path == NULL) luaL_error(L, "'package.%s' must be a string", pname); return searchpath(L, name, path, ".", dirsep); } static int checkload (lua_State *L, int stat, const char *filename) { if (stat) { /* module loaded successfully? */ lua_pushstring(L, filename); /* will be 2nd argument to module */ return 2; /* return open function and file name */ } else return luaL_error(L, "error loading module '%s' from file '%s':\n\t%s", lua_tostring(L, 1), filename, lua_tostring(L, -1)); } static int searcher_Lua (lua_State *L) { const char *filename; const char *name = luaL_checkstring(L, 1); filename = findfile(L, name, "path", LUA_LSUBSEP); if (filename == NULL) return 1; /* module not found in this path */ return checkload(L, (luaL_loadfile(L, filename) == LUA_OK), filename); } /* ** Try to find a load function for module 'modname' at file 'filename'. ** First, change '.' to '_' in 'modname'; then, if 'modname' has ** the form X-Y (that is, it has an "ignore mark"), build a function ** name "luaopen_X" and look for it. (For compatibility, if that ** fails, it also tries "luaopen_Y".) If there is no ignore mark, ** look for a function named "luaopen_modname". */ static int loadfunc (lua_State *L, const char *filename, const char *modname) { const char *openfunc; const char *mark; modname = luaL_gsub(L, modname, ".", LUA_OFSEP); mark = strchr(modname, *LUA_IGMARK); if (mark) { int stat; openfunc = lua_pushlstring(L, modname, mark - modname); openfunc = lua_pushfstring(L, LUA_POF"%s", openfunc); stat = lookforfunc(L, filename, openfunc); if (stat != ERRFUNC) return stat; modname = mark + 1; /* else go ahead and try old-style name */ } openfunc = lua_pushfstring(L, LUA_POF"%s", modname); return lookforfunc(L, filename, openfunc); } static int searcher_C (lua_State *L) { const char *name = luaL_checkstring(L, 1); const char *filename = findfile(L, name, "cpath", LUA_CSUBSEP); if (filename == NULL) return 1; /* module not found in this path */ return checkload(L, (loadfunc(L, filename, name) == 0), filename); } static int searcher_Croot (lua_State *L) { const char *filename; const char *name = luaL_checkstring(L, 1); const char *p = strchr(name, '.'); int stat; if (p == NULL) return 0; /* is root */ lua_pushlstring(L, name, p - name); filename = findfile(L, lua_tostring(L, -1), "cpath", LUA_CSUBSEP); if (filename == NULL) return 1; /* root not found */ if ((stat = loadfunc(L, filename, name)) != 0) { if (stat != ERRFUNC) return checkload(L, 0, filename); /* real error */ else { /* open function not found */ lua_pushfstring(L, "\n\tno module '%s' in file '%s'", name, filename); return 1; } } lua_pushstring(L, filename); /* will be 2nd argument to module */ return 2; } static int searcher_preload (lua_State *L) { const char *name = luaL_checkstring(L, 1); lua_getfield(L, LUA_REGISTRYINDEX, LUA_PRELOAD_TABLE); if (lua_getfield(L, -1, name) == LUA_TNIL) /* not found? */ lua_pushfstring(L, "\n\tno field package.preload['%s']", name); return 1; } static void findloader (lua_State *L, const char *name) { int i; luaL_Buffer msg; /* to build error message */ luaL_buffinit(L, &msg); /* push 'package.searchers' to index 3 in the stack */ if (lua_getfield(L, lua_upvalueindex(1), "searchers") != LUA_TTABLE) luaL_error(L, "'package.searchers' must be a table"); /* iterate over available searchers to find a loader */ for (i = 1; ; i++) { if (lua_rawgeti(L, 3, i) == LUA_TNIL) { /* no more searchers? */ lua_pop(L, 1); /* remove nil */ luaL_pushresult(&msg); /* create error message */ luaL_error(L, "module '%s' not found:%s", name, lua_tostring(L, -1)); } lua_pushstring(L, name); lua_call(L, 1, 2); /* call it */ if (lua_isfunction(L, -2)) /* did it find a loader? */ return; /* module loader found */ else if (lua_isstring(L, -2)) { /* searcher returned error message? */ lua_pop(L, 1); /* remove extra return */ luaL_addvalue(&msg); /* concatenate error message */ } else lua_pop(L, 2); /* remove both returns */ } } static int ll_require (lua_State *L) { const char *name = luaL_checkstring(L, 1); lua_settop(L, 1); /* LOADED table will be at index 2 */ lua_getfield(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE); lua_getfield(L, 2, name); /* LOADED[name] */ if (lua_toboolean(L, -1)) /* is it there? */ return 1; /* package is already loaded */ /* else must load package */ lua_pop(L, 1); /* remove 'getfield' result */ findloader(L, name); lua_pushstring(L, name); /* pass name as argument to module loader */ lua_insert(L, -2); /* name is 1st argument (before search data) */ lua_call(L, 2, 1); /* run loader to load module */ if (!lua_isnil(L, -1)) /* non-nil return? */ lua_setfield(L, 2, name); /* LOADED[name] = returned value */ if (lua_getfield(L, 2, name) == LUA_TNIL) { /* module set no value? */ lua_pushboolean(L, 1); /* use true as result */ lua_pushvalue(L, -1); /* extra copy to be returned */ lua_setfield(L, 2, name); /* LOADED[name] = true */ } return 1; } /* }====================================================== */ /* ** {====================================================== ** 'module' function ** ======================================================= */ #if defined(LUA_COMPAT_MODULE) /* ** changes the environment variable of calling function */ static void set_env (lua_State *L) { lua_Debug ar; if (lua_getstack(L, 1, &ar) == 0 || lua_getinfo(L, "f", &ar) == 0 || /* get calling function */ lua_iscfunction(L, -1)) luaL_error(L, "'module' not called from a Lua function"); lua_pushvalue(L, -2); /* copy new environment table to top */ lua_setupvalue(L, -2, 1); lua_pop(L, 1); /* remove function */ } static void dooptions (lua_State *L, int n) { int i; for (i = 2; i <= n; i++) { if (lua_isfunction(L, i)) { /* avoid 'calling' extra info. */ lua_pushvalue(L, i); /* get option (a function) */ lua_pushvalue(L, -2); /* module */ lua_call(L, 1, 0); } } } static void modinit (lua_State *L, const char *modname) { const char *dot; lua_pushvalue(L, -1); lua_setfield(L, -2, "_M"); /* module._M = module */ lua_pushstring(L, modname); lua_setfield(L, -2, "_NAME"); dot = strrchr(modname, '.'); /* look for last dot in module name */ if (dot == NULL) dot = modname; else dot++; /* set _PACKAGE as package name (full module name minus last part) */ lua_pushlstring(L, modname, dot - modname); lua_setfield(L, -2, "_PACKAGE"); } static int ll_module (lua_State *L) { const char *modname = luaL_checkstring(L, 1); int lastarg = lua_gettop(L); /* last parameter */ luaL_pushmodule(L, modname, 1); /* get/create module table */ /* check whether table already has a _NAME field */ if (lua_getfield(L, -1, "_NAME") != LUA_TNIL) lua_pop(L, 1); /* table is an initialized module */ else { /* no; initialize it */ lua_pop(L, 1); modinit(L, modname); } lua_pushvalue(L, -1); set_env(L); dooptions(L, lastarg); return 1; } static int ll_seeall (lua_State *L) { luaL_checktype(L, 1, LUA_TTABLE); if (!lua_getmetatable(L, 1)) { lua_createtable(L, 0, 1); /* create new metatable */ lua_pushvalue(L, -1); lua_setmetatable(L, 1); } lua_pushglobaltable(L); lua_setfield(L, -2, "__index"); /* mt.__index = _G */ return 0; } #endif /* }====================================================== */ static const luaL_Reg pk_funcs[] = { {"loadlib", ll_loadlib}, {"searchpath", ll_searchpath}, #if defined(LUA_COMPAT_MODULE) {"seeall", ll_seeall}, #endif /* placeholders */ {"preload", NULL}, {"cpath", NULL}, {"path", NULL}, {"searchers", NULL}, {"loaded", NULL}, {NULL, NULL} }; static const luaL_Reg ll_funcs[] = { #if defined(LUA_COMPAT_MODULE) {"module", ll_module}, #endif {"require", ll_require}, {NULL, NULL} }; static void createsearcherstable (lua_State *L) { static const lua_CFunction searchers[] = {searcher_preload, searcher_Lua, searcher_C, searcher_Croot, NULL}; int i; /* create 'searchers' table */ lua_createtable(L, sizeof(searchers)/sizeof(searchers[0]) - 1, 0); /* fill it with predefined searchers */ for (i=0; searchers[i] != NULL; i++) { lua_pushvalue(L, -2); /* set 'package' as upvalue for all searchers */ lua_pushcclosure(L, searchers[i], 1); lua_rawseti(L, -2, i+1); } #if defined(LUA_COMPAT_LOADERS) lua_pushvalue(L, -1); /* make a copy of 'searchers' table */ lua_setfield(L, -3, "loaders"); /* put it in field 'loaders' */ #endif lua_setfield(L, -2, "searchers"); /* put it in field 'searchers' */ } /* ** create table CLIBS to keep track of loaded C libraries, ** setting a finalizer to close all libraries when closing state. */ static void createclibstable (lua_State *L) { lua_newtable(L); /* create CLIBS table */ lua_createtable(L, 0, 1); /* create metatable for CLIBS */ lua_pushcfunction(L, gctm); lua_setfield(L, -2, "__gc"); /* set finalizer for CLIBS table */ lua_setmetatable(L, -2); lua_rawsetp(L, LUA_REGISTRYINDEX, &CLIBS); /* set CLIBS table in registry */ } LUAMOD_API int luaopen_package (lua_State *L) { createclibstable(L); luaL_newlib(L, pk_funcs); /* create 'package' table */ createsearcherstable(L); /* set paths */ setpath(L, "path", LUA_PATH_VAR, LUA_PATH_DEFAULT); setpath(L, "cpath", LUA_CPATH_VAR, LUA_CPATH_DEFAULT); /* store config information */ lua_pushliteral(L, LUA_DIRSEP "\n" LUA_PATH_SEP "\n" LUA_PATH_MARK "\n" LUA_EXEC_DIR "\n" LUA_IGMARK "\n"); lua_setfield(L, -2, "config"); /* set field 'loaded' */ luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE); lua_setfield(L, -2, "loaded"); /* set field 'preload' */ luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_PRELOAD_TABLE); lua_setfield(L, -2, "preload"); lua_pushglobaltable(L); lua_pushvalue(L, -2); /* set 'package' as upvalue for next lib */ luaL_setfuncs(L, ll_funcs, 1); /* open lib into global table */ lua_pop(L, 1); /* pop global table */ return 1; /* return 'package' table */ } ================================================ FILE: Tests/ApiExplorer/lua/src/lobject.c ================================================ /* ** $Id: lobject.c,v 2.113.1.1 2017/04/19 17:29:57 roberto Exp $ ** Some generic functions over Lua objects ** See Copyright Notice in lua.h */ #define lobject_c #define LUA_CORE #include "lprefix.h" #include #include #include #include #include #include #include "lua.h" #include "lctype.h" #include "ldebug.h" #include "ldo.h" #include "lmem.h" #include "lobject.h" #include "lstate.h" #include "lstring.h" #include "lvm.h" LUAI_DDEF const TValue luaO_nilobject_ = {NILCONSTANT}; /* ** converts an integer to a "floating point byte", represented as ** (eeeeexxx), where the real value is (1xxx) * 2^(eeeee - 1) if ** eeeee != 0 and (xxx) otherwise. */ int luaO_int2fb (unsigned int x) { int e = 0; /* exponent */ if (x < 8) return x; while (x >= (8 << 4)) { /* coarse steps */ x = (x + 0xf) >> 4; /* x = ceil(x / 16) */ e += 4; } while (x >= (8 << 1)) { /* fine steps */ x = (x + 1) >> 1; /* x = ceil(x / 2) */ e++; } return ((e+1) << 3) | (cast_int(x) - 8); } /* converts back */ int luaO_fb2int (int x) { return (x < 8) ? x : ((x & 7) + 8) << ((x >> 3) - 1); } /* ** Computes ceil(log2(x)) */ int luaO_ceillog2 (unsigned int x) { static const lu_byte log_2[256] = { /* log_2[i] = ceil(log2(i - 1)) */ 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 }; int l = 0; x--; while (x >= 256) { l += 8; x >>= 8; } return l + log_2[x]; } static lua_Integer intarith (lua_State *L, int op, lua_Integer v1, lua_Integer v2) { switch (op) { case LUA_OPADD: return intop(+, v1, v2); case LUA_OPSUB:return intop(-, v1, v2); case LUA_OPMUL:return intop(*, v1, v2); case LUA_OPMOD: return luaV_mod(L, v1, v2); case LUA_OPIDIV: return luaV_div(L, v1, v2); case LUA_OPBAND: return intop(&, v1, v2); case LUA_OPBOR: return intop(|, v1, v2); case LUA_OPBXOR: return intop(^, v1, v2); case LUA_OPSHL: return luaV_shiftl(v1, v2); case LUA_OPSHR: return luaV_shiftl(v1, -v2); case LUA_OPUNM: return intop(-, 0, v1); case LUA_OPBNOT: return intop(^, ~l_castS2U(0), v1); default: lua_assert(0); return 0; } } static lua_Number numarith (lua_State *L, int op, lua_Number v1, lua_Number v2) { switch (op) { case LUA_OPADD: return luai_numadd(L, v1, v2); case LUA_OPSUB: return luai_numsub(L, v1, v2); case LUA_OPMUL: return luai_nummul(L, v1, v2); case LUA_OPDIV: return luai_numdiv(L, v1, v2); case LUA_OPPOW: return luai_numpow(L, v1, v2); case LUA_OPIDIV: return luai_numidiv(L, v1, v2); case LUA_OPUNM: return luai_numunm(L, v1); case LUA_OPMOD: { lua_Number m; luai_nummod(L, v1, v2, m); return m; } default: lua_assert(0); return 0; } } void luaO_arith (lua_State *L, int op, const TValue *p1, const TValue *p2, TValue *res) { switch (op) { case LUA_OPBAND: case LUA_OPBOR: case LUA_OPBXOR: case LUA_OPSHL: case LUA_OPSHR: case LUA_OPBNOT: { /* operate only on integers */ lua_Integer i1; lua_Integer i2; if (tointeger(p1, &i1) && tointeger(p2, &i2)) { setivalue(res, intarith(L, op, i1, i2)); return; } else break; /* go to the end */ } case LUA_OPDIV: case LUA_OPPOW: { /* operate only on floats */ lua_Number n1; lua_Number n2; if (tonumber(p1, &n1) && tonumber(p2, &n2)) { setfltvalue(res, numarith(L, op, n1, n2)); return; } else break; /* go to the end */ } default: { /* other operations */ lua_Number n1; lua_Number n2; if (ttisinteger(p1) && ttisinteger(p2)) { setivalue(res, intarith(L, op, ivalue(p1), ivalue(p2))); return; } else if (tonumber(p1, &n1) && tonumber(p2, &n2)) { setfltvalue(res, numarith(L, op, n1, n2)); return; } else break; /* go to the end */ } } /* could not perform raw operation; try metamethod */ lua_assert(L != NULL); /* should not fail when folding (compile time) */ luaT_trybinTM(L, p1, p2, res, cast(TMS, (op - LUA_OPADD) + TM_ADD)); } int luaO_hexavalue (int c) { if (lisdigit(c)) return c - '0'; else return (ltolower(c) - 'a') + 10; } static int isneg (const char **s) { if (**s == '-') { (*s)++; return 1; } else if (**s == '+') (*s)++; return 0; } /* ** {================================================================== ** Lua's implementation for 'lua_strx2number' ** =================================================================== */ #if !defined(lua_strx2number) /* maximum number of significant digits to read (to avoid overflows even with single floats) */ #define MAXSIGDIG 30 /* ** convert an hexadecimal numeric string to a number, following ** C99 specification for 'strtod' */ static lua_Number lua_strx2number (const char *s, char **endptr) { int dot = lua_getlocaledecpoint(); lua_Number r = 0.0; /* result (accumulator) */ int sigdig = 0; /* number of significant digits */ int nosigdig = 0; /* number of non-significant digits */ int e = 0; /* exponent correction */ int neg; /* 1 if number is negative */ int hasdot = 0; /* true after seen a dot */ *endptr = cast(char *, s); /* nothing is valid yet */ while (lisspace(cast_uchar(*s))) s++; /* skip initial spaces */ neg = isneg(&s); /* check signal */ if (!(*s == '0' && (*(s + 1) == 'x' || *(s + 1) == 'X'))) /* check '0x' */ return 0.0; /* invalid format (no '0x') */ for (s += 2; ; s++) { /* skip '0x' and read numeral */ if (*s == dot) { if (hasdot) break; /* second dot? stop loop */ else hasdot = 1; } else if (lisxdigit(cast_uchar(*s))) { if (sigdig == 0 && *s == '0') /* non-significant digit (zero)? */ nosigdig++; else if (++sigdig <= MAXSIGDIG) /* can read it without overflow? */ r = (r * cast_num(16.0)) + luaO_hexavalue(*s); else e++; /* too many digits; ignore, but still count for exponent */ if (hasdot) e--; /* decimal digit? correct exponent */ } else break; /* neither a dot nor a digit */ } if (nosigdig + sigdig == 0) /* no digits? */ return 0.0; /* invalid format */ *endptr = cast(char *, s); /* valid up to here */ e *= 4; /* each digit multiplies/divides value by 2^4 */ if (*s == 'p' || *s == 'P') { /* exponent part? */ int exp1 = 0; /* exponent value */ int neg1; /* exponent signal */ s++; /* skip 'p' */ neg1 = isneg(&s); /* signal */ if (!lisdigit(cast_uchar(*s))) return 0.0; /* invalid; must have at least one digit */ while (lisdigit(cast_uchar(*s))) /* read exponent */ exp1 = exp1 * 10 + *(s++) - '0'; if (neg1) exp1 = -exp1; e += exp1; *endptr = cast(char *, s); /* valid up to here */ } if (neg) r = -r; return l_mathop(ldexp)(r, e); } #endif /* }====================================================== */ /* maximum length of a numeral */ #if !defined (L_MAXLENNUM) #define L_MAXLENNUM 200 #endif static const char *l_str2dloc (const char *s, lua_Number *result, int mode) { char *endptr; *result = (mode == 'x') ? lua_strx2number(s, &endptr) /* try to convert */ : lua_str2number(s, &endptr); if (endptr == s) return NULL; /* nothing recognized? */ while (lisspace(cast_uchar(*endptr))) endptr++; /* skip trailing spaces */ return (*endptr == '\0') ? endptr : NULL; /* OK if no trailing characters */ } /* ** Convert string 's' to a Lua number (put in 'result'). Return NULL ** on fail or the address of the ending '\0' on success. ** 'pmode' points to (and 'mode' contains) special things in the string: ** - 'x'/'X' means an hexadecimal numeral ** - 'n'/'N' means 'inf' or 'nan' (which should be rejected) ** - '.' just optimizes the search for the common case (nothing special) ** This function accepts both the current locale or a dot as the radix ** mark. If the convertion fails, it may mean number has a dot but ** locale accepts something else. In that case, the code copies 's' ** to a buffer (because 's' is read-only), changes the dot to the ** current locale radix mark, and tries to convert again. */ static const char *l_str2d (const char *s, lua_Number *result) { const char *endptr; const char *pmode = strpbrk(s, ".xXnN"); int mode = pmode ? ltolower(cast_uchar(*pmode)) : 0; if (mode == 'n') /* reject 'inf' and 'nan' */ return NULL; endptr = l_str2dloc(s, result, mode); /* try to convert */ if (endptr == NULL) { /* failed? may be a different locale */ char buff[L_MAXLENNUM + 1]; const char *pdot = strchr(s, '.'); if (strlen(s) > L_MAXLENNUM || pdot == NULL) return NULL; /* string too long or no dot; fail */ strcpy(buff, s); /* copy string to buffer */ buff[pdot - s] = lua_getlocaledecpoint(); /* correct decimal point */ endptr = l_str2dloc(buff, result, mode); /* try again */ if (endptr != NULL) endptr = s + (endptr - buff); /* make relative to 's' */ } return endptr; } #define MAXBY10 cast(lua_Unsigned, LUA_MAXINTEGER / 10) #define MAXLASTD cast_int(LUA_MAXINTEGER % 10) static const char *l_str2int (const char *s, lua_Integer *result) { lua_Unsigned a = 0; int empty = 1; int neg; while (lisspace(cast_uchar(*s))) s++; /* skip initial spaces */ neg = isneg(&s); if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) { /* hex? */ s += 2; /* skip '0x' */ for (; lisxdigit(cast_uchar(*s)); s++) { a = a * 16 + luaO_hexavalue(*s); empty = 0; } } else { /* decimal */ for (; lisdigit(cast_uchar(*s)); s++) { int d = *s - '0'; if (a >= MAXBY10 && (a > MAXBY10 || d > MAXLASTD + neg)) /* overflow? */ return NULL; /* do not accept it (as integer) */ a = a * 10 + d; empty = 0; } } while (lisspace(cast_uchar(*s))) s++; /* skip trailing spaces */ if (empty || *s != '\0') return NULL; /* something wrong in the numeral */ else { *result = l_castU2S((neg) ? 0u - a : a); return s; } } size_t luaO_str2num (const char *s, TValue *o) { lua_Integer i; lua_Number n; const char *e; if ((e = l_str2int(s, &i)) != NULL) { /* try as an integer */ setivalue(o, i); } else if ((e = l_str2d(s, &n)) != NULL) { /* else try as a float */ setfltvalue(o, n); } else return 0; /* conversion failed */ return (e - s) + 1; /* success; return string size */ } int luaO_utf8esc (char *buff, unsigned long x) { int n = 1; /* number of bytes put in buffer (backwards) */ lua_assert(x <= 0x10FFFF); if (x < 0x80) /* ascii? */ buff[UTF8BUFFSZ - 1] = cast(char, x); else { /* need continuation bytes */ unsigned int mfb = 0x3f; /* maximum that fits in first byte */ do { /* add continuation bytes */ buff[UTF8BUFFSZ - (n++)] = cast(char, 0x80 | (x & 0x3f)); x >>= 6; /* remove added bits */ mfb >>= 1; /* now there is one less bit available in first byte */ } while (x > mfb); /* still needs continuation byte? */ buff[UTF8BUFFSZ - n] = cast(char, (~mfb << 1) | x); /* add first byte */ } return n; } /* maximum length of the conversion of a number to a string */ #define MAXNUMBER2STR 50 /* ** Convert a number object to a string */ void luaO_tostring (lua_State *L, StkId obj) { char buff[MAXNUMBER2STR]; size_t len; lua_assert(ttisnumber(obj)); if (ttisinteger(obj)) len = lua_integer2str(buff, sizeof(buff), ivalue(obj)); else { len = lua_number2str(buff, sizeof(buff), fltvalue(obj)); #if !defined(LUA_COMPAT_FLOATSTRING) if (buff[strspn(buff, "-0123456789")] == '\0') { /* looks like an int? */ buff[len++] = lua_getlocaledecpoint(); buff[len++] = '0'; /* adds '.0' to result */ } #endif } setsvalue2s(L, obj, luaS_newlstr(L, buff, len)); } static void pushstr (lua_State *L, const char *str, size_t l) { setsvalue2s(L, L->top, luaS_newlstr(L, str, l)); luaD_inctop(L); } /* ** this function handles only '%d', '%c', '%f', '%p', and '%s' conventional formats, plus Lua-specific '%I' and '%U' */ const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) { int n = 0; for (;;) { const char *e = strchr(fmt, '%'); if (e == NULL) break; pushstr(L, fmt, e - fmt); switch (*(e+1)) { case 's': { /* zero-terminated string */ const char *s = va_arg(argp, char *); if (s == NULL) s = "(null)"; pushstr(L, s, strlen(s)); break; } case 'c': { /* an 'int' as a character */ char buff = cast(char, va_arg(argp, int)); if (lisprint(cast_uchar(buff))) pushstr(L, &buff, 1); else /* non-printable character; print its code */ luaO_pushfstring(L, "<\\%d>", cast_uchar(buff)); break; } case 'd': { /* an 'int' */ setivalue(L->top, va_arg(argp, int)); goto top2str; } case 'I': { /* a 'lua_Integer' */ setivalue(L->top, cast(lua_Integer, va_arg(argp, l_uacInt))); goto top2str; } case 'f': { /* a 'lua_Number' */ setfltvalue(L->top, cast_num(va_arg(argp, l_uacNumber))); top2str: /* convert the top element to a string */ luaD_inctop(L); luaO_tostring(L, L->top - 1); break; } case 'p': { /* a pointer */ char buff[4*sizeof(void *) + 8]; /* should be enough space for a '%p' */ void *p = va_arg(argp, void *); int l = lua_pointer2str(buff, sizeof(buff), p); pushstr(L, buff, l); break; } case 'U': { /* an 'int' as a UTF-8 sequence */ char buff[UTF8BUFFSZ]; int l = luaO_utf8esc(buff, cast(long, va_arg(argp, long))); pushstr(L, buff + UTF8BUFFSZ - l, l); break; } case '%': { pushstr(L, "%", 1); break; } default: { luaG_runerror(L, "invalid option '%%%c' to 'lua_pushfstring'", *(e + 1)); } } n += 2; fmt = e+2; } luaD_checkstack(L, 1); pushstr(L, fmt, strlen(fmt)); if (n > 0) luaV_concat(L, n + 1); return svalue(L->top - 1); } const char *luaO_pushfstring (lua_State *L, const char *fmt, ...) { const char *msg; va_list argp; va_start(argp, fmt); msg = luaO_pushvfstring(L, fmt, argp); va_end(argp); return msg; } /* number of chars of a literal string without the ending \0 */ #define LL(x) (sizeof(x)/sizeof(char) - 1) #define RETS "..." #define PRE "[string \"" #define POS "\"]" #define addstr(a,b,l) ( memcpy(a,b,(l) * sizeof(char)), a += (l) ) void luaO_chunkid (char *out, const char *source, size_t bufflen) { size_t l = strlen(source); if (*source == '=') { /* 'literal' source */ if (l <= bufflen) /* small enough? */ memcpy(out, source + 1, l * sizeof(char)); else { /* truncate it */ addstr(out, source + 1, bufflen - 1); *out = '\0'; } } else if (*source == '@') { /* file name */ if (l <= bufflen) /* small enough? */ memcpy(out, source + 1, l * sizeof(char)); else { /* add '...' before rest of name */ addstr(out, RETS, LL(RETS)); bufflen -= LL(RETS); memcpy(out, source + 1 + l - bufflen, bufflen * sizeof(char)); } } else { /* string; format as [string "source"] */ const char *nl = strchr(source, '\n'); /* find first new line (if any) */ addstr(out, PRE, LL(PRE)); /* add prefix */ bufflen -= LL(PRE RETS POS) + 1; /* save space for prefix+suffix+'\0' */ if (l < bufflen && nl == NULL) { /* small one-line source? */ addstr(out, source, l); /* keep it */ } else { if (nl != NULL) l = nl - source; /* stop at first newline */ if (l > bufflen) l = bufflen; addstr(out, source, l); addstr(out, RETS, LL(RETS)); } memcpy(out, POS, (LL(POS) + 1) * sizeof(char)); } } ================================================ FILE: Tests/ApiExplorer/lua/src/lobject.h ================================================ /* ** $Id: lobject.h,v 2.117.1.1 2017/04/19 17:39:34 roberto Exp $ ** Type definitions for Lua objects ** See Copyright Notice in lua.h */ #ifndef lobject_h #define lobject_h #include #include "llimits.h" #include "lua.h" /* ** Extra tags for non-values */ #define LUA_TPROTO LUA_NUMTAGS /* function prototypes */ #define LUA_TDEADKEY (LUA_NUMTAGS+1) /* removed keys in tables */ /* ** number of all possible tags (including LUA_TNONE but excluding DEADKEY) */ #define LUA_TOTALTAGS (LUA_TPROTO + 2) /* ** tags for Tagged Values have the following use of bits: ** bits 0-3: actual tag (a LUA_T* value) ** bits 4-5: variant bits ** bit 6: whether value is collectable */ /* ** LUA_TFUNCTION variants: ** 0 - Lua function ** 1 - light C function ** 2 - regular C function (closure) */ /* Variant tags for functions */ #define LUA_TLCL (LUA_TFUNCTION | (0 << 4)) /* Lua closure */ #define LUA_TLCF (LUA_TFUNCTION | (1 << 4)) /* light C function */ #define LUA_TCCL (LUA_TFUNCTION | (2 << 4)) /* C closure */ /* Variant tags for strings */ #define LUA_TSHRSTR (LUA_TSTRING | (0 << 4)) /* short strings */ #define LUA_TLNGSTR (LUA_TSTRING | (1 << 4)) /* long strings */ /* Variant tags for numbers */ #define LUA_TNUMFLT (LUA_TNUMBER | (0 << 4)) /* float numbers */ #define LUA_TNUMINT (LUA_TNUMBER | (1 << 4)) /* integer numbers */ /* Bit mark for collectable types */ #define BIT_ISCOLLECTABLE (1 << 6) /* mark a tag as collectable */ #define ctb(t) ((t) | BIT_ISCOLLECTABLE) /* ** Common type for all collectable objects */ typedef struct GCObject GCObject; /* ** Common Header for all collectable objects (in macro form, to be ** included in other objects) */ #define CommonHeader GCObject *next; lu_byte tt; lu_byte marked /* ** Common type has only the common header */ struct GCObject { CommonHeader; }; /* ** Tagged Values. This is the basic representation of values in Lua, ** an actual value plus a tag with its type. */ /* ** Union of all Lua values */ typedef union Value { GCObject *gc; /* collectable objects */ void *p; /* light userdata */ int b; /* booleans */ lua_CFunction f; /* light C functions */ lua_Integer i; /* integer numbers */ lua_Number n; /* float numbers */ } Value; #define TValuefields Value value_; int tt_ typedef struct lua_TValue { TValuefields; } TValue; /* macro defining a nil value */ #define NILCONSTANT {NULL}, LUA_TNIL #define val_(o) ((o)->value_) /* raw type tag of a TValue */ #define rttype(o) ((o)->tt_) /* tag with no variants (bits 0-3) */ #define novariant(x) ((x) & 0x0F) /* type tag of a TValue (bits 0-3 for tags + variant bits 4-5) */ #define ttype(o) (rttype(o) & 0x3F) /* type tag of a TValue with no variants (bits 0-3) */ #define ttnov(o) (novariant(rttype(o))) /* Macros to test type */ #define checktag(o,t) (rttype(o) == (t)) #define checktype(o,t) (ttnov(o) == (t)) #define ttisnumber(o) checktype((o), LUA_TNUMBER) #define ttisfloat(o) checktag((o), LUA_TNUMFLT) #define ttisinteger(o) checktag((o), LUA_TNUMINT) #define ttisnil(o) checktag((o), LUA_TNIL) #define ttisboolean(o) checktag((o), LUA_TBOOLEAN) #define ttislightuserdata(o) checktag((o), LUA_TLIGHTUSERDATA) #define ttisstring(o) checktype((o), LUA_TSTRING) #define ttisshrstring(o) checktag((o), ctb(LUA_TSHRSTR)) #define ttislngstring(o) checktag((o), ctb(LUA_TLNGSTR)) #define ttistable(o) checktag((o), ctb(LUA_TTABLE)) #define ttisfunction(o) checktype(o, LUA_TFUNCTION) #define ttisclosure(o) ((rttype(o) & 0x1F) == LUA_TFUNCTION) #define ttisCclosure(o) checktag((o), ctb(LUA_TCCL)) #define ttisLclosure(o) checktag((o), ctb(LUA_TLCL)) #define ttislcf(o) checktag((o), LUA_TLCF) #define ttisfulluserdata(o) checktag((o), ctb(LUA_TUSERDATA)) #define ttisthread(o) checktag((o), ctb(LUA_TTHREAD)) #define ttisdeadkey(o) checktag((o), LUA_TDEADKEY) /* Macros to access values */ #define ivalue(o) check_exp(ttisinteger(o), val_(o).i) #define fltvalue(o) check_exp(ttisfloat(o), val_(o).n) #define nvalue(o) check_exp(ttisnumber(o), \ (ttisinteger(o) ? cast_num(ivalue(o)) : fltvalue(o))) #define gcvalue(o) check_exp(iscollectable(o), val_(o).gc) #define pvalue(o) check_exp(ttislightuserdata(o), val_(o).p) #define tsvalue(o) check_exp(ttisstring(o), gco2ts(val_(o).gc)) #define uvalue(o) check_exp(ttisfulluserdata(o), gco2u(val_(o).gc)) #define clvalue(o) check_exp(ttisclosure(o), gco2cl(val_(o).gc)) #define clLvalue(o) check_exp(ttisLclosure(o), gco2lcl(val_(o).gc)) #define clCvalue(o) check_exp(ttisCclosure(o), gco2ccl(val_(o).gc)) #define fvalue(o) check_exp(ttislcf(o), val_(o).f) #define hvalue(o) check_exp(ttistable(o), gco2t(val_(o).gc)) #define bvalue(o) check_exp(ttisboolean(o), val_(o).b) #define thvalue(o) check_exp(ttisthread(o), gco2th(val_(o).gc)) /* a dead value may get the 'gc' field, but cannot access its contents */ #define deadvalue(o) check_exp(ttisdeadkey(o), cast(void *, val_(o).gc)) #define l_isfalse(o) (ttisnil(o) || (ttisboolean(o) && bvalue(o) == 0)) #define iscollectable(o) (rttype(o) & BIT_ISCOLLECTABLE) /* Macros for internal tests */ #define righttt(obj) (ttype(obj) == gcvalue(obj)->tt) #define checkliveness(L,obj) \ lua_longassert(!iscollectable(obj) || \ (righttt(obj) && (L == NULL || !isdead(G(L),gcvalue(obj))))) /* Macros to set values */ #define settt_(o,t) ((o)->tt_=(t)) #define setfltvalue(obj,x) \ { TValue *io=(obj); val_(io).n=(x); settt_(io, LUA_TNUMFLT); } #define chgfltvalue(obj,x) \ { TValue *io=(obj); lua_assert(ttisfloat(io)); val_(io).n=(x); } #define setivalue(obj,x) \ { TValue *io=(obj); val_(io).i=(x); settt_(io, LUA_TNUMINT); } #define chgivalue(obj,x) \ { TValue *io=(obj); lua_assert(ttisinteger(io)); val_(io).i=(x); } #define setnilvalue(obj) settt_(obj, LUA_TNIL) #define setfvalue(obj,x) \ { TValue *io=(obj); val_(io).f=(x); settt_(io, LUA_TLCF); } #define setpvalue(obj,x) \ { TValue *io=(obj); val_(io).p=(x); settt_(io, LUA_TLIGHTUSERDATA); } #define setbvalue(obj,x) \ { TValue *io=(obj); val_(io).b=(x); settt_(io, LUA_TBOOLEAN); } #define setgcovalue(L,obj,x) \ { TValue *io = (obj); GCObject *i_g=(x); \ val_(io).gc = i_g; settt_(io, ctb(i_g->tt)); } #define setsvalue(L,obj,x) \ { TValue *io = (obj); TString *x_ = (x); \ val_(io).gc = obj2gco(x_); settt_(io, ctb(x_->tt)); \ checkliveness(L,io); } #define setuvalue(L,obj,x) \ { TValue *io = (obj); Udata *x_ = (x); \ val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_TUSERDATA)); \ checkliveness(L,io); } #define setthvalue(L,obj,x) \ { TValue *io = (obj); lua_State *x_ = (x); \ val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_TTHREAD)); \ checkliveness(L,io); } #define setclLvalue(L,obj,x) \ { TValue *io = (obj); LClosure *x_ = (x); \ val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_TLCL)); \ checkliveness(L,io); } #define setclCvalue(L,obj,x) \ { TValue *io = (obj); CClosure *x_ = (x); \ val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_TCCL)); \ checkliveness(L,io); } #define sethvalue(L,obj,x) \ { TValue *io = (obj); Table *x_ = (x); \ val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_TTABLE)); \ checkliveness(L,io); } #define setdeadvalue(obj) settt_(obj, LUA_TDEADKEY) #define setobj(L,obj1,obj2) \ { TValue *io1=(obj1); *io1 = *(obj2); \ (void)L; checkliveness(L,io1); } /* ** different types of assignments, according to destination */ /* from stack to (same) stack */ #define setobjs2s setobj /* to stack (not from same stack) */ #define setobj2s setobj #define setsvalue2s setsvalue #define sethvalue2s sethvalue #define setptvalue2s setptvalue /* from table to same table */ #define setobjt2t setobj /* to new object */ #define setobj2n setobj #define setsvalue2n setsvalue /* to table (define it as an expression to be used in macros) */ #define setobj2t(L,o1,o2) ((void)L, *(o1)=*(o2), checkliveness(L,(o1))) /* ** {====================================================== ** types and prototypes ** ======================================================= */ typedef TValue *StkId; /* index to stack elements */ /* ** Header for string value; string bytes follow the end of this structure ** (aligned according to 'UTString'; see next). */ typedef struct TString { CommonHeader; lu_byte extra; /* reserved words for short strings; "has hash" for longs */ lu_byte shrlen; /* length for short strings */ unsigned int hash; union { size_t lnglen; /* length for long strings */ struct TString *hnext; /* linked list for hash table */ } u; } TString; /* ** Ensures that address after this type is always fully aligned. */ typedef union UTString { L_Umaxalign dummy; /* ensures maximum alignment for strings */ TString tsv; } UTString; /* ** Get the actual string (array of bytes) from a 'TString'. ** (Access to 'extra' ensures that value is really a 'TString'.) */ #define getstr(ts) \ check_exp(sizeof((ts)->extra), cast(char *, (ts)) + sizeof(UTString)) /* get the actual string (array of bytes) from a Lua value */ #define svalue(o) getstr(tsvalue(o)) /* get string length from 'TString *s' */ #define tsslen(s) ((s)->tt == LUA_TSHRSTR ? (s)->shrlen : (s)->u.lnglen) /* get string length from 'TValue *o' */ #define vslen(o) tsslen(tsvalue(o)) /* ** Header for userdata; memory area follows the end of this structure ** (aligned according to 'UUdata'; see next). */ typedef struct Udata { CommonHeader; lu_byte ttuv_; /* user value's tag */ struct Table *metatable; size_t len; /* number of bytes */ union Value user_; /* user value */ } Udata; /* ** Ensures that address after this type is always fully aligned. */ typedef union UUdata { L_Umaxalign dummy; /* ensures maximum alignment for 'local' udata */ Udata uv; } UUdata; /* ** Get the address of memory block inside 'Udata'. ** (Access to 'ttuv_' ensures that value is really a 'Udata'.) */ #define getudatamem(u) \ check_exp(sizeof((u)->ttuv_), (cast(char*, (u)) + sizeof(UUdata))) #define setuservalue(L,u,o) \ { const TValue *io=(o); Udata *iu = (u); \ iu->user_ = io->value_; iu->ttuv_ = rttype(io); \ checkliveness(L,io); } #define getuservalue(L,u,o) \ { TValue *io=(o); const Udata *iu = (u); \ io->value_ = iu->user_; settt_(io, iu->ttuv_); \ checkliveness(L,io); } /* ** Description of an upvalue for function prototypes */ typedef struct Upvaldesc { TString *name; /* upvalue name (for debug information) */ lu_byte instack; /* whether it is in stack (register) */ lu_byte idx; /* index of upvalue (in stack or in outer function's list) */ } Upvaldesc; /* ** Description of a local variable for function prototypes ** (used for debug information) */ typedef struct LocVar { TString *varname; int startpc; /* first point where variable is active */ int endpc; /* first point where variable is dead */ } LocVar; /* ** Function Prototypes */ typedef struct Proto { CommonHeader; lu_byte numparams; /* number of fixed parameters */ lu_byte is_vararg; lu_byte maxstacksize; /* number of registers needed by this function */ int sizeupvalues; /* size of 'upvalues' */ int sizek; /* size of 'k' */ int sizecode; int sizelineinfo; int sizep; /* size of 'p' */ int sizelocvars; int linedefined; /* debug information */ int lastlinedefined; /* debug information */ TValue *k; /* constants used by the function */ Instruction *code; /* opcodes */ struct Proto **p; /* functions defined inside the function */ int *lineinfo; /* map from opcodes to source lines (debug information) */ LocVar *locvars; /* information about local variables (debug information) */ Upvaldesc *upvalues; /* upvalue information */ struct LClosure *cache; /* last-created closure with this prototype */ TString *source; /* used for debug information */ GCObject *gclist; } Proto; /* ** Lua Upvalues */ typedef struct UpVal UpVal; /* ** Closures */ #define ClosureHeader \ CommonHeader; lu_byte nupvalues; GCObject *gclist typedef struct CClosure { ClosureHeader; lua_CFunction f; TValue upvalue[1]; /* list of upvalues */ } CClosure; typedef struct LClosure { ClosureHeader; struct Proto *p; UpVal *upvals[1]; /* list of upvalues */ } LClosure; typedef union Closure { CClosure c; LClosure l; } Closure; #define isLfunction(o) ttisLclosure(o) #define getproto(o) (clLvalue(o)->p) /* ** Tables */ typedef union TKey { struct { TValuefields; int next; /* for chaining (offset for next node) */ } nk; TValue tvk; } TKey; /* copy a value into a key without messing up field 'next' */ #define setnodekey(L,key,obj) \ { TKey *k_=(key); const TValue *io_=(obj); \ k_->nk.value_ = io_->value_; k_->nk.tt_ = io_->tt_; \ (void)L; checkliveness(L,io_); } typedef struct Node { TValue i_val; TKey i_key; } Node; typedef struct Table { CommonHeader; lu_byte flags; /* 1<

lsizenode)) /* ** (address of) a fixed nil value */ #define luaO_nilobject (&luaO_nilobject_) LUAI_DDEC const TValue luaO_nilobject_; /* size of buffer for 'luaO_utf8esc' function */ #define UTF8BUFFSZ 8 LUAI_FUNC int luaO_int2fb (unsigned int x); LUAI_FUNC int luaO_fb2int (int x); LUAI_FUNC int luaO_utf8esc (char *buff, unsigned long x); LUAI_FUNC int luaO_ceillog2 (unsigned int x); LUAI_FUNC void luaO_arith (lua_State *L, int op, const TValue *p1, const TValue *p2, TValue *res); LUAI_FUNC size_t luaO_str2num (const char *s, TValue *o); LUAI_FUNC int luaO_hexavalue (int c); LUAI_FUNC void luaO_tostring (lua_State *L, StkId obj); LUAI_FUNC const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp); LUAI_FUNC const char *luaO_pushfstring (lua_State *L, const char *fmt, ...); LUAI_FUNC void luaO_chunkid (char *out, const char *source, size_t len); #endif ================================================ FILE: Tests/ApiExplorer/lua/src/lopcodes.c ================================================ /* ** $Id: lopcodes.c,v 1.55.1.1 2017/04/19 17:20:42 roberto Exp $ ** Opcodes for Lua virtual machine ** See Copyright Notice in lua.h */ #define lopcodes_c #define LUA_CORE #include "lprefix.h" #include #include "lopcodes.h" /* ORDER OP */ LUAI_DDEF const char *const luaP_opnames[NUM_OPCODES+1] = { "MOVE", "LOADK", "LOADKX", "LOADBOOL", "LOADNIL", "GETUPVAL", "GETTABUP", "GETTABLE", "SETTABUP", "SETUPVAL", "SETTABLE", "NEWTABLE", "SELF", "ADD", "SUB", "MUL", "MOD", "POW", "DIV", "IDIV", "BAND", "BOR", "BXOR", "SHL", "SHR", "UNM", "BNOT", "NOT", "LEN", "CONCAT", "JMP", "EQ", "LT", "LE", "TEST", "TESTSET", "CALL", "TAILCALL", "RETURN", "FORLOOP", "FORPREP", "TFORCALL", "TFORLOOP", "SETLIST", "CLOSURE", "VARARG", "EXTRAARG", NULL }; #define opmode(t,a,b,c,m) (((t)<<7) | ((a)<<6) | ((b)<<4) | ((c)<<2) | (m)) LUAI_DDEF const lu_byte luaP_opmodes[NUM_OPCODES] = { /* T A B C mode opcode */ opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_MOVE */ ,opmode(0, 1, OpArgK, OpArgN, iABx) /* OP_LOADK */ ,opmode(0, 1, OpArgN, OpArgN, iABx) /* OP_LOADKX */ ,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_LOADBOOL */ ,opmode(0, 1, OpArgU, OpArgN, iABC) /* OP_LOADNIL */ ,opmode(0, 1, OpArgU, OpArgN, iABC) /* OP_GETUPVAL */ ,opmode(0, 1, OpArgU, OpArgK, iABC) /* OP_GETTABUP */ ,opmode(0, 1, OpArgR, OpArgK, iABC) /* OP_GETTABLE */ ,opmode(0, 0, OpArgK, OpArgK, iABC) /* OP_SETTABUP */ ,opmode(0, 0, OpArgU, OpArgN, iABC) /* OP_SETUPVAL */ ,opmode(0, 0, OpArgK, OpArgK, iABC) /* OP_SETTABLE */ ,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_NEWTABLE */ ,opmode(0, 1, OpArgR, OpArgK, iABC) /* OP_SELF */ ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_ADD */ ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_SUB */ ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_MUL */ ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_MOD */ ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_POW */ ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_DIV */ ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_IDIV */ ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_BAND */ ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_BOR */ ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_BXOR */ ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_SHL */ ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_SHR */ ,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_UNM */ ,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_BNOT */ ,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_NOT */ ,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_LEN */ ,opmode(0, 1, OpArgR, OpArgR, iABC) /* OP_CONCAT */ ,opmode(0, 0, OpArgR, OpArgN, iAsBx) /* OP_JMP */ ,opmode(1, 0, OpArgK, OpArgK, iABC) /* OP_EQ */ ,opmode(1, 0, OpArgK, OpArgK, iABC) /* OP_LT */ ,opmode(1, 0, OpArgK, OpArgK, iABC) /* OP_LE */ ,opmode(1, 0, OpArgN, OpArgU, iABC) /* OP_TEST */ ,opmode(1, 1, OpArgR, OpArgU, iABC) /* OP_TESTSET */ ,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_CALL */ ,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_TAILCALL */ ,opmode(0, 0, OpArgU, OpArgN, iABC) /* OP_RETURN */ ,opmode(0, 1, OpArgR, OpArgN, iAsBx) /* OP_FORLOOP */ ,opmode(0, 1, OpArgR, OpArgN, iAsBx) /* OP_FORPREP */ ,opmode(0, 0, OpArgN, OpArgU, iABC) /* OP_TFORCALL */ ,opmode(0, 1, OpArgR, OpArgN, iAsBx) /* OP_TFORLOOP */ ,opmode(0, 0, OpArgU, OpArgU, iABC) /* OP_SETLIST */ ,opmode(0, 1, OpArgU, OpArgN, iABx) /* OP_CLOSURE */ ,opmode(0, 1, OpArgU, OpArgN, iABC) /* OP_VARARG */ ,opmode(0, 0, OpArgU, OpArgU, iAx) /* OP_EXTRAARG */ }; ================================================ FILE: Tests/ApiExplorer/lua/src/lopcodes.h ================================================ /* ** $Id: lopcodes.h,v 1.149.1.1 2017/04/19 17:20:42 roberto Exp $ ** Opcodes for Lua virtual machine ** See Copyright Notice in lua.h */ #ifndef lopcodes_h #define lopcodes_h #include "llimits.h" /*=========================================================================== We assume that instructions are unsigned numbers. All instructions have an opcode in the first 6 bits. Instructions can have the following fields: 'A' : 8 bits 'B' : 9 bits 'C' : 9 bits 'Ax' : 26 bits ('A', 'B', and 'C' together) 'Bx' : 18 bits ('B' and 'C' together) 'sBx' : signed Bx A signed argument is represented in excess K; that is, the number value is the unsigned value minus K. K is exactly the maximum value for that argument (so that -max is represented by 0, and +max is represented by 2*max), which is half the maximum for the corresponding unsigned argument. ===========================================================================*/ enum OpMode {iABC, iABx, iAsBx, iAx}; /* basic instruction format */ /* ** size and position of opcode arguments. */ #define SIZE_C 9 #define SIZE_B 9 #define SIZE_Bx (SIZE_C + SIZE_B) #define SIZE_A 8 #define SIZE_Ax (SIZE_C + SIZE_B + SIZE_A) #define SIZE_OP 6 #define POS_OP 0 #define POS_A (POS_OP + SIZE_OP) #define POS_C (POS_A + SIZE_A) #define POS_B (POS_C + SIZE_C) #define POS_Bx POS_C #define POS_Ax POS_A /* ** limits for opcode arguments. ** we use (signed) int to manipulate most arguments, ** so they must fit in LUAI_BITSINT-1 bits (-1 for sign) */ #if SIZE_Bx < LUAI_BITSINT-1 #define MAXARG_Bx ((1<>1) /* 'sBx' is signed */ #else #define MAXARG_Bx MAX_INT #define MAXARG_sBx MAX_INT #endif #if SIZE_Ax < LUAI_BITSINT-1 #define MAXARG_Ax ((1<>POS_OP) & MASK1(SIZE_OP,0))) #define SET_OPCODE(i,o) ((i) = (((i)&MASK0(SIZE_OP,POS_OP)) | \ ((cast(Instruction, o)<>pos) & MASK1(size,0))) #define setarg(i,v,pos,size) ((i) = (((i)&MASK0(size,pos)) | \ ((cast(Instruction, v)<> RK(C) */ OP_UNM,/* A B R(A) := -R(B) */ OP_BNOT,/* A B R(A) := ~R(B) */ OP_NOT,/* A B R(A) := not R(B) */ OP_LEN,/* A B R(A) := length of R(B) */ OP_CONCAT,/* A B C R(A) := R(B).. ... ..R(C) */ OP_JMP,/* A sBx pc+=sBx; if (A) close all upvalues >= R(A - 1) */ OP_EQ,/* A B C if ((RK(B) == RK(C)) ~= A) then pc++ */ OP_LT,/* A B C if ((RK(B) < RK(C)) ~= A) then pc++ */ OP_LE,/* A B C if ((RK(B) <= RK(C)) ~= A) then pc++ */ OP_TEST,/* A C if not (R(A) <=> C) then pc++ */ OP_TESTSET,/* A B C if (R(B) <=> C) then R(A) := R(B) else pc++ */ OP_CALL,/* A B C R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)) */ OP_TAILCALL,/* A B C return R(A)(R(A+1), ... ,R(A+B-1)) */ OP_RETURN,/* A B return R(A), ... ,R(A+B-2) (see note) */ OP_FORLOOP,/* A sBx R(A)+=R(A+2); if R(A) > 4) & 3)) #define getCMode(m) (cast(enum OpArgMask, (luaP_opmodes[m] >> 2) & 3)) #define testAMode(m) (luaP_opmodes[m] & (1 << 6)) #define testTMode(m) (luaP_opmodes[m] & (1 << 7)) LUAI_DDEC const char *const luaP_opnames[NUM_OPCODES+1]; /* opcode names */ /* number of list items to accumulate before a SETLIST instruction */ #define LFIELDS_PER_FLUSH 50 #endif ================================================ FILE: Tests/ApiExplorer/lua/src/loslib.c ================================================ /* ** $Id: loslib.c,v 1.65.1.1 2017/04/19 17:29:57 roberto Exp $ ** Standard Operating System library ** See Copyright Notice in lua.h */ #define loslib_c #define LUA_LIB #include "lprefix.h" #include #include #include #include #include #include "lua.h" #include "lauxlib.h" #include "lualib.h" /* ** {================================================================== ** List of valid conversion specifiers for the 'strftime' function; ** options are grouped by length; group of length 2 start with '||'. ** =================================================================== */ #if !defined(LUA_STRFTIMEOPTIONS) /* { */ /* options for ANSI C 89 (only 1-char options) */ #define L_STRFTIMEC89 "aAbBcdHIjmMpSUwWxXyYZ%" /* options for ISO C 99 and POSIX */ #define L_STRFTIMEC99 "aAbBcCdDeFgGhHIjmMnprRStTuUVwWxXyYzZ%" \ "||" "EcECExEXEyEY" "OdOeOHOIOmOMOSOuOUOVOwOWOy" /* two-char options */ /* options for Windows */ #define L_STRFTIMEWIN "aAbBcdHIjmMpSUwWxXyYzZ%" \ "||" "#c#x#d#H#I#j#m#M#S#U#w#W#y#Y" /* two-char options */ #if defined(LUA_USE_WINDOWS) #define LUA_STRFTIMEOPTIONS L_STRFTIMEWIN #elif defined(LUA_USE_C89) #define LUA_STRFTIMEOPTIONS L_STRFTIMEC89 #else /* C99 specification */ #define LUA_STRFTIMEOPTIONS L_STRFTIMEC99 #endif #endif /* } */ /* }================================================================== */ /* ** {================================================================== ** Configuration for time-related stuff ** =================================================================== */ #if !defined(l_time_t) /* { */ /* ** type to represent time_t in Lua */ #define l_timet lua_Integer #define l_pushtime(L,t) lua_pushinteger(L,(lua_Integer)(t)) static time_t l_checktime (lua_State *L, int arg) { lua_Integer t = luaL_checkinteger(L, arg); luaL_argcheck(L, (time_t)t == t, arg, "time out-of-bounds"); return (time_t)t; } #endif /* } */ #if !defined(l_gmtime) /* { */ /* ** By default, Lua uses gmtime/localtime, except when POSIX is available, ** where it uses gmtime_r/localtime_r */ #if defined(LUA_USE_POSIX) /* { */ #define l_gmtime(t,r) gmtime_r(t,r) #define l_localtime(t,r) localtime_r(t,r) #else /* }{ */ /* ISO C definitions */ #define l_gmtime(t,r) ((void)(r)->tm_sec, gmtime(t)) #define l_localtime(t,r) ((void)(r)->tm_sec, localtime(t)) #endif /* } */ #endif /* } */ /* }================================================================== */ /* ** {================================================================== ** Configuration for 'tmpnam': ** By default, Lua uses tmpnam except when POSIX is available, where ** it uses mkstemp. ** =================================================================== */ #if !defined(lua_tmpnam) /* { */ #if defined(LUA_USE_POSIX) /* { */ #include #define LUA_TMPNAMBUFSIZE 32 #if !defined(LUA_TMPNAMTEMPLATE) #define LUA_TMPNAMTEMPLATE "/tmp/lua_XXXXXX" #endif #define lua_tmpnam(b,e) { \ strcpy(b, LUA_TMPNAMTEMPLATE); \ e = mkstemp(b); \ if (e != -1) close(e); \ e = (e == -1); } #else /* }{ */ /* ISO C definitions */ #define LUA_TMPNAMBUFSIZE L_tmpnam #define lua_tmpnam(b,e) { e = (tmpnam(b) == NULL); } #endif /* } */ #endif /* } */ /* }================================================================== */ static int os_execute (lua_State *L) { const char *cmd = luaL_optstring(L, 1, NULL); #if HC_PLATFORM_IS_MICROSOFT && HC_PLATFORM != HC_PLATFORM_XDK int stat = system(cmd); #else int stat = cmd == NULL ? 0 : -1; #endif if (cmd != NULL) return luaL_execresult(L, stat); else { lua_pushboolean(L, stat); /* true if there is a shell */ return 1; } } static int os_remove (lua_State *L) { const char *filename = luaL_checkstring(L, 1); return luaL_fileresult(L, remove(filename) == 0, filename); } static int os_rename (lua_State *L) { const char *fromname = luaL_checkstring(L, 1); const char *toname = luaL_checkstring(L, 2); return luaL_fileresult(L, rename(fromname, toname) == 0, NULL); } static int os_tmpname (lua_State *L) { char buff[LUA_TMPNAMBUFSIZE]; int err; lua_tmpnam(buff, err); if (err) return luaL_error(L, "unable to generate a unique filename"); lua_pushstring(L, buff); return 1; } static int os_getenv (lua_State *L) { #if HC_PLATFORM != HC_PLATFORM_XDK lua_pushstring(L, getenv(luaL_checkstring(L, 1))); /* if NULL push nil */ #else lua_pushstring(L, NULL); #endif return 1; } static int os_clock (lua_State *L) { lua_pushnumber(L, ((lua_Number)clock())/(lua_Number)CLOCKS_PER_SEC); return 1; } /* ** {====================================================== ** Time/Date operations ** { year=%Y, month=%m, day=%d, hour=%H, min=%M, sec=%S, ** wday=%w+1, yday=%j, isdst=? } ** ======================================================= */ static void setfield (lua_State *L, const char *key, int value) { lua_pushinteger(L, value); lua_setfield(L, -2, key); } static void setboolfield (lua_State *L, const char *key, int value) { if (value < 0) /* undefined? */ return; /* does not set field */ lua_pushboolean(L, value); lua_setfield(L, -2, key); } /* ** Set all fields from structure 'tm' in the table on top of the stack */ static void setallfields (lua_State *L, struct tm *stm) { setfield(L, "sec", stm->tm_sec); setfield(L, "min", stm->tm_min); setfield(L, "hour", stm->tm_hour); setfield(L, "day", stm->tm_mday); setfield(L, "month", stm->tm_mon + 1); setfield(L, "year", stm->tm_year + 1900); setfield(L, "wday", stm->tm_wday + 1); setfield(L, "yday", stm->tm_yday + 1); setboolfield(L, "isdst", stm->tm_isdst); } static int getboolfield (lua_State *L, const char *key) { int res; res = (lua_getfield(L, -1, key) == LUA_TNIL) ? -1 : lua_toboolean(L, -1); lua_pop(L, 1); return res; } /* maximum value for date fields (to avoid arithmetic overflows with 'int') */ #if !defined(L_MAXDATEFIELD) #define L_MAXDATEFIELD (INT_MAX / 2) #endif static int getfield (lua_State *L, const char *key, int d, int delta) { int isnum; int t = lua_getfield(L, -1, key); /* get field and its type */ lua_Integer res = lua_tointegerx(L, -1, &isnum); if (!isnum) { /* field is not an integer? */ if (t != LUA_TNIL) /* some other value? */ return luaL_error(L, "field '%s' is not an integer", key); else if (d < 0) /* absent field; no default? */ return luaL_error(L, "field '%s' missing in date table", key); res = d; } else { if (!(-L_MAXDATEFIELD <= res && res <= L_MAXDATEFIELD)) return luaL_error(L, "field '%s' is out-of-bound", key); res -= delta; } lua_pop(L, 1); return (int)res; } static const char *checkoption (lua_State *L, const char *conv, ptrdiff_t convlen, char *buff) { const char *option = LUA_STRFTIMEOPTIONS; int oplen = 1; /* length of options being checked */ for (; *option != '\0' && oplen <= convlen; option += oplen) { if (*option == '|') /* next block? */ oplen++; /* will check options with next length (+1) */ else if (memcmp(conv, option, oplen) == 0) { /* match? */ memcpy(buff, conv, oplen); /* copy valid option to buffer */ buff[oplen] = '\0'; return conv + oplen; /* return next item */ } } luaL_argerror(L, 1, lua_pushfstring(L, "invalid conversion specifier '%%%s'", conv)); return conv; /* to avoid warnings */ } /* maximum size for an individual 'strftime' item */ #define SIZETIMEFMT 250 static int os_date (lua_State *L) { size_t slen; const char *s = luaL_optlstring(L, 1, "%c", &slen); time_t t = luaL_opt(L, l_checktime, 2, time(NULL)); const char *se = s + slen; /* 's' end */ struct tm tmr, *stm; if (*s == '!') { /* UTC? */ stm = l_gmtime(&t, &tmr); s++; /* skip '!' */ } else stm = l_localtime(&t, &tmr); if (stm == NULL) /* invalid date? */ return luaL_error(L, "time result cannot be represented in this installation"); if (strcmp(s, "*t") == 0) { lua_createtable(L, 0, 9); /* 9 = number of fields */ setallfields(L, stm); } else { char cc[4]; /* buffer for individual conversion specifiers */ luaL_Buffer b; cc[0] = '%'; luaL_buffinit(L, &b); while (s < se) { if (*s != '%') /* not a conversion specifier? */ luaL_addchar(&b, *s++); else { size_t reslen; char *buff = luaL_prepbuffsize(&b, SIZETIMEFMT); s++; /* skip '%' */ s = checkoption(L, s, se - s, cc + 1); /* copy specifier to 'cc' */ reslen = strftime(buff, SIZETIMEFMT, cc, stm); luaL_addsize(&b, reslen); } } luaL_pushresult(&b); } return 1; } static int os_time (lua_State *L) { time_t t; if (lua_isnoneornil(L, 1)) /* called without args? */ t = time(NULL); /* get current time */ else { struct tm ts; luaL_checktype(L, 1, LUA_TTABLE); lua_settop(L, 1); /* make sure table is at the top */ ts.tm_sec = getfield(L, "sec", 0, 0); ts.tm_min = getfield(L, "min", 0, 0); ts.tm_hour = getfield(L, "hour", 12, 0); ts.tm_mday = getfield(L, "day", -1, 0); ts.tm_mon = getfield(L, "month", -1, 1); ts.tm_year = getfield(L, "year", -1, 1900); ts.tm_isdst = getboolfield(L, "isdst"); t = mktime(&ts); setallfields(L, &ts); /* update fields with normalized values */ } if (t != (time_t)(l_timet)t || t == (time_t)(-1)) return luaL_error(L, "time result cannot be represented in this installation"); l_pushtime(L, t); return 1; } static int os_difftime (lua_State *L) { time_t t1 = l_checktime(L, 1); time_t t2 = l_checktime(L, 2); lua_pushnumber(L, (lua_Number)difftime(t1, t2)); return 1; } /* }====================================================== */ static int os_setlocale (lua_State *L) { static const int cat[] = {LC_ALL, LC_COLLATE, LC_CTYPE, LC_MONETARY, LC_NUMERIC, LC_TIME}; static const char *const catnames[] = {"all", "collate", "ctype", "monetary", "numeric", "time", NULL}; const char *l = luaL_optstring(L, 1, NULL); int op = luaL_checkoption(L, 2, "all", catnames); lua_pushstring(L, setlocale(cat[op], l)); return 1; } static int os_exit (lua_State *L) { int status; if (lua_isboolean(L, 1)) status = (lua_toboolean(L, 1) ? EXIT_SUCCESS : EXIT_FAILURE); else status = (int)luaL_optinteger(L, 1, EXIT_SUCCESS); if (lua_toboolean(L, 2)) lua_close(L); if (L) exit(status); /* 'if' to avoid warnings for unreachable 'return' */ return 0; } static const luaL_Reg syslib[] = { {"clock", os_clock}, {"date", os_date}, {"difftime", os_difftime}, {"execute", os_execute}, {"exit", os_exit}, {"getenv", os_getenv}, {"remove", os_remove}, {"rename", os_rename}, {"setlocale", os_setlocale}, {"time", os_time}, {"tmpname", os_tmpname}, {NULL, NULL} }; /* }====================================================== */ LUAMOD_API int luaopen_os (lua_State *L) { luaL_newlib(L, syslib); return 1; } ================================================ FILE: Tests/ApiExplorer/lua/src/lparser.c ================================================ /* ** $Id: lparser.c,v 2.155.1.2 2017/04/29 18:11:40 roberto Exp $ ** Lua Parser ** See Copyright Notice in lua.h */ #define lparser_c #define LUA_CORE #include "lprefix.h" #include #include "lua.h" #include "lcode.h" #include "ldebug.h" #include "ldo.h" #include "lfunc.h" #include "llex.h" #include "lmem.h" #include "lobject.h" #include "lopcodes.h" #include "lparser.h" #include "lstate.h" #include "lstring.h" #include "ltable.h" /* maximum number of local variables per function (must be smaller than 250, due to the bytecode format) */ #define MAXVARS 200 #define hasmultret(k) ((k) == VCALL || (k) == VVARARG) /* because all strings are unified by the scanner, the parser can use pointer equality for string equality */ #define eqstr(a,b) ((a) == (b)) /* ** nodes for block list (list of active blocks) */ typedef struct BlockCnt { struct BlockCnt *previous; /* chain */ int firstlabel; /* index of first label in this block */ int firstgoto; /* index of first pending goto in this block */ lu_byte nactvar; /* # active locals outside the block */ lu_byte upval; /* true if some variable in the block is an upvalue */ lu_byte isloop; /* true if 'block' is a loop */ } BlockCnt; /* ** prototypes for recursive non-terminal functions */ static void statement (LexState *ls); static void expr (LexState *ls, expdesc *v); /* semantic error */ static l_noret semerror (LexState *ls, const char *msg) { ls->t.token = 0; /* remove "near " from final message */ luaX_syntaxerror(ls, msg); } static l_noret error_expected (LexState *ls, int token) { luaX_syntaxerror(ls, luaO_pushfstring(ls->L, "%s expected", luaX_token2str(ls, token))); } static l_noret errorlimit (FuncState *fs, int limit, const char *what) { lua_State *L = fs->ls->L; const char *msg; int line = fs->f->linedefined; const char *where = (line == 0) ? "main function" : luaO_pushfstring(L, "function at line %d", line); msg = luaO_pushfstring(L, "too many %s (limit is %d) in %s", what, limit, where); luaX_syntaxerror(fs->ls, msg); } static void checklimit (FuncState *fs, int v, int l, const char *what) { if (v > l) errorlimit(fs, l, what); } static int testnext (LexState *ls, int c) { if (ls->t.token == c) { luaX_next(ls); return 1; } else return 0; } static void check (LexState *ls, int c) { if (ls->t.token != c) error_expected(ls, c); } static void checknext (LexState *ls, int c) { check(ls, c); luaX_next(ls); } #define check_condition(ls,c,msg) { if (!(c)) luaX_syntaxerror(ls, msg); } static void check_match (LexState *ls, int what, int who, int where) { if (!testnext(ls, what)) { if (where == ls->linenumber) error_expected(ls, what); else { luaX_syntaxerror(ls, luaO_pushfstring(ls->L, "%s expected (to close %s at line %d)", luaX_token2str(ls, what), luaX_token2str(ls, who), where)); } } } static TString *str_checkname (LexState *ls) { TString *ts; check(ls, TK_NAME); ts = ls->t.seminfo.ts; luaX_next(ls); return ts; } static void init_exp (expdesc *e, expkind k, int i) { e->f = e->t = NO_JUMP; e->k = k; e->u.info = i; } static void codestring (LexState *ls, expdesc *e, TString *s) { init_exp(e, VK, luaK_stringK(ls->fs, s)); } static void checkname (LexState *ls, expdesc *e) { codestring(ls, e, str_checkname(ls)); } static int registerlocalvar (LexState *ls, TString *varname) { FuncState *fs = ls->fs; Proto *f = fs->f; int oldsize = f->sizelocvars; luaM_growvector(ls->L, f->locvars, fs->nlocvars, f->sizelocvars, LocVar, SHRT_MAX, "local variables"); while (oldsize < f->sizelocvars) f->locvars[oldsize++].varname = NULL; f->locvars[fs->nlocvars].varname = varname; luaC_objbarrier(ls->L, f, varname); return fs->nlocvars++; } static void new_localvar (LexState *ls, TString *name) { FuncState *fs = ls->fs; Dyndata *dyd = ls->dyd; int reg = registerlocalvar(ls, name); checklimit(fs, dyd->actvar.n + 1 - fs->firstlocal, MAXVARS, "local variables"); luaM_growvector(ls->L, dyd->actvar.arr, dyd->actvar.n + 1, dyd->actvar.size, Vardesc, MAX_INT, "local variables"); dyd->actvar.arr[dyd->actvar.n++].idx = cast(short, reg); } static void new_localvarliteral_ (LexState *ls, const char *name, size_t sz) { new_localvar(ls, luaX_newstring(ls, name, sz)); } #define new_localvarliteral(ls,v) \ new_localvarliteral_(ls, "" v, (sizeof(v)/sizeof(char))-1) static LocVar *getlocvar (FuncState *fs, int i) { int idx = fs->ls->dyd->actvar.arr[fs->firstlocal + i].idx; lua_assert(idx < fs->nlocvars); return &fs->f->locvars[idx]; } static void adjustlocalvars (LexState *ls, int nvars) { FuncState *fs = ls->fs; fs->nactvar = cast_byte(fs->nactvar + nvars); for (; nvars; nvars--) { getlocvar(fs, fs->nactvar - nvars)->startpc = fs->pc; } } static void removevars (FuncState *fs, int tolevel) { fs->ls->dyd->actvar.n -= (fs->nactvar - tolevel); while (fs->nactvar > tolevel) getlocvar(fs, --fs->nactvar)->endpc = fs->pc; } static int searchupvalue (FuncState *fs, TString *name) { int i; Upvaldesc *up = fs->f->upvalues; for (i = 0; i < fs->nups; i++) { if (eqstr(up[i].name, name)) return i; } return -1; /* not found */ } static int newupvalue (FuncState *fs, TString *name, expdesc *v) { Proto *f = fs->f; int oldsize = f->sizeupvalues; checklimit(fs, fs->nups + 1, MAXUPVAL, "upvalues"); luaM_growvector(fs->ls->L, f->upvalues, fs->nups, f->sizeupvalues, Upvaldesc, MAXUPVAL, "upvalues"); while (oldsize < f->sizeupvalues) f->upvalues[oldsize++].name = NULL; f->upvalues[fs->nups].instack = (v->k == VLOCAL); f->upvalues[fs->nups].idx = cast_byte(v->u.info); f->upvalues[fs->nups].name = name; luaC_objbarrier(fs->ls->L, f, name); return fs->nups++; } static int searchvar (FuncState *fs, TString *n) { int i; for (i = cast_int(fs->nactvar) - 1; i >= 0; i--) { if (eqstr(n, getlocvar(fs, i)->varname)) return i; } return -1; /* not found */ } /* Mark block where variable at given level was defined (to emit close instructions later). */ static void markupval (FuncState *fs, int level) { BlockCnt *bl = fs->bl; while (bl->nactvar > level) bl = bl->previous; bl->upval = 1; } /* Find variable with given name 'n'. If it is an upvalue, add this upvalue into all intermediate functions. */ static void singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) { if (fs == NULL) /* no more levels? */ init_exp(var, VVOID, 0); /* default is global */ else { int v = searchvar(fs, n); /* look up locals at current level */ if (v >= 0) { /* found? */ init_exp(var, VLOCAL, v); /* variable is local */ if (!base) markupval(fs, v); /* local will be used as an upval */ } else { /* not found as local at current level; try upvalues */ int idx = searchupvalue(fs, n); /* try existing upvalues */ if (idx < 0) { /* not found? */ singlevaraux(fs->prev, n, var, 0); /* try upper levels */ if (var->k == VVOID) /* not found? */ return; /* it is a global */ /* else was LOCAL or UPVAL */ idx = newupvalue(fs, n, var); /* will be a new upvalue */ } init_exp(var, VUPVAL, idx); /* new or old upvalue */ } } } static void singlevar (LexState *ls, expdesc *var) { TString *varname = str_checkname(ls); FuncState *fs = ls->fs; singlevaraux(fs, varname, var, 1); if (var->k == VVOID) { /* global name? */ expdesc key; singlevaraux(fs, ls->envn, var, 1); /* get environment variable */ lua_assert(var->k != VVOID); /* this one must exist */ codestring(ls, &key, varname); /* key is variable name */ luaK_indexed(fs, var, &key); /* env[varname] */ } } static void adjust_assign (LexState *ls, int nvars, int nexps, expdesc *e) { FuncState *fs = ls->fs; int extra = nvars - nexps; if (hasmultret(e->k)) { extra++; /* includes call itself */ if (extra < 0) extra = 0; luaK_setreturns(fs, e, extra); /* last exp. provides the difference */ if (extra > 1) luaK_reserveregs(fs, extra-1); } else { if (e->k != VVOID) luaK_exp2nextreg(fs, e); /* close last expression */ if (extra > 0) { int reg = fs->freereg; luaK_reserveregs(fs, extra); luaK_nil(fs, reg, extra); } } if (nexps > nvars) ls->fs->freereg -= nexps - nvars; /* remove extra values */ } static void enterlevel (LexState *ls) { lua_State *L = ls->L; ++L->nCcalls; checklimit(ls->fs, L->nCcalls, LUAI_MAXCCALLS, "C levels"); } #define leavelevel(ls) ((ls)->L->nCcalls--) static void closegoto (LexState *ls, int g, Labeldesc *label) { int i; FuncState *fs = ls->fs; Labellist *gl = &ls->dyd->gt; Labeldesc *gt = &gl->arr[g]; lua_assert(eqstr(gt->name, label->name)); if (gt->nactvar < label->nactvar) { TString *vname = getlocvar(fs, gt->nactvar)->varname; const char *msg = luaO_pushfstring(ls->L, " at line %d jumps into the scope of local '%s'", getstr(gt->name), gt->line, getstr(vname)); semerror(ls, msg); } luaK_patchlist(fs, gt->pc, label->pc); /* remove goto from pending list */ for (i = g; i < gl->n - 1; i++) gl->arr[i] = gl->arr[i + 1]; gl->n--; } /* ** try to close a goto with existing labels; this solves backward jumps */ static int findlabel (LexState *ls, int g) { int i; BlockCnt *bl = ls->fs->bl; Dyndata *dyd = ls->dyd; Labeldesc *gt = &dyd->gt.arr[g]; /* check labels in current block for a match */ for (i = bl->firstlabel; i < dyd->label.n; i++) { Labeldesc *lb = &dyd->label.arr[i]; if (eqstr(lb->name, gt->name)) { /* correct label? */ if (gt->nactvar > lb->nactvar && (bl->upval || dyd->label.n > bl->firstlabel)) luaK_patchclose(ls->fs, gt->pc, lb->nactvar); closegoto(ls, g, lb); /* close it */ return 1; } } return 0; /* label not found; cannot close goto */ } static int newlabelentry (LexState *ls, Labellist *l, TString *name, int line, int pc) { int n = l->n; luaM_growvector(ls->L, l->arr, n, l->size, Labeldesc, SHRT_MAX, "labels/gotos"); l->arr[n].name = name; l->arr[n].line = line; l->arr[n].nactvar = ls->fs->nactvar; l->arr[n].pc = pc; l->n = n + 1; return n; } /* ** check whether new label 'lb' matches any pending gotos in current ** block; solves forward jumps */ static void findgotos (LexState *ls, Labeldesc *lb) { Labellist *gl = &ls->dyd->gt; int i = ls->fs->bl->firstgoto; while (i < gl->n) { if (eqstr(gl->arr[i].name, lb->name)) closegoto(ls, i, lb); else i++; } } /* ** export pending gotos to outer level, to check them against ** outer labels; if the block being exited has upvalues, and ** the goto exits the scope of any variable (which can be the ** upvalue), close those variables being exited. */ static void movegotosout (FuncState *fs, BlockCnt *bl) { int i = bl->firstgoto; Labellist *gl = &fs->ls->dyd->gt; /* correct pending gotos to current block and try to close it with visible labels */ while (i < gl->n) { Labeldesc *gt = &gl->arr[i]; if (gt->nactvar > bl->nactvar) { if (bl->upval) luaK_patchclose(fs, gt->pc, bl->nactvar); gt->nactvar = bl->nactvar; } if (!findlabel(fs->ls, i)) i++; /* move to next one */ } } static void enterblock (FuncState *fs, BlockCnt *bl, lu_byte isloop) { bl->isloop = isloop; bl->nactvar = fs->nactvar; bl->firstlabel = fs->ls->dyd->label.n; bl->firstgoto = fs->ls->dyd->gt.n; bl->upval = 0; bl->previous = fs->bl; fs->bl = bl; lua_assert(fs->freereg == fs->nactvar); } /* ** create a label named 'break' to resolve break statements */ static void breaklabel (LexState *ls) { TString *n = luaS_new(ls->L, "break"); int l = newlabelentry(ls, &ls->dyd->label, n, 0, ls->fs->pc); findgotos(ls, &ls->dyd->label.arr[l]); } /* ** generates an error for an undefined 'goto'; choose appropriate ** message when label name is a reserved word (which can only be 'break') */ static l_noret undefgoto (LexState *ls, Labeldesc *gt) { const char *msg = isreserved(gt->name) ? "<%s> at line %d not inside a loop" : "no visible label '%s' for at line %d"; msg = luaO_pushfstring(ls->L, msg, getstr(gt->name), gt->line); semerror(ls, msg); } static void leaveblock (FuncState *fs) { BlockCnt *bl = fs->bl; LexState *ls = fs->ls; if (bl->previous && bl->upval) { /* create a 'jump to here' to close upvalues */ int j = luaK_jump(fs); luaK_patchclose(fs, j, bl->nactvar); luaK_patchtohere(fs, j); } if (bl->isloop) breaklabel(ls); /* close pending breaks */ fs->bl = bl->previous; removevars(fs, bl->nactvar); lua_assert(bl->nactvar == fs->nactvar); fs->freereg = fs->nactvar; /* free registers */ ls->dyd->label.n = bl->firstlabel; /* remove local labels */ if (bl->previous) /* inner block? */ movegotosout(fs, bl); /* update pending gotos to outer block */ else if (bl->firstgoto < ls->dyd->gt.n) /* pending gotos in outer block? */ undefgoto(ls, &ls->dyd->gt.arr[bl->firstgoto]); /* error */ } /* ** adds a new prototype into list of prototypes */ static Proto *addprototype (LexState *ls) { Proto *clp; lua_State *L = ls->L; FuncState *fs = ls->fs; Proto *f = fs->f; /* prototype of current function */ if (fs->np >= f->sizep) { int oldsize = f->sizep; luaM_growvector(L, f->p, fs->np, f->sizep, Proto *, MAXARG_Bx, "functions"); while (oldsize < f->sizep) f->p[oldsize++] = NULL; } f->p[fs->np++] = clp = luaF_newproto(L); luaC_objbarrier(L, f, clp); return clp; } /* ** codes instruction to create new closure in parent function. ** The OP_CLOSURE instruction must use the last available register, ** so that, if it invokes the GC, the GC knows which registers ** are in use at that time. */ static void codeclosure (LexState *ls, expdesc *v) { FuncState *fs = ls->fs->prev; init_exp(v, VRELOCABLE, luaK_codeABx(fs, OP_CLOSURE, 0, fs->np - 1)); luaK_exp2nextreg(fs, v); /* fix it at the last register */ } static void open_func (LexState *ls, FuncState *fs, BlockCnt *bl) { Proto *f; fs->prev = ls->fs; /* linked list of funcstates */ fs->ls = ls; ls->fs = fs; fs->pc = 0; fs->lasttarget = 0; fs->jpc = NO_JUMP; fs->freereg = 0; fs->nk = 0; fs->np = 0; fs->nups = 0; fs->nlocvars = 0; fs->nactvar = 0; fs->firstlocal = ls->dyd->actvar.n; fs->bl = NULL; f = fs->f; f->source = ls->source; f->maxstacksize = 2; /* registers 0/1 are always valid */ enterblock(fs, bl, 0); } static void close_func (LexState *ls) { lua_State *L = ls->L; FuncState *fs = ls->fs; Proto *f = fs->f; luaK_ret(fs, 0, 0); /* final return */ leaveblock(fs); luaM_reallocvector(L, f->code, f->sizecode, fs->pc, Instruction); f->sizecode = fs->pc; luaM_reallocvector(L, f->lineinfo, f->sizelineinfo, fs->pc, int); f->sizelineinfo = fs->pc; luaM_reallocvector(L, f->k, f->sizek, fs->nk, TValue); f->sizek = fs->nk; luaM_reallocvector(L, f->p, f->sizep, fs->np, Proto *); f->sizep = fs->np; luaM_reallocvector(L, f->locvars, f->sizelocvars, fs->nlocvars, LocVar); f->sizelocvars = fs->nlocvars; luaM_reallocvector(L, f->upvalues, f->sizeupvalues, fs->nups, Upvaldesc); f->sizeupvalues = fs->nups; lua_assert(fs->bl == NULL); ls->fs = fs->prev; luaC_checkGC(L); } /*============================================================*/ /* GRAMMAR RULES */ /*============================================================*/ /* ** check whether current token is in the follow set of a block. ** 'until' closes syntactical blocks, but do not close scope, ** so it is handled in separate. */ static int block_follow (LexState *ls, int withuntil) { switch (ls->t.token) { case TK_ELSE: case TK_ELSEIF: case TK_END: case TK_EOS: return 1; case TK_UNTIL: return withuntil; default: return 0; } } static void statlist (LexState *ls) { /* statlist -> { stat [';'] } */ while (!block_follow(ls, 1)) { if (ls->t.token == TK_RETURN) { statement(ls); return; /* 'return' must be last statement */ } statement(ls); } } static void fieldsel (LexState *ls, expdesc *v) { /* fieldsel -> ['.' | ':'] NAME */ FuncState *fs = ls->fs; expdesc key; luaK_exp2anyregup(fs, v); luaX_next(ls); /* skip the dot or colon */ checkname(ls, &key); luaK_indexed(fs, v, &key); } static void yindex (LexState *ls, expdesc *v) { /* index -> '[' expr ']' */ luaX_next(ls); /* skip the '[' */ expr(ls, v); luaK_exp2val(ls->fs, v); checknext(ls, ']'); } /* ** {====================================================================== ** Rules for Constructors ** ======================================================================= */ struct ConsControl { expdesc v; /* last list item read */ expdesc *t; /* table descriptor */ int nh; /* total number of 'record' elements */ int na; /* total number of array elements */ int tostore; /* number of array elements pending to be stored */ }; static void recfield (LexState *ls, struct ConsControl *cc) { /* recfield -> (NAME | '['exp1']') = exp1 */ FuncState *fs = ls->fs; int reg = ls->fs->freereg; expdesc key, val; int rkkey; if (ls->t.token == TK_NAME) { checklimit(fs, cc->nh, MAX_INT, "items in a constructor"); checkname(ls, &key); } else /* ls->t.token == '[' */ yindex(ls, &key); cc->nh++; checknext(ls, '='); rkkey = luaK_exp2RK(fs, &key); expr(ls, &val); luaK_codeABC(fs, OP_SETTABLE, cc->t->u.info, rkkey, luaK_exp2RK(fs, &val)); fs->freereg = reg; /* free registers */ } static void closelistfield (FuncState *fs, struct ConsControl *cc) { if (cc->v.k == VVOID) return; /* there is no list item */ luaK_exp2nextreg(fs, &cc->v); cc->v.k = VVOID; if (cc->tostore == LFIELDS_PER_FLUSH) { luaK_setlist(fs, cc->t->u.info, cc->na, cc->tostore); /* flush */ cc->tostore = 0; /* no more items pending */ } } static void lastlistfield (FuncState *fs, struct ConsControl *cc) { if (cc->tostore == 0) return; if (hasmultret(cc->v.k)) { luaK_setmultret(fs, &cc->v); luaK_setlist(fs, cc->t->u.info, cc->na, LUA_MULTRET); cc->na--; /* do not count last expression (unknown number of elements) */ } else { if (cc->v.k != VVOID) luaK_exp2nextreg(fs, &cc->v); luaK_setlist(fs, cc->t->u.info, cc->na, cc->tostore); } } static void listfield (LexState *ls, struct ConsControl *cc) { /* listfield -> exp */ expr(ls, &cc->v); checklimit(ls->fs, cc->na, MAX_INT, "items in a constructor"); cc->na++; cc->tostore++; } static void field (LexState *ls, struct ConsControl *cc) { /* field -> listfield | recfield */ switch(ls->t.token) { case TK_NAME: { /* may be 'listfield' or 'recfield' */ if (luaX_lookahead(ls) != '=') /* expression? */ listfield(ls, cc); else recfield(ls, cc); break; } case '[': { recfield(ls, cc); break; } default: { listfield(ls, cc); break; } } } static void constructor (LexState *ls, expdesc *t) { /* constructor -> '{' [ field { sep field } [sep] ] '}' sep -> ',' | ';' */ FuncState *fs = ls->fs; int line = ls->linenumber; int pc = luaK_codeABC(fs, OP_NEWTABLE, 0, 0, 0); struct ConsControl cc; cc.na = cc.nh = cc.tostore = 0; cc.t = t; init_exp(t, VRELOCABLE, pc); init_exp(&cc.v, VVOID, 0); /* no value (yet) */ luaK_exp2nextreg(ls->fs, t); /* fix it at stack top */ checknext(ls, '{'); do { lua_assert(cc.v.k == VVOID || cc.tostore > 0); if (ls->t.token == '}') break; closelistfield(fs, &cc); field(ls, &cc); } while (testnext(ls, ',') || testnext(ls, ';')); check_match(ls, '}', '{', line); lastlistfield(fs, &cc); SETARG_B(fs->f->code[pc], luaO_int2fb(cc.na)); /* set initial array size */ SETARG_C(fs->f->code[pc], luaO_int2fb(cc.nh)); /* set initial table size */ } /* }====================================================================== */ static void parlist (LexState *ls) { /* parlist -> [ param { ',' param } ] */ FuncState *fs = ls->fs; Proto *f = fs->f; int nparams = 0; f->is_vararg = 0; if (ls->t.token != ')') { /* is 'parlist' not empty? */ do { switch (ls->t.token) { case TK_NAME: { /* param -> NAME */ new_localvar(ls, str_checkname(ls)); nparams++; break; } case TK_DOTS: { /* param -> '...' */ luaX_next(ls); f->is_vararg = 1; /* declared vararg */ break; } default: luaX_syntaxerror(ls, " or '...' expected"); } } while (!f->is_vararg && testnext(ls, ',')); } adjustlocalvars(ls, nparams); f->numparams = cast_byte(fs->nactvar); luaK_reserveregs(fs, fs->nactvar); /* reserve register for parameters */ } static void body (LexState *ls, expdesc *e, int ismethod, int line) { /* body -> '(' parlist ')' block END */ FuncState new_fs; BlockCnt bl; new_fs.f = addprototype(ls); new_fs.f->linedefined = line; open_func(ls, &new_fs, &bl); checknext(ls, '('); if (ismethod) { new_localvarliteral(ls, "self"); /* create 'self' parameter */ adjustlocalvars(ls, 1); } parlist(ls); checknext(ls, ')'); statlist(ls); new_fs.f->lastlinedefined = ls->linenumber; check_match(ls, TK_END, TK_FUNCTION, line); codeclosure(ls, e); close_func(ls); } static int explist (LexState *ls, expdesc *v) { /* explist -> expr { ',' expr } */ int n = 1; /* at least one expression */ expr(ls, v); while (testnext(ls, ',')) { luaK_exp2nextreg(ls->fs, v); expr(ls, v); n++; } return n; } static void funcargs (LexState *ls, expdesc *f, int line) { FuncState *fs = ls->fs; expdesc args; int base, nparams; switch (ls->t.token) { case '(': { /* funcargs -> '(' [ explist ] ')' */ luaX_next(ls); if (ls->t.token == ')') /* arg list is empty? */ args.k = VVOID; else { explist(ls, &args); luaK_setmultret(fs, &args); } check_match(ls, ')', '(', line); break; } case '{': { /* funcargs -> constructor */ constructor(ls, &args); break; } case TK_STRING: { /* funcargs -> STRING */ codestring(ls, &args, ls->t.seminfo.ts); luaX_next(ls); /* must use 'seminfo' before 'next' */ break; } default: { luaX_syntaxerror(ls, "function arguments expected"); } } lua_assert(f->k == VNONRELOC); base = f->u.info; /* base register for call */ if (hasmultret(args.k)) nparams = LUA_MULTRET; /* open call */ else { if (args.k != VVOID) luaK_exp2nextreg(fs, &args); /* close last argument */ nparams = fs->freereg - (base+1); } init_exp(f, VCALL, luaK_codeABC(fs, OP_CALL, base, nparams+1, 2)); luaK_fixline(fs, line); fs->freereg = base+1; /* call remove function and arguments and leaves (unless changed) one result */ } /* ** {====================================================================== ** Expression parsing ** ======================================================================= */ static void primaryexp (LexState *ls, expdesc *v) { /* primaryexp -> NAME | '(' expr ')' */ switch (ls->t.token) { case '(': { int line = ls->linenumber; luaX_next(ls); expr(ls, v); check_match(ls, ')', '(', line); luaK_dischargevars(ls->fs, v); return; } case TK_NAME: { singlevar(ls, v); return; } default: { luaX_syntaxerror(ls, "unexpected symbol"); } } } static void suffixedexp (LexState *ls, expdesc *v) { /* suffixedexp -> primaryexp { '.' NAME | '[' exp ']' | ':' NAME funcargs | funcargs } */ FuncState *fs = ls->fs; int line = ls->linenumber; primaryexp(ls, v); for (;;) { switch (ls->t.token) { case '.': { /* fieldsel */ fieldsel(ls, v); break; } case '[': { /* '[' exp1 ']' */ expdesc key; luaK_exp2anyregup(fs, v); yindex(ls, &key); luaK_indexed(fs, v, &key); break; } case ':': { /* ':' NAME funcargs */ expdesc key; luaX_next(ls); checkname(ls, &key); luaK_self(fs, v, &key); funcargs(ls, v, line); break; } case '(': case TK_STRING: case '{': { /* funcargs */ luaK_exp2nextreg(fs, v); funcargs(ls, v, line); break; } default: return; } } } static void simpleexp (LexState *ls, expdesc *v) { /* simpleexp -> FLT | INT | STRING | NIL | TRUE | FALSE | ... | constructor | FUNCTION body | suffixedexp */ switch (ls->t.token) { case TK_FLT: { init_exp(v, VKFLT, 0); v->u.nval = ls->t.seminfo.r; break; } case TK_INT: { init_exp(v, VKINT, 0); v->u.ival = ls->t.seminfo.i; break; } case TK_STRING: { codestring(ls, v, ls->t.seminfo.ts); break; } case TK_NIL: { init_exp(v, VNIL, 0); break; } case TK_TRUE: { init_exp(v, VTRUE, 0); break; } case TK_FALSE: { init_exp(v, VFALSE, 0); break; } case TK_DOTS: { /* vararg */ FuncState *fs = ls->fs; check_condition(ls, fs->f->is_vararg, "cannot use '...' outside a vararg function"); init_exp(v, VVARARG, luaK_codeABC(fs, OP_VARARG, 0, 1, 0)); break; } case '{': { /* constructor */ constructor(ls, v); return; } case TK_FUNCTION: { luaX_next(ls); body(ls, v, 0, ls->linenumber); return; } default: { suffixedexp(ls, v); return; } } luaX_next(ls); } static UnOpr getunopr (int op) { switch (op) { case TK_NOT: return OPR_NOT; case '-': return OPR_MINUS; case '~': return OPR_BNOT; case '#': return OPR_LEN; default: return OPR_NOUNOPR; } } static BinOpr getbinopr (int op) { switch (op) { case '+': return OPR_ADD; case '-': return OPR_SUB; case '*': return OPR_MUL; case '%': return OPR_MOD; case '^': return OPR_POW; case '/': return OPR_DIV; case TK_IDIV: return OPR_IDIV; case '&': return OPR_BAND; case '|': return OPR_BOR; case '~': return OPR_BXOR; case TK_SHL: return OPR_SHL; case TK_SHR: return OPR_SHR; case TK_CONCAT: return OPR_CONCAT; case TK_NE: return OPR_NE; case TK_EQ: return OPR_EQ; case '<': return OPR_LT; case TK_LE: return OPR_LE; case '>': return OPR_GT; case TK_GE: return OPR_GE; case TK_AND: return OPR_AND; case TK_OR: return OPR_OR; default: return OPR_NOBINOPR; } } static const struct { lu_byte left; /* left priority for each binary operator */ lu_byte right; /* right priority */ } priority[] = { /* ORDER OPR */ {10, 10}, {10, 10}, /* '+' '-' */ {11, 11}, {11, 11}, /* '*' '%' */ {14, 13}, /* '^' (right associative) */ {11, 11}, {11, 11}, /* '/' '//' */ {6, 6}, {4, 4}, {5, 5}, /* '&' '|' '~' */ {7, 7}, {7, 7}, /* '<<' '>>' */ {9, 8}, /* '..' (right associative) */ {3, 3}, {3, 3}, {3, 3}, /* ==, <, <= */ {3, 3}, {3, 3}, {3, 3}, /* ~=, >, >= */ {2, 2}, {1, 1} /* and, or */ }; #define UNARY_PRIORITY 12 /* priority for unary operators */ /* ** subexpr -> (simpleexp | unop subexpr) { binop subexpr } ** where 'binop' is any binary operator with a priority higher than 'limit' */ static BinOpr subexpr (LexState *ls, expdesc *v, int limit) { BinOpr op; UnOpr uop; enterlevel(ls); uop = getunopr(ls->t.token); if (uop != OPR_NOUNOPR) { int line = ls->linenumber; luaX_next(ls); subexpr(ls, v, UNARY_PRIORITY); luaK_prefix(ls->fs, uop, v, line); } else simpleexp(ls, v); /* expand while operators have priorities higher than 'limit' */ op = getbinopr(ls->t.token); while (op != OPR_NOBINOPR && priority[op].left > limit) { expdesc v2; BinOpr nextop; int line = ls->linenumber; luaX_next(ls); luaK_infix(ls->fs, op, v); /* read sub-expression with higher priority */ nextop = subexpr(ls, &v2, priority[op].right); luaK_posfix(ls->fs, op, v, &v2, line); op = nextop; } leavelevel(ls); return op; /* return first untreated operator */ } static void expr (LexState *ls, expdesc *v) { subexpr(ls, v, 0); } /* }==================================================================== */ /* ** {====================================================================== ** Rules for Statements ** ======================================================================= */ static void block (LexState *ls) { /* block -> statlist */ FuncState *fs = ls->fs; BlockCnt bl; enterblock(fs, &bl, 0); statlist(ls); leaveblock(fs); } /* ** structure to chain all variables in the left-hand side of an ** assignment */ struct LHS_assign { struct LHS_assign *prev; expdesc v; /* variable (global, local, upvalue, or indexed) */ }; /* ** check whether, in an assignment to an upvalue/local variable, the ** upvalue/local variable is begin used in a previous assignment to a ** table. If so, save original upvalue/local value in a safe place and ** use this safe copy in the previous assignment. */ static void check_conflict (LexState *ls, struct LHS_assign *lh, expdesc *v) { FuncState *fs = ls->fs; int extra = fs->freereg; /* eventual position to save local variable */ int conflict = 0; for (; lh; lh = lh->prev) { /* check all previous assignments */ if (lh->v.k == VINDEXED) { /* assigning to a table? */ /* table is the upvalue/local being assigned now? */ if (lh->v.u.ind.vt == v->k && lh->v.u.ind.t == v->u.info) { conflict = 1; lh->v.u.ind.vt = VLOCAL; lh->v.u.ind.t = extra; /* previous assignment will use safe copy */ } /* index is the local being assigned? (index cannot be upvalue) */ if (v->k == VLOCAL && lh->v.u.ind.idx == v->u.info) { conflict = 1; lh->v.u.ind.idx = extra; /* previous assignment will use safe copy */ } } } if (conflict) { /* copy upvalue/local value to a temporary (in position 'extra') */ OpCode op = (v->k == VLOCAL) ? OP_MOVE : OP_GETUPVAL; luaK_codeABC(fs, op, extra, v->u.info, 0); luaK_reserveregs(fs, 1); } } static void assignment (LexState *ls, struct LHS_assign *lh, int nvars) { expdesc e; check_condition(ls, vkisvar(lh->v.k), "syntax error"); if (testnext(ls, ',')) { /* assignment -> ',' suffixedexp assignment */ struct LHS_assign nv; nv.prev = lh; suffixedexp(ls, &nv.v); if (nv.v.k != VINDEXED) check_conflict(ls, lh, &nv.v); checklimit(ls->fs, nvars + ls->L->nCcalls, LUAI_MAXCCALLS, "C levels"); assignment(ls, &nv, nvars+1); } else { /* assignment -> '=' explist */ int nexps; checknext(ls, '='); nexps = explist(ls, &e); if (nexps != nvars) adjust_assign(ls, nvars, nexps, &e); else { luaK_setoneret(ls->fs, &e); /* close last expression */ luaK_storevar(ls->fs, &lh->v, &e); return; /* avoid default */ } } init_exp(&e, VNONRELOC, ls->fs->freereg-1); /* default assignment */ luaK_storevar(ls->fs, &lh->v, &e); } static int cond (LexState *ls) { /* cond -> exp */ expdesc v; expr(ls, &v); /* read condition */ if (v.k == VNIL) v.k = VFALSE; /* 'falses' are all equal here */ luaK_goiftrue(ls->fs, &v); return v.f; } static void gotostat (LexState *ls, int pc) { int line = ls->linenumber; TString *label; int g; if (testnext(ls, TK_GOTO)) label = str_checkname(ls); else { luaX_next(ls); /* skip break */ label = luaS_new(ls->L, "break"); } g = newlabelentry(ls, &ls->dyd->gt, label, line, pc); findlabel(ls, g); /* close it if label already defined */ } /* check for repeated labels on the same block */ static void checkrepeated (FuncState *fs, Labellist *ll, TString *label) { int i; for (i = fs->bl->firstlabel; i < ll->n; i++) { if (eqstr(label, ll->arr[i].name)) { const char *msg = luaO_pushfstring(fs->ls->L, "label '%s' already defined on line %d", getstr(label), ll->arr[i].line); semerror(fs->ls, msg); } } } /* skip no-op statements */ static void skipnoopstat (LexState *ls) { while (ls->t.token == ';' || ls->t.token == TK_DBCOLON) statement(ls); } static void labelstat (LexState *ls, TString *label, int line) { /* label -> '::' NAME '::' */ FuncState *fs = ls->fs; Labellist *ll = &ls->dyd->label; int l; /* index of new label being created */ checkrepeated(fs, ll, label); /* check for repeated labels */ checknext(ls, TK_DBCOLON); /* skip double colon */ /* create new entry for this label */ l = newlabelentry(ls, ll, label, line, luaK_getlabel(fs)); skipnoopstat(ls); /* skip other no-op statements */ if (block_follow(ls, 0)) { /* label is last no-op statement in the block? */ /* assume that locals are already out of scope */ ll->arr[l].nactvar = fs->bl->nactvar; } findgotos(ls, &ll->arr[l]); } static void whilestat (LexState *ls, int line) { /* whilestat -> WHILE cond DO block END */ FuncState *fs = ls->fs; int whileinit; int condexit; BlockCnt bl; luaX_next(ls); /* skip WHILE */ whileinit = luaK_getlabel(fs); condexit = cond(ls); enterblock(fs, &bl, 1); checknext(ls, TK_DO); block(ls); luaK_jumpto(fs, whileinit); check_match(ls, TK_END, TK_WHILE, line); leaveblock(fs); luaK_patchtohere(fs, condexit); /* false conditions finish the loop */ } static void repeatstat (LexState *ls, int line) { /* repeatstat -> REPEAT block UNTIL cond */ int condexit; FuncState *fs = ls->fs; int repeat_init = luaK_getlabel(fs); BlockCnt bl1, bl2; enterblock(fs, &bl1, 1); /* loop block */ enterblock(fs, &bl2, 0); /* scope block */ luaX_next(ls); /* skip REPEAT */ statlist(ls); check_match(ls, TK_UNTIL, TK_REPEAT, line); condexit = cond(ls); /* read condition (inside scope block) */ if (bl2.upval) /* upvalues? */ luaK_patchclose(fs, condexit, bl2.nactvar); leaveblock(fs); /* finish scope */ luaK_patchlist(fs, condexit, repeat_init); /* close the loop */ leaveblock(fs); /* finish loop */ } static int exp1 (LexState *ls) { expdesc e; int reg; expr(ls, &e); luaK_exp2nextreg(ls->fs, &e); lua_assert(e.k == VNONRELOC); reg = e.u.info; return reg; } static void forbody (LexState *ls, int base, int line, int nvars, int isnum) { /* forbody -> DO block */ BlockCnt bl; FuncState *fs = ls->fs; int prep, endfor; adjustlocalvars(ls, 3); /* control variables */ checknext(ls, TK_DO); prep = isnum ? luaK_codeAsBx(fs, OP_FORPREP, base, NO_JUMP) : luaK_jump(fs); enterblock(fs, &bl, 0); /* scope for declared variables */ adjustlocalvars(ls, nvars); luaK_reserveregs(fs, nvars); block(ls); leaveblock(fs); /* end of scope for declared variables */ luaK_patchtohere(fs, prep); if (isnum) /* numeric for? */ endfor = luaK_codeAsBx(fs, OP_FORLOOP, base, NO_JUMP); else { /* generic for */ luaK_codeABC(fs, OP_TFORCALL, base, 0, nvars); luaK_fixline(fs, line); endfor = luaK_codeAsBx(fs, OP_TFORLOOP, base + 2, NO_JUMP); } luaK_patchlist(fs, endfor, prep + 1); luaK_fixline(fs, line); } static void fornum (LexState *ls, TString *varname, int line) { /* fornum -> NAME = exp1,exp1[,exp1] forbody */ FuncState *fs = ls->fs; int base = fs->freereg; new_localvarliteral(ls, "(for index)"); new_localvarliteral(ls, "(for limit)"); new_localvarliteral(ls, "(for step)"); new_localvar(ls, varname); checknext(ls, '='); exp1(ls); /* initial value */ checknext(ls, ','); exp1(ls); /* limit */ if (testnext(ls, ',')) exp1(ls); /* optional step */ else { /* default step = 1 */ luaK_codek(fs, fs->freereg, luaK_intK(fs, 1)); luaK_reserveregs(fs, 1); } forbody(ls, base, line, 1, 1); } static void forlist (LexState *ls, TString *indexname) { /* forlist -> NAME {,NAME} IN explist forbody */ FuncState *fs = ls->fs; expdesc e; int nvars = 4; /* gen, state, control, plus at least one declared var */ int line; int base = fs->freereg; /* create control variables */ new_localvarliteral(ls, "(for generator)"); new_localvarliteral(ls, "(for state)"); new_localvarliteral(ls, "(for control)"); /* create declared variables */ new_localvar(ls, indexname); while (testnext(ls, ',')) { new_localvar(ls, str_checkname(ls)); nvars++; } checknext(ls, TK_IN); line = ls->linenumber; adjust_assign(ls, 3, explist(ls, &e), &e); luaK_checkstack(fs, 3); /* extra space to call generator */ forbody(ls, base, line, nvars - 3, 0); } static void forstat (LexState *ls, int line) { /* forstat -> FOR (fornum | forlist) END */ FuncState *fs = ls->fs; TString *varname; BlockCnt bl; enterblock(fs, &bl, 1); /* scope for loop and control variables */ luaX_next(ls); /* skip 'for' */ varname = str_checkname(ls); /* first variable name */ switch (ls->t.token) { case '=': fornum(ls, varname, line); break; case ',': case TK_IN: forlist(ls, varname); break; default: luaX_syntaxerror(ls, "'=' or 'in' expected"); } check_match(ls, TK_END, TK_FOR, line); leaveblock(fs); /* loop scope ('break' jumps to this point) */ } static void test_then_block (LexState *ls, int *escapelist) { /* test_then_block -> [IF | ELSEIF] cond THEN block */ BlockCnt bl; FuncState *fs = ls->fs; expdesc v; int jf; /* instruction to skip 'then' code (if condition is false) */ luaX_next(ls); /* skip IF or ELSEIF */ expr(ls, &v); /* read condition */ checknext(ls, TK_THEN); if (ls->t.token == TK_GOTO || ls->t.token == TK_BREAK) { luaK_goiffalse(ls->fs, &v); /* will jump to label if condition is true */ enterblock(fs, &bl, 0); /* must enter block before 'goto' */ gotostat(ls, v.t); /* handle goto/break */ while (testnext(ls, ';')) {} /* skip colons */ if (block_follow(ls, 0)) { /* 'goto' is the entire block? */ leaveblock(fs); return; /* and that is it */ } else /* must skip over 'then' part if condition is false */ jf = luaK_jump(fs); } else { /* regular case (not goto/break) */ luaK_goiftrue(ls->fs, &v); /* skip over block if condition is false */ enterblock(fs, &bl, 0); jf = v.f; } statlist(ls); /* 'then' part */ leaveblock(fs); if (ls->t.token == TK_ELSE || ls->t.token == TK_ELSEIF) /* followed by 'else'/'elseif'? */ luaK_concat(fs, escapelist, luaK_jump(fs)); /* must jump over it */ luaK_patchtohere(fs, jf); } static void ifstat (LexState *ls, int line) { /* ifstat -> IF cond THEN block {ELSEIF cond THEN block} [ELSE block] END */ FuncState *fs = ls->fs; int escapelist = NO_JUMP; /* exit list for finished parts */ test_then_block(ls, &escapelist); /* IF cond THEN block */ while (ls->t.token == TK_ELSEIF) test_then_block(ls, &escapelist); /* ELSEIF cond THEN block */ if (testnext(ls, TK_ELSE)) block(ls); /* 'else' part */ check_match(ls, TK_END, TK_IF, line); luaK_patchtohere(fs, escapelist); /* patch escape list to 'if' end */ } static void localfunc (LexState *ls) { expdesc b; FuncState *fs = ls->fs; new_localvar(ls, str_checkname(ls)); /* new local variable */ adjustlocalvars(ls, 1); /* enter its scope */ body(ls, &b, 0, ls->linenumber); /* function created in next register */ /* debug information will only see the variable after this point! */ getlocvar(fs, b.u.info)->startpc = fs->pc; } static void localstat (LexState *ls) { /* stat -> LOCAL NAME {',' NAME} ['=' explist] */ int nvars = 0; int nexps; expdesc e; do { new_localvar(ls, str_checkname(ls)); nvars++; } while (testnext(ls, ',')); if (testnext(ls, '=')) nexps = explist(ls, &e); else { e.k = VVOID; nexps = 0; } adjust_assign(ls, nvars, nexps, &e); adjustlocalvars(ls, nvars); } static int funcname (LexState *ls, expdesc *v) { /* funcname -> NAME {fieldsel} [':' NAME] */ int ismethod = 0; singlevar(ls, v); while (ls->t.token == '.') fieldsel(ls, v); if (ls->t.token == ':') { ismethod = 1; fieldsel(ls, v); } return ismethod; } static void funcstat (LexState *ls, int line) { /* funcstat -> FUNCTION funcname body */ int ismethod; expdesc v, b; luaX_next(ls); /* skip FUNCTION */ ismethod = funcname(ls, &v); body(ls, &b, ismethod, line); luaK_storevar(ls->fs, &v, &b); luaK_fixline(ls->fs, line); /* definition "happens" in the first line */ } static void exprstat (LexState *ls) { /* stat -> func | assignment */ FuncState *fs = ls->fs; struct LHS_assign v; suffixedexp(ls, &v.v); if (ls->t.token == '=' || ls->t.token == ',') { /* stat -> assignment ? */ v.prev = NULL; assignment(ls, &v, 1); } else { /* stat -> func */ check_condition(ls, v.v.k == VCALL, "syntax error"); SETARG_C(getinstruction(fs, &v.v), 1); /* call statement uses no results */ } } static void retstat (LexState *ls) { /* stat -> RETURN [explist] [';'] */ FuncState *fs = ls->fs; expdesc e; int first, nret; /* registers with returned values */ if (block_follow(ls, 1) || ls->t.token == ';') first = nret = 0; /* return no values */ else { nret = explist(ls, &e); /* optional return values */ if (hasmultret(e.k)) { luaK_setmultret(fs, &e); if (e.k == VCALL && nret == 1) { /* tail call? */ SET_OPCODE(getinstruction(fs,&e), OP_TAILCALL); lua_assert(GETARG_A(getinstruction(fs,&e)) == fs->nactvar); } first = fs->nactvar; nret = LUA_MULTRET; /* return all values */ } else { if (nret == 1) /* only one single value? */ first = luaK_exp2anyreg(fs, &e); else { luaK_exp2nextreg(fs, &e); /* values must go to the stack */ first = fs->nactvar; /* return all active values */ lua_assert(nret == fs->freereg - first); } } } luaK_ret(fs, first, nret); testnext(ls, ';'); /* skip optional semicolon */ } static void statement (LexState *ls) { int line = ls->linenumber; /* may be needed for error messages */ enterlevel(ls); switch (ls->t.token) { case ';': { /* stat -> ';' (empty statement) */ luaX_next(ls); /* skip ';' */ break; } case TK_IF: { /* stat -> ifstat */ ifstat(ls, line); break; } case TK_WHILE: { /* stat -> whilestat */ whilestat(ls, line); break; } case TK_DO: { /* stat -> DO block END */ luaX_next(ls); /* skip DO */ block(ls); check_match(ls, TK_END, TK_DO, line); break; } case TK_FOR: { /* stat -> forstat */ forstat(ls, line); break; } case TK_REPEAT: { /* stat -> repeatstat */ repeatstat(ls, line); break; } case TK_FUNCTION: { /* stat -> funcstat */ funcstat(ls, line); break; } case TK_LOCAL: { /* stat -> localstat */ luaX_next(ls); /* skip LOCAL */ if (testnext(ls, TK_FUNCTION)) /* local function? */ localfunc(ls); else localstat(ls); break; } case TK_DBCOLON: { /* stat -> label */ luaX_next(ls); /* skip double colon */ labelstat(ls, str_checkname(ls), line); break; } case TK_RETURN: { /* stat -> retstat */ luaX_next(ls); /* skip RETURN */ retstat(ls); break; } case TK_BREAK: /* stat -> breakstat */ case TK_GOTO: { /* stat -> 'goto' NAME */ gotostat(ls, luaK_jump(ls->fs)); break; } default: { /* stat -> func | assignment */ exprstat(ls); break; } } lua_assert(ls->fs->f->maxstacksize >= ls->fs->freereg && ls->fs->freereg >= ls->fs->nactvar); ls->fs->freereg = ls->fs->nactvar; /* free registers */ leavelevel(ls); } /* }====================================================================== */ /* ** compiles the main function, which is a regular vararg function with an ** upvalue named LUA_ENV */ static void mainfunc (LexState *ls, FuncState *fs) { BlockCnt bl; expdesc v; open_func(ls, fs, &bl); fs->f->is_vararg = 1; /* main function is always declared vararg */ init_exp(&v, VLOCAL, 0); /* create and... */ newupvalue(fs, ls->envn, &v); /* ...set environment upvalue */ luaX_next(ls); /* read first token */ statlist(ls); /* parse main body */ check(ls, TK_EOS); close_func(ls); } LClosure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, Dyndata *dyd, const char *name, int firstchar) { LexState lexstate; FuncState funcstate; LClosure *cl = luaF_newLclosure(L, 1); /* create main closure */ setclLvalue(L, L->top, cl); /* anchor it (to avoid being collected) */ luaD_inctop(L); lexstate.h = luaH_new(L); /* create table for scanner */ sethvalue(L, L->top, lexstate.h); /* anchor it */ luaD_inctop(L); funcstate.f = cl->p = luaF_newproto(L); funcstate.f->source = luaS_new(L, name); /* create and anchor TString */ lua_assert(iswhite(funcstate.f)); /* do not need barrier here */ lexstate.buff = buff; lexstate.dyd = dyd; dyd->actvar.n = dyd->gt.n = dyd->label.n = 0; luaX_setinput(L, &lexstate, z, funcstate.f->source, firstchar); mainfunc(&lexstate, &funcstate); lua_assert(!funcstate.prev && funcstate.nups == 1 && !lexstate.fs); /* all scopes should be correctly finished */ lua_assert(dyd->actvar.n == 0 && dyd->gt.n == 0 && dyd->label.n == 0); L->top--; /* remove scanner's table */ return cl; /* closure is on the stack, too */ } ================================================ FILE: Tests/ApiExplorer/lua/src/lparser.h ================================================ /* ** $Id: lparser.h,v 1.76.1.1 2017/04/19 17:20:42 roberto Exp $ ** Lua Parser ** See Copyright Notice in lua.h */ #ifndef lparser_h #define lparser_h #include "llimits.h" #include "lobject.h" #include "lzio.h" /* ** Expression and variable descriptor. ** Code generation for variables and expressions can be delayed to allow ** optimizations; An 'expdesc' structure describes a potentially-delayed ** variable/expression. It has a description of its "main" value plus a ** list of conditional jumps that can also produce its value (generated ** by short-circuit operators 'and'/'or'). */ /* kinds of variables/expressions */ typedef enum { VVOID, /* when 'expdesc' describes the last expression a list, this kind means an empty list (so, no expression) */ VNIL, /* constant nil */ VTRUE, /* constant true */ VFALSE, /* constant false */ VK, /* constant in 'k'; info = index of constant in 'k' */ VKFLT, /* floating constant; nval = numerical float value */ VKINT, /* integer constant; nval = numerical integer value */ VNONRELOC, /* expression has its value in a fixed register; info = result register */ VLOCAL, /* local variable; info = local register */ VUPVAL, /* upvalue variable; info = index of upvalue in 'upvalues' */ VINDEXED, /* indexed variable; ind.vt = whether 't' is register or upvalue; ind.t = table register or upvalue; ind.idx = key's R/K index */ VJMP, /* expression is a test/comparison; info = pc of corresponding jump instruction */ VRELOCABLE, /* expression can put result in any register; info = instruction pc */ VCALL, /* expression is a function call; info = instruction pc */ VVARARG /* vararg expression; info = instruction pc */ } expkind; #define vkisvar(k) (VLOCAL <= (k) && (k) <= VINDEXED) #define vkisinreg(k) ((k) == VNONRELOC || (k) == VLOCAL) typedef struct expdesc { expkind k; union { lua_Integer ival; /* for VKINT */ lua_Number nval; /* for VKFLT */ int info; /* for generic use */ struct { /* for indexed variables (VINDEXED) */ short idx; /* index (R/K) */ lu_byte t; /* table (register or upvalue) */ lu_byte vt; /* whether 't' is register (VLOCAL) or upvalue (VUPVAL) */ } ind; } u; int t; /* patch list of 'exit when true' */ int f; /* patch list of 'exit when false' */ } expdesc; /* description of active local variable */ typedef struct Vardesc { short idx; /* variable index in stack */ } Vardesc; /* description of pending goto statements and label statements */ typedef struct Labeldesc { TString *name; /* label identifier */ int pc; /* position in code */ int line; /* line where it appeared */ lu_byte nactvar; /* local level where it appears in current block */ } Labeldesc; /* list of labels or gotos */ typedef struct Labellist { Labeldesc *arr; /* array */ int n; /* number of entries in use */ int size; /* array size */ } Labellist; /* dynamic structures used by the parser */ typedef struct Dyndata { struct { /* list of active local variables */ Vardesc *arr; int n; int size; } actvar; Labellist gt; /* list of pending gotos */ Labellist label; /* list of active labels */ } Dyndata; /* control of blocks */ struct BlockCnt; /* defined in lparser.c */ /* state needed to generate code for a given function */ typedef struct FuncState { Proto *f; /* current function header */ struct FuncState *prev; /* enclosing function */ struct LexState *ls; /* lexical state */ struct BlockCnt *bl; /* chain of current blocks */ int pc; /* next position to code (equivalent to 'ncode') */ int lasttarget; /* 'label' of last 'jump label' */ int jpc; /* list of pending jumps to 'pc' */ int nk; /* number of elements in 'k' */ int np; /* number of elements in 'p' */ int firstlocal; /* index of first local var (in Dyndata array) */ short nlocvars; /* number of elements in 'f->locvars' */ lu_byte nactvar; /* number of active local variables */ lu_byte nups; /* number of upvalues */ lu_byte freereg; /* first free register */ } FuncState; LUAI_FUNC LClosure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, Dyndata *dyd, const char *name, int firstchar); #endif ================================================ FILE: Tests/ApiExplorer/lua/src/lprefix.h ================================================ /* ** $Id: lprefix.h,v 1.2.1.1 2017/04/19 17:20:42 roberto Exp $ ** Definitions for Lua code that must come before any other header file ** See Copyright Notice in lua.h */ #ifndef lprefix_h #define lprefix_h /* ** Allows POSIX/XSI stuff */ #if !defined(LUA_USE_C89) /* { */ #if !defined(_XOPEN_SOURCE) #define _XOPEN_SOURCE 600 #elif _XOPEN_SOURCE == 0 #undef _XOPEN_SOURCE /* use -D_XOPEN_SOURCE=0 to undefine it */ #endif /* ** Allows manipulation of large files in gcc and some other compilers */ #if !defined(LUA_32BITS) && !defined(_FILE_OFFSET_BITS) #define _LARGEFILE_SOURCE 1 #define _FILE_OFFSET_BITS 64 #endif #endif /* } */ /* ** Windows stuff */ #if defined(_WIN32) /* { */ #if !defined(_CRT_SECURE_NO_WARNINGS) #define _CRT_SECURE_NO_WARNINGS /* avoid warnings about ISO C functions */ #endif #endif /* } */ #endif ================================================ FILE: Tests/ApiExplorer/lua/src/lstate.c ================================================ /* ** $Id: lstate.c,v 2.133.1.1 2017/04/19 17:39:34 roberto Exp $ ** Global State ** See Copyright Notice in lua.h */ #define lstate_c #define LUA_CORE #include "lprefix.h" #include #include #include "lua.h" #include "lapi.h" #include "ldebug.h" #include "ldo.h" #include "lfunc.h" #include "lgc.h" #include "llex.h" #include "lmem.h" #include "lstate.h" #include "lstring.h" #include "ltable.h" #include "ltm.h" #if !defined(LUAI_GCPAUSE) #define LUAI_GCPAUSE 200 /* 200% */ #endif #if !defined(LUAI_GCMUL) #define LUAI_GCMUL 200 /* GC runs 'twice the speed' of memory allocation */ #endif /* ** a macro to help the creation of a unique random seed when a state is ** created; the seed is used to randomize hashes. */ #if !defined(luai_makeseed) #include #define luai_makeseed() cast(unsigned int, time(NULL)) #endif /* ** thread state + extra space */ typedef struct LX { lu_byte extra_[LUA_EXTRASPACE]; lua_State l; } LX; /* ** Main thread combines a thread state and the global state */ typedef struct LG { LX l; global_State g; } LG; #define fromstate(L) (cast(LX *, cast(lu_byte *, (L)) - offsetof(LX, l))) /* ** Compute an initial seed as random as possible. Rely on Address Space ** Layout Randomization (if present) to increase randomness.. */ #define addbuff(b,p,e) \ { size_t t = cast(size_t, e); \ memcpy(b + p, &t, sizeof(t)); p += sizeof(t); } static unsigned int makeseed (lua_State *L) { char buff[4 * sizeof(size_t)]; unsigned int h = luai_makeseed(); int p = 0; addbuff(buff, p, L); /* heap variable */ addbuff(buff, p, &h); /* local variable */ addbuff(buff, p, luaO_nilobject); /* global variable */ addbuff(buff, p, &lua_newstate); /* public function */ lua_assert(p == sizeof(buff)); return luaS_hash(buff, p, h); } /* ** set GCdebt to a new value keeping the value (totalbytes + GCdebt) ** invariant (and avoiding underflows in 'totalbytes') */ void luaE_setdebt (global_State *g, l_mem debt) { l_mem tb = gettotalbytes(g); lua_assert(tb > 0); if (debt < tb - MAX_LMEM) debt = tb - MAX_LMEM; /* will make 'totalbytes == MAX_LMEM' */ g->totalbytes = tb - debt; g->GCdebt = debt; } CallInfo *luaE_extendCI (lua_State *L) { CallInfo *ci = luaM_new(L, CallInfo); lua_assert(L->ci->next == NULL); L->ci->next = ci; ci->previous = L->ci; ci->next = NULL; L->nci++; return ci; } /* ** free all CallInfo structures not in use by a thread */ void luaE_freeCI (lua_State *L) { CallInfo *ci = L->ci; CallInfo *next = ci->next; ci->next = NULL; while ((ci = next) != NULL) { next = ci->next; luaM_free(L, ci); L->nci--; } } /* ** free half of the CallInfo structures not in use by a thread */ void luaE_shrinkCI (lua_State *L) { CallInfo *ci = L->ci; CallInfo *next2; /* next's next */ /* while there are two nexts */ while (ci->next != NULL && (next2 = ci->next->next) != NULL) { luaM_free(L, ci->next); /* free next */ L->nci--; ci->next = next2; /* remove 'next' from the list */ next2->previous = ci; ci = next2; /* keep next's next */ } } static void stack_init (lua_State *L1, lua_State *L) { int i; CallInfo *ci; /* initialize stack array */ L1->stack = luaM_newvector(L, BASIC_STACK_SIZE, TValue); L1->stacksize = BASIC_STACK_SIZE; for (i = 0; i < BASIC_STACK_SIZE; i++) setnilvalue(L1->stack + i); /* erase new stack */ L1->top = L1->stack; L1->stack_last = L1->stack + L1->stacksize - EXTRA_STACK; /* initialize first ci */ ci = &L1->base_ci; ci->next = ci->previous = NULL; ci->callstatus = 0; ci->func = L1->top; setnilvalue(L1->top++); /* 'function' entry for this 'ci' */ ci->top = L1->top + LUA_MINSTACK; L1->ci = ci; } static void freestack (lua_State *L) { if (L->stack == NULL) return; /* stack not completely built yet */ L->ci = &L->base_ci; /* free the entire 'ci' list */ luaE_freeCI(L); lua_assert(L->nci == 0); luaM_freearray(L, L->stack, L->stacksize); /* free stack array */ } /* ** Create registry table and its predefined values */ static void init_registry (lua_State *L, global_State *g) { TValue temp; /* create registry */ Table *registry = luaH_new(L); sethvalue(L, &g->l_registry, registry); luaH_resize(L, registry, LUA_RIDX_LAST, 0); /* registry[LUA_RIDX_MAINTHREAD] = L */ setthvalue(L, &temp, L); /* temp = L */ luaH_setint(L, registry, LUA_RIDX_MAINTHREAD, &temp); /* registry[LUA_RIDX_GLOBALS] = table of globals */ sethvalue(L, &temp, luaH_new(L)); /* temp = new table (global table) */ luaH_setint(L, registry, LUA_RIDX_GLOBALS, &temp); } /* ** open parts of the state that may cause memory-allocation errors. ** ('g->version' != NULL flags that the state was completely build) */ static void f_luaopen (lua_State *L, void *ud) { global_State *g = G(L); UNUSED(ud); stack_init(L, L); /* init stack */ init_registry(L, g); luaS_init(L); luaT_init(L); luaX_init(L); g->gcrunning = 1; /* allow gc */ g->version = lua_version(NULL); luai_userstateopen(L); } /* ** preinitialize a thread with consistent values without allocating ** any memory (to avoid errors) */ static void preinit_thread (lua_State *L, global_State *g) { G(L) = g; L->stack = NULL; L->ci = NULL; L->nci = 0; L->stacksize = 0; L->twups = L; /* thread has no upvalues */ L->errorJmp = NULL; L->nCcalls = 0; L->hook = NULL; L->hookmask = 0; L->basehookcount = 0; L->allowhook = 1; resethookcount(L); L->openupval = NULL; L->nny = 1; L->status = LUA_OK; L->errfunc = 0; } static void close_state (lua_State *L) { global_State *g = G(L); luaF_close(L, L->stack); /* close all upvalues for this thread */ luaC_freeallobjects(L); /* collect all objects */ if (g->version) /* closing a fully built state? */ luai_userstateclose(L); luaM_freearray(L, G(L)->strt.hash, G(L)->strt.size); freestack(L); lua_assert(gettotalbytes(g) == sizeof(LG)); (*g->frealloc)(g->ud, fromstate(L), sizeof(LG), 0); /* free main block */ } LUA_API lua_State *lua_newthread (lua_State *L) { global_State *g = G(L); lua_State *L1; lua_lock(L); luaC_checkGC(L); /* create new thread */ L1 = &cast(LX *, luaM_newobject(L, LUA_TTHREAD, sizeof(LX)))->l; L1->marked = luaC_white(g); L1->tt = LUA_TTHREAD; /* link it on list 'allgc' */ L1->next = g->allgc; g->allgc = obj2gco(L1); /* anchor it on L stack */ setthvalue(L, L->top, L1); api_incr_top(L); preinit_thread(L1, g); L1->hookmask = L->hookmask; L1->basehookcount = L->basehookcount; L1->hook = L->hook; resethookcount(L1); /* initialize L1 extra space */ memcpy(lua_getextraspace(L1), lua_getextraspace(g->mainthread), LUA_EXTRASPACE); luai_userstatethread(L, L1); stack_init(L1, L); /* init stack */ lua_unlock(L); return L1; } void luaE_freethread (lua_State *L, lua_State *L1) { LX *l = fromstate(L1); luaF_close(L1, L1->stack); /* close all upvalues for this thread */ lua_assert(L1->openupval == NULL); luai_userstatefree(L, L1); freestack(L1); luaM_free(L, l); } LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) { int i; lua_State *L; global_State *g; LG *l = cast(LG *, (*f)(ud, NULL, LUA_TTHREAD, sizeof(LG))); if (l == NULL) return NULL; L = &l->l.l; g = &l->g; L->next = NULL; L->tt = LUA_TTHREAD; g->currentwhite = bitmask(WHITE0BIT); L->marked = luaC_white(g); preinit_thread(L, g); g->frealloc = f; g->ud = ud; g->mainthread = L; g->seed = makeseed(L); g->gcrunning = 0; /* no GC while building state */ g->GCestimate = 0; g->strt.size = g->strt.nuse = 0; g->strt.hash = NULL; setnilvalue(&g->l_registry); g->panic = NULL; g->version = NULL; g->gcstate = GCSpause; g->gckind = KGC_NORMAL; g->allgc = g->finobj = g->tobefnz = g->fixedgc = NULL; g->sweepgc = NULL; g->gray = g->grayagain = NULL; g->weak = g->ephemeron = g->allweak = NULL; g->twups = NULL; g->totalbytes = sizeof(LG); g->GCdebt = 0; g->gcfinnum = 0; g->gcpause = LUAI_GCPAUSE; g->gcstepmul = LUAI_GCMUL; for (i=0; i < LUA_NUMTAGS; i++) g->mt[i] = NULL; if (luaD_rawrunprotected(L, f_luaopen, NULL) != LUA_OK) { /* memory allocation error: free partial state */ close_state(L); L = NULL; } return L; } LUA_API void lua_close (lua_State *L) { L = G(L)->mainthread; /* only the main thread can be closed */ lua_lock(L); close_state(L); } ================================================ FILE: Tests/ApiExplorer/lua/src/lstate.h ================================================ /* ** $Id: lstate.h,v 2.133.1.1 2017/04/19 17:39:34 roberto Exp $ ** Global State ** See Copyright Notice in lua.h */ #ifndef lstate_h #define lstate_h #include "lua.h" #include "lobject.h" #include "ltm.h" #include "lzio.h" /* ** Some notes about garbage-collected objects: All objects in Lua must ** be kept somehow accessible until being freed, so all objects always ** belong to one (and only one) of these lists, using field 'next' of ** the 'CommonHeader' for the link: ** ** 'allgc': all objects not marked for finalization; ** 'finobj': all objects marked for finalization; ** 'tobefnz': all objects ready to be finalized; ** 'fixedgc': all objects that are not to be collected (currently ** only small strings, such as reserved words). ** ** Moreover, there is another set of lists that control gray objects. ** These lists are linked by fields 'gclist'. (All objects that ** can become gray have such a field. The field is not the same ** in all objects, but it always has this name.) Any gray object ** must belong to one of these lists, and all objects in these lists ** must be gray: ** ** 'gray': regular gray objects, still waiting to be visited. ** 'grayagain': objects that must be revisited at the atomic phase. ** That includes ** - black objects got in a write barrier; ** - all kinds of weak tables during propagation phase; ** - all threads. ** 'weak': tables with weak values to be cleared; ** 'ephemeron': ephemeron tables with white->white entries; ** 'allweak': tables with weak keys and/or weak values to be cleared. ** The last three lists are used only during the atomic phase. */ struct lua_longjmp; /* defined in ldo.c */ /* ** Atomic type (relative to signals) to better ensure that 'lua_sethook' ** is thread safe */ #if !defined(l_signalT) #include #define l_signalT sig_atomic_t #endif /* extra stack space to handle TM calls and some other extras */ #define EXTRA_STACK 5 #define BASIC_STACK_SIZE (2*LUA_MINSTACK) /* kinds of Garbage Collection */ #define KGC_NORMAL 0 #define KGC_EMERGENCY 1 /* gc was forced by an allocation failure */ typedef struct stringtable { TString **hash; int nuse; /* number of elements */ int size; } stringtable; /* ** Information about a call. ** When a thread yields, 'func' is adjusted to pretend that the ** top function has only the yielded values in its stack; in that ** case, the actual 'func' value is saved in field 'extra'. ** When a function calls another with a continuation, 'extra' keeps ** the function index so that, in case of errors, the continuation ** function can be called with the correct top. */ typedef struct CallInfo { StkId func; /* function index in the stack */ StkId top; /* top for this function */ struct CallInfo *previous, *next; /* dynamic call link */ union { struct { /* only for Lua functions */ StkId base; /* base for this function */ const Instruction *savedpc; } l; struct { /* only for C functions */ lua_KFunction k; /* continuation in case of yields */ ptrdiff_t old_errfunc; lua_KContext ctx; /* context info. in case of yields */ } c; } u; ptrdiff_t extra; short nresults; /* expected number of results from this function */ unsigned short callstatus; } CallInfo; /* ** Bits in CallInfo status */ #define CIST_OAH (1<<0) /* original value of 'allowhook' */ #define CIST_LUA (1<<1) /* call is running a Lua function */ #define CIST_HOOKED (1<<2) /* call is running a debug hook */ #define CIST_FRESH (1<<3) /* call is running on a fresh invocation of luaV_execute */ #define CIST_YPCALL (1<<4) /* call is a yieldable protected call */ #define CIST_TAIL (1<<5) /* call was tail called */ #define CIST_HOOKYIELD (1<<6) /* last hook called yielded */ #define CIST_LEQ (1<<7) /* using __lt for __le */ #define CIST_FIN (1<<8) /* call is running a finalizer */ #define isLua(ci) ((ci)->callstatus & CIST_LUA) /* assume that CIST_OAH has offset 0 and that 'v' is strictly 0/1 */ #define setoah(st,v) ((st) = ((st) & ~CIST_OAH) | (v)) #define getoah(st) ((st) & CIST_OAH) /* ** 'global state', shared by all threads of this state */ typedef struct global_State { lua_Alloc frealloc; /* function to reallocate memory */ void *ud; /* auxiliary data to 'frealloc' */ l_mem totalbytes; /* number of bytes currently allocated - GCdebt */ l_mem GCdebt; /* bytes allocated not yet compensated by the collector */ lu_mem GCmemtrav; /* memory traversed by the GC */ lu_mem GCestimate; /* an estimate of the non-garbage memory in use */ stringtable strt; /* hash table for strings */ TValue l_registry; unsigned int seed; /* randomized seed for hashes */ lu_byte currentwhite; lu_byte gcstate; /* state of garbage collector */ lu_byte gckind; /* kind of GC running */ lu_byte gcrunning; /* true if GC is running */ GCObject *allgc; /* list of all collectable objects */ GCObject **sweepgc; /* current position of sweep in list */ GCObject *finobj; /* list of collectable objects with finalizers */ GCObject *gray; /* list of gray objects */ GCObject *grayagain; /* list of objects to be traversed atomically */ GCObject *weak; /* list of tables with weak values */ GCObject *ephemeron; /* list of ephemeron tables (weak keys) */ GCObject *allweak; /* list of all-weak tables */ GCObject *tobefnz; /* list of userdata to be GC */ GCObject *fixedgc; /* list of objects not to be collected */ struct lua_State *twups; /* list of threads with open upvalues */ unsigned int gcfinnum; /* number of finalizers to call in each GC step */ int gcpause; /* size of pause between successive GCs */ int gcstepmul; /* GC 'granularity' */ lua_CFunction panic; /* to be called in unprotected errors */ struct lua_State *mainthread; const lua_Number *version; /* pointer to version number */ TString *memerrmsg; /* memory-error message */ TString *tmname[TM_N]; /* array with tag-method names */ struct Table *mt[LUA_NUMTAGS]; /* metatables for basic types */ TString *strcache[STRCACHE_N][STRCACHE_M]; /* cache for strings in API */ } global_State; /* ** 'per thread' state */ struct lua_State { CommonHeader; unsigned short nci; /* number of items in 'ci' list */ lu_byte status; StkId top; /* first free slot in the stack */ global_State *l_G; CallInfo *ci; /* call info for current function */ const Instruction *oldpc; /* last pc traced */ StkId stack_last; /* last free slot in the stack */ StkId stack; /* stack base */ UpVal *openupval; /* list of open upvalues in this stack */ GCObject *gclist; struct lua_State *twups; /* list of threads with open upvalues */ struct lua_longjmp *errorJmp; /* current error recover point */ CallInfo base_ci; /* CallInfo for first level (C calling Lua) */ volatile lua_Hook hook; ptrdiff_t errfunc; /* current error handling function (stack index) */ int stacksize; int basehookcount; int hookcount; unsigned short nny; /* number of non-yieldable calls in stack */ unsigned short nCcalls; /* number of nested C calls */ l_signalT hookmask; lu_byte allowhook; }; #define G(L) (L->l_G) /* ** Union of all collectable objects (only for conversions) */ union GCUnion { GCObject gc; /* common header */ struct TString ts; struct Udata u; union Closure cl; struct Table h; struct Proto p; struct lua_State th; /* thread */ }; #define cast_u(o) cast(union GCUnion *, (o)) /* macros to convert a GCObject into a specific value */ #define gco2ts(o) \ check_exp(novariant((o)->tt) == LUA_TSTRING, &((cast_u(o))->ts)) #define gco2u(o) check_exp((o)->tt == LUA_TUSERDATA, &((cast_u(o))->u)) #define gco2lcl(o) check_exp((o)->tt == LUA_TLCL, &((cast_u(o))->cl.l)) #define gco2ccl(o) check_exp((o)->tt == LUA_TCCL, &((cast_u(o))->cl.c)) #define gco2cl(o) \ check_exp(novariant((o)->tt) == LUA_TFUNCTION, &((cast_u(o))->cl)) #define gco2t(o) check_exp((o)->tt == LUA_TTABLE, &((cast_u(o))->h)) #define gco2p(o) check_exp((o)->tt == LUA_TPROTO, &((cast_u(o))->p)) #define gco2th(o) check_exp((o)->tt == LUA_TTHREAD, &((cast_u(o))->th)) /* macro to convert a Lua object into a GCObject */ #define obj2gco(v) \ check_exp(novariant((v)->tt) < LUA_TDEADKEY, (&(cast_u(v)->gc))) /* actual number of total bytes allocated */ #define gettotalbytes(g) cast(lu_mem, (g)->totalbytes + (g)->GCdebt) LUAI_FUNC void luaE_setdebt (global_State *g, l_mem debt); LUAI_FUNC void luaE_freethread (lua_State *L, lua_State *L1); LUAI_FUNC CallInfo *luaE_extendCI (lua_State *L); LUAI_FUNC void luaE_freeCI (lua_State *L); LUAI_FUNC void luaE_shrinkCI (lua_State *L); #endif ================================================ FILE: Tests/ApiExplorer/lua/src/lstring.c ================================================ /* ** $Id: lstring.c,v 2.56.1.1 2017/04/19 17:20:42 roberto Exp $ ** String table (keeps all strings handled by Lua) ** See Copyright Notice in lua.h */ #define lstring_c #define LUA_CORE #include "lprefix.h" #include #include "lua.h" #include "ldebug.h" #include "ldo.h" #include "lmem.h" #include "lobject.h" #include "lstate.h" #include "lstring.h" #define MEMERRMSG "not enough memory" /* ** Lua will use at most ~(2^LUAI_HASHLIMIT) bytes from a string to ** compute its hash */ #if !defined(LUAI_HASHLIMIT) #define LUAI_HASHLIMIT 5 #endif /* ** equality for long strings */ int luaS_eqlngstr (TString *a, TString *b) { size_t len = a->u.lnglen; lua_assert(a->tt == LUA_TLNGSTR && b->tt == LUA_TLNGSTR); return (a == b) || /* same instance or... */ ((len == b->u.lnglen) && /* equal length and ... */ (memcmp(getstr(a), getstr(b), len) == 0)); /* equal contents */ } unsigned int luaS_hash (const char *str, size_t l, unsigned int seed) { unsigned int h = seed ^ cast(unsigned int, l); size_t step = (l >> LUAI_HASHLIMIT) + 1; for (; l >= step; l -= step) h ^= ((h<<5) + (h>>2) + cast_byte(str[l - 1])); return h; } unsigned int luaS_hashlongstr (TString *ts) { lua_assert(ts->tt == LUA_TLNGSTR); if (ts->extra == 0) { /* no hash? */ ts->hash = luaS_hash(getstr(ts), ts->u.lnglen, ts->hash); ts->extra = 1; /* now it has its hash */ } return ts->hash; } /* ** resizes the string table */ void luaS_resize (lua_State *L, int newsize) { int i; stringtable *tb = &G(L)->strt; if (newsize > tb->size) { /* grow table if needed */ luaM_reallocvector(L, tb->hash, tb->size, newsize, TString *); for (i = tb->size; i < newsize; i++) tb->hash[i] = NULL; } for (i = 0; i < tb->size; i++) { /* rehash */ TString *p = tb->hash[i]; tb->hash[i] = NULL; while (p) { /* for each node in the list */ TString *hnext = p->u.hnext; /* save next */ unsigned int h = lmod(p->hash, newsize); /* new position */ p->u.hnext = tb->hash[h]; /* chain it */ tb->hash[h] = p; p = hnext; } } if (newsize < tb->size) { /* shrink table if needed */ /* vanishing slice should be empty */ lua_assert(tb->hash[newsize] == NULL && tb->hash[tb->size - 1] == NULL); luaM_reallocvector(L, tb->hash, tb->size, newsize, TString *); } tb->size = newsize; } /* ** Clear API string cache. (Entries cannot be empty, so fill them with ** a non-collectable string.) */ void luaS_clearcache (global_State *g) { int i, j; for (i = 0; i < STRCACHE_N; i++) for (j = 0; j < STRCACHE_M; j++) { if (iswhite(g->strcache[i][j])) /* will entry be collected? */ g->strcache[i][j] = g->memerrmsg; /* replace it with something fixed */ } } /* ** Initialize the string table and the string cache */ void luaS_init (lua_State *L) { global_State *g = G(L); int i, j; luaS_resize(L, MINSTRTABSIZE); /* initial size of string table */ /* pre-create memory-error message */ g->memerrmsg = luaS_newliteral(L, MEMERRMSG); luaC_fix(L, obj2gco(g->memerrmsg)); /* it should never be collected */ for (i = 0; i < STRCACHE_N; i++) /* fill cache with valid strings */ for (j = 0; j < STRCACHE_M; j++) g->strcache[i][j] = g->memerrmsg; } /* ** creates a new string object */ static TString *createstrobj (lua_State *L, size_t l, int tag, unsigned int h) { TString *ts; GCObject *o; size_t totalsize; /* total size of TString object */ totalsize = sizelstring(l); o = luaC_newobj(L, tag, totalsize); ts = gco2ts(o); ts->hash = h; ts->extra = 0; getstr(ts)[l] = '\0'; /* ending 0 */ return ts; } TString *luaS_createlngstrobj (lua_State *L, size_t l) { TString *ts = createstrobj(L, l, LUA_TLNGSTR, G(L)->seed); ts->u.lnglen = l; return ts; } void luaS_remove (lua_State *L, TString *ts) { stringtable *tb = &G(L)->strt; TString **p = &tb->hash[lmod(ts->hash, tb->size)]; while (*p != ts) /* find previous element */ p = &(*p)->u.hnext; *p = (*p)->u.hnext; /* remove element from its list */ tb->nuse--; } /* ** checks whether short string exists and reuses it or creates a new one */ static TString *internshrstr (lua_State *L, const char *str, size_t l) { TString *ts; global_State *g = G(L); unsigned int h = luaS_hash(str, l, g->seed); TString **list = &g->strt.hash[lmod(h, g->strt.size)]; lua_assert(str != NULL); /* otherwise 'memcmp'/'memcpy' are undefined */ for (ts = *list; ts != NULL; ts = ts->u.hnext) { if (l == ts->shrlen && (memcmp(str, getstr(ts), l * sizeof(char)) == 0)) { /* found! */ if (isdead(g, ts)) /* dead (but not collected yet)? */ changewhite(ts); /* resurrect it */ return ts; } } if (g->strt.nuse >= g->strt.size && g->strt.size <= MAX_INT/2) { luaS_resize(L, g->strt.size * 2); list = &g->strt.hash[lmod(h, g->strt.size)]; /* recompute with new size */ } ts = createstrobj(L, l, LUA_TSHRSTR, h); memcpy(getstr(ts), str, l * sizeof(char)); ts->shrlen = cast_byte(l); ts->u.hnext = *list; *list = ts; g->strt.nuse++; return ts; } /* ** new string (with explicit length) */ TString *luaS_newlstr (lua_State *L, const char *str, size_t l) { if (l <= LUAI_MAXSHORTLEN) /* short string? */ return internshrstr(L, str, l); else { TString *ts; if (l >= (MAX_SIZE - sizeof(TString))/sizeof(char)) luaM_toobig(L); ts = luaS_createlngstrobj(L, l); memcpy(getstr(ts), str, l * sizeof(char)); return ts; } } /* ** Create or reuse a zero-terminated string, first checking in the ** cache (using the string address as a key). The cache can contain ** only zero-terminated strings, so it is safe to use 'strcmp' to ** check hits. */ TString *luaS_new (lua_State *L, const char *str) { unsigned int i = point2uint(str) % STRCACHE_N; /* hash */ int j; TString **p = G(L)->strcache[i]; for (j = 0; j < STRCACHE_M; j++) { if (strcmp(str, getstr(p[j])) == 0) /* hit? */ return p[j]; /* that is it */ } /* normal route */ for (j = STRCACHE_M - 1; j > 0; j--) p[j] = p[j - 1]; /* move out last element */ /* new element is first in the list */ p[0] = luaS_newlstr(L, str, strlen(str)); return p[0]; } Udata *luaS_newudata (lua_State *L, size_t s) { Udata *u; GCObject *o; if (s > MAX_SIZE - sizeof(Udata)) luaM_toobig(L); o = luaC_newobj(L, LUA_TUSERDATA, sizeludata(s)); u = gco2u(o); u->len = s; u->metatable = NULL; setuservalue(L, u, luaO_nilobject); return u; } ================================================ FILE: Tests/ApiExplorer/lua/src/lstring.h ================================================ /* ** $Id: lstring.h,v 1.61.1.1 2017/04/19 17:20:42 roberto Exp $ ** String table (keep all strings handled by Lua) ** See Copyright Notice in lua.h */ #ifndef lstring_h #define lstring_h #include "lgc.h" #include "lobject.h" #include "lstate.h" #define sizelstring(l) (sizeof(union UTString) + ((l) + 1) * sizeof(char)) #define sizeludata(l) (sizeof(union UUdata) + (l)) #define sizeudata(u) sizeludata((u)->len) #define luaS_newliteral(L, s) (luaS_newlstr(L, "" s, \ (sizeof(s)/sizeof(char))-1)) /* ** test whether a string is a reserved word */ #define isreserved(s) ((s)->tt == LUA_TSHRSTR && (s)->extra > 0) /* ** equality for short strings, which are always internalized */ #define eqshrstr(a,b) check_exp((a)->tt == LUA_TSHRSTR, (a) == (b)) LUAI_FUNC unsigned int luaS_hash (const char *str, size_t l, unsigned int seed); LUAI_FUNC unsigned int luaS_hashlongstr (TString *ts); LUAI_FUNC int luaS_eqlngstr (TString *a, TString *b); LUAI_FUNC void luaS_resize (lua_State *L, int newsize); LUAI_FUNC void luaS_clearcache (global_State *g); LUAI_FUNC void luaS_init (lua_State *L); LUAI_FUNC void luaS_remove (lua_State *L, TString *ts); LUAI_FUNC Udata *luaS_newudata (lua_State *L, size_t s); LUAI_FUNC TString *luaS_newlstr (lua_State *L, const char *str, size_t l); LUAI_FUNC TString *luaS_new (lua_State *L, const char *str); LUAI_FUNC TString *luaS_createlngstrobj (lua_State *L, size_t l); #endif ================================================ FILE: Tests/ApiExplorer/lua/src/lstrlib.c ================================================ #pragma warning(disable: 4310) /* ** $Id: lstrlib.c,v 1.254.1.1 2017/04/19 17:29:57 roberto Exp $ ** Standard library for string operations and pattern-matching ** See Copyright Notice in lua.h */ #define lstrlib_c #define LUA_LIB #include "lprefix.h" #include #include #include #include #include #include #include #include #include "lua.h" #include "lauxlib.h" #include "lualib.h" /* ** maximum number of captures that a pattern can do during ** pattern-matching. This limit is arbitrary, but must fit in ** an unsigned char. */ #if !defined(LUA_MAXCAPTURES) #define LUA_MAXCAPTURES 32 #endif /* macro to 'unsign' a character */ #define uchar(c) ((unsigned char)(c)) /* ** Some sizes are better limited to fit in 'int', but must also fit in ** 'size_t'. (We assume that 'lua_Integer' cannot be smaller than 'int'.) */ #define MAX_SIZET ((size_t)(~(size_t)0)) #define MAXSIZE \ (sizeof(size_t) < sizeof(int) ? MAX_SIZET : (size_t)(INT_MAX)) static int str_len (lua_State *L) { size_t l; luaL_checklstring(L, 1, &l); lua_pushinteger(L, (lua_Integer)l); return 1; } /* translate a relative string position: negative means back from end */ static lua_Integer posrelat (lua_Integer pos, size_t len) { if (pos >= 0) return pos; else if (0u - (size_t)pos > len) return 0; else return (lua_Integer)len + pos + 1; } static int str_sub (lua_State *L) { size_t l; const char *s = luaL_checklstring(L, 1, &l); lua_Integer start = posrelat(luaL_checkinteger(L, 2), l); lua_Integer end = posrelat(luaL_optinteger(L, 3, -1), l); if (start < 1) start = 1; if (end > (lua_Integer)l) end = l; if (start <= end) lua_pushlstring(L, s + start - 1, (size_t)(end - start) + 1); else lua_pushliteral(L, ""); return 1; } static int str_reverse (lua_State *L) { size_t l, i; luaL_Buffer b; const char *s = luaL_checklstring(L, 1, &l); char *p = luaL_buffinitsize(L, &b, l); for (i = 0; i < l; i++) p[i] = s[l - i - 1]; luaL_pushresultsize(&b, l); return 1; } static int str_lower (lua_State *L) { size_t l; size_t i; luaL_Buffer b; const char *s = luaL_checklstring(L, 1, &l); char *p = luaL_buffinitsize(L, &b, l); for (i=0; i MAXSIZE / n) /* may overflow? */ return luaL_error(L, "resulting string too large"); else { size_t totallen = (size_t)n * l + (size_t)(n - 1) * lsep; luaL_Buffer b; char *p = luaL_buffinitsize(L, &b, totallen); while (n-- > 1) { /* first n-1 copies (followed by separator) */ memcpy(p, s, l * sizeof(char)); p += l; if (lsep > 0) { /* empty 'memcpy' is not that cheap */ memcpy(p, sep, lsep * sizeof(char)); p += lsep; } } memcpy(p, s, l * sizeof(char)); /* last copy (not followed by separator) */ luaL_pushresultsize(&b, totallen); } return 1; } static int str_byte (lua_State *L) { size_t l; const char *s = luaL_checklstring(L, 1, &l); lua_Integer posi = posrelat(luaL_optinteger(L, 2, 1), l); lua_Integer pose = posrelat(luaL_optinteger(L, 3, posi), l); int n, i; if (posi < 1) posi = 1; if (pose > (lua_Integer)l) pose = l; if (posi > pose) return 0; /* empty interval; return no values */ if (pose - posi >= INT_MAX) /* arithmetic overflow? */ return luaL_error(L, "string slice too long"); n = (int)(pose - posi) + 1; luaL_checkstack(L, n, "string slice too long"); for (i=0; i= ms->level || ms->capture[l].len == CAP_UNFINISHED) return luaL_error(ms->L, "invalid capture index %%%d", l + 1); return l; } static int capture_to_close (MatchState *ms) { int level = ms->level; for (level--; level>=0; level--) if (ms->capture[level].len == CAP_UNFINISHED) return level; return luaL_error(ms->L, "invalid pattern capture"); } static const char *classend (MatchState *ms, const char *p) { switch (*p++) { case L_ESC: { if (p == ms->p_end) luaL_error(ms->L, "malformed pattern (ends with '%%')"); return p+1; } case '[': { if (*p == '^') p++; do { /* look for a ']' */ if (p == ms->p_end) luaL_error(ms->L, "malformed pattern (missing ']')"); if (*(p++) == L_ESC && p < ms->p_end) p++; /* skip escapes (e.g. '%]') */ } while (*p != ']'); return p+1; } default: { return p; } } } static int match_class (int c, int cl) { int res; switch (tolower(cl)) { case 'a' : res = isalpha(c); break; case 'c' : res = iscntrl(c); break; case 'd' : res = isdigit(c); break; case 'g' : res = isgraph(c); break; case 'l' : res = islower(c); break; case 'p' : res = ispunct(c); break; case 's' : res = isspace(c); break; case 'u' : res = isupper(c); break; case 'w' : res = isalnum(c); break; case 'x' : res = isxdigit(c); break; case 'z' : res = (c == 0); break; /* deprecated option */ default: return (cl == c); } return (islower(cl) ? res : !res); } static int matchbracketclass (int c, const char *p, const char *ec) { int sig = 1; if (*(p+1) == '^') { sig = 0; p++; /* skip the '^' */ } while (++p < ec) { if (*p == L_ESC) { p++; if (match_class(c, uchar(*p))) return sig; } else if ((*(p+1) == '-') && (p+2 < ec)) { p+=2; if (uchar(*(p-2)) <= c && c <= uchar(*p)) return sig; } else if (uchar(*p) == c) return sig; } return !sig; } static int singlematch (MatchState *ms, const char *s, const char *p, const char *ep) { if (s >= ms->src_end) return 0; else { int c = uchar(*s); switch (*p) { case '.': return 1; /* matches any char */ case L_ESC: return match_class(c, uchar(*(p+1))); case '[': return matchbracketclass(c, p, ep-1); default: return (uchar(*p) == c); } } } static const char *matchbalance (MatchState *ms, const char *s, const char *p) { if (p >= ms->p_end - 1) luaL_error(ms->L, "malformed pattern (missing arguments to '%%b')"); if (*s != *p) return NULL; else { int b = *p; int e = *(p+1); int cont = 1; while (++s < ms->src_end) { if (*s == e) { if (--cont == 0) return s+1; } else if (*s == b) cont++; } } return NULL; /* string ends out of balance */ } static const char *max_expand (MatchState *ms, const char *s, const char *p, const char *ep) { ptrdiff_t i = 0; /* counts maximum expand for item */ while (singlematch(ms, s + i, p, ep)) i++; /* keeps trying to match with the maximum repetitions */ while (i>=0) { const char *res = match(ms, (s+i), ep+1); if (res) return res; i--; /* else didn't match; reduce 1 repetition to try again */ } return NULL; } static const char *min_expand (MatchState *ms, const char *s, const char *p, const char *ep) { for (;;) { const char *res = match(ms, s, ep+1); if (res != NULL) return res; else if (singlematch(ms, s, p, ep)) s++; /* try with one more repetition */ else return NULL; } } static const char *start_capture (MatchState *ms, const char *s, const char *p, int what) { const char *res; int level = ms->level; if (level >= LUA_MAXCAPTURES) luaL_error(ms->L, "too many captures"); ms->capture[level].init = s; ms->capture[level].len = what; ms->level = level+1; if ((res=match(ms, s, p)) == NULL) /* match failed? */ ms->level--; /* undo capture */ return res; } static const char *end_capture (MatchState *ms, const char *s, const char *p) { int l = capture_to_close(ms); const char *res; ms->capture[l].len = s - ms->capture[l].init; /* close capture */ if ((res = match(ms, s, p)) == NULL) /* match failed? */ ms->capture[l].len = CAP_UNFINISHED; /* undo capture */ return res; } static const char *match_capture (MatchState *ms, const char *s, int l) { size_t len; l = check_capture(ms, l); len = ms->capture[l].len; if ((size_t)(ms->src_end-s) >= len && memcmp(ms->capture[l].init, s, len) == 0) return s+len; else return NULL; } static const char *match (MatchState *ms, const char *s, const char *p) { if (ms->matchdepth-- == 0) luaL_error(ms->L, "pattern too complex"); init: /* using goto's to optimize tail recursion */ if (p != ms->p_end) { /* end of pattern? */ switch (*p) { case '(': { /* start capture */ if (*(p + 1) == ')') /* position capture? */ s = start_capture(ms, s, p + 2, CAP_POSITION); else s = start_capture(ms, s, p + 1, CAP_UNFINISHED); break; } case ')': { /* end capture */ s = end_capture(ms, s, p + 1); break; } case '$': { if ((p + 1) != ms->p_end) /* is the '$' the last char in pattern? */ goto dflt; /* no; go to default */ s = (s == ms->src_end) ? s : NULL; /* check end of string */ break; } case L_ESC: { /* escaped sequences not in the format class[*+?-]? */ switch (*(p + 1)) { case 'b': { /* balanced string? */ s = matchbalance(ms, s, p + 2); if (s != NULL) { p += 4; goto init; /* return match(ms, s, p + 4); */ } /* else fail (s == NULL) */ break; } case 'f': { /* frontier? */ const char *ep; char previous; p += 2; if (*p != '[') luaL_error(ms->L, "missing '[' after '%%f' in pattern"); ep = classend(ms, p); /* points to what is next */ previous = (s == ms->src_init) ? '\0' : *(s - 1); if (!matchbracketclass(uchar(previous), p, ep - 1) && matchbracketclass(uchar(*s), p, ep - 1)) { p = ep; goto init; /* return match(ms, s, ep); */ } s = NULL; /* match failed */ break; } case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { /* capture results (%0-%9)? */ s = match_capture(ms, s, uchar(*(p + 1))); if (s != NULL) { p += 2; goto init; /* return match(ms, s, p + 2) */ } break; } default: goto dflt; } break; } default: dflt: { /* pattern class plus optional suffix */ const char *ep = classend(ms, p); /* points to optional suffix */ /* does not match at least once? */ if (!singlematch(ms, s, p, ep)) { if (*ep == '*' || *ep == '?' || *ep == '-') { /* accept empty? */ p = ep + 1; goto init; /* return match(ms, s, ep + 1); */ } else /* '+' or no suffix */ s = NULL; /* fail */ } else { /* matched once */ switch (*ep) { /* handle optional suffix */ case '?': { /* optional */ const char *res; if ((res = match(ms, s + 1, ep + 1)) != NULL) s = res; else { p = ep + 1; goto init; /* else return match(ms, s, ep + 1); */ } break; } case '+': /* 1 or more repetitions */ s++; /* 1 match already done */ /* FALLTHROUGH */ case '*': /* 0 or more repetitions */ s = max_expand(ms, s, p, ep); break; case '-': /* 0 or more repetitions (minimum) */ s = min_expand(ms, s, p, ep); break; default: /* no suffix */ s++; p = ep; goto init; /* return match(ms, s + 1, ep); */ } } break; } } } ms->matchdepth++; return s; } static const char *lmemfind (const char *s1, size_t l1, const char *s2, size_t l2) { if (l2 == 0) return s1; /* empty strings are everywhere */ else if (l2 > l1) return NULL; /* avoids a negative 'l1' */ else { const char *init; /* to search for a '*s2' inside 's1' */ l2--; /* 1st char will be checked by 'memchr' */ l1 = l1-l2; /* 's2' cannot be found after that */ while (l1 > 0 && (init = (const char *)memchr(s1, *s2, l1)) != NULL) { init++; /* 1st char is already checked */ if (memcmp(init, s2+1, l2) == 0) return init-1; else { /* correct 'l1' and 's1' to try again */ l1 -= init-s1; s1 = init; } } return NULL; /* not found */ } } static void push_onecapture (MatchState *ms, int i, const char *s, const char *e) { if (i >= ms->level) { if (i == 0) /* ms->level == 0, too */ lua_pushlstring(ms->L, s, e - s); /* add whole match */ else luaL_error(ms->L, "invalid capture index %%%d", i + 1); } else { ptrdiff_t l = ms->capture[i].len; if (l == CAP_UNFINISHED) luaL_error(ms->L, "unfinished capture"); if (l == CAP_POSITION) lua_pushinteger(ms->L, (ms->capture[i].init - ms->src_init) + 1); else lua_pushlstring(ms->L, ms->capture[i].init, l); } } static int push_captures (MatchState *ms, const char *s, const char *e) { int i; int nlevels = (ms->level == 0 && s) ? 1 : ms->level; luaL_checkstack(ms->L, nlevels, "too many captures"); for (i = 0; i < nlevels; i++) push_onecapture(ms, i, s, e); return nlevels; /* number of strings pushed */ } /* check whether pattern has no special characters */ static int nospecials (const char *p, size_t l) { size_t upto = 0; do { if (strpbrk(p + upto, SPECIALS)) return 0; /* pattern has a special character */ upto += strlen(p + upto) + 1; /* may have more after \0 */ } while (upto <= l); return 1; /* no special chars found */ } static void prepstate (MatchState *ms, lua_State *L, const char *s, size_t ls, const char *p, size_t lp) { ms->L = L; ms->matchdepth = MAXCCALLS; ms->src_init = s; ms->src_end = s + ls; ms->p_end = p + lp; } static void reprepstate (MatchState *ms) { ms->level = 0; lua_assert(ms->matchdepth == MAXCCALLS); } static int str_find_aux (lua_State *L, int find) { size_t ls, lp; const char *s = luaL_checklstring(L, 1, &ls); const char *p = luaL_checklstring(L, 2, &lp); lua_Integer init = posrelat(luaL_optinteger(L, 3, 1), ls); if (init < 1) init = 1; else if (init > (lua_Integer)ls + 1) { /* start after string's end? */ lua_pushnil(L); /* cannot find anything */ return 1; } /* explicit request or no special characters? */ if (find && (lua_toboolean(L, 4) || nospecials(p, lp))) { /* do a plain search */ const char *s2 = lmemfind(s + init - 1, ls - (size_t)init + 1, p, lp); if (s2) { lua_pushinteger(L, (s2 - s) + 1); lua_pushinteger(L, (s2 - s) + lp); return 2; } } else { MatchState ms; const char *s1 = s + init - 1; int anchor = (*p == '^'); if (anchor) { p++; lp--; /* skip anchor character */ } prepstate(&ms, L, s, ls, p, lp); do { const char *res; reprepstate(&ms); if ((res=match(&ms, s1, p)) != NULL) { if (find) { lua_pushinteger(L, (s1 - s) + 1); /* start */ lua_pushinteger(L, res - s); /* end */ return push_captures(&ms, NULL, 0) + 2; } else return push_captures(&ms, s1, res); } } while (s1++ < ms.src_end && !anchor); } lua_pushnil(L); /* not found */ return 1; } static int str_find (lua_State *L) { return str_find_aux(L, 1); } static int str_match (lua_State *L) { return str_find_aux(L, 0); } /* state for 'gmatch' */ typedef struct GMatchState { const char *src; /* current position */ const char *p; /* pattern */ const char *lastmatch; /* end of last match */ MatchState ms; /* match state */ } GMatchState; static int gmatch_aux (lua_State *L) { GMatchState *gm = (GMatchState *)lua_touserdata(L, lua_upvalueindex(3)); const char *src; gm->ms.L = L; for (src = gm->src; src <= gm->ms.src_end; src++) { const char *e; reprepstate(&gm->ms); if ((e = match(&gm->ms, src, gm->p)) != NULL && e != gm->lastmatch) { gm->src = gm->lastmatch = e; return push_captures(&gm->ms, src, e); } } return 0; /* not found */ } static int gmatch (lua_State *L) { size_t ls, lp; const char *s = luaL_checklstring(L, 1, &ls); const char *p = luaL_checklstring(L, 2, &lp); GMatchState *gm; lua_settop(L, 2); /* keep them on closure to avoid being collected */ gm = (GMatchState *)lua_newuserdata(L, sizeof(GMatchState)); prepstate(&gm->ms, L, s, ls, p, lp); gm->src = s; gm->p = p; gm->lastmatch = NULL; lua_pushcclosure(L, gmatch_aux, 3); return 1; } static void add_s (MatchState *ms, luaL_Buffer *b, const char *s, const char *e) { size_t l, i; lua_State *L = ms->L; const char *news = lua_tolstring(L, 3, &l); for (i = 0; i < l; i++) { if (news[i] != L_ESC) luaL_addchar(b, news[i]); else { i++; /* skip ESC */ if (!isdigit(uchar(news[i]))) { if (news[i] != L_ESC) luaL_error(L, "invalid use of '%c' in replacement string", L_ESC); luaL_addchar(b, news[i]); } else if (news[i] == '0') luaL_addlstring(b, s, e - s); else { push_onecapture(ms, news[i] - '1', s, e); luaL_tolstring(L, -1, NULL); /* if number, convert it to string */ lua_remove(L, -2); /* remove original value */ luaL_addvalue(b); /* add capture to accumulated result */ } } } } static void add_value (MatchState *ms, luaL_Buffer *b, const char *s, const char *e, int tr) { lua_State *L = ms->L; switch (tr) { case LUA_TFUNCTION: { int n; lua_pushvalue(L, 3); n = push_captures(ms, s, e); lua_call(L, n, 1); break; } case LUA_TTABLE: { push_onecapture(ms, 0, s, e); lua_gettable(L, 3); break; } default: { /* LUA_TNUMBER or LUA_TSTRING */ add_s(ms, b, s, e); return; } } if (!lua_toboolean(L, -1)) { /* nil or false? */ lua_pop(L, 1); lua_pushlstring(L, s, e - s); /* keep original text */ } else if (!lua_isstring(L, -1)) luaL_error(L, "invalid replacement value (a %s)", luaL_typename(L, -1)); luaL_addvalue(b); /* add result to accumulator */ } static int str_gsub (lua_State *L) { size_t srcl, lp; const char *src = luaL_checklstring(L, 1, &srcl); /* subject */ const char *p = luaL_checklstring(L, 2, &lp); /* pattern */ const char *lastmatch = NULL; /* end of last match */ int tr = lua_type(L, 3); /* replacement type */ lua_Integer max_s = luaL_optinteger(L, 4, srcl + 1); /* max replacements */ int anchor = (*p == '^'); lua_Integer n = 0; /* replacement count */ MatchState ms; luaL_Buffer b; luaL_argcheck(L, tr == LUA_TNUMBER || tr == LUA_TSTRING || tr == LUA_TFUNCTION || tr == LUA_TTABLE, 3, "string/function/table expected"); luaL_buffinit(L, &b); if (anchor) { p++; lp--; /* skip anchor character */ } prepstate(&ms, L, src, srcl, p, lp); while (n < max_s) { const char *e; reprepstate(&ms); /* (re)prepare state for new match */ if ((e = match(&ms, src, p)) != NULL && e != lastmatch) { /* match? */ n++; add_value(&ms, &b, src, e, tr); /* add replacement to buffer */ src = lastmatch = e; } else if (src < ms.src_end) /* otherwise, skip one character */ luaL_addchar(&b, *src++); else break; /* end of subject */ if (anchor) break; } luaL_addlstring(&b, src, ms.src_end-src); luaL_pushresult(&b); lua_pushinteger(L, n); /* number of substitutions */ return 2; } /* }====================================================== */ /* ** {====================================================== ** STRING FORMAT ** ======================================================= */ #if !defined(lua_number2strx) /* { */ /* ** Hexadecimal floating-point formatter */ #include #define SIZELENMOD (sizeof(LUA_NUMBER_FRMLEN)/sizeof(char)) /* ** Number of bits that goes into the first digit. It can be any value ** between 1 and 4; the following definition tries to align the number ** to nibble boundaries by making what is left after that first digit a ** multiple of 4. */ #define L_NBFD ((l_mathlim(MANT_DIG) - 1)%4 + 1) /* ** Add integer part of 'x' to buffer and return new 'x' */ static lua_Number adddigit (char *buff, int n, lua_Number x) { lua_Number dd = l_mathop(floor)(x); /* get integer part from 'x' */ int d = (int)dd; buff[n] = (d < 10 ? d + '0' : d - 10 + 'a'); /* add to buffer */ return x - dd; /* return what is left */ } static int num2straux (char *buff, int sz, lua_Number x) { /* if 'inf' or 'NaN', format it like '%g' */ if (x != x || x == (lua_Number)HUGE_VAL || x == -(lua_Number)HUGE_VAL) return l_sprintf(buff, sz, LUA_NUMBER_FMT, (LUAI_UACNUMBER)x); else if (x == 0) { /* can be -0... */ /* create "0" or "-0" followed by exponent */ return l_sprintf(buff, sz, LUA_NUMBER_FMT "x0p+0", (LUAI_UACNUMBER)x); } else { int e; lua_Number m = l_mathop(frexp)(x, &e); /* 'x' fraction and exponent */ int n = 0; /* character count */ if (m < 0) { /* is number negative? */ buff[n++] = '-'; /* add signal */ m = -m; /* make it positive */ } buff[n++] = '0'; buff[n++] = 'x'; /* add "0x" */ m = adddigit(buff, n++, m * (1 << L_NBFD)); /* add first digit */ e -= L_NBFD; /* this digit goes before the radix point */ if (m > 0) { /* more digits? */ buff[n++] = lua_getlocaledecpoint(); /* add radix point */ do { /* add as many digits as needed */ m = adddigit(buff, n++, m * 16); } while (m > 0); } n += l_sprintf(buff + n, sz - n, "p%+d", e); /* add exponent */ lua_assert(n < sz); return n; } } static int lua_number2strx (lua_State *L, char *buff, int sz, const char *fmt, lua_Number x) { int n = num2straux(buff, sz, x); if (fmt[SIZELENMOD] == 'A') { int i; for (i = 0; i < n; i++) buff[i] = toupper(uchar(buff[i])); } else if (fmt[SIZELENMOD] != 'a') return luaL_error(L, "modifiers for format '%%a'/'%%A' not implemented"); return n; } #endif /* } */ /* ** Maximum size of each formatted item. This maximum size is produced ** by format('%.99f', -maxfloat), and is equal to 99 + 3 ('-', '.', ** and '\0') + number of decimal digits to represent maxfloat (which ** is maximum exponent + 1). (99+3+1 then rounded to 120 for "extra ** expenses", such as locale-dependent stuff) */ #define MAX_ITEM (120 + l_mathlim(MAX_10_EXP)) /* valid flags in a format specification */ #define FLAGS "-+ #0" /* ** maximum size of each format specification (such as "%-099.99d") */ #define MAX_FORMAT 32 static void addquoted (luaL_Buffer *b, const char *s, size_t len) { luaL_addchar(b, '"'); while (len--) { if (*s == '"' || *s == '\\' || *s == '\n') { luaL_addchar(b, '\\'); luaL_addchar(b, *s); } else if (iscntrl(uchar(*s))) { char buff[10]; if (!isdigit(uchar(*(s+1)))) l_sprintf(buff, sizeof(buff), "\\%d", (int)uchar(*s)); else l_sprintf(buff, sizeof(buff), "\\%03d", (int)uchar(*s)); luaL_addstring(b, buff); } else luaL_addchar(b, *s); s++; } luaL_addchar(b, '"'); } /* ** Ensures the 'buff' string uses a dot as the radix character. */ static void checkdp (char *buff, int nb) { if (memchr(buff, '.', nb) == NULL) { /* no dot? */ char point = lua_getlocaledecpoint(); /* try locale point */ char *ppoint = (char *)memchr(buff, point, nb); if (ppoint) *ppoint = '.'; /* change it to a dot */ } } static void addliteral (lua_State *L, luaL_Buffer *b, int arg) { switch (lua_type(L, arg)) { case LUA_TSTRING: { size_t len; const char *s = lua_tolstring(L, arg, &len); addquoted(b, s, len); break; } case LUA_TNUMBER: { char *buff = luaL_prepbuffsize(b, MAX_ITEM); int nb; if (!lua_isinteger(L, arg)) { /* float? */ lua_Number n = lua_tonumber(L, arg); /* write as hexa ('%a') */ nb = lua_number2strx(L, buff, MAX_ITEM, "%" LUA_NUMBER_FRMLEN "a", n); checkdp(buff, nb); /* ensure it uses a dot */ } else { /* integers */ lua_Integer n = lua_tointeger(L, arg); const char *format = (n == LUA_MININTEGER) /* corner case? */ ? "0x%" LUA_INTEGER_FRMLEN "x" /* use hexa */ : LUA_INTEGER_FMT; /* else use default format */ nb = l_sprintf(buff, MAX_ITEM, format, (LUAI_UACINT)n); } luaL_addsize(b, nb); break; } case LUA_TNIL: case LUA_TBOOLEAN: { luaL_tolstring(L, arg, NULL); luaL_addvalue(b); break; } default: { luaL_argerror(L, arg, "value has no literal form"); } } } static const char *scanformat (lua_State *L, const char *strfrmt, char *form) { const char *p = strfrmt; while (*p != '\0' && strchr(FLAGS, *p) != NULL) p++; /* skip flags */ if ((size_t)(p - strfrmt) >= sizeof(FLAGS)/sizeof(char)) luaL_error(L, "invalid format (repeated flags)"); if (isdigit(uchar(*p))) p++; /* skip width */ if (isdigit(uchar(*p))) p++; /* (2 digits at most) */ if (*p == '.') { p++; if (isdigit(uchar(*p))) p++; /* skip precision */ if (isdigit(uchar(*p))) p++; /* (2 digits at most) */ } if (isdigit(uchar(*p))) luaL_error(L, "invalid format (width or precision too long)"); *(form++) = '%'; memcpy(form, strfrmt, ((p - strfrmt) + 1) * sizeof(char)); form += (p - strfrmt) + 1; *form = '\0'; return p; } /* ** add length modifier into formats */ static void addlenmod (char *form, const char *lenmod) { size_t l = strlen(form); size_t lm = strlen(lenmod); char spec = form[l - 1]; strcpy(form + l - 1, lenmod); form[l + lm - 1] = spec; form[l + lm] = '\0'; } static int str_format (lua_State *L) { int top = lua_gettop(L); int arg = 1; size_t sfl; const char *strfrmt = luaL_checklstring(L, arg, &sfl); const char *strfrmt_end = strfrmt+sfl; luaL_Buffer b; luaL_buffinit(L, &b); while (strfrmt < strfrmt_end) { if (*strfrmt != L_ESC) luaL_addchar(&b, *strfrmt++); else if (*++strfrmt == L_ESC) luaL_addchar(&b, *strfrmt++); /* %% */ else { /* format item */ char form[MAX_FORMAT]; /* to store the format ('%...') */ char *buff = luaL_prepbuffsize(&b, MAX_ITEM); /* to put formatted item */ int nb = 0; /* number of bytes in added item */ if (++arg > top) luaL_argerror(L, arg, "no value"); strfrmt = scanformat(L, strfrmt, form); switch (*strfrmt++) { case 'c': { nb = l_sprintf(buff, MAX_ITEM, form, (int)luaL_checkinteger(L, arg)); break; } case 'd': case 'i': case 'o': case 'u': case 'x': case 'X': { lua_Integer n = luaL_checkinteger(L, arg); addlenmod(form, LUA_INTEGER_FRMLEN); nb = l_sprintf(buff, MAX_ITEM, form, (LUAI_UACINT)n); break; } case 'a': case 'A': addlenmod(form, LUA_NUMBER_FRMLEN); nb = lua_number2strx(L, buff, MAX_ITEM, form, luaL_checknumber(L, arg)); break; case 'e': case 'E': case 'f': case 'g': case 'G': { lua_Number n = luaL_checknumber(L, arg); addlenmod(form, LUA_NUMBER_FRMLEN); nb = l_sprintf(buff, MAX_ITEM, form, (LUAI_UACNUMBER)n); break; } case 'q': { addliteral(L, &b, arg); break; } case 's': { size_t l; const char *s = luaL_tolstring(L, arg, &l); if (form[2] == '\0') /* no modifiers? */ luaL_addvalue(&b); /* keep entire string */ else { luaL_argcheck(L, l == strlen(s), arg, "string contains zeros"); if (!strchr(form, '.') && l >= 100) { /* no precision and string is too long to be formatted */ luaL_addvalue(&b); /* keep entire string */ } else { /* format the string into 'buff' */ nb = l_sprintf(buff, MAX_ITEM, form, s); lua_pop(L, 1); /* remove result from 'luaL_tolstring' */ } } break; } default: { /* also treat cases 'pnLlh' */ return luaL_error(L, "invalid option '%%%c' to 'format'", *(strfrmt - 1)); } } lua_assert(nb < MAX_ITEM); luaL_addsize(&b, nb); } } luaL_pushresult(&b); return 1; } /* }====================================================== */ /* ** {====================================================== ** PACK/UNPACK ** ======================================================= */ /* value used for padding */ #if !defined(LUAL_PACKPADBYTE) #define LUAL_PACKPADBYTE 0x00 #endif /* maximum size for the binary representation of an integer */ #define MAXINTSIZE 16 /* number of bits in a character */ #define NB CHAR_BIT /* mask for one character (NB 1's) */ #define MC ((1 << NB) - 1) /* size of a lua_Integer */ #define SZINT ((int)sizeof(lua_Integer)) /* dummy union to get native endianness */ static const union { int dummy; char little; /* true iff machine is little endian */ } nativeendian = {1}; /* dummy structure to get native alignment requirements */ struct cD { char c; union { double d; void *p; lua_Integer i; lua_Number n; } u; }; #define MAXALIGN (offsetof(struct cD, u)) /* ** Union for serializing floats */ typedef union Ftypes { float f; double d; lua_Number n; char buff[5 * sizeof(lua_Number)]; /* enough for any float type */ } Ftypes; /* ** information to pack/unpack stuff */ typedef struct Header { lua_State *L; int islittle; int maxalign; } Header; /* ** options for pack/unpack */ typedef enum KOption { Kint, /* signed integers */ Kuint, /* unsigned integers */ Kfloat, /* floating-point numbers */ Kchar, /* fixed-length strings */ Kstring, /* strings with prefixed length */ Kzstr, /* zero-terminated strings */ Kpadding, /* padding */ Kpaddalign, /* padding for alignment */ Knop /* no-op (configuration or spaces) */ } KOption; /* ** Read an integer numeral from string 'fmt' or return 'df' if ** there is no numeral */ static int digit (int c) { return '0' <= c && c <= '9'; } static int getnum (const char **fmt, int df) { if (!digit(**fmt)) /* no number? */ return df; /* return default value */ else { int a = 0; do { a = a*10 + (*((*fmt)++) - '0'); } while (digit(**fmt) && a <= ((int)MAXSIZE - 9)/10); return a; } } /* ** Read an integer numeral and raises an error if it is larger ** than the maximum size for integers. */ static int getnumlimit (Header *h, const char **fmt, int df) { int sz = getnum(fmt, df); if (sz > MAXINTSIZE || sz <= 0) return luaL_error(h->L, "integral size (%d) out of limits [1,%d]", sz, MAXINTSIZE); return sz; } /* ** Initialize Header */ static void initheader (lua_State *L, Header *h) { h->L = L; h->islittle = nativeendian.little; h->maxalign = 1; } /* ** Read and classify next option. 'size' is filled with option's size. */ static KOption getoption (Header *h, const char **fmt, int *size) { int opt = *((*fmt)++); *size = 0; /* default */ switch (opt) { case 'b': *size = sizeof(char); return Kint; case 'B': *size = sizeof(char); return Kuint; case 'h': *size = sizeof(short); return Kint; case 'H': *size = sizeof(short); return Kuint; case 'l': *size = sizeof(long); return Kint; case 'L': *size = sizeof(long); return Kuint; case 'j': *size = sizeof(lua_Integer); return Kint; case 'J': *size = sizeof(lua_Integer); return Kuint; case 'T': *size = sizeof(size_t); return Kuint; case 'f': *size = sizeof(float); return Kfloat; case 'd': *size = sizeof(double); return Kfloat; case 'n': *size = sizeof(lua_Number); return Kfloat; case 'i': *size = getnumlimit(h, fmt, sizeof(int)); return Kint; case 'I': *size = getnumlimit(h, fmt, sizeof(int)); return Kuint; case 's': *size = getnumlimit(h, fmt, sizeof(size_t)); return Kstring; case 'c': *size = getnum(fmt, -1); if (*size == -1) luaL_error(h->L, "missing size for format option 'c'"); return Kchar; case 'z': return Kzstr; case 'x': *size = 1; return Kpadding; case 'X': return Kpaddalign; case ' ': break; case '<': h->islittle = 1; break; case '>': h->islittle = 0; break; case '=': h->islittle = nativeendian.little; break; case '!': h->maxalign = getnumlimit(h, fmt, MAXALIGN); break; default: luaL_error(h->L, "invalid format option '%c'", opt); } return Knop; } /* ** Read, classify, and fill other details about the next option. ** 'psize' is filled with option's size, 'notoalign' with its ** alignment requirements. ** Local variable 'size' gets the size to be aligned. (Kpadal option ** always gets its full alignment, other options are limited by ** the maximum alignment ('maxalign'). Kchar option needs no alignment ** despite its size. */ static KOption getdetails (Header *h, size_t totalsize, const char **fmt, int *psize, int *ntoalign) { KOption opt = getoption(h, fmt, psize); int align = *psize; /* usually, alignment follows size */ if (opt == Kpaddalign) { /* 'X' gets alignment from following option */ if (**fmt == '\0' || getoption(h, fmt, &align) == Kchar || align == 0) luaL_argerror(h->L, 1, "invalid next option for option 'X'"); } if (align <= 1 || opt == Kchar) /* need no alignment? */ *ntoalign = 0; else { if (align > h->maxalign) /* enforce maximum alignment */ align = h->maxalign; if ((align & (align - 1)) != 0) /* is 'align' not a power of 2? */ luaL_argerror(h->L, 1, "format asks for alignment not power of 2"); *ntoalign = (align - (int)(totalsize & (align - 1))) & (align - 1); } return opt; } /* ** Pack integer 'n' with 'size' bytes and 'islittle' endianness. ** The final 'if' handles the case when 'size' is larger than ** the size of a Lua integer, correcting the extra sign-extension ** bytes if necessary (by default they would be zeros). */ static void packint (luaL_Buffer *b, lua_Unsigned n, int islittle, int size, int neg) { char *buff = luaL_prepbuffsize(b, size); int i; buff[islittle ? 0 : size - 1] = (char)(n & MC); /* first byte */ for (i = 1; i < size; i++) { n >>= NB; buff[islittle ? i : size - 1 - i] = (char)(n & MC); } if (neg && size > SZINT) { /* negative number need sign extension? */ for (i = SZINT; i < size; i++) /* correct extra bytes */ buff[islittle ? i : size - 1 - i] = (char)MC; } luaL_addsize(b, size); /* add result to buffer */ } /* ** Copy 'size' bytes from 'src' to 'dest', correcting endianness if ** given 'islittle' is different from native endianness. */ static void copywithendian (volatile char *dest, volatile const char *src, int size, int islittle) { if (islittle == nativeendian.little) { while (size-- != 0) *(dest++) = *(src++); } else { dest += size - 1; while (size-- != 0) *(dest--) = *(src++); } } static int str_pack (lua_State *L) { luaL_Buffer b; Header h; const char *fmt = luaL_checkstring(L, 1); /* format string */ int arg = 1; /* current argument to pack */ size_t totalsize = 0; /* accumulate total size of result */ initheader(L, &h); lua_pushnil(L); /* mark to separate arguments from string buffer */ luaL_buffinit(L, &b); while (*fmt != '\0') { int size, ntoalign; KOption opt = getdetails(&h, totalsize, &fmt, &size, &ntoalign); totalsize += ntoalign + size; while (ntoalign-- > 0) luaL_addchar(&b, LUAL_PACKPADBYTE); /* fill alignment */ arg++; switch (opt) { case Kint: { /* signed integers */ lua_Integer n = luaL_checkinteger(L, arg); if (size < SZINT) { /* need overflow check? */ lua_Integer lim = (lua_Integer)1 << ((size * NB) - 1); luaL_argcheck(L, -lim <= n && n < lim, arg, "integer overflow"); } packint(&b, (lua_Unsigned)n, h.islittle, size, (n < 0)); break; } case Kuint: { /* unsigned integers */ lua_Integer n = luaL_checkinteger(L, arg); if (size < SZINT) /* need overflow check? */ luaL_argcheck(L, (lua_Unsigned)n < ((lua_Unsigned)1 << (size * NB)), arg, "unsigned overflow"); packint(&b, (lua_Unsigned)n, h.islittle, size, 0); break; } case Kfloat: { /* floating-point options */ volatile Ftypes u; char *buff = luaL_prepbuffsize(&b, size); lua_Number n = luaL_checknumber(L, arg); /* get argument */ if (size == sizeof(u.f)) u.f = (float)n; /* copy it into 'u' */ else if (size == sizeof(u.d)) u.d = (double)n; else u.n = n; /* move 'u' to final result, correcting endianness if needed */ copywithendian(buff, u.buff, size, h.islittle); luaL_addsize(&b, size); break; } case Kchar: { /* fixed-size string */ size_t len; const char *s = luaL_checklstring(L, arg, &len); luaL_argcheck(L, len <= (size_t)size, arg, "string longer than given size"); luaL_addlstring(&b, s, len); /* add string */ while (len++ < (size_t)size) /* pad extra space */ luaL_addchar(&b, LUAL_PACKPADBYTE); break; } case Kstring: { /* strings with length count */ size_t len; const char *s = luaL_checklstring(L, arg, &len); luaL_argcheck(L, size >= (int)sizeof(size_t) || len < ((size_t)1 << (size * NB)), arg, "string length does not fit in given size"); packint(&b, (lua_Unsigned)len, h.islittle, size, 0); /* pack length */ luaL_addlstring(&b, s, len); totalsize += len; break; } case Kzstr: { /* zero-terminated string */ size_t len; const char *s = luaL_checklstring(L, arg, &len); luaL_argcheck(L, strlen(s) == len, arg, "string contains zeros"); luaL_addlstring(&b, s, len); luaL_addchar(&b, '\0'); /* add zero at the end */ totalsize += len + 1; break; } case Kpadding: luaL_addchar(&b, LUAL_PACKPADBYTE); /* FALLTHROUGH */ case Kpaddalign: case Knop: arg--; /* undo increment */ break; } } luaL_pushresult(&b); return 1; } static int str_packsize (lua_State *L) { Header h; const char *fmt = luaL_checkstring(L, 1); /* format string */ size_t totalsize = 0; /* accumulate total size of result */ initheader(L, &h); while (*fmt != '\0') { int size, ntoalign; KOption opt = getdetails(&h, totalsize, &fmt, &size, &ntoalign); size += ntoalign; /* total space used by option */ luaL_argcheck(L, totalsize <= MAXSIZE - size, 1, "format result too large"); totalsize += size; switch (opt) { case Kstring: /* strings with length count */ case Kzstr: /* zero-terminated string */ luaL_argerror(L, 1, "variable-length format"); /* call never return, but to avoid warnings: *//* FALLTHROUGH */ default: break; } } lua_pushinteger(L, (lua_Integer)totalsize); return 1; } /* ** Unpack an integer with 'size' bytes and 'islittle' endianness. ** If size is smaller than the size of a Lua integer and integer ** is signed, must do sign extension (propagating the sign to the ** higher bits); if size is larger than the size of a Lua integer, ** it must check the unread bytes to see whether they do not cause an ** overflow. */ static lua_Integer unpackint (lua_State *L, const char *str, int islittle, int size, int issigned) { lua_Unsigned res = 0; int i; int limit = (size <= SZINT) ? size : SZINT; for (i = limit - 1; i >= 0; i--) { res <<= NB; res |= (lua_Unsigned)(unsigned char)str[islittle ? i : size - 1 - i]; } if (size < SZINT) { /* real size smaller than lua_Integer? */ if (issigned) { /* needs sign extension? */ lua_Unsigned mask = (lua_Unsigned)1 << (size*NB - 1); res = ((res ^ mask) - mask); /* do sign extension */ } } else if (size > SZINT) { /* must check unread bytes */ int mask = (!issigned || (lua_Integer)res >= 0) ? 0 : MC; for (i = limit; i < size; i++) { if ((unsigned char)str[islittle ? i : size - 1 - i] != mask) luaL_error(L, "%d-byte integer does not fit into Lua Integer", size); } } return (lua_Integer)res; } static int str_unpack (lua_State *L) { Header h; const char *fmt = luaL_checkstring(L, 1); size_t ld; const char *data = luaL_checklstring(L, 2, &ld); size_t pos = (size_t)posrelat(luaL_optinteger(L, 3, 1), ld) - 1; int n = 0; /* number of results */ luaL_argcheck(L, pos <= ld, 3, "initial position out of string"); initheader(L, &h); while (*fmt != '\0') { int size, ntoalign; KOption opt = getdetails(&h, pos, &fmt, &size, &ntoalign); if ((size_t)ntoalign + size > ~pos || pos + ntoalign + size > ld) luaL_argerror(L, 2, "data string too short"); pos += ntoalign; /* skip alignment */ /* stack space for item + next position */ luaL_checkstack(L, 2, "too many results"); n++; switch (opt) { case Kint: case Kuint: { lua_Integer res = unpackint(L, data + pos, h.islittle, size, (opt == Kint)); lua_pushinteger(L, res); break; } case Kfloat: { volatile Ftypes u; lua_Number num; copywithendian(u.buff, data + pos, size, h.islittle); if (size == sizeof(u.f)) num = (lua_Number)u.f; else if (size == sizeof(u.d)) num = (lua_Number)u.d; else num = u.n; lua_pushnumber(L, num); break; } case Kchar: { lua_pushlstring(L, data + pos, size); break; } case Kstring: { size_t len = (size_t)unpackint(L, data + pos, h.islittle, size, 0); luaL_argcheck(L, pos + len + size <= ld, 2, "data string too short"); lua_pushlstring(L, data + pos + size, len); pos += len; /* skip string */ break; } case Kzstr: { size_t len = (int)strlen(data + pos); lua_pushlstring(L, data + pos, len); pos += len + 1; /* skip string plus final '\0' */ break; } case Kpaddalign: case Kpadding: case Knop: n--; /* undo increment */ break; } pos += size; } lua_pushinteger(L, pos + 1); /* next position */ return n + 1; } /* }====================================================== */ static const luaL_Reg strlib[] = { {"byte", str_byte}, {"char", str_char}, {"dump", str_dump}, {"find", str_find}, {"format", str_format}, {"gmatch", gmatch}, {"gsub", str_gsub}, {"len", str_len}, {"lower", str_lower}, {"match", str_match}, {"rep", str_rep}, {"reverse", str_reverse}, {"sub", str_sub}, {"upper", str_upper}, {"pack", str_pack}, {"packsize", str_packsize}, {"unpack", str_unpack}, {NULL, NULL} }; static void createmetatable (lua_State *L) { lua_createtable(L, 0, 1); /* table to be metatable for strings */ lua_pushliteral(L, ""); /* dummy string */ lua_pushvalue(L, -2); /* copy table */ lua_setmetatable(L, -2); /* set table as metatable for strings */ lua_pop(L, 1); /* pop dummy string */ lua_pushvalue(L, -2); /* get string library */ lua_setfield(L, -2, "__index"); /* metatable.__index = string */ lua_pop(L, 1); /* pop metatable */ } /* ** Open string library */ LUAMOD_API int luaopen_string (lua_State *L) { luaL_newlib(L, strlib); createmetatable(L); return 1; } ================================================ FILE: Tests/ApiExplorer/lua/src/ltable.c ================================================ /* ** $Id: ltable.c,v 2.118.1.4 2018/06/08 16:22:51 roberto Exp $ ** Lua tables (hash) ** See Copyright Notice in lua.h */ #define ltable_c #define LUA_CORE #include "lprefix.h" /* ** Implementation of tables (aka arrays, objects, or hash tables). ** Tables keep its elements in two parts: an array part and a hash part. ** Non-negative integer keys are all candidates to be kept in the array ** part. The actual size of the array is the largest 'n' such that ** more than half the slots between 1 and n are in use. ** Hash uses a mix of chained scatter table with Brent's variation. ** A main invariant of these tables is that, if an element is not ** in its main position (i.e. the 'original' position that its hash gives ** to it), then the colliding element is in its own main position. ** Hence even when the load factor reaches 100%, performance remains good. */ #include #include #include "lua.h" #include "ldebug.h" #include "ldo.h" #include "lgc.h" #include "lmem.h" #include "lobject.h" #include "lstate.h" #include "lstring.h" #include "ltable.h" #include "lvm.h" /* ** Maximum size of array part (MAXASIZE) is 2^MAXABITS. MAXABITS is ** the largest integer such that MAXASIZE fits in an unsigned int. */ #define MAXABITS cast_int(sizeof(int) * CHAR_BIT - 1) #define MAXASIZE (1u << MAXABITS) /* ** Maximum size of hash part is 2^MAXHBITS. MAXHBITS is the largest ** integer such that 2^MAXHBITS fits in a signed int. (Note that the ** maximum number of elements in a table, 2^MAXABITS + 2^MAXHBITS, still ** fits comfortably in an unsigned int.) */ #define MAXHBITS (MAXABITS - 1) #define hashpow2(t,n) (gnode(t, lmod((n), sizenode(t)))) #define hashstr(t,str) hashpow2(t, (str)->hash) #define hashboolean(t,p) hashpow2(t, p) #define hashint(t,i) hashpow2(t, i) /* ** for some types, it is better to avoid modulus by power of 2, as ** they tend to have many 2 factors. */ #define hashmod(t,n) (gnode(t, ((n) % ((sizenode(t)-1)|1)))) #define hashpointer(t,p) hashmod(t, point2uint(p)) #define dummynode (&dummynode_) static const Node dummynode_ = { {NILCONSTANT}, /* value */ {{NILCONSTANT, 0}} /* key */ }; /* ** Hash for floating-point numbers. ** The main computation should be just ** n = frexp(n, &i); return (n * INT_MAX) + i ** but there are some numerical subtleties. ** In a two-complement representation, INT_MAX does not has an exact ** representation as a float, but INT_MIN does; because the absolute ** value of 'frexp' is smaller than 1 (unless 'n' is inf/NaN), the ** absolute value of the product 'frexp * -INT_MIN' is smaller or equal ** to INT_MAX. Next, the use of 'unsigned int' avoids overflows when ** adding 'i'; the use of '~u' (instead of '-u') avoids problems with ** INT_MIN. */ #if !defined(l_hashfloat) static int l_hashfloat (lua_Number n) { int i; lua_Integer ni; n = l_mathop(frexp)(n, &i) * -cast_num(INT_MIN); if (!lua_numbertointeger(n, &ni)) { /* is 'n' inf/-inf/NaN? */ lua_assert(luai_numisnan(n) || l_mathop(fabs)(n) == cast_num(HUGE_VAL)); return 0; } else { /* normal case */ unsigned int u = cast(unsigned int, i) + cast(unsigned int, ni); return cast_int(u <= cast(unsigned int, INT_MAX) ? u : ~u); } } #endif /* ** returns the 'main' position of an element in a table (that is, the index ** of its hash value) */ static Node *mainposition (const Table *t, const TValue *key) { switch (ttype(key)) { case LUA_TNUMINT: return hashint(t, ivalue(key)); case LUA_TNUMFLT: return hashmod(t, l_hashfloat(fltvalue(key))); case LUA_TSHRSTR: return hashstr(t, tsvalue(key)); case LUA_TLNGSTR: return hashpow2(t, luaS_hashlongstr(tsvalue(key))); case LUA_TBOOLEAN: return hashboolean(t, bvalue(key)); case LUA_TLIGHTUSERDATA: return hashpointer(t, pvalue(key)); case LUA_TLCF: return hashpointer(t, fvalue(key)); default: lua_assert(!ttisdeadkey(key)); return hashpointer(t, gcvalue(key)); } } /* ** returns the index for 'key' if 'key' is an appropriate key to live in ** the array part of the table, 0 otherwise. */ static unsigned int arrayindex (const TValue *key) { if (ttisinteger(key)) { lua_Integer k = ivalue(key); if (0 < k && (lua_Unsigned)k <= MAXASIZE) return cast(unsigned int, k); /* 'key' is an appropriate array index */ } return 0; /* 'key' did not match some condition */ } /* ** returns the index of a 'key' for table traversals. First goes all ** elements in the array part, then elements in the hash part. The ** beginning of a traversal is signaled by 0. */ static unsigned int findindex (lua_State *L, Table *t, StkId key) { unsigned int i; if (ttisnil(key)) return 0; /* first iteration */ i = arrayindex(key); if (i != 0 && i <= t->sizearray) /* is 'key' inside array part? */ return i; /* yes; that's the index */ else { int nx; Node *n = mainposition(t, key); for (;;) { /* check whether 'key' is somewhere in the chain */ /* key may be dead already, but it is ok to use it in 'next' */ if (luaV_rawequalobj(gkey(n), key) || (ttisdeadkey(gkey(n)) && iscollectable(key) && deadvalue(gkey(n)) == gcvalue(key))) { i = cast_int(n - gnode(t, 0)); /* key index in hash table */ /* hash elements are numbered after array ones */ return (i + 1) + t->sizearray; } nx = gnext(n); if (nx == 0) luaG_runerror(L, "invalid key to 'next'"); /* key not found */ else n += nx; } } } int luaH_next (lua_State *L, Table *t, StkId key) { unsigned int i = findindex(L, t, key); /* find original element */ for (; i < t->sizearray; i++) { /* try first array part */ if (!ttisnil(&t->array[i])) { /* a non-nil value? */ setivalue(key, i + 1); setobj2s(L, key+1, &t->array[i]); return 1; } } for (i -= t->sizearray; cast_int(i) < sizenode(t); i++) { /* hash part */ if (!ttisnil(gval(gnode(t, i)))) { /* a non-nil value? */ setobj2s(L, key, gkey(gnode(t, i))); setobj2s(L, key+1, gval(gnode(t, i))); return 1; } } return 0; /* no more elements */ } /* ** {============================================================= ** Rehash ** ============================================================== */ /* ** Compute the optimal size for the array part of table 't'. 'nums' is a ** "count array" where 'nums[i]' is the number of integers in the table ** between 2^(i - 1) + 1 and 2^i. 'pna' enters with the total number of ** integer keys in the table and leaves with the number of keys that ** will go to the array part; return the optimal size. */ static unsigned int computesizes (unsigned int nums[], unsigned int *pna) { int i; unsigned int twotoi; /* 2^i (candidate for optimal size) */ unsigned int a = 0; /* number of elements smaller than 2^i */ unsigned int na = 0; /* number of elements to go to array part */ unsigned int optimal = 0; /* optimal size for array part */ /* loop while keys can fill more than half of total size */ for (i = 0, twotoi = 1; twotoi > 0 && *pna > twotoi / 2; i++, twotoi *= 2) { if (nums[i] > 0) { a += nums[i]; if (a > twotoi/2) { /* more than half elements present? */ optimal = twotoi; /* optimal size (till now) */ na = a; /* all elements up to 'optimal' will go to array part */ } } } lua_assert((optimal == 0 || optimal / 2 < na) && na <= optimal); *pna = na; return optimal; } static int countint (const TValue *key, unsigned int *nums) { unsigned int k = arrayindex(key); if (k != 0) { /* is 'key' an appropriate array index? */ nums[luaO_ceillog2(k)]++; /* count as such */ return 1; } else return 0; } /* ** Count keys in array part of table 't': Fill 'nums[i]' with ** number of keys that will go into corresponding slice and return ** total number of non-nil keys. */ static unsigned int numusearray (const Table *t, unsigned int *nums) { int lg; unsigned int ttlg; /* 2^lg */ unsigned int ause = 0; /* summation of 'nums' */ unsigned int i = 1; /* count to traverse all array keys */ /* traverse each slice */ for (lg = 0, ttlg = 1; lg <= MAXABITS; lg++, ttlg *= 2) { unsigned int lc = 0; /* counter */ unsigned int lim = ttlg; if (lim > t->sizearray) { lim = t->sizearray; /* adjust upper limit */ if (i > lim) break; /* no more elements to count */ } /* count elements in range (2^(lg - 1), 2^lg] */ for (; i <= lim; i++) { if (!ttisnil(&t->array[i-1])) lc++; } nums[lg] += lc; ause += lc; } return ause; } static int numusehash (const Table *t, unsigned int *nums, unsigned int *pna) { int totaluse = 0; /* total number of elements */ int ause = 0; /* elements added to 'nums' (can go to array part) */ int i = sizenode(t); while (i--) { Node *n = &t->node[i]; if (!ttisnil(gval(n))) { ause += countint(gkey(n), nums); totaluse++; } } *pna += ause; return totaluse; } static void setarrayvector (lua_State *L, Table *t, unsigned int size) { unsigned int i; luaM_reallocvector(L, t->array, t->sizearray, size, TValue); for (i=t->sizearray; iarray[i]); t->sizearray = size; } static void setnodevector (lua_State *L, Table *t, unsigned int size) { if (size == 0) { /* no elements to hash part? */ t->node = cast(Node *, dummynode); /* use common 'dummynode' */ t->lsizenode = 0; t->lastfree = NULL; /* signal that it is using dummy node */ } else { int i; int lsize = luaO_ceillog2(size); if (lsize > MAXHBITS) luaG_runerror(L, "table overflow"); size = twoto(lsize); t->node = luaM_newvector(L, size, Node); for (i = 0; i < (int)size; i++) { Node *n = gnode(t, i); gnext(n) = 0; setnilvalue(wgkey(n)); setnilvalue(gval(n)); } t->lsizenode = cast_byte(lsize); t->lastfree = gnode(t, size); /* all positions are free */ } } typedef struct { Table *t; unsigned int nhsize; } AuxsetnodeT; static void auxsetnode (lua_State *L, void *ud) { AuxsetnodeT *asn = cast(AuxsetnodeT *, ud); setnodevector(L, asn->t, asn->nhsize); } void luaH_resize (lua_State *L, Table *t, unsigned int nasize, unsigned int nhsize) { unsigned int i; int j; AuxsetnodeT asn; unsigned int oldasize = t->sizearray; int oldhsize = allocsizenode(t); Node *nold = t->node; /* save old hash ... */ if (nasize > oldasize) /* array part must grow? */ setarrayvector(L, t, nasize); /* create new hash part with appropriate size */ asn.t = t; asn.nhsize = nhsize; if (luaD_rawrunprotected(L, auxsetnode, &asn) != LUA_OK) { /* mem. error? */ setarrayvector(L, t, oldasize); /* array back to its original size */ luaD_throw(L, LUA_ERRMEM); /* rethrow memory error */ } if (nasize < oldasize) { /* array part must shrink? */ t->sizearray = nasize; /* re-insert elements from vanishing slice */ for (i=nasize; iarray[i])) luaH_setint(L, t, i + 1, &t->array[i]); } /* shrink array */ luaM_reallocvector(L, t->array, oldasize, nasize, TValue); } /* re-insert elements from hash part */ for (j = oldhsize - 1; j >= 0; j--) { Node *old = nold + j; if (!ttisnil(gval(old))) { /* doesn't need barrier/invalidate cache, as entry was already present in the table */ setobjt2t(L, luaH_set(L, t, gkey(old)), gval(old)); } } if (oldhsize > 0) /* not the dummy node? */ luaM_freearray(L, nold, cast(size_t, oldhsize)); /* free old hash */ } void luaH_resizearray (lua_State *L, Table *t, unsigned int nasize) { int nsize = allocsizenode(t); luaH_resize(L, t, nasize, nsize); } /* ** nums[i] = number of keys 'k' where 2^(i - 1) < k <= 2^i */ static void rehash (lua_State *L, Table *t, const TValue *ek) { unsigned int asize; /* optimal size for array part */ unsigned int na; /* number of keys in the array part */ unsigned int nums[MAXABITS + 1]; int i; int totaluse; for (i = 0; i <= MAXABITS; i++) nums[i] = 0; /* reset counts */ na = numusearray(t, nums); /* count keys in array part */ totaluse = na; /* all those keys are integer keys */ totaluse += numusehash(t, nums, &na); /* count keys in hash part */ /* count extra key */ na += countint(ek, nums); totaluse++; /* compute new size for array part */ asize = computesizes(nums, &na); /* resize the table to new computed sizes */ luaH_resize(L, t, asize, totaluse - na); } /* ** }============================================================= */ Table *luaH_new (lua_State *L) { GCObject *o = luaC_newobj(L, LUA_TTABLE, sizeof(Table)); Table *t = gco2t(o); t->metatable = NULL; t->flags = cast_byte(~0); t->array = NULL; t->sizearray = 0; setnodevector(L, t, 0); return t; } void luaH_free (lua_State *L, Table *t) { if (!isdummy(t)) luaM_freearray(L, t->node, cast(size_t, sizenode(t))); luaM_freearray(L, t->array, t->sizearray); luaM_free(L, t); } static Node *getfreepos (Table *t) { if (!isdummy(t)) { while (t->lastfree > t->node) { t->lastfree--; if (ttisnil(gkey(t->lastfree))) return t->lastfree; } } return NULL; /* could not find a free place */ } /* ** inserts a new key into a hash table; first, check whether key's main ** position is free. If not, check whether colliding node is in its main ** position or not: if it is not, move colliding node to an empty place and ** put new key in its main position; otherwise (colliding node is in its main ** position), new key goes to an empty position. */ TValue *luaH_newkey (lua_State *L, Table *t, const TValue *key) { Node *mp; TValue aux; if (ttisnil(key)) luaG_runerror(L, "table index is nil"); else if (ttisfloat(key)) { lua_Integer k; if (luaV_tointeger(key, &k, 0)) { /* does index fit in an integer? */ setivalue(&aux, k); key = &aux; /* insert it as an integer */ } else if (luai_numisnan(fltvalue(key))) luaG_runerror(L, "table index is NaN"); } mp = mainposition(t, key); if (!ttisnil(gval(mp)) || isdummy(t)) { /* main position is taken? */ Node *othern; Node *f = getfreepos(t); /* get a free place */ if (f == NULL) { /* cannot find a free place? */ rehash(L, t, key); /* grow table */ /* whatever called 'newkey' takes care of TM cache */ return luaH_set(L, t, key); /* insert key into grown table */ } lua_assert(!isdummy(t)); othern = mainposition(t, gkey(mp)); if (othern != mp) { /* is colliding node out of its main position? */ /* yes; move colliding node into free position */ while (othern + gnext(othern) != mp) /* find previous */ othern += gnext(othern); gnext(othern) = cast_int(f - othern); /* rechain to point to 'f' */ *f = *mp; /* copy colliding node into free pos. (mp->next also goes) */ if (gnext(mp) != 0) { gnext(f) += cast_int(mp - f); /* correct 'next' */ gnext(mp) = 0; /* now 'mp' is free */ } setnilvalue(gval(mp)); } else { /* colliding node is in its own main position */ /* new node will go into free position */ if (gnext(mp) != 0) gnext(f) = cast_int((mp + gnext(mp)) - f); /* chain new position */ else lua_assert(gnext(f) == 0); gnext(mp) = cast_int(f - mp); mp = f; } } setnodekey(L, &mp->i_key, key); luaC_barrierback(L, t, key); lua_assert(ttisnil(gval(mp))); return gval(mp); } /* ** search function for integers */ const TValue *luaH_getint (Table *t, lua_Integer key) { /* (1 <= key && key <= t->sizearray) */ if (l_castS2U(key) - 1 < t->sizearray) return &t->array[key - 1]; else { Node *n = hashint(t, key); for (;;) { /* check whether 'key' is somewhere in the chain */ if (ttisinteger(gkey(n)) && ivalue(gkey(n)) == key) return gval(n); /* that's it */ else { int nx = gnext(n); if (nx == 0) break; n += nx; } } return luaO_nilobject; } } /* ** search function for short strings */ const TValue *luaH_getshortstr (Table *t, TString *key) { Node *n = hashstr(t, key); lua_assert(key->tt == LUA_TSHRSTR); for (;;) { /* check whether 'key' is somewhere in the chain */ const TValue *k = gkey(n); if (ttisshrstring(k) && eqshrstr(tsvalue(k), key)) return gval(n); /* that's it */ else { int nx = gnext(n); if (nx == 0) return luaO_nilobject; /* not found */ n += nx; } } } /* ** "Generic" get version. (Not that generic: not valid for integers, ** which may be in array part, nor for floats with integral values.) */ static const TValue *getgeneric (Table *t, const TValue *key) { Node *n = mainposition(t, key); for (;;) { /* check whether 'key' is somewhere in the chain */ if (luaV_rawequalobj(gkey(n), key)) return gval(n); /* that's it */ else { int nx = gnext(n); if (nx == 0) return luaO_nilobject; /* not found */ n += nx; } } } const TValue *luaH_getstr (Table *t, TString *key) { if (key->tt == LUA_TSHRSTR) return luaH_getshortstr(t, key); else { /* for long strings, use generic case */ TValue ko; setsvalue(cast(lua_State *, NULL), &ko, key); return getgeneric(t, &ko); } } /* ** main search function */ const TValue *luaH_get (Table *t, const TValue *key) { switch (ttype(key)) { case LUA_TSHRSTR: return luaH_getshortstr(t, tsvalue(key)); case LUA_TNUMINT: return luaH_getint(t, ivalue(key)); case LUA_TNIL: return luaO_nilobject; case LUA_TNUMFLT: { lua_Integer k; if (luaV_tointeger(key, &k, 0)) /* index is int? */ return luaH_getint(t, k); /* use specialized version */ /* else... */ } /* FALLTHROUGH */ default: return getgeneric(t, key); } } /* ** beware: when using this function you probably need to check a GC ** barrier and invalidate the TM cache. */ TValue *luaH_set (lua_State *L, Table *t, const TValue *key) { const TValue *p = luaH_get(t, key); if (p != luaO_nilobject) return cast(TValue *, p); else return luaH_newkey(L, t, key); } void luaH_setint (lua_State *L, Table *t, lua_Integer key, TValue *value) { const TValue *p = luaH_getint(t, key); TValue *cell; if (p != luaO_nilobject) cell = cast(TValue *, p); else { TValue k; setivalue(&k, key); cell = luaH_newkey(L, t, &k); } setobj2t(L, cell, value); } static lua_Unsigned unbound_search (Table *t, lua_Unsigned j) { lua_Unsigned i = j; /* i is zero or a present index */ j++; /* find 'i' and 'j' such that i is present and j is not */ while (!ttisnil(luaH_getint(t, j))) { i = j; if (j > l_castS2U(LUA_MAXINTEGER) / 2) { /* overflow? */ /* table was built with bad purposes: resort to linear search */ i = 1; while (!ttisnil(luaH_getint(t, i))) i++; return i - 1; } j *= 2; } /* now do a binary search between them */ while (j - i > 1) { lua_Unsigned m = (i+j)/2; if (ttisnil(luaH_getint(t, m))) j = m; else i = m; } return i; } /* ** Try to find a boundary in table 't'. A 'boundary' is an integer index ** such that t[i] is non-nil and t[i+1] is nil (and 0 if t[1] is nil). */ lua_Unsigned luaH_getn (Table *t) { unsigned int j = t->sizearray; if (j > 0 && ttisnil(&t->array[j - 1])) { /* there is a boundary in the array part: (binary) search for it */ unsigned int i = 0; while (j - i > 1) { unsigned int m = (i+j)/2; if (ttisnil(&t->array[m - 1])) j = m; else i = m; } return i; } /* else must find a boundary in hash part */ else if (isdummy(t)) /* hash part is empty? */ return j; /* that is easy... */ else return unbound_search(t, j); } #if defined(LUA_DEBUG) Node *luaH_mainposition (const Table *t, const TValue *key) { return mainposition(t, key); } int luaH_isdummy (const Table *t) { return isdummy(t); } #endif ================================================ FILE: Tests/ApiExplorer/lua/src/ltable.h ================================================ /* ** $Id: ltable.h,v 2.23.1.2 2018/05/24 19:39:05 roberto Exp $ ** Lua tables (hash) ** See Copyright Notice in lua.h */ #ifndef ltable_h #define ltable_h #include "lobject.h" #define gnode(t,i) (&(t)->node[i]) #define gval(n) (&(n)->i_val) #define gnext(n) ((n)->i_key.nk.next) /* 'const' to avoid wrong writings that can mess up field 'next' */ #define gkey(n) cast(const TValue*, (&(n)->i_key.tvk)) /* ** writable version of 'gkey'; allows updates to individual fields, ** but not to the whole (which has incompatible type) */ #define wgkey(n) (&(n)->i_key.nk) #define invalidateTMcache(t) ((t)->flags = 0) /* true when 't' is using 'dummynode' as its hash part */ #define isdummy(t) ((t)->lastfree == NULL) /* allocated size for hash nodes */ #define allocsizenode(t) (isdummy(t) ? 0 : sizenode(t)) /* returns the key, given the value of a table entry */ #define keyfromval(v) \ (gkey(cast(Node *, cast(char *, (v)) - offsetof(Node, i_val)))) LUAI_FUNC const TValue *luaH_getint (Table *t, lua_Integer key); LUAI_FUNC void luaH_setint (lua_State *L, Table *t, lua_Integer key, TValue *value); LUAI_FUNC const TValue *luaH_getshortstr (Table *t, TString *key); LUAI_FUNC const TValue *luaH_getstr (Table *t, TString *key); LUAI_FUNC const TValue *luaH_get (Table *t, const TValue *key); LUAI_FUNC TValue *luaH_newkey (lua_State *L, Table *t, const TValue *key); LUAI_FUNC TValue *luaH_set (lua_State *L, Table *t, const TValue *key); LUAI_FUNC Table *luaH_new (lua_State *L); LUAI_FUNC void luaH_resize (lua_State *L, Table *t, unsigned int nasize, unsigned int nhsize); LUAI_FUNC void luaH_resizearray (lua_State *L, Table *t, unsigned int nasize); LUAI_FUNC void luaH_free (lua_State *L, Table *t); LUAI_FUNC int luaH_next (lua_State *L, Table *t, StkId key); LUAI_FUNC lua_Unsigned luaH_getn (Table *t); #if defined(LUA_DEBUG) LUAI_FUNC Node *luaH_mainposition (const Table *t, const TValue *key); LUAI_FUNC int luaH_isdummy (const Table *t); #endif #endif ================================================ FILE: Tests/ApiExplorer/lua/src/ltablib.c ================================================ /* ** $Id: ltablib.c,v 1.93.1.1 2017/04/19 17:20:42 roberto Exp $ ** Library for Table Manipulation ** See Copyright Notice in lua.h */ #define ltablib_c #define LUA_LIB #include "lprefix.h" #include #include #include #include "lua.h" #include "lauxlib.h" #include "lualib.h" /* ** Operations that an object must define to mimic a table ** (some functions only need some of them) */ #define TAB_R 1 /* read */ #define TAB_W 2 /* write */ #define TAB_L 4 /* length */ #define TAB_RW (TAB_R | TAB_W) /* read/write */ #define aux_getn(L,n,w) (checktab(L, n, (w) | TAB_L), luaL_len(L, n)) static int checkfield (lua_State *L, const char *key, int n) { lua_pushstring(L, key); return (lua_rawget(L, -n) != LUA_TNIL); } /* ** Check that 'arg' either is a table or can behave like one (that is, ** has a metatable with the required metamethods) */ static void checktab (lua_State *L, int arg, int what) { if (lua_type(L, arg) != LUA_TTABLE) { /* is it not a table? */ int n = 1; /* number of elements to pop */ if (lua_getmetatable(L, arg) && /* must have metatable */ (!(what & TAB_R) || checkfield(L, "__index", ++n)) && (!(what & TAB_W) || checkfield(L, "__newindex", ++n)) && (!(what & TAB_L) || checkfield(L, "__len", ++n))) { lua_pop(L, n); /* pop metatable and tested metamethods */ } else luaL_checktype(L, arg, LUA_TTABLE); /* force an error */ } } #if defined(LUA_COMPAT_MAXN) static int maxn (lua_State *L) { lua_Number max = 0; luaL_checktype(L, 1, LUA_TTABLE); lua_pushnil(L); /* first key */ while (lua_next(L, 1)) { lua_pop(L, 1); /* remove value */ if (lua_type(L, -1) == LUA_TNUMBER) { lua_Number v = lua_tonumber(L, -1); if (v > max) max = v; } } lua_pushnumber(L, max); return 1; } #endif static int tinsert (lua_State *L) { lua_Integer e = aux_getn(L, 1, TAB_RW) + 1; /* first empty element */ lua_Integer pos; /* where to insert new element */ switch (lua_gettop(L)) { case 2: { /* called with only 2 arguments */ pos = e; /* insert new element at the end */ break; } case 3: { lua_Integer i; pos = luaL_checkinteger(L, 2); /* 2nd argument is the position */ luaL_argcheck(L, 1 <= pos && pos <= e, 2, "position out of bounds"); for (i = e; i > pos; i--) { /* move up elements */ lua_geti(L, 1, i - 1); lua_seti(L, 1, i); /* t[i] = t[i - 1] */ } break; } default: { return luaL_error(L, "wrong number of arguments to 'insert'"); } } lua_seti(L, 1, pos); /* t[pos] = v */ return 0; } static int tremove (lua_State *L) { lua_Integer size = aux_getn(L, 1, TAB_RW); lua_Integer pos = luaL_optinteger(L, 2, size); if (pos != size) /* validate 'pos' if given */ luaL_argcheck(L, 1 <= pos && pos <= size + 1, 1, "position out of bounds"); lua_geti(L, 1, pos); /* result = t[pos] */ for ( ; pos < size; pos++) { lua_geti(L, 1, pos + 1); lua_seti(L, 1, pos); /* t[pos] = t[pos + 1] */ } lua_pushnil(L); lua_seti(L, 1, pos); /* t[pos] = nil */ return 1; } /* ** Copy elements (1[f], ..., 1[e]) into (tt[t], tt[t+1], ...). Whenever ** possible, copy in increasing order, which is better for rehashing. ** "possible" means destination after original range, or smaller ** than origin, or copying to another table. */ static int tmove (lua_State *L) { lua_Integer f = luaL_checkinteger(L, 2); lua_Integer e = luaL_checkinteger(L, 3); lua_Integer t = luaL_checkinteger(L, 4); int tt = !lua_isnoneornil(L, 5) ? 5 : 1; /* destination table */ checktab(L, 1, TAB_R); checktab(L, tt, TAB_W); if (e >= f) { /* otherwise, nothing to move */ lua_Integer n, i; luaL_argcheck(L, f > 0 || e < LUA_MAXINTEGER + f, 3, "too many elements to move"); n = e - f + 1; /* number of elements to move */ luaL_argcheck(L, t <= LUA_MAXINTEGER - n + 1, 4, "destination wrap around"); if (t > e || t <= f || (tt != 1 && !lua_compare(L, 1, tt, LUA_OPEQ))) { for (i = 0; i < n; i++) { lua_geti(L, 1, f + i); lua_seti(L, tt, t + i); } } else { for (i = n - 1; i >= 0; i--) { lua_geti(L, 1, f + i); lua_seti(L, tt, t + i); } } } lua_pushvalue(L, tt); /* return destination table */ return 1; } static void addfield (lua_State *L, luaL_Buffer *b, lua_Integer i) { lua_geti(L, 1, i); if (!lua_isstring(L, -1)) luaL_error(L, "invalid value (%s) at index %d in table for 'concat'", luaL_typename(L, -1), i); luaL_addvalue(b); } static int tconcat (lua_State *L) { luaL_Buffer b; lua_Integer last = aux_getn(L, 1, TAB_R); size_t lsep; const char *sep = luaL_optlstring(L, 2, "", &lsep); lua_Integer i = luaL_optinteger(L, 3, 1); last = luaL_optinteger(L, 4, last); luaL_buffinit(L, &b); for (; i < last; i++) { addfield(L, &b, i); luaL_addlstring(&b, sep, lsep); } if (i == last) /* add last value (if interval was not empty) */ addfield(L, &b, i); luaL_pushresult(&b); return 1; } /* ** {====================================================== ** Pack/unpack ** ======================================================= */ static int pack (lua_State *L) { int i; int n = lua_gettop(L); /* number of elements to pack */ lua_createtable(L, n, 1); /* create result table */ lua_insert(L, 1); /* put it at index 1 */ for (i = n; i >= 1; i--) /* assign elements */ lua_seti(L, 1, i); lua_pushinteger(L, n); lua_setfield(L, 1, "n"); /* t.n = number of elements */ return 1; /* return table */ } static int unpack (lua_State *L) { lua_Unsigned n; lua_Integer i = luaL_optinteger(L, 2, 1); lua_Integer e = luaL_opt(L, luaL_checkinteger, 3, luaL_len(L, 1)); if (i > e) return 0; /* empty range */ n = (lua_Unsigned)e - i; /* number of elements minus 1 (avoid overflows) */ if (n >= (unsigned int)INT_MAX || !lua_checkstack(L, (int)(++n))) return luaL_error(L, "too many results to unpack"); for (; i < e; i++) { /* push arg[i..e - 1] (to avoid overflows) */ lua_geti(L, 1, i); } lua_geti(L, 1, e); /* push last element */ return (int)n; } /* }====================================================== */ /* ** {====================================================== ** Quicksort ** (based on 'Algorithms in MODULA-3', Robert Sedgewick; ** Addison-Wesley, 1993.) ** ======================================================= */ /* type for array indices */ typedef unsigned int IdxT; /* ** Produce a "random" 'unsigned int' to randomize pivot choice. This ** macro is used only when 'sort' detects a big imbalance in the result ** of a partition. (If you don't want/need this "randomness", ~0 is a ** good choice.) */ #if !defined(l_randomizePivot) /* { */ #include /* size of 'e' measured in number of 'unsigned int's */ #define sof(e) (sizeof(e) / sizeof(unsigned int)) /* ** Use 'time' and 'clock' as sources of "randomness". Because we don't ** know the types 'clock_t' and 'time_t', we cannot cast them to ** anything without risking overflows. A safe way to use their values ** is to copy them to an array of a known type and use the array values. */ static unsigned int l_randomizePivot (void) { clock_t c = clock(); time_t t = time(NULL); unsigned int buff[sof(c) + sof(t)]; unsigned int i, rnd = 0; memcpy(buff, &c, sof(c) * sizeof(unsigned int)); memcpy(buff + sof(c), &t, sof(t) * sizeof(unsigned int)); for (i = 0; i < sof(buff); i++) rnd += buff[i]; return rnd; } #endif /* } */ /* arrays larger than 'RANLIMIT' may use randomized pivots */ #define RANLIMIT 100u static void set2 (lua_State *L, IdxT i, IdxT j) { lua_seti(L, 1, i); lua_seti(L, 1, j); } /* ** Return true iff value at stack index 'a' is less than the value at ** index 'b' (according to the order of the sort). */ static int sort_comp (lua_State *L, int a, int b) { if (lua_isnil(L, 2)) /* no function? */ return lua_compare(L, a, b, LUA_OPLT); /* a < b */ else { /* function */ int res; lua_pushvalue(L, 2); /* push function */ lua_pushvalue(L, a-1); /* -1 to compensate function */ lua_pushvalue(L, b-2); /* -2 to compensate function and 'a' */ lua_call(L, 2, 1); /* call function */ res = lua_toboolean(L, -1); /* get result */ lua_pop(L, 1); /* pop result */ return res; } } /* ** Does the partition: Pivot P is at the top of the stack. ** precondition: a[lo] <= P == a[up-1] <= a[up], ** so it only needs to do the partition from lo + 1 to up - 2. ** Pos-condition: a[lo .. i - 1] <= a[i] == P <= a[i + 1 .. up] ** returns 'i'. */ static IdxT partition (lua_State *L, IdxT lo, IdxT up) { IdxT i = lo; /* will be incremented before first use */ IdxT j = up - 1; /* will be decremented before first use */ /* loop invariant: a[lo .. i] <= P <= a[j .. up] */ for (;;) { /* next loop: repeat ++i while a[i] < P */ while (lua_geti(L, 1, ++i), sort_comp(L, -1, -2)) { if (i == up - 1) /* a[i] < P but a[up - 1] == P ?? */ luaL_error(L, "invalid order function for sorting"); lua_pop(L, 1); /* remove a[i] */ } /* after the loop, a[i] >= P and a[lo .. i - 1] < P */ /* next loop: repeat --j while P < a[j] */ while (lua_geti(L, 1, --j), sort_comp(L, -3, -1)) { if (j < i) /* j < i but a[j] > P ?? */ luaL_error(L, "invalid order function for sorting"); lua_pop(L, 1); /* remove a[j] */ } /* after the loop, a[j] <= P and a[j + 1 .. up] >= P */ if (j < i) { /* no elements out of place? */ /* a[lo .. i - 1] <= P <= a[j + 1 .. i .. up] */ lua_pop(L, 1); /* pop a[j] */ /* swap pivot (a[up - 1]) with a[i] to satisfy pos-condition */ set2(L, up - 1, i); return i; } /* otherwise, swap a[i] - a[j] to restore invariant and repeat */ set2(L, i, j); } } /* ** Choose an element in the middle (2nd-3th quarters) of [lo,up] ** "randomized" by 'rnd' */ static IdxT choosePivot (IdxT lo, IdxT up, unsigned int rnd) { IdxT r4 = (up - lo) / 4; /* range/4 */ IdxT p = rnd % (r4 * 2) + (lo + r4); lua_assert(lo + r4 <= p && p <= up - r4); return p; } /* ** QuickSort algorithm (recursive function) */ static void auxsort (lua_State *L, IdxT lo, IdxT up, unsigned int rnd) { while (lo < up) { /* loop for tail recursion */ IdxT p; /* Pivot index */ IdxT n; /* to be used later */ /* sort elements 'lo', 'p', and 'up' */ lua_geti(L, 1, lo); lua_geti(L, 1, up); if (sort_comp(L, -1, -2)) /* a[up] < a[lo]? */ set2(L, lo, up); /* swap a[lo] - a[up] */ else lua_pop(L, 2); /* remove both values */ if (up - lo == 1) /* only 2 elements? */ return; /* already sorted */ if (up - lo < RANLIMIT || rnd == 0) /* small interval or no randomize? */ p = (lo + up)/2; /* middle element is a good pivot */ else /* for larger intervals, it is worth a random pivot */ p = choosePivot(lo, up, rnd); lua_geti(L, 1, p); lua_geti(L, 1, lo); if (sort_comp(L, -2, -1)) /* a[p] < a[lo]? */ set2(L, p, lo); /* swap a[p] - a[lo] */ else { lua_pop(L, 1); /* remove a[lo] */ lua_geti(L, 1, up); if (sort_comp(L, -1, -2)) /* a[up] < a[p]? */ set2(L, p, up); /* swap a[up] - a[p] */ else lua_pop(L, 2); } if (up - lo == 2) /* only 3 elements? */ return; /* already sorted */ lua_geti(L, 1, p); /* get middle element (Pivot) */ lua_pushvalue(L, -1); /* push Pivot */ lua_geti(L, 1, up - 1); /* push a[up - 1] */ set2(L, p, up - 1); /* swap Pivot (a[p]) with a[up - 1] */ p = partition(L, lo, up); /* a[lo .. p - 1] <= a[p] == P <= a[p + 1 .. up] */ if (p - lo < up - p) { /* lower interval is smaller? */ auxsort(L, lo, p - 1, rnd); /* call recursively for lower interval */ n = p - lo; /* size of smaller interval */ lo = p + 1; /* tail call for [p + 1 .. up] (upper interval) */ } else { auxsort(L, p + 1, up, rnd); /* call recursively for upper interval */ n = up - p; /* size of smaller interval */ up = p - 1; /* tail call for [lo .. p - 1] (lower interval) */ } if ((up - lo) / 128 > n) /* partition too imbalanced? */ rnd = l_randomizePivot(); /* try a new randomization */ } /* tail call auxsort(L, lo, up, rnd) */ } static int sort (lua_State *L) { lua_Integer n = aux_getn(L, 1, TAB_RW); if (n > 1) { /* non-trivial interval? */ luaL_argcheck(L, n < INT_MAX, 1, "array too big"); if (!lua_isnoneornil(L, 2)) /* is there a 2nd argument? */ luaL_checktype(L, 2, LUA_TFUNCTION); /* must be a function */ lua_settop(L, 2); /* make sure there are two arguments */ auxsort(L, 1, (IdxT)n, 0); } return 0; } /* }====================================================== */ static const luaL_Reg tab_funcs[] = { {"concat", tconcat}, #if defined(LUA_COMPAT_MAXN) {"maxn", maxn}, #endif {"insert", tinsert}, {"pack", pack}, {"unpack", unpack}, {"remove", tremove}, {"move", tmove}, {"sort", sort}, {NULL, NULL} }; LUAMOD_API int luaopen_table (lua_State *L) { luaL_newlib(L, tab_funcs); #if defined(LUA_COMPAT_UNPACK) /* _G.unpack = table.unpack */ lua_getfield(L, -1, "unpack"); lua_setglobal(L, "unpack"); #endif return 1; } ================================================ FILE: Tests/ApiExplorer/lua/src/ltm.c ================================================ /* ** $Id: ltm.c,v 2.38.1.1 2017/04/19 17:39:34 roberto Exp $ ** Tag methods ** See Copyright Notice in lua.h */ #define ltm_c #define LUA_CORE #include "lprefix.h" #include #include "lua.h" #include "ldebug.h" #include "ldo.h" #include "lobject.h" #include "lstate.h" #include "lstring.h" #include "ltable.h" #include "ltm.h" #include "lvm.h" static const char udatatypename[] = "userdata"; LUAI_DDEF const char *const luaT_typenames_[LUA_TOTALTAGS] = { "no value", "nil", "boolean", udatatypename, "number", "string", "table", "function", udatatypename, "thread", "proto" /* this last case is used for tests only */ }; void luaT_init (lua_State *L) { static const char *const luaT_eventname[] = { /* ORDER TM */ "__index", "__newindex", "__gc", "__mode", "__len", "__eq", "__add", "__sub", "__mul", "__mod", "__pow", "__div", "__idiv", "__band", "__bor", "__bxor", "__shl", "__shr", "__unm", "__bnot", "__lt", "__le", "__concat", "__call" }; int i; for (i=0; itmname[i] = luaS_new(L, luaT_eventname[i]); luaC_fix(L, obj2gco(G(L)->tmname[i])); /* never collect these names */ } } /* ** function to be used with macro "fasttm": optimized for absence of ** tag methods */ const TValue *luaT_gettm (Table *events, TMS event, TString *ename) { const TValue *tm = luaH_getshortstr(events, ename); lua_assert(event <= TM_EQ); if (ttisnil(tm)) { /* no tag method? */ events->flags |= cast_byte(1u<metatable; break; case LUA_TUSERDATA: mt = uvalue(o)->metatable; break; default: mt = G(L)->mt[ttnov(o)]; } return (mt ? luaH_getshortstr(mt, G(L)->tmname[event]) : luaO_nilobject); } /* ** Return the name of the type of an object. For tables and userdata ** with metatable, use their '__name' metafield, if present. */ const char *luaT_objtypename (lua_State *L, const TValue *o) { Table *mt; if ((ttistable(o) && (mt = hvalue(o)->metatable) != NULL) || (ttisfulluserdata(o) && (mt = uvalue(o)->metatable) != NULL)) { const TValue *name = luaH_getshortstr(mt, luaS_new(L, "__name")); if (ttisstring(name)) /* is '__name' a string? */ return getstr(tsvalue(name)); /* use it as type name */ } return ttypename(ttnov(o)); /* else use standard type name */ } void luaT_callTM (lua_State *L, const TValue *f, const TValue *p1, const TValue *p2, TValue *p3, int hasres) { ptrdiff_t result = savestack(L, p3); StkId func = L->top; setobj2s(L, func, f); /* push function (assume EXTRA_STACK) */ setobj2s(L, func + 1, p1); /* 1st argument */ setobj2s(L, func + 2, p2); /* 2nd argument */ L->top += 3; if (!hasres) /* no result? 'p3' is third argument */ setobj2s(L, L->top++, p3); /* 3rd argument */ /* metamethod may yield only when called from Lua code */ if (isLua(L->ci)) luaD_call(L, func, hasres); else luaD_callnoyield(L, func, hasres); if (hasres) { /* if has result, move it to its place */ p3 = restorestack(L, result); setobjs2s(L, p3, --L->top); } } int luaT_callbinTM (lua_State *L, const TValue *p1, const TValue *p2, StkId res, TMS event) { const TValue *tm = luaT_gettmbyobj(L, p1, event); /* try first operand */ if (ttisnil(tm)) tm = luaT_gettmbyobj(L, p2, event); /* try second operand */ if (ttisnil(tm)) return 0; luaT_callTM(L, tm, p1, p2, res, 1); return 1; } void luaT_trybinTM (lua_State *L, const TValue *p1, const TValue *p2, StkId res, TMS event) { if (!luaT_callbinTM(L, p1, p2, res, event)) { switch (event) { case TM_CONCAT: luaG_concaterror(L, p1, p2); /* call never returns, but to avoid warnings: *//* FALLTHROUGH */ case TM_BAND: case TM_BOR: case TM_BXOR: case TM_SHL: case TM_SHR: case TM_BNOT: { lua_Number dummy; if (tonumber(p1, &dummy) && tonumber(p2, &dummy)) luaG_tointerror(L, p1, p2); else luaG_opinterror(L, p1, p2, "perform bitwise operation on"); } /* calls never return, but to avoid warnings: *//* FALLTHROUGH */ default: luaG_opinterror(L, p1, p2, "perform arithmetic on"); } } } int luaT_callorderTM (lua_State *L, const TValue *p1, const TValue *p2, TMS event) { if (!luaT_callbinTM(L, p1, p2, L->top, event)) return -1; /* no metamethod */ else return !l_isfalse(L->top); } ================================================ FILE: Tests/ApiExplorer/lua/src/ltm.h ================================================ /* ** $Id: ltm.h,v 2.22.1.1 2017/04/19 17:20:42 roberto Exp $ ** Tag methods ** See Copyright Notice in lua.h */ #ifndef ltm_h #define ltm_h #include "lobject.h" /* * WARNING: if you change the order of this enumeration, * grep "ORDER TM" and "ORDER OP" */ typedef enum { TM_INDEX, TM_NEWINDEX, TM_GC, TM_MODE, TM_LEN, TM_EQ, /* last tag method with fast access */ TM_ADD, TM_SUB, TM_MUL, TM_MOD, TM_POW, TM_DIV, TM_IDIV, TM_BAND, TM_BOR, TM_BXOR, TM_SHL, TM_SHR, TM_UNM, TM_BNOT, TM_LT, TM_LE, TM_CONCAT, TM_CALL, TM_N /* number of elements in the enum */ } TMS; #define gfasttm(g,et,e) ((et) == NULL ? NULL : \ ((et)->flags & (1u<<(e))) ? NULL : luaT_gettm(et, e, (g)->tmname[e])) #define fasttm(l,et,e) gfasttm(G(l), et, e) #define ttypename(x) luaT_typenames_[(x) + 1] LUAI_DDEC const char *const luaT_typenames_[LUA_TOTALTAGS]; LUAI_FUNC const char *luaT_objtypename (lua_State *L, const TValue *o); LUAI_FUNC const TValue *luaT_gettm (Table *events, TMS event, TString *ename); LUAI_FUNC const TValue *luaT_gettmbyobj (lua_State *L, const TValue *o, TMS event); LUAI_FUNC void luaT_init (lua_State *L); LUAI_FUNC void luaT_callTM (lua_State *L, const TValue *f, const TValue *p1, const TValue *p2, TValue *p3, int hasres); LUAI_FUNC int luaT_callbinTM (lua_State *L, const TValue *p1, const TValue *p2, StkId res, TMS event); LUAI_FUNC void luaT_trybinTM (lua_State *L, const TValue *p1, const TValue *p2, StkId res, TMS event); LUAI_FUNC int luaT_callorderTM (lua_State *L, const TValue *p1, const TValue *p2, TMS event); #endif ================================================ FILE: Tests/ApiExplorer/lua/src/lua.h ================================================ /* ** $Id: lua.h,v 1.332.1.2 2018/06/13 16:58:17 roberto Exp $ ** Lua - A Scripting Language ** Lua.org, PUC-Rio, Brazil (http://www.lua.org) ** See Copyright Notice at the end of this file */ #ifndef lua_h #define lua_h #include #include #include "luaconf.h" #define LUA_VERSION_MAJOR "5" #define LUA_VERSION_MINOR "3" #define LUA_VERSION_NUM 503 #define LUA_VERSION_RELEASE "5" #define LUA_VERSION "Lua " LUA_VERSION_MAJOR "." LUA_VERSION_MINOR #define LUA_RELEASE LUA_VERSION "." LUA_VERSION_RELEASE #define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2018 Lua.org, PUC-Rio" #define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo, W. Celes" /* mark for precompiled code ('Lua') */ #define LUA_SIGNATURE "\x1bLua" /* option for multiple returns in 'lua_pcall' and 'lua_call' */ #define LUA_MULTRET (-1) /* ** Pseudo-indices ** (-LUAI_MAXSTACK is the minimum valid index; we keep some free empty ** space after that to help overflow detection) */ #define LUA_REGISTRYINDEX (-LUAI_MAXSTACK - 1000) #define lua_upvalueindex(i) (LUA_REGISTRYINDEX - (i)) /* thread status */ #define LUA_OK 0 #define LUA_YIELD 1 #define LUA_ERRRUN 2 #define LUA_ERRSYNTAX 3 #define LUA_ERRMEM 4 #define LUA_ERRGCMM 5 #define LUA_ERRERR 6 typedef struct lua_State lua_State; /* ** basic types */ #define LUA_TNONE (-1) #define LUA_TNIL 0 #define LUA_TBOOLEAN 1 #define LUA_TLIGHTUSERDATA 2 #define LUA_TNUMBER 3 #define LUA_TSTRING 4 #define LUA_TTABLE 5 #define LUA_TFUNCTION 6 #define LUA_TUSERDATA 7 #define LUA_TTHREAD 8 #define LUA_NUMTAGS 9 /* minimum Lua stack available to a C function */ #define LUA_MINSTACK 20 /* predefined values in the registry */ #define LUA_RIDX_MAINTHREAD 1 #define LUA_RIDX_GLOBALS 2 #define LUA_RIDX_LAST LUA_RIDX_GLOBALS /* type of numbers in Lua */ typedef LUA_NUMBER lua_Number; /* type for integer functions */ typedef LUA_INTEGER lua_Integer; /* unsigned integer type */ typedef LUA_UNSIGNED lua_Unsigned; /* type for continuation-function contexts */ typedef LUA_KCONTEXT lua_KContext; /* ** Type for C functions registered with Lua */ typedef int (*lua_CFunction) (lua_State *L); /* ** Type for continuation functions */ typedef int (*lua_KFunction) (lua_State *L, int status, lua_KContext ctx); /* ** Type for functions that read/write blocks when loading/dumping Lua chunks */ typedef const char * (*lua_Reader) (lua_State *L, void *ud, size_t *sz); typedef int (*lua_Writer) (lua_State *L, const void *p, size_t sz, void *ud); /* ** Type for memory-allocation functions */ typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize); /* ** generic extra include file */ #if defined(LUA_USER_H) #include LUA_USER_H #endif /* ** RCS ident string */ extern const char lua_ident[]; /* ** state manipulation */ LUA_API lua_State *(lua_newstate) (lua_Alloc f, void *ud); LUA_API void (lua_close) (lua_State *L); LUA_API lua_State *(lua_newthread) (lua_State *L); LUA_API lua_CFunction (lua_atpanic) (lua_State *L, lua_CFunction panicf); LUA_API const lua_Number *(lua_version) (lua_State *L); /* ** basic stack manipulation */ LUA_API int (lua_absindex) (lua_State *L, int idx); LUA_API int (lua_gettop) (lua_State *L); LUA_API void (lua_settop) (lua_State *L, int idx); LUA_API void (lua_pushvalue) (lua_State *L, int idx); LUA_API void (lua_rotate) (lua_State *L, int idx, int n); LUA_API void (lua_copy) (lua_State *L, int fromidx, int toidx); LUA_API int (lua_checkstack) (lua_State *L, int n); LUA_API void (lua_xmove) (lua_State *from, lua_State *to, int n); /* ** access functions (stack -> C) */ LUA_API int (lua_isnumber) (lua_State *L, int idx); LUA_API int (lua_isstring) (lua_State *L, int idx); LUA_API int (lua_iscfunction) (lua_State *L, int idx); LUA_API int (lua_isinteger) (lua_State *L, int idx); LUA_API int (lua_isuserdata) (lua_State *L, int idx); LUA_API int (lua_type) (lua_State *L, int idx); LUA_API const char *(lua_typename) (lua_State *L, int tp); LUA_API lua_Number (lua_tonumberx) (lua_State *L, int idx, int *isnum); LUA_API lua_Integer (lua_tointegerx) (lua_State *L, int idx, int *isnum); LUA_API int (lua_toboolean) (lua_State *L, int idx); LUA_API const char *(lua_tolstring) (lua_State *L, int idx, size_t *len); LUA_API size_t (lua_rawlen) (lua_State *L, int idx); LUA_API lua_CFunction (lua_tocfunction) (lua_State *L, int idx); LUA_API void *(lua_touserdata) (lua_State *L, int idx); LUA_API lua_State *(lua_tothread) (lua_State *L, int idx); LUA_API const void *(lua_topointer) (lua_State *L, int idx); /* ** Comparison and arithmetic functions */ #define LUA_OPADD 0 /* ORDER TM, ORDER OP */ #define LUA_OPSUB 1 #define LUA_OPMUL 2 #define LUA_OPMOD 3 #define LUA_OPPOW 4 #define LUA_OPDIV 5 #define LUA_OPIDIV 6 #define LUA_OPBAND 7 #define LUA_OPBOR 8 #define LUA_OPBXOR 9 #define LUA_OPSHL 10 #define LUA_OPSHR 11 #define LUA_OPUNM 12 #define LUA_OPBNOT 13 LUA_API void (lua_arith) (lua_State *L, int op); #define LUA_OPEQ 0 #define LUA_OPLT 1 #define LUA_OPLE 2 LUA_API int (lua_rawequal) (lua_State *L, int idx1, int idx2); LUA_API int (lua_compare) (lua_State *L, int idx1, int idx2, int op); /* ** push functions (C -> stack) */ LUA_API void (lua_pushnil) (lua_State *L); LUA_API void (lua_pushnumber) (lua_State *L, lua_Number n); LUA_API void (lua_pushinteger) (lua_State *L, lua_Integer n); LUA_API const char *(lua_pushlstring) (lua_State *L, const char *s, size_t len); LUA_API const char *(lua_pushstring) (lua_State *L, const char *s); LUA_API const char *(lua_pushvfstring) (lua_State *L, const char *fmt, va_list argp); LUA_API const char *(lua_pushfstring) (lua_State *L, const char *fmt, ...); LUA_API void (lua_pushcclosure) (lua_State *L, lua_CFunction fn, int n); LUA_API void (lua_pushboolean) (lua_State *L, int b); LUA_API void (lua_pushlightuserdata) (lua_State *L, void *p); LUA_API int (lua_pushthread) (lua_State *L); /* ** get functions (Lua -> stack) */ LUA_API int (lua_getglobal) (lua_State *L, const char *name); LUA_API int (lua_gettable) (lua_State *L, int idx); LUA_API int (lua_getfield) (lua_State *L, int idx, const char *k); LUA_API int (lua_geti) (lua_State *L, int idx, lua_Integer n); LUA_API int (lua_rawget) (lua_State *L, int idx); LUA_API int (lua_rawgeti) (lua_State *L, int idx, lua_Integer n); LUA_API int (lua_rawgetp) (lua_State *L, int idx, const void *p); LUA_API void (lua_createtable) (lua_State *L, int narr, int nrec); LUA_API void *(lua_newuserdata) (lua_State *L, size_t sz); LUA_API int (lua_getmetatable) (lua_State *L, int objindex); LUA_API int (lua_getuservalue) (lua_State *L, int idx); /* ** set functions (stack -> Lua) */ LUA_API void (lua_setglobal) (lua_State *L, const char *name); LUA_API void (lua_settable) (lua_State *L, int idx); LUA_API void (lua_setfield) (lua_State *L, int idx, const char *k); LUA_API void (lua_seti) (lua_State *L, int idx, lua_Integer n); LUA_API void (lua_rawset) (lua_State *L, int idx); LUA_API void (lua_rawseti) (lua_State *L, int idx, lua_Integer n); LUA_API void (lua_rawsetp) (lua_State *L, int idx, const void *p); LUA_API int (lua_setmetatable) (lua_State *L, int objindex); LUA_API void (lua_setuservalue) (lua_State *L, int idx); /* ** 'load' and 'call' functions (load and run Lua code) */ LUA_API void (lua_callk) (lua_State *L, int nargs, int nresults, lua_KContext ctx, lua_KFunction k); #define lua_call(L,n,r) lua_callk(L, (n), (r), 0, NULL) LUA_API int (lua_pcallk) (lua_State *L, int nargs, int nresults, int errfunc, lua_KContext ctx, lua_KFunction k); #define lua_pcall(L,n,r,f) lua_pcallk(L, (n), (r), (f), 0, NULL) LUA_API int (lua_load) (lua_State *L, lua_Reader reader, void *dt, const char *chunkname, const char *mode); LUA_API int (lua_dump) (lua_State *L, lua_Writer writer, void *data, int strip); /* ** coroutine functions */ LUA_API int (lua_yieldk) (lua_State *L, int nresults, lua_KContext ctx, lua_KFunction k); LUA_API int (lua_resume) (lua_State *L, lua_State *from, int narg); LUA_API int (lua_status) (lua_State *L); LUA_API int (lua_isyieldable) (lua_State *L); #define lua_yield(L,n) lua_yieldk(L, (n), 0, NULL) /* ** garbage-collection function and options */ #define LUA_GCSTOP 0 #define LUA_GCRESTART 1 #define LUA_GCCOLLECT 2 #define LUA_GCCOUNT 3 #define LUA_GCCOUNTB 4 #define LUA_GCSTEP 5 #define LUA_GCSETPAUSE 6 #define LUA_GCSETSTEPMUL 7 #define LUA_GCISRUNNING 9 LUA_API int (lua_gc) (lua_State *L, int what, int data); /* ** miscellaneous functions */ LUA_API int (lua_error) (lua_State *L); LUA_API int (lua_next) (lua_State *L, int idx); LUA_API void (lua_concat) (lua_State *L, int n); LUA_API void (lua_len) (lua_State *L, int idx); LUA_API size_t (lua_stringtonumber) (lua_State *L, const char *s); LUA_API lua_Alloc (lua_getallocf) (lua_State *L, void **ud); LUA_API void (lua_setallocf) (lua_State *L, lua_Alloc f, void *ud); /* ** {============================================================== ** some useful macros ** =============================================================== */ #define lua_getextraspace(L) ((void *)((char *)(L) - LUA_EXTRASPACE)) #define lua_tonumber(L,i) lua_tonumberx(L,(i),NULL) #define lua_tointeger(L,i) lua_tointegerx(L,(i),NULL) #define lua_pop(L,n) lua_settop(L, -(n)-1) #define lua_newtable(L) lua_createtable(L, 0, 0) #define lua_register(L,n,f) (lua_pushcfunction(L, (f)), lua_setglobal(L, (n))) #define lua_pushcfunction(L,f) lua_pushcclosure(L, (f), 0) #define lua_isfunction(L,n) (lua_type(L, (n)) == LUA_TFUNCTION) #define lua_istable(L,n) (lua_type(L, (n)) == LUA_TTABLE) #define lua_islightuserdata(L,n) (lua_type(L, (n)) == LUA_TLIGHTUSERDATA) #define lua_isnil(L,n) (lua_type(L, (n)) == LUA_TNIL) #define lua_isboolean(L,n) (lua_type(L, (n)) == LUA_TBOOLEAN) #define lua_isthread(L,n) (lua_type(L, (n)) == LUA_TTHREAD) #define lua_isnone(L,n) (lua_type(L, (n)) == LUA_TNONE) #define lua_isnoneornil(L, n) (lua_type(L, (n)) <= 0) #define lua_pushliteral(L, s) lua_pushstring(L, "" s) #define lua_pushglobaltable(L) \ ((void)lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS)) #define lua_tostring(L,i) lua_tolstring(L, (i), NULL) #define lua_insert(L,idx) lua_rotate(L, (idx), 1) #define lua_remove(L,idx) (lua_rotate(L, (idx), -1), lua_pop(L, 1)) #define lua_replace(L,idx) (lua_copy(L, -1, (idx)), lua_pop(L, 1)) /* }============================================================== */ /* ** {============================================================== ** compatibility macros for unsigned conversions ** =============================================================== */ #if defined(LUA_COMPAT_APIINTCASTS) #define lua_pushunsigned(L,n) lua_pushinteger(L, (lua_Integer)(n)) #define lua_tounsignedx(L,i,is) ((lua_Unsigned)lua_tointegerx(L,i,is)) #define lua_tounsigned(L,i) lua_tounsignedx(L,(i),NULL) #endif /* }============================================================== */ /* ** {====================================================================== ** Debug API ** ======================================================================= */ /* ** Event codes */ #define LUA_HOOKCALL 0 #define LUA_HOOKRET 1 #define LUA_HOOKLINE 2 #define LUA_HOOKCOUNT 3 #define LUA_HOOKTAILCALL 4 /* ** Event masks */ #define LUA_MASKCALL (1 << LUA_HOOKCALL) #define LUA_MASKRET (1 << LUA_HOOKRET) #define LUA_MASKLINE (1 << LUA_HOOKLINE) #define LUA_MASKCOUNT (1 << LUA_HOOKCOUNT) typedef struct lua_Debug lua_Debug; /* activation record */ /* Functions to be called by the debugger in specific events */ typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar); LUA_API int (lua_getstack) (lua_State *L, int level, lua_Debug *ar); LUA_API int (lua_getinfo) (lua_State *L, const char *what, lua_Debug *ar); LUA_API const char *(lua_getlocal) (lua_State *L, const lua_Debug *ar, int n); LUA_API const char *(lua_setlocal) (lua_State *L, const lua_Debug *ar, int n); LUA_API const char *(lua_getupvalue) (lua_State *L, int funcindex, int n); LUA_API const char *(lua_setupvalue) (lua_State *L, int funcindex, int n); LUA_API void *(lua_upvalueid) (lua_State *L, int fidx, int n); LUA_API void (lua_upvaluejoin) (lua_State *L, int fidx1, int n1, int fidx2, int n2); LUA_API void (lua_sethook) (lua_State *L, lua_Hook func, int mask, int count); LUA_API lua_Hook (lua_gethook) (lua_State *L); LUA_API int (lua_gethookmask) (lua_State *L); LUA_API int (lua_gethookcount) (lua_State *L); struct lua_Debug { int event; const char *name; /* (n) */ const char *namewhat; /* (n) 'global', 'local', 'field', 'method' */ const char *what; /* (S) 'Lua', 'C', 'main', 'tail' */ const char *source; /* (S) */ int currentline; /* (l) */ int linedefined; /* (S) */ int lastlinedefined; /* (S) */ unsigned char nups; /* (u) number of upvalues */ unsigned char nparams;/* (u) number of parameters */ char isvararg; /* (u) */ char istailcall; /* (t) */ char short_src[LUA_IDSIZE]; /* (S) */ /* private part */ struct CallInfo *i_ci; /* active function */ }; /* }====================================================================== */ /****************************************************************************** * Copyright (C) 1994-2018 Lua.org, PUC-Rio. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ******************************************************************************/ #endif ================================================ FILE: Tests/ApiExplorer/lua/src/lua.hpp ================================================ // lua.hpp // Lua header files for C++ // <> not supplied automatically because Lua also compiles as C++ extern "C" { #include "lua.h" #include "lualib.h" #include "lauxlib.h" } ================================================ FILE: Tests/ApiExplorer/lua/src/luaconf.h ================================================ /* ** $Id: luaconf.h,v 1.259.1.1 2017/04/19 17:29:57 roberto Exp $ ** Configuration file for Lua ** See Copyright Notice in lua.h */ #ifndef luaconf_h #define luaconf_h #include #include /* ** =================================================================== ** Search for "@@" to find all configurable definitions. ** =================================================================== */ /* ** {==================================================================== ** System Configuration: macros to adapt (if needed) Lua to some ** particular platform, for instance compiling it with 32-bit numbers or ** restricting it to C89. ** ===================================================================== */ /* @@ LUA_32BITS enables Lua with 32-bit integers and 32-bit floats. You ** can also define LUA_32BITS in the make file, but changing here you ** ensure that all software connected to Lua will be compiled with the ** same configuration. */ /* #define LUA_32BITS */ /* @@ LUA_USE_C89 controls the use of non-ISO-C89 features. ** Define it if you want Lua to avoid the use of a few C99 features ** or Windows-specific features on Windows. */ /* #define LUA_USE_C89 */ /* ** By default, Lua on Windows use (some) specific Windows features */ #if !defined(LUA_USE_C89) && defined(_WIN32) && !defined(_WIN32_WCE) #define LUA_USE_WINDOWS /* enable goodies for regular Windows */ #endif #if defined(LUA_USE_WINDOWS) #define LUA_DL_DLL /* enable support for DLL */ #define LUA_USE_C89 /* broadly, Windows is C89 */ #endif #if defined(LUA_USE_LINUX) #define LUA_USE_POSIX #define LUA_USE_DLOPEN /* needs an extra library: -ldl */ #define LUA_USE_READLINE /* needs some extra libraries */ #endif #if defined(LUA_USE_MACOSX) #define LUA_USE_POSIX #define LUA_USE_DLOPEN /* MacOS does not need -ldl */ #define LUA_USE_READLINE /* needs an extra library: -lreadline */ #endif /* @@ LUA_C89_NUMBERS ensures that Lua uses the largest types available for ** C89 ('long' and 'double'); Windows always has '__int64', so it does ** not need to use this case. */ #if defined(LUA_USE_C89) && !defined(LUA_USE_WINDOWS) #define LUA_C89_NUMBERS #endif /* @@ LUAI_BITSINT defines the (minimum) number of bits in an 'int'. */ /* avoid undefined shifts */ #if ((INT_MAX >> 15) >> 15) >= 1 #define LUAI_BITSINT 32 #else /* 'int' always must have at least 16 bits */ #define LUAI_BITSINT 16 #endif /* @@ LUA_INT_TYPE defines the type for Lua integers. @@ LUA_FLOAT_TYPE defines the type for Lua floats. ** Lua should work fine with any mix of these options (if supported ** by your C compiler). The usual configurations are 64-bit integers ** and 'double' (the default), 32-bit integers and 'float' (for ** restricted platforms), and 'long'/'double' (for C compilers not ** compliant with C99, which may not have support for 'long long'). */ /* predefined options for LUA_INT_TYPE */ #define LUA_INT_INT 1 #define LUA_INT_LONG 2 #define LUA_INT_LONGLONG 3 /* predefined options for LUA_FLOAT_TYPE */ #define LUA_FLOAT_FLOAT 1 #define LUA_FLOAT_DOUBLE 2 #define LUA_FLOAT_LONGDOUBLE 3 #if defined(LUA_32BITS) /* { */ /* ** 32-bit integers and 'float' */ #if LUAI_BITSINT >= 32 /* use 'int' if big enough */ #define LUA_INT_TYPE LUA_INT_INT #else /* otherwise use 'long' */ #define LUA_INT_TYPE LUA_INT_LONG #endif #define LUA_FLOAT_TYPE LUA_FLOAT_FLOAT #elif defined(LUA_C89_NUMBERS) /* }{ */ /* ** largest types available for C89 ('long' and 'double') */ #define LUA_INT_TYPE LUA_INT_LONG #define LUA_FLOAT_TYPE LUA_FLOAT_DOUBLE #endif /* } */ /* ** default configuration for 64-bit Lua ('long long' and 'double') */ #if !defined(LUA_INT_TYPE) #define LUA_INT_TYPE LUA_INT_LONGLONG #endif #if !defined(LUA_FLOAT_TYPE) #define LUA_FLOAT_TYPE LUA_FLOAT_DOUBLE #endif /* }================================================================== */ /* ** {================================================================== ** Configuration for Paths. ** =================================================================== */ /* ** LUA_PATH_SEP is the character that separates templates in a path. ** LUA_PATH_MARK is the string that marks the substitution points in a ** template. ** LUA_EXEC_DIR in a Windows path is replaced by the executable's ** directory. */ #define LUA_PATH_SEP ";" #define LUA_PATH_MARK "?" #define LUA_EXEC_DIR "!" /* @@ LUA_PATH_DEFAULT is the default path that Lua uses to look for ** Lua libraries. @@ LUA_CPATH_DEFAULT is the default path that Lua uses to look for ** C libraries. ** CHANGE them if your machine has a non-conventional directory ** hierarchy or if you want to install your libraries in ** non-conventional directories. */ #define LUA_VDIR LUA_VERSION_MAJOR "." LUA_VERSION_MINOR #if defined(_WIN32) /* { */ /* ** In Windows, any exclamation mark ('!') in the path is replaced by the ** path of the directory of the executable file of the current process. */ #define LUA_LDIR "!\\lua\\" #define LUA_CDIR "!\\" #define LUA_SHRDIR "!\\..\\share\\lua\\" LUA_VDIR "\\" #define LUA_PATH_DEFAULT \ LUA_LDIR"?.lua;" LUA_LDIR"?\\init.lua;" \ LUA_CDIR"?.lua;" LUA_CDIR"?\\init.lua;" \ LUA_SHRDIR"?.lua;" LUA_SHRDIR"?\\init.lua;" \ ".\\?.lua;" ".\\?\\init.lua" #define LUA_CPATH_DEFAULT \ LUA_CDIR"?.dll;" \ LUA_CDIR"..\\lib\\lua\\" LUA_VDIR "\\?.dll;" \ LUA_CDIR"loadall.dll;" ".\\?.dll" #else /* }{ */ #define LUA_ROOT "/usr/local/" #define LUA_LDIR LUA_ROOT "share/lua/" LUA_VDIR "/" #define LUA_CDIR LUA_ROOT "lib/lua/" LUA_VDIR "/" #define LUA_PATH_DEFAULT \ LUA_LDIR"?.lua;" LUA_LDIR"?/init.lua;" \ LUA_CDIR"?.lua;" LUA_CDIR"?/init.lua;" \ "./?.lua;" "./?/init.lua" #define LUA_CPATH_DEFAULT \ LUA_CDIR"?.so;" LUA_CDIR"loadall.so;" "./?.so" #endif /* } */ /* @@ LUA_DIRSEP is the directory separator (for submodules). ** CHANGE it if your machine does not use "/" as the directory separator ** and is not Windows. (On Windows Lua automatically uses "\".) */ #if defined(_WIN32) #define LUA_DIRSEP "\\" #else #define LUA_DIRSEP "/" #endif /* }================================================================== */ /* ** {================================================================== ** Marks for exported symbols in the C code ** =================================================================== */ /* @@ LUA_API is a mark for all core API functions. @@ LUALIB_API is a mark for all auxiliary library functions. @@ LUAMOD_API is a mark for all standard library opening functions. ** CHANGE them if you need to define those functions in some special way. ** For instance, if you want to create one Windows DLL with the core and ** the libraries, you may want to use the following definition (define ** LUA_BUILD_AS_DLL to get it). */ #if defined(LUA_BUILD_AS_DLL) /* { */ #if defined(LUA_CORE) || defined(LUA_LIB) /* { */ #define LUA_API __declspec(dllexport) #else /* }{ */ #define LUA_API __declspec(dllimport) #endif /* } */ #else /* }{ */ #define LUA_API extern #endif /* } */ /* more often than not the libs go together with the core */ #define LUALIB_API LUA_API #define LUAMOD_API LUALIB_API /* @@ LUAI_FUNC is a mark for all extern functions that are not to be ** exported to outside modules. @@ LUAI_DDEF and LUAI_DDEC are marks for all extern (const) variables ** that are not to be exported to outside modules (LUAI_DDEF for ** definitions and LUAI_DDEC for declarations). ** CHANGE them if you need to mark them in some special way. Elf/gcc ** (versions 3.2 and later) mark them as "hidden" to optimize access ** when Lua is compiled as a shared library. Not all elf targets support ** this attribute. Unfortunately, gcc does not offer a way to check ** whether the target offers that support, and those without support ** give a warning about it. To avoid these warnings, change to the ** default definition. */ #if defined(__GNUC__) && ((__GNUC__*100 + __GNUC_MINOR__) >= 302) && \ defined(__ELF__) /* { */ #define LUAI_FUNC __attribute__((visibility("hidden"))) extern #else /* }{ */ #define LUAI_FUNC extern #endif /* } */ #define LUAI_DDEC LUAI_FUNC #define LUAI_DDEF /* empty */ /* }================================================================== */ /* ** {================================================================== ** Compatibility with previous versions ** =================================================================== */ /* @@ LUA_COMPAT_5_2 controls other macros for compatibility with Lua 5.2. @@ LUA_COMPAT_5_1 controls other macros for compatibility with Lua 5.1. ** You can define it to get all options, or change specific options ** to fit your specific needs. */ #if defined(LUA_COMPAT_5_2) /* { */ /* @@ LUA_COMPAT_MATHLIB controls the presence of several deprecated ** functions in the mathematical library. */ #define LUA_COMPAT_MATHLIB /* @@ LUA_COMPAT_BITLIB controls the presence of library 'bit32'. */ #define LUA_COMPAT_BITLIB /* @@ LUA_COMPAT_IPAIRS controls the effectiveness of the __ipairs metamethod. */ #define LUA_COMPAT_IPAIRS /* @@ LUA_COMPAT_APIINTCASTS controls the presence of macros for ** manipulating other integer types (lua_pushunsigned, lua_tounsigned, ** luaL_checkint, luaL_checklong, etc.) */ #define LUA_COMPAT_APIINTCASTS #endif /* } */ #if defined(LUA_COMPAT_5_1) /* { */ /* Incompatibilities from 5.2 -> 5.3 */ #define LUA_COMPAT_MATHLIB #define LUA_COMPAT_APIINTCASTS /* @@ LUA_COMPAT_UNPACK controls the presence of global 'unpack'. ** You can replace it with 'table.unpack'. */ #define LUA_COMPAT_UNPACK /* @@ LUA_COMPAT_LOADERS controls the presence of table 'package.loaders'. ** You can replace it with 'package.searchers'. */ #define LUA_COMPAT_LOADERS /* @@ macro 'lua_cpcall' emulates deprecated function lua_cpcall. ** You can call your C function directly (with light C functions). */ #define lua_cpcall(L,f,u) \ (lua_pushcfunction(L, (f)), \ lua_pushlightuserdata(L,(u)), \ lua_pcall(L,1,0,0)) /* @@ LUA_COMPAT_LOG10 defines the function 'log10' in the math library. ** You can rewrite 'log10(x)' as 'log(x, 10)'. */ #define LUA_COMPAT_LOG10 /* @@ LUA_COMPAT_LOADSTRING defines the function 'loadstring' in the base ** library. You can rewrite 'loadstring(s)' as 'load(s)'. */ #define LUA_COMPAT_LOADSTRING /* @@ LUA_COMPAT_MAXN defines the function 'maxn' in the table library. */ #define LUA_COMPAT_MAXN /* @@ The following macros supply trivial compatibility for some ** changes in the API. The macros themselves document how to ** change your code to avoid using them. */ #define lua_strlen(L,i) lua_rawlen(L, (i)) #define lua_objlen(L,i) lua_rawlen(L, (i)) #define lua_equal(L,idx1,idx2) lua_compare(L,(idx1),(idx2),LUA_OPEQ) #define lua_lessthan(L,idx1,idx2) lua_compare(L,(idx1),(idx2),LUA_OPLT) /* @@ LUA_COMPAT_MODULE controls compatibility with previous ** module functions 'module' (Lua) and 'luaL_register' (C). */ #define LUA_COMPAT_MODULE #endif /* } */ /* @@ LUA_COMPAT_FLOATSTRING makes Lua format integral floats without a @@ a float mark ('.0'). ** This macro is not on by default even in compatibility mode, ** because this is not really an incompatibility. */ /* #define LUA_COMPAT_FLOATSTRING */ /* }================================================================== */ /* ** {================================================================== ** Configuration for Numbers. ** Change these definitions if no predefined LUA_FLOAT_* / LUA_INT_* ** satisfy your needs. ** =================================================================== */ /* @@ LUA_NUMBER is the floating-point type used by Lua. @@ LUAI_UACNUMBER is the result of a 'default argument promotion' @@ over a floating number. @@ l_mathlim(x) corrects limit name 'x' to the proper float type ** by prefixing it with one of FLT/DBL/LDBL. @@ LUA_NUMBER_FRMLEN is the length modifier for writing floats. @@ LUA_NUMBER_FMT is the format for writing floats. @@ lua_number2str converts a float to a string. @@ l_mathop allows the addition of an 'l' or 'f' to all math operations. @@ l_floor takes the floor of a float. @@ lua_str2number converts a decimal numeric string to a number. */ /* The following definitions are good for most cases here */ #define l_floor(x) (l_mathop(floor)(x)) #define lua_number2str(s,sz,n) \ l_sprintf((s), sz, LUA_NUMBER_FMT, (LUAI_UACNUMBER)(n)) /* @@ lua_numbertointeger converts a float number to an integer, or ** returns 0 if float is not within the range of a lua_Integer. ** (The range comparisons are tricky because of rounding. The tests ** here assume a two-complement representation, where MININTEGER always ** has an exact representation as a float; MAXINTEGER may not have one, ** and therefore its conversion to float may have an ill-defined value.) */ #define lua_numbertointeger(n,p) \ ((n) >= (LUA_NUMBER)(LUA_MININTEGER) && \ (n) < -(LUA_NUMBER)(LUA_MININTEGER) && \ (*(p) = (LUA_INTEGER)(n), 1)) /* now the variable definitions */ #if LUA_FLOAT_TYPE == LUA_FLOAT_FLOAT /* { single float */ #define LUA_NUMBER float #define l_mathlim(n) (FLT_##n) #define LUAI_UACNUMBER double #define LUA_NUMBER_FRMLEN "" #define LUA_NUMBER_FMT "%.7g" #define l_mathop(op) op##f #define lua_str2number(s,p) strtof((s), (p)) #elif LUA_FLOAT_TYPE == LUA_FLOAT_LONGDOUBLE /* }{ long double */ #define LUA_NUMBER long double #define l_mathlim(n) (LDBL_##n) #define LUAI_UACNUMBER long double #define LUA_NUMBER_FRMLEN "L" #define LUA_NUMBER_FMT "%.19Lg" #define l_mathop(op) op##l #define lua_str2number(s,p) strtold((s), (p)) #elif LUA_FLOAT_TYPE == LUA_FLOAT_DOUBLE /* }{ double */ #define LUA_NUMBER double #define l_mathlim(n) (DBL_##n) #define LUAI_UACNUMBER double #define LUA_NUMBER_FRMLEN "" #define LUA_NUMBER_FMT "%.14g" #define l_mathop(op) op #define lua_str2number(s,p) strtod((s), (p)) #else /* }{ */ #error "numeric float type not defined" #endif /* } */ /* @@ LUA_INTEGER is the integer type used by Lua. ** @@ LUA_UNSIGNED is the unsigned version of LUA_INTEGER. ** @@ LUAI_UACINT is the result of a 'default argument promotion' @@ over a lUA_INTEGER. @@ LUA_INTEGER_FRMLEN is the length modifier for reading/writing integers. @@ LUA_INTEGER_FMT is the format for writing integers. @@ LUA_MAXINTEGER is the maximum value for a LUA_INTEGER. @@ LUA_MININTEGER is the minimum value for a LUA_INTEGER. @@ lua_integer2str converts an integer to a string. */ /* The following definitions are good for most cases here */ #define LUA_INTEGER_FMT "%" LUA_INTEGER_FRMLEN "d" #define LUAI_UACINT LUA_INTEGER #define lua_integer2str(s,sz,n) \ l_sprintf((s), sz, LUA_INTEGER_FMT, (LUAI_UACINT)(n)) /* ** use LUAI_UACINT here to avoid problems with promotions (which ** can turn a comparison between unsigneds into a signed comparison) */ #define LUA_UNSIGNED unsigned LUAI_UACINT /* now the variable definitions */ #if LUA_INT_TYPE == LUA_INT_INT /* { int */ #define LUA_INTEGER int #define LUA_INTEGER_FRMLEN "" #define LUA_MAXINTEGER INT_MAX #define LUA_MININTEGER INT_MIN #elif LUA_INT_TYPE == LUA_INT_LONG /* }{ long */ #define LUA_INTEGER long #define LUA_INTEGER_FRMLEN "l" #define LUA_MAXINTEGER LONG_MAX #define LUA_MININTEGER LONG_MIN #elif LUA_INT_TYPE == LUA_INT_LONGLONG /* }{ long long */ /* use presence of macro LLONG_MAX as proxy for C99 compliance */ #if defined(LLONG_MAX) /* { */ /* use ISO C99 stuff */ #define LUA_INTEGER long long #define LUA_INTEGER_FRMLEN "ll" #define LUA_MAXINTEGER LLONG_MAX #define LUA_MININTEGER LLONG_MIN #elif defined(LUA_USE_WINDOWS) /* }{ */ /* in Windows, can use specific Windows types */ #define LUA_INTEGER __int64 #define LUA_INTEGER_FRMLEN "I64" #define LUA_MAXINTEGER _I64_MAX #define LUA_MININTEGER _I64_MIN #else /* }{ */ #error "Compiler does not support 'long long'. Use option '-DLUA_32BITS' \ or '-DLUA_C89_NUMBERS' (see file 'luaconf.h' for details)" #endif /* } */ #else /* }{ */ #error "numeric integer type not defined" #endif /* } */ /* }================================================================== */ /* ** {================================================================== ** Dependencies with C99 and other C details ** =================================================================== */ /* @@ l_sprintf is equivalent to 'snprintf' or 'sprintf' in C89. ** (All uses in Lua have only one format item.) */ #if !defined(LUA_USE_C89) #define l_sprintf(s,sz,f,i) snprintf(s,sz,f,i) #else #define l_sprintf(s,sz,f,i) ((void)(sz), sprintf(s,f,i)) #endif /* @@ lua_strx2number converts an hexadecimal numeric string to a number. ** In C99, 'strtod' does that conversion. Otherwise, you can ** leave 'lua_strx2number' undefined and Lua will provide its own ** implementation. */ #if !defined(LUA_USE_C89) #define lua_strx2number(s,p) lua_str2number(s,p) #endif /* @@ lua_pointer2str converts a pointer to a readable string in a ** non-specified way. */ #define lua_pointer2str(buff,sz,p) l_sprintf(buff,sz,"%p",p) /* @@ lua_number2strx converts a float to an hexadecimal numeric string. ** In C99, 'sprintf' (with format specifiers '%a'/'%A') does that. ** Otherwise, you can leave 'lua_number2strx' undefined and Lua will ** provide its own implementation. */ #if !defined(LUA_USE_C89) #define lua_number2strx(L,b,sz,f,n) \ ((void)L, l_sprintf(b,sz,f,(LUAI_UACNUMBER)(n))) #endif /* ** 'strtof' and 'opf' variants for math functions are not valid in ** C89. Otherwise, the macro 'HUGE_VALF' is a good proxy for testing the ** availability of these variants. ('math.h' is already included in ** all files that use these macros.) */ #if defined(LUA_USE_C89) || (defined(HUGE_VAL) && !defined(HUGE_VALF)) #undef l_mathop /* variants not available */ #undef lua_str2number #define l_mathop(op) (lua_Number)op /* no variant */ #define lua_str2number(s,p) ((lua_Number)strtod((s), (p))) #endif /* @@ LUA_KCONTEXT is the type of the context ('ctx') for continuation ** functions. It must be a numerical type; Lua will use 'intptr_t' if ** available, otherwise it will use 'ptrdiff_t' (the nearest thing to ** 'intptr_t' in C89) */ #define LUA_KCONTEXT ptrdiff_t #if !defined(LUA_USE_C89) && defined(__STDC_VERSION__) && \ __STDC_VERSION__ >= 199901L #include #if defined(INTPTR_MAX) /* even in C99 this type is optional */ #undef LUA_KCONTEXT #define LUA_KCONTEXT intptr_t #endif #endif /* @@ lua_getlocaledecpoint gets the locale "radix character" (decimal point). ** Change that if you do not want to use C locales. (Code using this ** macro must include header 'locale.h'.) */ #if !defined(lua_getlocaledecpoint) #define lua_getlocaledecpoint() (localeconv()->decimal_point[0]) #endif /* }================================================================== */ /* ** {================================================================== ** Language Variations ** ===================================================================== */ /* @@ LUA_NOCVTN2S/LUA_NOCVTS2N control how Lua performs some ** coercions. Define LUA_NOCVTN2S to turn off automatic coercion from ** numbers to strings. Define LUA_NOCVTS2N to turn off automatic ** coercion from strings to numbers. */ /* #define LUA_NOCVTN2S */ /* #define LUA_NOCVTS2N */ /* @@ LUA_USE_APICHECK turns on several consistency checks on the C API. ** Define it as a help when debugging C code. */ #if defined(LUA_USE_APICHECK) #include #define luai_apicheck(l,e) assert(e) #endif /* }================================================================== */ /* ** {================================================================== ** Macros that affect the API and must be stable (that is, must be the ** same when you compile Lua and when you compile code that links to ** Lua). You probably do not want/need to change them. ** ===================================================================== */ /* @@ LUAI_MAXSTACK limits the size of the Lua stack. ** CHANGE it if you need a different limit. This limit is arbitrary; ** its only purpose is to stop Lua from consuming unlimited stack ** space (and to reserve some numbers for pseudo-indices). */ #if LUAI_BITSINT >= 32 #define LUAI_MAXSTACK 1000000 #else #define LUAI_MAXSTACK 15000 #endif /* @@ LUA_EXTRASPACE defines the size of a raw memory area associated with ** a Lua state with very fast access. ** CHANGE it if you need a different size. */ #define LUA_EXTRASPACE (sizeof(void *)) /* @@ LUA_IDSIZE gives the maximum size for the description of the source @@ of a function in debug information. ** CHANGE it if you want a different size. */ #define LUA_IDSIZE 60 /* @@ LUAL_BUFFERSIZE is the buffer size used by the lauxlib buffer system. ** CHANGE it if it uses too much C-stack space. (For long double, ** 'string.format("%.99f", -1e4932)' needs 5034 bytes, so a ** smaller buffer would force a memory allocation for each call to ** 'string.format'.) */ #if LUA_FLOAT_TYPE == LUA_FLOAT_LONGDOUBLE #define LUAL_BUFFERSIZE 8192 #else #define LUAL_BUFFERSIZE ((int)(0x80 * sizeof(void*) * sizeof(lua_Integer))) #endif /* }================================================================== */ /* @@ LUA_QL describes how error messages quote program elements. ** Lua does not use these macros anymore; they are here for ** compatibility only. */ #define LUA_QL(x) "'" x "'" #define LUA_QS LUA_QL("%s") /* =================================================================== */ /* ** Local configuration. You can use this space to add your redefinitions ** without modifying the main part of the file. */ #endif ================================================ FILE: Tests/ApiExplorer/lua/src/lualib.h ================================================ /* ** $Id: lualib.h,v 1.45.1.1 2017/04/19 17:20:42 roberto Exp $ ** Lua standard libraries ** See Copyright Notice in lua.h */ #ifndef lualib_h #define lualib_h #include "lua.h" /* version suffix for environment variable names */ #define LUA_VERSUFFIX "_" LUA_VERSION_MAJOR "_" LUA_VERSION_MINOR LUAMOD_API int (luaopen_base) (lua_State *L); #define LUA_COLIBNAME "coroutine" LUAMOD_API int (luaopen_coroutine) (lua_State *L); #define LUA_TABLIBNAME "table" LUAMOD_API int (luaopen_table) (lua_State *L); #define LUA_IOLIBNAME "io" LUAMOD_API int (luaopen_io) (lua_State *L); #define LUA_OSLIBNAME "os" LUAMOD_API int (luaopen_os) (lua_State *L); #define LUA_STRLIBNAME "string" LUAMOD_API int (luaopen_string) (lua_State *L); #define LUA_UTF8LIBNAME "utf8" LUAMOD_API int (luaopen_utf8) (lua_State *L); #define LUA_BITLIBNAME "bit32" LUAMOD_API int (luaopen_bit32) (lua_State *L); #define LUA_MATHLIBNAME "math" LUAMOD_API int (luaopen_math) (lua_State *L); #define LUA_DBLIBNAME "debug" LUAMOD_API int (luaopen_debug) (lua_State *L); #define LUA_LOADLIBNAME "package" LUAMOD_API int (luaopen_package) (lua_State *L); /* open all previous libraries */ LUALIB_API void (luaL_openlibs) (lua_State *L); #if !defined(lua_assert) #define lua_assert(x) ((void)0) #endif #endif ================================================ FILE: Tests/ApiExplorer/lua/src/lundump.c ================================================ /* ** $Id: lundump.c,v 2.44.1.1 2017/04/19 17:20:42 roberto Exp $ ** load precompiled Lua chunks ** See Copyright Notice in lua.h */ #define lundump_c #define LUA_CORE #include "lprefix.h" #include #include "lua.h" #include "ldebug.h" #include "ldo.h" #include "lfunc.h" #include "lmem.h" #include "lobject.h" #include "lstring.h" #include "lundump.h" #include "lzio.h" #if !defined(luai_verifycode) #define luai_verifycode(L,b,f) /* empty */ #endif typedef struct { lua_State *L; ZIO *Z; const char *name; } LoadState; static l_noret error(LoadState *S, const char *why) { luaO_pushfstring(S->L, "%s: %s precompiled chunk", S->name, why); luaD_throw(S->L, LUA_ERRSYNTAX); } /* ** All high-level loads go through LoadVector; you can change it to ** adapt to the endianness of the input */ #define LoadVector(S,b,n) LoadBlock(S,b,(n)*sizeof((b)[0])) static void LoadBlock (LoadState *S, void *b, size_t size) { if (luaZ_read(S->Z, b, size) != 0) error(S, "truncated"); } #define LoadVar(S,x) LoadVector(S,&x,1) static lu_byte LoadByte (LoadState *S) { lu_byte x; LoadVar(S, x); return x; } static int LoadInt (LoadState *S) { int x; LoadVar(S, x); return x; } static lua_Number LoadNumber (LoadState *S) { lua_Number x; LoadVar(S, x); return x; } static lua_Integer LoadInteger (LoadState *S) { lua_Integer x; LoadVar(S, x); return x; } static TString *LoadString (LoadState *S) { size_t size = LoadByte(S); if (size == 0xFF) LoadVar(S, size); if (size == 0) return NULL; else if (--size <= LUAI_MAXSHORTLEN) { /* short string? */ char buff[LUAI_MAXSHORTLEN]; LoadVector(S, buff, size); return luaS_newlstr(S->L, buff, size); } else { /* long string */ TString *ts = luaS_createlngstrobj(S->L, size); LoadVector(S, getstr(ts), size); /* load directly in final place */ return ts; } } static void LoadCode (LoadState *S, Proto *f) { int n = LoadInt(S); f->code = luaM_newvector(S->L, n, Instruction); f->sizecode = n; LoadVector(S, f->code, n); } static void LoadFunction(LoadState *S, Proto *f, TString *psource); static void LoadConstants (LoadState *S, Proto *f) { int i; int n = LoadInt(S); f->k = luaM_newvector(S->L, n, TValue); f->sizek = n; for (i = 0; i < n; i++) setnilvalue(&f->k[i]); for (i = 0; i < n; i++) { TValue *o = &f->k[i]; int t = LoadByte(S); switch (t) { case LUA_TNIL: setnilvalue(o); break; case LUA_TBOOLEAN: setbvalue(o, LoadByte(S)); break; case LUA_TNUMFLT: setfltvalue(o, LoadNumber(S)); break; case LUA_TNUMINT: setivalue(o, LoadInteger(S)); break; case LUA_TSHRSTR: case LUA_TLNGSTR: setsvalue2n(S->L, o, LoadString(S)); break; default: lua_assert(0); } } } static void LoadProtos (LoadState *S, Proto *f) { int i; int n = LoadInt(S); f->p = luaM_newvector(S->L, n, Proto *); f->sizep = n; for (i = 0; i < n; i++) f->p[i] = NULL; for (i = 0; i < n; i++) { f->p[i] = luaF_newproto(S->L); LoadFunction(S, f->p[i], f->source); } } static void LoadUpvalues (LoadState *S, Proto *f) { int i, n; n = LoadInt(S); f->upvalues = luaM_newvector(S->L, n, Upvaldesc); f->sizeupvalues = n; for (i = 0; i < n; i++) f->upvalues[i].name = NULL; for (i = 0; i < n; i++) { f->upvalues[i].instack = LoadByte(S); f->upvalues[i].idx = LoadByte(S); } } static void LoadDebug (LoadState *S, Proto *f) { int i, n; n = LoadInt(S); f->lineinfo = luaM_newvector(S->L, n, int); f->sizelineinfo = n; LoadVector(S, f->lineinfo, n); n = LoadInt(S); f->locvars = luaM_newvector(S->L, n, LocVar); f->sizelocvars = n; for (i = 0; i < n; i++) f->locvars[i].varname = NULL; for (i = 0; i < n; i++) { f->locvars[i].varname = LoadString(S); f->locvars[i].startpc = LoadInt(S); f->locvars[i].endpc = LoadInt(S); } n = LoadInt(S); for (i = 0; i < n; i++) f->upvalues[i].name = LoadString(S); } static void LoadFunction (LoadState *S, Proto *f, TString *psource) { f->source = LoadString(S); if (f->source == NULL) /* no source in dump? */ f->source = psource; /* reuse parent's source */ f->linedefined = LoadInt(S); f->lastlinedefined = LoadInt(S); f->numparams = LoadByte(S); f->is_vararg = LoadByte(S); f->maxstacksize = LoadByte(S); LoadCode(S, f); LoadConstants(S, f); LoadUpvalues(S, f); LoadProtos(S, f); LoadDebug(S, f); } static void checkliteral (LoadState *S, const char *s, const char *msg) { char buff[sizeof(LUA_SIGNATURE) + sizeof(LUAC_DATA)]; /* larger than both */ size_t len = strlen(s); LoadVector(S, buff, len); if (memcmp(s, buff, len) != 0) error(S, msg); } static void fchecksize (LoadState *S, size_t size, const char *tname) { if (LoadByte(S) != size) error(S, luaO_pushfstring(S->L, "%s size mismatch in", tname)); } #define checksize(S,t) fchecksize(S,sizeof(t),#t) static void checkHeader (LoadState *S) { #if HC_PLATFORM != HC_PLATFORM_XDK && !APIEXPLORER_UWP // TODO: wrong, but not using compiled LUA files checkliteral(S, LUA_SIGNATURE + 1, "not a"); /* 1st char already checked */ #endif if (LoadByte(S) != LUAC_VERSION) error(S, "version mismatch in"); if (LoadByte(S) != LUAC_FORMAT) error(S, "format mismatch in"); checkliteral(S, LUAC_DATA, "corrupted"); checksize(S, int); checksize(S, size_t); checksize(S, Instruction); checksize(S, lua_Integer); checksize(S, lua_Number); if (LoadInteger(S) != LUAC_INT) error(S, "endianness mismatch in"); if (LoadNumber(S) != LUAC_NUM) error(S, "float format mismatch in"); } /* ** load precompiled chunk */ LClosure *luaU_undump(lua_State *L, ZIO *Z, const char *name) { LoadState S; LClosure *cl; if (*name == '@' || *name == '=') S.name = name + 1; else if (*name == LUA_SIGNATURE[0]) S.name = "binary string"; else S.name = name; S.L = L; S.Z = Z; checkHeader(&S); cl = luaF_newLclosure(L, LoadByte(&S)); setclLvalue(L, L->top, cl); luaD_inctop(L); cl->p = luaF_newproto(L); LoadFunction(&S, cl->p, NULL); lua_assert(cl->nupvalues == cl->p->sizeupvalues); luai_verifycode(L, buff, cl->p); return cl; } ================================================ FILE: Tests/ApiExplorer/lua/src/lundump.h ================================================ /* ** $Id: lundump.h,v 1.45.1.1 2017/04/19 17:20:42 roberto Exp $ ** load precompiled Lua chunks ** See Copyright Notice in lua.h */ #ifndef lundump_h #define lundump_h #include "llimits.h" #include "lobject.h" #include "lzio.h" /* data to catch conversion errors */ #define LUAC_DATA "\x19\x93\r\n\x1a\n" #define LUAC_INT 0x5678 #define LUAC_NUM cast_num(370.5) #define MYINT(s) (s[0]-'0') #define LUAC_VERSION (MYINT(LUA_VERSION_MAJOR)*16+MYINT(LUA_VERSION_MINOR)) #define LUAC_FORMAT 0 /* this is the official format */ /* load one chunk; from lundump.c */ LUAI_FUNC LClosure* luaU_undump (lua_State* L, ZIO* Z, const char* name); /* dump one chunk; from ldump.c */ LUAI_FUNC int luaU_dump (lua_State* L, const Proto* f, lua_Writer w, void* data, int strip); #endif ================================================ FILE: Tests/ApiExplorer/lua/src/lutf8lib.c ================================================ /* ** $Id: lutf8lib.c,v 1.16.1.1 2017/04/19 17:29:57 roberto Exp $ ** Standard library for UTF-8 manipulation ** See Copyright Notice in lua.h */ #define lutf8lib_c #define LUA_LIB #include "lprefix.h" #include #include #include #include #include "lua.h" #include "lauxlib.h" #include "lualib.h" #define MAXUNICODE 0x10FFFF #define iscont(p) ((*(p) & 0xC0) == 0x80) /* from strlib */ /* translate a relative string position: negative means back from end */ static lua_Integer u_posrelat (lua_Integer pos, size_t len) { if (pos >= 0) return pos; else if (0u - (size_t)pos > len) return 0; else return (lua_Integer)len + pos + 1; } /* ** Decode one UTF-8 sequence, returning NULL if byte sequence is invalid. */ static const char *utf8_decode (const char *o, int *val) { static const unsigned int limits[] = {0xFF, 0x7F, 0x7FF, 0xFFFF}; const unsigned char *s = (const unsigned char *)o; unsigned int c = s[0]; unsigned int res = 0; /* final result */ if (c < 0x80) /* ascii? */ res = c; else { int count = 0; /* to count number of continuation bytes */ while (c & 0x40) { /* still have continuation bytes? */ int cc = s[++count]; /* read next byte */ if ((cc & 0xC0) != 0x80) /* not a continuation byte? */ return NULL; /* invalid byte sequence */ res = (res << 6) | (cc & 0x3F); /* add lower 6 bits from cont. byte */ c <<= 1; /* to test next bit */ } res |= ((c & 0x7F) << (count * 5)); /* add first byte */ if (count > 3 || res > MAXUNICODE || res <= limits[count]) return NULL; /* invalid byte sequence */ s += count; /* skip continuation bytes read */ } if (val) *val = res; return (const char *)s + 1; /* +1 to include first byte */ } /* ** utf8len(s [, i [, j]]) --> number of characters that start in the ** range [i,j], or nil + current position if 's' is not well formed in ** that interval */ static int utflen (lua_State *L) { int n = 0; size_t len; const char *s = luaL_checklstring(L, 1, &len); lua_Integer posi = u_posrelat(luaL_optinteger(L, 2, 1), len); lua_Integer posj = u_posrelat(luaL_optinteger(L, 3, -1), len); luaL_argcheck(L, 1 <= posi && --posi <= (lua_Integer)len, 2, "initial position out of string"); luaL_argcheck(L, --posj < (lua_Integer)len, 3, "final position out of string"); while (posi <= posj) { const char *s1 = utf8_decode(s + posi, NULL); if (s1 == NULL) { /* conversion error? */ lua_pushnil(L); /* return nil ... */ lua_pushinteger(L, posi + 1); /* ... and current position */ return 2; } posi = s1 - s; n++; } lua_pushinteger(L, n); return 1; } /* ** codepoint(s, [i, [j]]) -> returns codepoints for all characters ** that start in the range [i,j] */ static int codepoint (lua_State *L) { size_t len; const char *s = luaL_checklstring(L, 1, &len); lua_Integer posi = u_posrelat(luaL_optinteger(L, 2, 1), len); lua_Integer pose = u_posrelat(luaL_optinteger(L, 3, posi), len); int n; const char *se; luaL_argcheck(L, posi >= 1, 2, "out of range"); luaL_argcheck(L, pose <= (lua_Integer)len, 3, "out of range"); if (posi > pose) return 0; /* empty interval; return no values */ if (pose - posi >= INT_MAX) /* (lua_Integer -> int) overflow? */ return luaL_error(L, "string slice too long"); n = (int)(pose - posi) + 1; luaL_checkstack(L, n, "string slice too long"); n = 0; se = s + pose; for (s += posi - 1; s < se;) { int code; s = utf8_decode(s, &code); if (s == NULL) return luaL_error(L, "invalid UTF-8 code"); lua_pushinteger(L, code); n++; } return n; } static void pushutfchar (lua_State *L, int arg) { lua_Integer code = luaL_checkinteger(L, arg); luaL_argcheck(L, 0 <= code && code <= MAXUNICODE, arg, "value out of range"); lua_pushfstring(L, "%U", (long)code); } /* ** utfchar(n1, n2, ...) -> char(n1)..char(n2)... */ static int utfchar (lua_State *L) { int n = lua_gettop(L); /* number of arguments */ if (n == 1) /* optimize common case of single char */ pushutfchar(L, 1); else { int i; luaL_Buffer b; luaL_buffinit(L, &b); for (i = 1; i <= n; i++) { pushutfchar(L, i); luaL_addvalue(&b); } luaL_pushresult(&b); } return 1; } /* ** offset(s, n, [i]) -> index where n-th character counting from ** position 'i' starts; 0 means character at 'i'. */ static int byteoffset (lua_State *L) { size_t len; const char *s = luaL_checklstring(L, 1, &len); lua_Integer n = luaL_checkinteger(L, 2); lua_Integer posi = (n >= 0) ? 1 : len + 1; posi = u_posrelat(luaL_optinteger(L, 3, posi), len); luaL_argcheck(L, 1 <= posi && --posi <= (lua_Integer)len, 3, "position out of range"); if (n == 0) { /* find beginning of current byte sequence */ while (posi > 0 && iscont(s + posi)) posi--; } else { if (iscont(s + posi)) return luaL_error(L, "initial position is a continuation byte"); if (n < 0) { while (n < 0 && posi > 0) { /* move back */ do { /* find beginning of previous character */ posi--; } while (posi > 0 && iscont(s + posi)); n++; } } else { n--; /* do not move for 1st character */ while (n > 0 && posi < (lua_Integer)len) { do { /* find beginning of next character */ posi++; } while (iscont(s + posi)); /* (cannot pass final '\0') */ n--; } } } if (n == 0) /* did it find given character? */ lua_pushinteger(L, posi + 1); else /* no such character */ lua_pushnil(L); return 1; } static int iter_aux (lua_State *L) { size_t len; const char *s = luaL_checklstring(L, 1, &len); lua_Integer n = lua_tointeger(L, 2) - 1; if (n < 0) /* first iteration? */ n = 0; /* start from here */ else if (n < (lua_Integer)len) { n++; /* skip current byte */ while (iscont(s + n)) n++; /* and its continuations */ } if (n >= (lua_Integer)len) return 0; /* no more codepoints */ else { int code; const char *next = utf8_decode(s + n, &code); if (next == NULL || iscont(next)) return luaL_error(L, "invalid UTF-8 code"); lua_pushinteger(L, n + 1); lua_pushinteger(L, code); return 2; } } static int iter_codes (lua_State *L) { luaL_checkstring(L, 1); lua_pushcfunction(L, iter_aux); lua_pushvalue(L, 1); lua_pushinteger(L, 0); return 3; } /* pattern to match a single UTF-8 character */ #define UTF8PATT "[\0-\x7F\xC2-\xF4][\x80-\xBF]*" static const luaL_Reg funcs[] = { {"offset", byteoffset}, {"codepoint", codepoint}, {"char", utfchar}, {"len", utflen}, {"codes", iter_codes}, /* placeholders */ {"charpattern", NULL}, {NULL, NULL} }; LUAMOD_API int luaopen_utf8 (lua_State *L) { luaL_newlib(L, funcs); lua_pushlstring(L, UTF8PATT, sizeof(UTF8PATT)/sizeof(char) - 1); lua_setfield(L, -2, "charpattern"); return 1; } ================================================ FILE: Tests/ApiExplorer/lua/src/lvm.c ================================================ /* ** $Id: lvm.c,v 2.268.1.1 2017/04/19 17:39:34 roberto Exp $ ** Lua virtual machine ** See Copyright Notice in lua.h */ #define lvm_c #define LUA_CORE #include "lprefix.h" #include #include #include #include #include #include #include "lua.h" #include "ldebug.h" #include "ldo.h" #include "lfunc.h" #include "lgc.h" #include "lobject.h" #include "lopcodes.h" #include "lstate.h" #include "lstring.h" #include "ltable.h" #include "ltm.h" #include "lvm.h" /* limit for table tag-method chains (to avoid loops) */ #define MAXTAGLOOP 2000 /* ** 'l_intfitsf' checks whether a given integer can be converted to a ** float without rounding. Used in comparisons. Left undefined if ** all integers fit in a float precisely. */ #if !defined(l_intfitsf) /* number of bits in the mantissa of a float */ #define NBM (l_mathlim(MANT_DIG)) /* ** Check whether some integers may not fit in a float, that is, whether ** (maxinteger >> NBM) > 0 (that implies (1 << NBM) <= maxinteger). ** (The shifts are done in parts to avoid shifting by more than the size ** of an integer. In a worst case, NBM == 113 for long double and ** sizeof(integer) == 32.) */ #if ((((LUA_MAXINTEGER >> (NBM / 4)) >> (NBM / 4)) >> (NBM / 4)) \ >> (NBM - (3 * (NBM / 4)))) > 0 #define l_intfitsf(i) \ (-((lua_Integer)1 << NBM) <= (i) && (i) <= ((lua_Integer)1 << NBM)) #endif #endif /* ** Try to convert a value to a float. The float case is already handled ** by the macro 'tonumber'. */ int luaV_tonumber_ (const TValue *obj, lua_Number *n) { TValue v; if (ttisinteger(obj)) { *n = cast_num(ivalue(obj)); return 1; } else if (cvt2num(obj) && /* string convertible to number? */ luaO_str2num(svalue(obj), &v) == vslen(obj) + 1) { *n = nvalue(&v); /* convert result of 'luaO_str2num' to a float */ return 1; } else return 0; /* conversion failed */ } /* ** try to convert a value to an integer, rounding according to 'mode': ** mode == 0: accepts only integral values ** mode == 1: takes the floor of the number ** mode == 2: takes the ceil of the number */ int luaV_tointeger (const TValue *obj, lua_Integer *p, int mode) { TValue v; again: if (ttisfloat(obj)) { lua_Number n = fltvalue(obj); lua_Number f = l_floor(n); if (n != f) { /* not an integral value? */ if (mode == 0) return 0; /* fails if mode demands integral value */ else if (mode > 1) /* needs ceil? */ f += 1; /* convert floor to ceil (remember: n != f) */ } return lua_numbertointeger(f, p); } else if (ttisinteger(obj)) { *p = ivalue(obj); return 1; } else if (cvt2num(obj) && luaO_str2num(svalue(obj), &v) == vslen(obj) + 1) { obj = &v; goto again; /* convert result from 'luaO_str2num' to an integer */ } return 0; /* conversion failed */ } /* ** Try to convert a 'for' limit to an integer, preserving the ** semantics of the loop. ** (The following explanation assumes a non-negative step; it is valid ** for negative steps mutatis mutandis.) ** If the limit can be converted to an integer, rounding down, that is ** it. ** Otherwise, check whether the limit can be converted to a number. If ** the number is too large, it is OK to set the limit as LUA_MAXINTEGER, ** which means no limit. If the number is too negative, the loop ** should not run, because any initial integer value is larger than the ** limit. So, it sets the limit to LUA_MININTEGER. 'stopnow' corrects ** the extreme case when the initial value is LUA_MININTEGER, in which ** case the LUA_MININTEGER limit would still run the loop once. */ static int forlimit (const TValue *obj, lua_Integer *p, lua_Integer step, int *stopnow) { *stopnow = 0; /* usually, let loops run */ if (!luaV_tointeger(obj, p, (step < 0 ? 2 : 1))) { /* not fit in integer? */ lua_Number n; /* try to convert to float */ if (!tonumber(obj, &n)) /* cannot convert to float? */ return 0; /* not a number */ if (luai_numlt(0, n)) { /* if true, float is larger than max integer */ *p = LUA_MAXINTEGER; if (step < 0) *stopnow = 1; } else { /* float is smaller than min integer */ *p = LUA_MININTEGER; if (step >= 0) *stopnow = 1; } } return 1; } /* ** Finish the table access 'val = t[key]'. ** if 'slot' is NULL, 't' is not a table; otherwise, 'slot' points to ** t[k] entry (which must be nil). */ void luaV_finishget (lua_State *L, const TValue *t, TValue *key, StkId val, const TValue *slot) { int loop; /* counter to avoid infinite loops */ const TValue *tm; /* metamethod */ for (loop = 0; loop < MAXTAGLOOP; loop++) { if (slot == NULL) { /* 't' is not a table? */ lua_assert(!ttistable(t)); tm = luaT_gettmbyobj(L, t, TM_INDEX); if (ttisnil(tm)) luaG_typeerror(L, t, "index"); /* no metamethod */ /* else will try the metamethod */ } else { /* 't' is a table */ lua_assert(ttisnil(slot)); tm = fasttm(L, hvalue(t)->metatable, TM_INDEX); /* table's metamethod */ if (tm == NULL) { /* no metamethod? */ setnilvalue(val); /* result is nil */ return; } /* else will try the metamethod */ } if (ttisfunction(tm)) { /* is metamethod a function? */ luaT_callTM(L, tm, t, key, val, 1); /* call it */ return; } t = tm; /* else try to access 'tm[key]' */ if (luaV_fastget(L,t,key,slot,luaH_get)) { /* fast track? */ setobj2s(L, val, slot); /* done */ return; } /* else repeat (tail call 'luaV_finishget') */ } luaG_runerror(L, "'__index' chain too long; possible loop"); } /* ** Finish a table assignment 't[key] = val'. ** If 'slot' is NULL, 't' is not a table. Otherwise, 'slot' points ** to the entry 't[key]', or to 'luaO_nilobject' if there is no such ** entry. (The value at 'slot' must be nil, otherwise 'luaV_fastset' ** would have done the job.) */ void luaV_finishset (lua_State *L, const TValue *t, TValue *key, StkId val, const TValue *slot) { int loop; /* counter to avoid infinite loops */ for (loop = 0; loop < MAXTAGLOOP; loop++) { const TValue *tm; /* '__newindex' metamethod */ if (slot != NULL) { /* is 't' a table? */ Table *h = hvalue(t); /* save 't' table */ lua_assert(ttisnil(slot)); /* old value must be nil */ tm = fasttm(L, h->metatable, TM_NEWINDEX); /* get metamethod */ if (tm == NULL) { /* no metamethod? */ if (slot == luaO_nilobject) /* no previous entry? */ slot = luaH_newkey(L, h, key); /* create one */ /* no metamethod and (now) there is an entry with given key */ setobj2t(L, cast(TValue *, slot), val); /* set its new value */ invalidateTMcache(h); luaC_barrierback(L, h, val); return; } /* else will try the metamethod */ } else { /* not a table; check metamethod */ if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_NEWINDEX))) luaG_typeerror(L, t, "index"); } /* try the metamethod */ if (ttisfunction(tm)) { luaT_callTM(L, tm, t, key, val, 0); return; } t = tm; /* else repeat assignment over 'tm' */ if (luaV_fastset(L, t, key, slot, luaH_get, val)) return; /* done */ /* else loop */ } luaG_runerror(L, "'__newindex' chain too long; possible loop"); } /* ** Compare two strings 'ls' x 'rs', returning an integer smaller-equal- ** -larger than zero if 'ls' is smaller-equal-larger than 'rs'. ** The code is a little tricky because it allows '\0' in the strings ** and it uses 'strcoll' (to respect locales) for each segments ** of the strings. */ static int l_strcmp (const TString *ls, const TString *rs) { const char *l = getstr(ls); size_t ll = tsslen(ls); const char *r = getstr(rs); size_t lr = tsslen(rs); for (;;) { /* for each segment */ int temp = strcoll(l, r); if (temp != 0) /* not equal? */ return temp; /* done */ else { /* strings are equal up to a '\0' */ size_t len = strlen(l); /* index of first '\0' in both strings */ if (len == lr) /* 'rs' is finished? */ return (len == ll) ? 0 : 1; /* check 'ls' */ else if (len == ll) /* 'ls' is finished? */ return -1; /* 'ls' is smaller than 'rs' ('rs' is not finished) */ /* both strings longer than 'len'; go on comparing after the '\0' */ len++; l += len; ll -= len; r += len; lr -= len; } } } /* ** Check whether integer 'i' is less than float 'f'. If 'i' has an ** exact representation as a float ('l_intfitsf'), compare numbers as ** floats. Otherwise, if 'f' is outside the range for integers, result ** is trivial. Otherwise, compare them as integers. (When 'i' has no ** float representation, either 'f' is "far away" from 'i' or 'f' has ** no precision left for a fractional part; either way, how 'f' is ** truncated is irrelevant.) When 'f' is NaN, comparisons must result ** in false. */ static int LTintfloat (lua_Integer i, lua_Number f) { #if defined(l_intfitsf) if (!l_intfitsf(i)) { if (f >= -cast_num(LUA_MININTEGER)) /* -minint == maxint + 1 */ return 1; /* f >= maxint + 1 > i */ else if (f > cast_num(LUA_MININTEGER)) /* minint < f <= maxint ? */ return (i < cast(lua_Integer, f)); /* compare them as integers */ else /* f <= minint <= i (or 'f' is NaN) --> not(i < f) */ return 0; } #endif return luai_numlt(cast_num(i), f); /* compare them as floats */ } /* ** Check whether integer 'i' is less than or equal to float 'f'. ** See comments on previous function. */ static int LEintfloat (lua_Integer i, lua_Number f) { #if defined(l_intfitsf) if (!l_intfitsf(i)) { if (f >= -cast_num(LUA_MININTEGER)) /* -minint == maxint + 1 */ return 1; /* f >= maxint + 1 > i */ else if (f >= cast_num(LUA_MININTEGER)) /* minint <= f <= maxint ? */ return (i <= cast(lua_Integer, f)); /* compare them as integers */ else /* f < minint <= i (or 'f' is NaN) --> not(i <= f) */ return 0; } #endif return luai_numle(cast_num(i), f); /* compare them as floats */ } /* ** Return 'l < r', for numbers. */ static int LTnum (const TValue *l, const TValue *r) { if (ttisinteger(l)) { lua_Integer li = ivalue(l); if (ttisinteger(r)) return li < ivalue(r); /* both are integers */ else /* 'l' is int and 'r' is float */ return LTintfloat(li, fltvalue(r)); /* l < r ? */ } else { lua_Number lf = fltvalue(l); /* 'l' must be float */ if (ttisfloat(r)) return luai_numlt(lf, fltvalue(r)); /* both are float */ else if (luai_numisnan(lf)) /* 'r' is int and 'l' is float */ return 0; /* NaN < i is always false */ else /* without NaN, (l < r) <--> not(r <= l) */ return !LEintfloat(ivalue(r), lf); /* not (r <= l) ? */ } } /* ** Return 'l <= r', for numbers. */ static int LEnum (const TValue *l, const TValue *r) { if (ttisinteger(l)) { lua_Integer li = ivalue(l); if (ttisinteger(r)) return li <= ivalue(r); /* both are integers */ else /* 'l' is int and 'r' is float */ return LEintfloat(li, fltvalue(r)); /* l <= r ? */ } else { lua_Number lf = fltvalue(l); /* 'l' must be float */ if (ttisfloat(r)) return luai_numle(lf, fltvalue(r)); /* both are float */ else if (luai_numisnan(lf)) /* 'r' is int and 'l' is float */ return 0; /* NaN <= i is always false */ else /* without NaN, (l <= r) <--> not(r < l) */ return !LTintfloat(ivalue(r), lf); /* not (r < l) ? */ } } /* ** Main operation less than; return 'l < r'. */ int luaV_lessthan (lua_State *L, const TValue *l, const TValue *r) { int res; if (ttisnumber(l) && ttisnumber(r)) /* both operands are numbers? */ return LTnum(l, r); else if (ttisstring(l) && ttisstring(r)) /* both are strings? */ return l_strcmp(tsvalue(l), tsvalue(r)) < 0; else if ((res = luaT_callorderTM(L, l, r, TM_LT)) < 0) /* no metamethod? */ luaG_ordererror(L, l, r); /* error */ return res; } /* ** Main operation less than or equal to; return 'l <= r'. If it needs ** a metamethod and there is no '__le', try '__lt', based on ** l <= r iff !(r < l) (assuming a total order). If the metamethod ** yields during this substitution, the continuation has to know ** about it (to negate the result of r= 0) /* try 'le' */ return res; else { /* try 'lt': */ L->ci->callstatus |= CIST_LEQ; /* mark it is doing 'lt' for 'le' */ res = luaT_callorderTM(L, r, l, TM_LT); L->ci->callstatus ^= CIST_LEQ; /* clear mark */ if (res < 0) luaG_ordererror(L, l, r); return !res; /* result is negated */ } } /* ** Main operation for equality of Lua values; return 't1 == t2'. ** L == NULL means raw equality (no metamethods) */ int luaV_equalobj (lua_State *L, const TValue *t1, const TValue *t2) { const TValue *tm; if (ttype(t1) != ttype(t2)) { /* not the same variant? */ if (ttnov(t1) != ttnov(t2) || ttnov(t1) != LUA_TNUMBER) return 0; /* only numbers can be equal with different variants */ else { /* two numbers with different variants */ lua_Integer i1, i2; /* compare them as integers */ return (tointeger(t1, &i1) && tointeger(t2, &i2) && i1 == i2); } } /* values have same type and same variant */ switch (ttype(t1)) { case LUA_TNIL: return 1; case LUA_TNUMINT: return (ivalue(t1) == ivalue(t2)); case LUA_TNUMFLT: return luai_numeq(fltvalue(t1), fltvalue(t2)); case LUA_TBOOLEAN: return bvalue(t1) == bvalue(t2); /* true must be 1 !! */ case LUA_TLIGHTUSERDATA: return pvalue(t1) == pvalue(t2); case LUA_TLCF: return fvalue(t1) == fvalue(t2); case LUA_TSHRSTR: return eqshrstr(tsvalue(t1), tsvalue(t2)); case LUA_TLNGSTR: return luaS_eqlngstr(tsvalue(t1), tsvalue(t2)); case LUA_TUSERDATA: { if (uvalue(t1) == uvalue(t2)) return 1; else if (L == NULL) return 0; tm = fasttm(L, uvalue(t1)->metatable, TM_EQ); if (tm == NULL) tm = fasttm(L, uvalue(t2)->metatable, TM_EQ); break; /* will try TM */ } case LUA_TTABLE: { if (hvalue(t1) == hvalue(t2)) return 1; else if (L == NULL) return 0; tm = fasttm(L, hvalue(t1)->metatable, TM_EQ); if (tm == NULL) tm = fasttm(L, hvalue(t2)->metatable, TM_EQ); break; /* will try TM */ } default: return gcvalue(t1) == gcvalue(t2); } if (tm == NULL) /* no TM? */ return 0; /* objects are different */ luaT_callTM(L, tm, t1, t2, L->top, 1); /* call TM */ return !l_isfalse(L->top); } /* macro used by 'luaV_concat' to ensure that element at 'o' is a string */ #define tostring(L,o) \ (ttisstring(o) || (cvt2str(o) && (luaO_tostring(L, o), 1))) #define isemptystr(o) (ttisshrstring(o) && tsvalue(o)->shrlen == 0) /* copy strings in stack from top - n up to top - 1 to buffer */ static void copy2buff (StkId top, int n, char *buff) { size_t tl = 0; /* size already copied */ do { size_t l = vslen(top - n); /* length of string being copied */ memcpy(buff + tl, svalue(top - n), l * sizeof(char)); tl += l; } while (--n > 0); } /* ** Main operation for concatenation: concat 'total' values in the stack, ** from 'L->top - total' up to 'L->top - 1'. */ void luaV_concat (lua_State *L, int total) { lua_assert(total >= 2); do { StkId top = L->top; int n = 2; /* number of elements handled in this pass (at least 2) */ if (!(ttisstring(top-2) || cvt2str(top-2)) || !tostring(L, top-1)) luaT_trybinTM(L, top-2, top-1, top-2, TM_CONCAT); else if (isemptystr(top - 1)) /* second operand is empty? */ cast_void(tostring(L, top - 2)); /* result is first operand */ else if (isemptystr(top - 2)) { /* first operand is an empty string? */ setobjs2s(L, top - 2, top - 1); /* result is second op. */ } else { /* at least two non-empty string values; get as many as possible */ size_t tl = vslen(top - 1); TString *ts; /* collect total length and number of strings */ for (n = 1; n < total && tostring(L, top - n - 1); n++) { size_t l = vslen(top - n - 1); if (l >= (MAX_SIZE/sizeof(char)) - tl) luaG_runerror(L, "string length overflow"); tl += l; } if (tl <= LUAI_MAXSHORTLEN) { /* is result a short string? */ char buff[LUAI_MAXSHORTLEN]; copy2buff(top, n, buff); /* copy strings to buffer */ ts = luaS_newlstr(L, buff, tl); } else { /* long string; copy strings directly to final result */ ts = luaS_createlngstrobj(L, tl); copy2buff(top, n, getstr(ts)); } setsvalue2s(L, top - n, ts); /* create result */ } total -= n-1; /* got 'n' strings to create 1 new */ L->top -= n-1; /* popped 'n' strings and pushed one */ } while (total > 1); /* repeat until only 1 result left */ } /* ** Main operation 'ra' = #rb'. */ void luaV_objlen (lua_State *L, StkId ra, const TValue *rb) { const TValue *tm; switch (ttype(rb)) { case LUA_TTABLE: { Table *h = hvalue(rb); tm = fasttm(L, h->metatable, TM_LEN); if (tm) break; /* metamethod? break switch to call it */ setivalue(ra, luaH_getn(h)); /* else primitive len */ return; } case LUA_TSHRSTR: { setivalue(ra, tsvalue(rb)->shrlen); return; } case LUA_TLNGSTR: { setivalue(ra, tsvalue(rb)->u.lnglen); return; } default: { /* try metamethod */ tm = luaT_gettmbyobj(L, rb, TM_LEN); if (ttisnil(tm)) /* no metamethod? */ luaG_typeerror(L, rb, "get length of"); break; } } luaT_callTM(L, tm, rb, rb, ra, 1); } /* ** Integer division; return 'm // n', that is, floor(m/n). ** C division truncates its result (rounds towards zero). ** 'floor(q) == trunc(q)' when 'q >= 0' or when 'q' is integer, ** otherwise 'floor(q) == trunc(q) - 1'. */ lua_Integer luaV_div (lua_State *L, lua_Integer m, lua_Integer n) { if (l_castS2U(n) + 1u <= 1u) { /* special cases: -1 or 0 */ if (n == 0) luaG_runerror(L, "attempt to divide by zero"); return intop(-, 0, m); /* n==-1; avoid overflow with 0x80000...//-1 */ } else { lua_Integer q = m / n; /* perform C division */ if ((m ^ n) < 0 && m % n != 0) /* 'm/n' would be negative non-integer? */ q -= 1; /* correct result for different rounding */ return q; } } /* ** Integer modulus; return 'm % n'. (Assume that C '%' with ** negative operands follows C99 behavior. See previous comment ** about luaV_div.) */ lua_Integer luaV_mod (lua_State *L, lua_Integer m, lua_Integer n) { if (l_castS2U(n) + 1u <= 1u) { /* special cases: -1 or 0 */ if (n == 0) luaG_runerror(L, "attempt to perform 'n%%0'"); return 0; /* m % -1 == 0; avoid overflow with 0x80000...%-1 */ } else { lua_Integer r = m % n; if (r != 0 && (m ^ n) < 0) /* 'm/n' would be non-integer negative? */ r += n; /* correct result for different rounding */ return r; } } /* number of bits in an integer */ #define NBITS cast_int(sizeof(lua_Integer) * CHAR_BIT) /* ** Shift left operation. (Shift right just negates 'y'.) */ lua_Integer luaV_shiftl (lua_Integer x, lua_Integer y) { if (y < 0) { /* shift right? */ if (y <= -NBITS) return 0; else return intop(>>, x, -y); } else { /* shift left */ if (y >= NBITS) return 0; else return intop(<<, x, y); } } /* ** check whether cached closure in prototype 'p' may be reused, that is, ** whether there is a cached closure with the same upvalues needed by ** new closure to be created. */ static LClosure *getcached (Proto *p, UpVal **encup, StkId base) { LClosure *c = p->cache; if (c != NULL) { /* is there a cached closure? */ int nup = p->sizeupvalues; Upvaldesc *uv = p->upvalues; int i; for (i = 0; i < nup; i++) { /* check whether it has right upvalues */ TValue *v = uv[i].instack ? base + uv[i].idx : encup[uv[i].idx]->v; if (c->upvals[i]->v != v) return NULL; /* wrong upvalue; cannot reuse closure */ } } return c; /* return cached closure (or NULL if no cached closure) */ } /* ** create a new Lua closure, push it in the stack, and initialize ** its upvalues. Note that the closure is not cached if prototype is ** already black (which means that 'cache' was already cleared by the ** GC). */ static void pushclosure (lua_State *L, Proto *p, UpVal **encup, StkId base, StkId ra) { int nup = p->sizeupvalues; Upvaldesc *uv = p->upvalues; int i; LClosure *ncl = luaF_newLclosure(L, nup); ncl->p = p; setclLvalue(L, ra, ncl); /* anchor new closure in stack */ for (i = 0; i < nup; i++) { /* fill in its upvalues */ if (uv[i].instack) /* upvalue refers to local variable? */ ncl->upvals[i] = luaF_findupval(L, base + uv[i].idx); else /* get upvalue from enclosing function */ ncl->upvals[i] = encup[uv[i].idx]; ncl->upvals[i]->refcount++; /* new closure is white, so we do not need a barrier here */ } if (!isblack(p)) /* cache will not break GC invariant? */ p->cache = ncl; /* save it on cache for reuse */ } /* ** finish execution of an opcode interrupted by an yield */ void luaV_finishOp (lua_State *L) { CallInfo *ci = L->ci; StkId base = ci->u.l.base; Instruction inst = *(ci->u.l.savedpc - 1); /* interrupted instruction */ OpCode op = GET_OPCODE(inst); switch (op) { /* finish its execution */ case OP_ADD: case OP_SUB: case OP_MUL: case OP_DIV: case OP_IDIV: case OP_BAND: case OP_BOR: case OP_BXOR: case OP_SHL: case OP_SHR: case OP_MOD: case OP_POW: case OP_UNM: case OP_BNOT: case OP_LEN: case OP_GETTABUP: case OP_GETTABLE: case OP_SELF: { setobjs2s(L, base + GETARG_A(inst), --L->top); break; } case OP_LE: case OP_LT: case OP_EQ: { int res = !l_isfalse(L->top - 1); L->top--; if (ci->callstatus & CIST_LEQ) { /* "<=" using "<" instead? */ lua_assert(op == OP_LE); ci->callstatus ^= CIST_LEQ; /* clear mark */ res = !res; /* negate result */ } lua_assert(GET_OPCODE(*ci->u.l.savedpc) == OP_JMP); if (res != GETARG_A(inst)) /* condition failed? */ ci->u.l.savedpc++; /* skip jump instruction */ break; } case OP_CONCAT: { StkId top = L->top - 1; /* top when 'luaT_trybinTM' was called */ int b = GETARG_B(inst); /* first element to concatenate */ int total = cast_int(top - 1 - (base + b)); /* yet to concatenate */ setobj2s(L, top - 2, top); /* put TM result in proper position */ if (total > 1) { /* are there elements to concat? */ L->top = top - 1; /* top is one after last element (at top-2) */ luaV_concat(L, total); /* concat them (may yield again) */ } /* move final result to final position */ setobj2s(L, ci->u.l.base + GETARG_A(inst), L->top - 1); L->top = ci->top; /* restore top */ break; } case OP_TFORCALL: { lua_assert(GET_OPCODE(*ci->u.l.savedpc) == OP_TFORLOOP); L->top = ci->top; /* correct top */ break; } case OP_CALL: { if (GETARG_C(inst) - 1 >= 0) /* nresults >= 0? */ L->top = ci->top; /* adjust results */ break; } case OP_TAILCALL: case OP_SETTABUP: case OP_SETTABLE: break; default: lua_assert(0); } } /* ** {================================================================== ** Function 'luaV_execute': main interpreter loop ** =================================================================== */ /* ** some macros for common tasks in 'luaV_execute' */ #define RA(i) (base+GETARG_A(i)) #define RB(i) check_exp(getBMode(GET_OPCODE(i)) == OpArgR, base+GETARG_B(i)) #define RC(i) check_exp(getCMode(GET_OPCODE(i)) == OpArgR, base+GETARG_C(i)) #define RKB(i) check_exp(getBMode(GET_OPCODE(i)) == OpArgK, \ ISK(GETARG_B(i)) ? k+INDEXK(GETARG_B(i)) : base+GETARG_B(i)) #define RKC(i) check_exp(getCMode(GET_OPCODE(i)) == OpArgK, \ ISK(GETARG_C(i)) ? k+INDEXK(GETARG_C(i)) : base+GETARG_C(i)) /* execute a jump instruction */ #define dojump(ci,i,e) \ { int a = GETARG_A(i); \ if (a != 0) luaF_close(L, ci->u.l.base + a - 1); \ ci->u.l.savedpc += GETARG_sBx(i) + e; } /* for test instructions, execute the jump instruction that follows it */ #define donextjump(ci) { i = *ci->u.l.savedpc; dojump(ci, i, 1); } #define Protect(x) { {x;}; base = ci->u.l.base; } #define checkGC(L,c) \ { luaC_condGC(L, L->top = (c), /* limit of live values */ \ Protect(L->top = ci->top)); /* restore top */ \ luai_threadyield(L); } /* fetch an instruction and prepare its execution */ #define vmfetch() { \ i = *(ci->u.l.savedpc++); \ if (L->hookmask & (LUA_MASKLINE | LUA_MASKCOUNT)) \ Protect(luaG_traceexec(L)); \ ra = RA(i); /* WARNING: any stack reallocation invalidates 'ra' */ \ lua_assert(base == ci->u.l.base); \ lua_assert(base <= L->top && L->top < L->stack + L->stacksize); \ } #define vmdispatch(o) switch(o) #define vmcase(l) case l: #define vmbreak break /* ** copy of 'luaV_gettable', but protecting the call to potential ** metamethod (which can reallocate the stack) */ #define gettableProtected(L,t,k,v) { const TValue *slot; \ if (luaV_fastget(L,t,k,slot,luaH_get)) { setobj2s(L, v, slot); } \ else Protect(luaV_finishget(L,t,k,v,slot)); } /* same for 'luaV_settable' */ #define settableProtected(L,t,k,v) { const TValue *slot; \ if (!luaV_fastset(L,t,k,slot,luaH_get,v)) \ Protect(luaV_finishset(L,t,k,v,slot)); } void luaV_execute (lua_State *L) { CallInfo *ci = L->ci; LClosure *cl; TValue *k; StkId base; ci->callstatus |= CIST_FRESH; /* fresh invocation of 'luaV_execute" */ newframe: /* reentry point when frame changes (call/return) */ lua_assert(ci == L->ci); cl = clLvalue(ci->func); /* local reference to function's closure */ k = cl->p->k; /* local reference to function's constant table */ base = ci->u.l.base; /* local copy of function's base */ /* main loop of interpreter */ for (;;) { Instruction i; StkId ra; vmfetch(); vmdispatch (GET_OPCODE(i)) { vmcase(OP_MOVE) { setobjs2s(L, ra, RB(i)); vmbreak; } vmcase(OP_LOADK) { TValue *rb = k + GETARG_Bx(i); setobj2s(L, ra, rb); vmbreak; } vmcase(OP_LOADKX) { TValue *rb; lua_assert(GET_OPCODE(*ci->u.l.savedpc) == OP_EXTRAARG); rb = k + GETARG_Ax(*ci->u.l.savedpc++); setobj2s(L, ra, rb); vmbreak; } vmcase(OP_LOADBOOL) { setbvalue(ra, GETARG_B(i)); if (GETARG_C(i)) ci->u.l.savedpc++; /* skip next instruction (if C) */ vmbreak; } vmcase(OP_LOADNIL) { int b = GETARG_B(i); do { setnilvalue(ra++); } while (b--); vmbreak; } vmcase(OP_GETUPVAL) { int b = GETARG_B(i); setobj2s(L, ra, cl->upvals[b]->v); vmbreak; } vmcase(OP_GETTABUP) { TValue *upval = cl->upvals[GETARG_B(i)]->v; TValue *rc = RKC(i); gettableProtected(L, upval, rc, ra); vmbreak; } vmcase(OP_GETTABLE) { StkId rb = RB(i); TValue *rc = RKC(i); gettableProtected(L, rb, rc, ra); vmbreak; } vmcase(OP_SETTABUP) { TValue *upval = cl->upvals[GETARG_A(i)]->v; TValue *rb = RKB(i); TValue *rc = RKC(i); settableProtected(L, upval, rb, rc); vmbreak; } vmcase(OP_SETUPVAL) { UpVal *uv = cl->upvals[GETARG_B(i)]; setobj(L, uv->v, ra); luaC_upvalbarrier(L, uv); vmbreak; } vmcase(OP_SETTABLE) { TValue *rb = RKB(i); TValue *rc = RKC(i); settableProtected(L, ra, rb, rc); vmbreak; } vmcase(OP_NEWTABLE) { int b = GETARG_B(i); int c = GETARG_C(i); Table *t = luaH_new(L); sethvalue(L, ra, t); if (b != 0 || c != 0) luaH_resize(L, t, luaO_fb2int(b), luaO_fb2int(c)); checkGC(L, ra + 1); vmbreak; } vmcase(OP_SELF) { const TValue *aux; StkId rb = RB(i); TValue *rc = RKC(i); TString *key = tsvalue(rc); /* key must be a string */ setobjs2s(L, ra + 1, rb); if (luaV_fastget(L, rb, key, aux, luaH_getstr)) { setobj2s(L, ra, aux); } else Protect(luaV_finishget(L, rb, rc, ra, aux)); vmbreak; } vmcase(OP_ADD) { TValue *rb = RKB(i); TValue *rc = RKC(i); lua_Number nb; lua_Number nc; if (ttisinteger(rb) && ttisinteger(rc)) { lua_Integer ib = ivalue(rb); lua_Integer ic = ivalue(rc); setivalue(ra, intop(+, ib, ic)); } else if (tonumber(rb, &nb) && tonumber(rc, &nc)) { setfltvalue(ra, luai_numadd(L, nb, nc)); } else { Protect(luaT_trybinTM(L, rb, rc, ra, TM_ADD)); } vmbreak; } vmcase(OP_SUB) { TValue *rb = RKB(i); TValue *rc = RKC(i); lua_Number nb; lua_Number nc; if (ttisinteger(rb) && ttisinteger(rc)) { lua_Integer ib = ivalue(rb); lua_Integer ic = ivalue(rc); setivalue(ra, intop(-, ib, ic)); } else if (tonumber(rb, &nb) && tonumber(rc, &nc)) { setfltvalue(ra, luai_numsub(L, nb, nc)); } else { Protect(luaT_trybinTM(L, rb, rc, ra, TM_SUB)); } vmbreak; } vmcase(OP_MUL) { TValue *rb = RKB(i); TValue *rc = RKC(i); lua_Number nb; lua_Number nc; if (ttisinteger(rb) && ttisinteger(rc)) { lua_Integer ib = ivalue(rb); lua_Integer ic = ivalue(rc); setivalue(ra, intop(*, ib, ic)); } else if (tonumber(rb, &nb) && tonumber(rc, &nc)) { setfltvalue(ra, luai_nummul(L, nb, nc)); } else { Protect(luaT_trybinTM(L, rb, rc, ra, TM_MUL)); } vmbreak; } vmcase(OP_DIV) { /* float division (always with floats) */ TValue *rb = RKB(i); TValue *rc = RKC(i); lua_Number nb; lua_Number nc; if (tonumber(rb, &nb) && tonumber(rc, &nc)) { setfltvalue(ra, luai_numdiv(L, nb, nc)); } else { Protect(luaT_trybinTM(L, rb, rc, ra, TM_DIV)); } vmbreak; } vmcase(OP_BAND) { TValue *rb = RKB(i); TValue *rc = RKC(i); lua_Integer ib; lua_Integer ic; if (tointeger(rb, &ib) && tointeger(rc, &ic)) { setivalue(ra, intop(&, ib, ic)); } else { Protect(luaT_trybinTM(L, rb, rc, ra, TM_BAND)); } vmbreak; } vmcase(OP_BOR) { TValue *rb = RKB(i); TValue *rc = RKC(i); lua_Integer ib; lua_Integer ic; if (tointeger(rb, &ib) && tointeger(rc, &ic)) { setivalue(ra, intop(|, ib, ic)); } else { Protect(luaT_trybinTM(L, rb, rc, ra, TM_BOR)); } vmbreak; } vmcase(OP_BXOR) { TValue *rb = RKB(i); TValue *rc = RKC(i); lua_Integer ib; lua_Integer ic; if (tointeger(rb, &ib) && tointeger(rc, &ic)) { setivalue(ra, intop(^, ib, ic)); } else { Protect(luaT_trybinTM(L, rb, rc, ra, TM_BXOR)); } vmbreak; } vmcase(OP_SHL) { TValue *rb = RKB(i); TValue *rc = RKC(i); lua_Integer ib; lua_Integer ic; if (tointeger(rb, &ib) && tointeger(rc, &ic)) { setivalue(ra, luaV_shiftl(ib, ic)); } else { Protect(luaT_trybinTM(L, rb, rc, ra, TM_SHL)); } vmbreak; } vmcase(OP_SHR) { TValue *rb = RKB(i); TValue *rc = RKC(i); lua_Integer ib; lua_Integer ic; if (tointeger(rb, &ib) && tointeger(rc, &ic)) { setivalue(ra, luaV_shiftl(ib, -ic)); } else { Protect(luaT_trybinTM(L, rb, rc, ra, TM_SHR)); } vmbreak; } vmcase(OP_MOD) { TValue *rb = RKB(i); TValue *rc = RKC(i); lua_Number nb; lua_Number nc; if (ttisinteger(rb) && ttisinteger(rc)) { lua_Integer ib = ivalue(rb); lua_Integer ic = ivalue(rc); setivalue(ra, luaV_mod(L, ib, ic)); } else if (tonumber(rb, &nb) && tonumber(rc, &nc)) { lua_Number m; luai_nummod(L, nb, nc, m); setfltvalue(ra, m); } else { Protect(luaT_trybinTM(L, rb, rc, ra, TM_MOD)); } vmbreak; } vmcase(OP_IDIV) { /* floor division */ TValue *rb = RKB(i); TValue *rc = RKC(i); lua_Number nb; lua_Number nc; if (ttisinteger(rb) && ttisinteger(rc)) { lua_Integer ib = ivalue(rb); lua_Integer ic = ivalue(rc); setivalue(ra, luaV_div(L, ib, ic)); } else if (tonumber(rb, &nb) && tonumber(rc, &nc)) { setfltvalue(ra, luai_numidiv(L, nb, nc)); } else { Protect(luaT_trybinTM(L, rb, rc, ra, TM_IDIV)); } vmbreak; } vmcase(OP_POW) { TValue *rb = RKB(i); TValue *rc = RKC(i); lua_Number nb; lua_Number nc; if (tonumber(rb, &nb) && tonumber(rc, &nc)) { setfltvalue(ra, luai_numpow(L, nb, nc)); } else { Protect(luaT_trybinTM(L, rb, rc, ra, TM_POW)); } vmbreak; } vmcase(OP_UNM) { TValue *rb = RB(i); lua_Number nb; if (ttisinteger(rb)) { lua_Integer ib = ivalue(rb); setivalue(ra, intop(-, 0, ib)); } else if (tonumber(rb, &nb)) { setfltvalue(ra, luai_numunm(L, nb)); } else { Protect(luaT_trybinTM(L, rb, rb, ra, TM_UNM)); } vmbreak; } vmcase(OP_BNOT) { TValue *rb = RB(i); lua_Integer ib; if (tointeger(rb, &ib)) { setivalue(ra, intop(^, ~l_castS2U(0), ib)); } else { Protect(luaT_trybinTM(L, rb, rb, ra, TM_BNOT)); } vmbreak; } vmcase(OP_NOT) { TValue *rb = RB(i); int res = l_isfalse(rb); /* next assignment may change this value */ setbvalue(ra, res); vmbreak; } vmcase(OP_LEN) { Protect(luaV_objlen(L, ra, RB(i))); vmbreak; } vmcase(OP_CONCAT) { int b = GETARG_B(i); int c = GETARG_C(i); StkId rb; L->top = base + c + 1; /* mark the end of concat operands */ Protect(luaV_concat(L, c - b + 1)); ra = RA(i); /* 'luaV_concat' may invoke TMs and move the stack */ rb = base + b; setobjs2s(L, ra, rb); checkGC(L, (ra >= rb ? ra + 1 : rb)); L->top = ci->top; /* restore top */ vmbreak; } vmcase(OP_JMP) { dojump(ci, i, 0); vmbreak; } vmcase(OP_EQ) { TValue *rb = RKB(i); TValue *rc = RKC(i); Protect( if (luaV_equalobj(L, rb, rc) != GETARG_A(i)) ci->u.l.savedpc++; else donextjump(ci); ) vmbreak; } vmcase(OP_LT) { Protect( if (luaV_lessthan(L, RKB(i), RKC(i)) != GETARG_A(i)) ci->u.l.savedpc++; else donextjump(ci); ) vmbreak; } vmcase(OP_LE) { Protect( if (luaV_lessequal(L, RKB(i), RKC(i)) != GETARG_A(i)) ci->u.l.savedpc++; else donextjump(ci); ) vmbreak; } vmcase(OP_TEST) { if (GETARG_C(i) ? l_isfalse(ra) : !l_isfalse(ra)) ci->u.l.savedpc++; else donextjump(ci); vmbreak; } vmcase(OP_TESTSET) { TValue *rb = RB(i); if (GETARG_C(i) ? l_isfalse(rb) : !l_isfalse(rb)) ci->u.l.savedpc++; else { setobjs2s(L, ra, rb); donextjump(ci); } vmbreak; } vmcase(OP_CALL) { int b = GETARG_B(i); int nresults = GETARG_C(i) - 1; if (b != 0) L->top = ra+b; /* else previous instruction set top */ if (luaD_precall(L, ra, nresults)) { /* C function? */ if (nresults >= 0) L->top = ci->top; /* adjust results */ Protect((void)0); /* update 'base' */ } else { /* Lua function */ ci = L->ci; goto newframe; /* restart luaV_execute over new Lua function */ } vmbreak; } vmcase(OP_TAILCALL) { int b = GETARG_B(i); if (b != 0) L->top = ra+b; /* else previous instruction set top */ lua_assert(GETARG_C(i) - 1 == LUA_MULTRET); if (luaD_precall(L, ra, LUA_MULTRET)) { /* C function? */ Protect((void)0); /* update 'base' */ } else { /* tail call: put called frame (n) in place of caller one (o) */ CallInfo *nci = L->ci; /* called frame */ CallInfo *oci = nci->previous; /* caller frame */ StkId nfunc = nci->func; /* called function */ StkId ofunc = oci->func; /* caller function */ /* last stack slot filled by 'precall' */ StkId lim = nci->u.l.base + getproto(nfunc)->numparams; int aux; /* close all upvalues from previous call */ if (cl->p->sizep > 0) luaF_close(L, oci->u.l.base); /* move new frame into old one */ for (aux = 0; nfunc + aux < lim; aux++) setobjs2s(L, ofunc + aux, nfunc + aux); oci->u.l.base = ofunc + (nci->u.l.base - nfunc); /* correct base */ oci->top = L->top = ofunc + (L->top - nfunc); /* correct top */ oci->u.l.savedpc = nci->u.l.savedpc; oci->callstatus |= CIST_TAIL; /* function was tail called */ ci = L->ci = oci; /* remove new frame */ lua_assert(L->top == oci->u.l.base + getproto(ofunc)->maxstacksize); goto newframe; /* restart luaV_execute over new Lua function */ } vmbreak; } vmcase(OP_RETURN) { int b = GETARG_B(i); if (cl->p->sizep > 0) luaF_close(L, base); b = luaD_poscall(L, ci, ra, (b != 0 ? b - 1 : cast_int(L->top - ra))); if (ci->callstatus & CIST_FRESH) /* local 'ci' still from callee */ return; /* external invocation: return */ else { /* invocation via reentry: continue execution */ ci = L->ci; if (b) L->top = ci->top; lua_assert(isLua(ci)); lua_assert(GET_OPCODE(*((ci)->u.l.savedpc - 1)) == OP_CALL); goto newframe; /* restart luaV_execute over new Lua function */ } } vmcase(OP_FORLOOP) { if (ttisinteger(ra)) { /* integer loop? */ lua_Integer step = ivalue(ra + 2); lua_Integer idx = intop(+, ivalue(ra), step); /* increment index */ lua_Integer limit = ivalue(ra + 1); if ((0 < step) ? (idx <= limit) : (limit <= idx)) { ci->u.l.savedpc += GETARG_sBx(i); /* jump back */ chgivalue(ra, idx); /* update internal index... */ setivalue(ra + 3, idx); /* ...and external index */ } } else { /* floating loop */ lua_Number step = fltvalue(ra + 2); lua_Number idx = luai_numadd(L, fltvalue(ra), step); /* inc. index */ lua_Number limit = fltvalue(ra + 1); if (luai_numlt(0, step) ? luai_numle(idx, limit) : luai_numle(limit, idx)) { ci->u.l.savedpc += GETARG_sBx(i); /* jump back */ chgfltvalue(ra, idx); /* update internal index... */ setfltvalue(ra + 3, idx); /* ...and external index */ } } vmbreak; } vmcase(OP_FORPREP) { TValue *init = ra; TValue *plimit = ra + 1; TValue *pstep = ra + 2; lua_Integer ilimit; int stopnow; if (ttisinteger(init) && ttisinteger(pstep) && forlimit(plimit, &ilimit, ivalue(pstep), &stopnow)) { /* all values are integer */ lua_Integer initv = (stopnow ? 0 : ivalue(init)); setivalue(plimit, ilimit); setivalue(init, intop(-, initv, ivalue(pstep))); } else { /* try making all values floats */ lua_Number ninit; lua_Number nlimit; lua_Number nstep; if (!tonumber(plimit, &nlimit)) luaG_runerror(L, "'for' limit must be a number"); setfltvalue(plimit, nlimit); if (!tonumber(pstep, &nstep)) luaG_runerror(L, "'for' step must be a number"); setfltvalue(pstep, nstep); if (!tonumber(init, &ninit)) luaG_runerror(L, "'for' initial value must be a number"); setfltvalue(init, luai_numsub(L, ninit, nstep)); } ci->u.l.savedpc += GETARG_sBx(i); vmbreak; } vmcase(OP_TFORCALL) { StkId cb = ra + 3; /* call base */ setobjs2s(L, cb+2, ra+2); setobjs2s(L, cb+1, ra+1); setobjs2s(L, cb, ra); L->top = cb + 3; /* func. + 2 args (state and index) */ Protect(luaD_call(L, cb, GETARG_C(i))); L->top = ci->top; i = *(ci->u.l.savedpc++); /* go to next instruction */ ra = RA(i); lua_assert(GET_OPCODE(i) == OP_TFORLOOP); goto l_tforloop; } vmcase(OP_TFORLOOP) { l_tforloop: if (!ttisnil(ra + 1)) { /* continue loop? */ setobjs2s(L, ra, ra + 1); /* save control variable */ ci->u.l.savedpc += GETARG_sBx(i); /* jump back */ } vmbreak; } vmcase(OP_SETLIST) { int n = GETARG_B(i); int c = GETARG_C(i); unsigned int last; Table *h; if (n == 0) n = cast_int(L->top - ra) - 1; if (c == 0) { lua_assert(GET_OPCODE(*ci->u.l.savedpc) == OP_EXTRAARG); c = GETARG_Ax(*ci->u.l.savedpc++); } h = hvalue(ra); last = ((c-1)*LFIELDS_PER_FLUSH) + n; if (last > h->sizearray) /* needs more space? */ luaH_resizearray(L, h, last); /* preallocate it at once */ for (; n > 0; n--) { TValue *val = ra+n; luaH_setint(L, h, last--, val); luaC_barrierback(L, h, val); } L->top = ci->top; /* correct top (in case of previous open call) */ vmbreak; } vmcase(OP_CLOSURE) { Proto *p = cl->p->p[GETARG_Bx(i)]; LClosure *ncl = getcached(p, cl->upvals, base); /* cached closure */ if (ncl == NULL) /* no match? */ pushclosure(L, p, cl->upvals, base, ra); /* create a new one */ else setclLvalue(L, ra, ncl); /* push cashed closure */ checkGC(L, ra + 1); vmbreak; } vmcase(OP_VARARG) { int b = GETARG_B(i) - 1; /* required results */ int j; int n = cast_int(base - ci->func) - cl->p->numparams - 1; if (n < 0) /* less arguments than parameters? */ n = 0; /* no vararg arguments */ if (b < 0) { /* B == 0? */ b = n; /* get all var. arguments */ Protect(luaD_checkstack(L, n)); ra = RA(i); /* previous call may change the stack */ L->top = ra + n; } for (j = 0; j < b && j < n; j++) setobjs2s(L, ra + j, base - n + j); for (; j < b; j++) /* complete required results with nil */ setnilvalue(ra + j); vmbreak; } vmcase(OP_EXTRAARG) { lua_assert(0); vmbreak; } } } } /* }================================================================== */ ================================================ FILE: Tests/ApiExplorer/lua/src/lvm.h ================================================ /* ** $Id: lvm.h,v 2.41.1.1 2017/04/19 17:20:42 roberto Exp $ ** Lua virtual machine ** See Copyright Notice in lua.h */ #ifndef lvm_h #define lvm_h #include "ldo.h" #include "lobject.h" #include "ltm.h" #if !defined(LUA_NOCVTN2S) #define cvt2str(o) ttisnumber(o) #else #define cvt2str(o) 0 /* no conversion from numbers to strings */ #endif #if !defined(LUA_NOCVTS2N) #define cvt2num(o) ttisstring(o) #else #define cvt2num(o) 0 /* no conversion from strings to numbers */ #endif /* ** You can define LUA_FLOORN2I if you want to convert floats to integers ** by flooring them (instead of raising an error if they are not ** integral values) */ #if !defined(LUA_FLOORN2I) #define LUA_FLOORN2I 0 #endif #define tonumber(o,n) \ (ttisfloat(o) ? (*(n) = fltvalue(o), 1) : luaV_tonumber_(o,n)) #define tointeger(o,i) \ (ttisinteger(o) ? (*(i) = ivalue(o), 1) : luaV_tointeger(o,i,LUA_FLOORN2I)) #define intop(op,v1,v2) l_castU2S(l_castS2U(v1) op l_castS2U(v2)) #define luaV_rawequalobj(t1,t2) luaV_equalobj(NULL,t1,t2) /* ** fast track for 'gettable': if 't' is a table and 't[k]' is not nil, ** return 1 with 'slot' pointing to 't[k]' (final result). Otherwise, ** return 0 (meaning it will have to check metamethod) with 'slot' ** pointing to a nil 't[k]' (if 't' is a table) or NULL (otherwise). ** 'f' is the raw get function to use. */ #define luaV_fastget(L,t,k,slot,f) \ (!ttistable(t) \ ? (slot = NULL, 0) /* not a table; 'slot' is NULL and result is 0 */ \ : (slot = f(hvalue(t), k), /* else, do raw access */ \ !ttisnil(slot))) /* result not nil? */ /* ** standard implementation for 'gettable' */ #define luaV_gettable(L,t,k,v) { const TValue *slot; \ if (luaV_fastget(L,t,k,slot,luaH_get)) { setobj2s(L, v, slot); } \ else luaV_finishget(L,t,k,v,slot); } /* ** Fast track for set table. If 't' is a table and 't[k]' is not nil, ** call GC barrier, do a raw 't[k]=v', and return true; otherwise, ** return false with 'slot' equal to NULL (if 't' is not a table) or ** 'nil'. (This is needed by 'luaV_finishget'.) Note that, if the macro ** returns true, there is no need to 'invalidateTMcache', because the ** call is not creating a new entry. */ #define luaV_fastset(L,t,k,slot,f,v) \ (!ttistable(t) \ ? (slot = NULL, 0) \ : (slot = f(hvalue(t), k), \ ttisnil(slot) ? 0 \ : (luaC_barrierback(L, hvalue(t), v), \ setobj2t(L, cast(TValue *,slot), v), \ 1))) #define luaV_settable(L,t,k,v) { const TValue *slot; \ if (!luaV_fastset(L,t,k,slot,luaH_get,v)) \ luaV_finishset(L,t,k,v,slot); } LUAI_FUNC int luaV_equalobj (lua_State *L, const TValue *t1, const TValue *t2); LUAI_FUNC int luaV_lessthan (lua_State *L, const TValue *l, const TValue *r); LUAI_FUNC int luaV_lessequal (lua_State *L, const TValue *l, const TValue *r); LUAI_FUNC int luaV_tonumber_ (const TValue *obj, lua_Number *n); LUAI_FUNC int luaV_tointeger (const TValue *obj, lua_Integer *p, int mode); LUAI_FUNC void luaV_finishget (lua_State *L, const TValue *t, TValue *key, StkId val, const TValue *slot); LUAI_FUNC void luaV_finishset (lua_State *L, const TValue *t, TValue *key, StkId val, const TValue *slot); LUAI_FUNC void luaV_finishOp (lua_State *L); LUAI_FUNC void luaV_execute (lua_State *L); LUAI_FUNC void luaV_concat (lua_State *L, int total); LUAI_FUNC lua_Integer luaV_div (lua_State *L, lua_Integer x, lua_Integer y); LUAI_FUNC lua_Integer luaV_mod (lua_State *L, lua_Integer x, lua_Integer y); LUAI_FUNC lua_Integer luaV_shiftl (lua_Integer x, lua_Integer y); LUAI_FUNC void luaV_objlen (lua_State *L, StkId ra, const TValue *rb); #endif ================================================ FILE: Tests/ApiExplorer/lua/src/lzio.c ================================================ /* ** $Id: lzio.c,v 1.37.1.1 2017/04/19 17:20:42 roberto Exp $ ** Buffered streams ** See Copyright Notice in lua.h */ #define lzio_c #define LUA_CORE #include "lprefix.h" #include #include "lua.h" #include "llimits.h" #include "lmem.h" #include "lstate.h" #include "lzio.h" int luaZ_fill (ZIO *z) { size_t size; lua_State *L = z->L; const char *buff; lua_unlock(L); buff = z->reader(L, z->data, &size); lua_lock(L); if (buff == NULL || size == 0) return EOZ; z->n = size - 1; /* discount char being returned */ z->p = buff; return cast_uchar(*(z->p++)); } void luaZ_init (lua_State *L, ZIO *z, lua_Reader reader, void *data) { z->L = L; z->reader = reader; z->data = data; z->n = 0; z->p = NULL; } /* --------------------------------------------------------------- read --- */ size_t luaZ_read (ZIO *z, void *b, size_t n) { while (n) { size_t m; if (z->n == 0) { /* no bytes in buffer? */ if (luaZ_fill(z) == EOZ) /* try to read more */ return n; /* no more input; return number of missing bytes */ else { z->n++; /* luaZ_fill consumed first byte; put it back */ z->p--; } } m = (n <= z->n) ? n : z->n; /* min. between n and z->n */ memcpy(b, z->p, m); z->n -= m; z->p += m; b = (char *)b + m; n -= m; } return 0; } ================================================ FILE: Tests/ApiExplorer/lua/src/lzio.h ================================================ /* ** $Id: lzio.h,v 1.31.1.1 2017/04/19 17:20:42 roberto Exp $ ** Buffered streams ** See Copyright Notice in lua.h */ #ifndef lzio_h #define lzio_h #include "lua.h" #include "lmem.h" #define EOZ (-1) /* end of stream */ typedef struct Zio ZIO; #define zgetc(z) (((z)->n--)>0 ? cast_uchar(*(z)->p++) : luaZ_fill(z)) typedef struct Mbuffer { char *buffer; size_t n; size_t buffsize; } Mbuffer; #define luaZ_initbuffer(L, buff) ((buff)->buffer = NULL, (buff)->buffsize = 0) #define luaZ_buffer(buff) ((buff)->buffer) #define luaZ_sizebuffer(buff) ((buff)->buffsize) #define luaZ_bufflen(buff) ((buff)->n) #define luaZ_buffremove(buff,i) ((buff)->n -= (i)) #define luaZ_resetbuffer(buff) ((buff)->n = 0) #define luaZ_resizebuffer(L, buff, size) \ ((buff)->buffer = luaM_reallocvchar(L, (buff)->buffer, \ (buff)->buffsize, size), \ (buff)->buffsize = size) #define luaZ_freebuffer(L, buff) luaZ_resizebuffer(L, buff, 0) LUAI_FUNC void luaZ_init (lua_State *L, ZIO *z, lua_Reader reader, void *data); LUAI_FUNC size_t luaZ_read (ZIO* z, void *b, size_t n); /* read next n bytes */ /* --------- Private Part ------------------ */ struct Zio { size_t n; /* bytes still unread */ const char *p; /* current position in buffer */ lua_Reader reader; /* reader function */ void *data; /* additional data */ lua_State *L; /* Lua state (for reader) */ }; LUAI_FUNC int luaZ_fill (ZIO *z); #endif ================================================ FILE: Tests/GDK/APIRunner.GDK/.gitignore ================================================ Kits/DirectXTK12/Src/Shaders/Compiled/ ================================================ FILE: Tests/GDK/APIRunner.GDK/APIRunner.143.GDK.Src.vcxproj ================================================ Debug Gaming.Xbox.Scarlett.x64 Profile Gaming.Xbox.Scarlett.x64 Release Gaming.Xbox.Scarlett.x64 Release Gaming.Xbox.XboxOne.x64 Profile Gaming.Xbox.XboxOne.x64 Debug Gaming.Xbox.XboxOne.x64 Release Gaming.Desktop.x64 Profile Gaming.Desktop.x64 Debug Gaming.Desktop.x64 APIRunner_GDK {46F31A54-4C74-41A8-B294-26B5689E51EF} en-US Win32Proj 17.0 Native Xbox.Services.API.C x64 10.0 Application v143 false true Unicode false false Application v143 false true Unicode false false Application v143 false true Unicode false false Application v143 false true Unicode false false Application v143 true Unicode false false Application v143 true Unicode false false Application v143 false Unicode Application v143 false true Unicode Application v143 true Unicode $(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath) $(Console_SdkLibPath) $(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath) $(Console_SdkIncludeRoot) $(Console_SdkRoot)bin;$(Console_SdkToolPath);$(ExecutablePath) false $(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath) $(Console_SdkLibPath) $(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath) $(Console_SdkIncludeRoot) $(Console_SdkRoot)bin;$(Console_SdkToolPath);$(ExecutablePath) false $(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath) $(Console_SdkLibPath) $(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath) $(Console_SdkIncludeRoot) $(Console_SdkRoot)bin;$(Console_SdkToolPath);$(ExecutablePath) false $(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath) $(Console_SdkLibPath) $(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath) $(Console_SdkIncludeRoot) $(Console_SdkRoot)bin;$(Console_SdkToolPath);$(ExecutablePath) false $(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath) $(Console_SdkLibPath) $(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath) $(Console_SdkIncludeRoot) $(Console_SdkRoot)bin;$(Console_SdkToolPath);$(ExecutablePath) true $(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath) $(Console_SdkLibPath) $(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath) $(Console_SdkIncludeRoot) $(Console_SdkRoot)bin;$(Console_SdkToolPath);$(ExecutablePath) true $(Console_SdkLibPath);$(LibraryPath) $(Console_SdkIncludeRoot);$(IncludePath) $(Console_SdkRoot)bin;$(Console_SdkToolPath);$(ExecutablePath) false $(Console_SdkLibPath);$(LibraryPath) $(Console_SdkIncludeRoot);$(IncludePath) $(Console_SdkRoot)bin;$(Console_SdkToolPath);$(ExecutablePath) false $(Console_SdkLibPath);$(LibraryPath) $(Console_SdkIncludeRoot);$(IncludePath) $(Console_SdkRoot)bin;$(Console_SdkToolPath);$(ExecutablePath) true $(ProjectDir);Kits\DirectXTK12\Inc;Kits\ATGTK;Kits\ATGTelemetry\Gsdk;$(ProjectDir)..\..\APIExplorer\lua\src;$(ProjectDir)..\..\APIExplorer\Shared;$(ProjectDir)..\..\APIExplorer\Include;%(AdditionalIncludeDirectories) SAMPLE_BUILD_WITH_CPP;%(PreprocessorDefinitions) SAMPLE_BUILD_FOR_DESKTOP;%(PreprocessorDefinitions) true $([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), xsapi.staticlib.props))\xsapi.staticlib.props 1 $([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), xsapi.paths.props))\xsapi.paths.props copy /Y "$(ProjectDir)APIRunnerSrc143-MicrosoftGame.Config" "$(ProjectDir)MicrosoftGame.Config" xcopy /Y /I /E "$(ProjectDir)Assets\*.*" "$(TargetDir)Assets" & xcopy /Y /I /E "$(ProjectDir)Media\*.*" "$(TargetDir)Media" & xcopy /Y /I /E "$(ProjectDir)Media\Fonts\*.*" "$(TargetDir)" & xcopy /Y /I /E "$(ProjectDir)Media\Textures\*.*" "$(TargetDir)" dbghelp.lib;uuid.lib;$(Console_Libs);%(XboxExtensionsDependencies);%(AdditionalDependencies) true Windows true true Use pch.h $(ProjectDir);Kits\ATGTelemetry\GDK;Kits\LiveTK;Kits\DirectXTK12\Inc;Kits\ATGTK;%(AdditionalIncludeDirectories) MaxSpeed ATG_ENABLE_TELEMETRY;NDEBUG;%(PreprocessorDefinitions) Level4 true true true stdcpp17 5.1 dbghelp.lib;uuid.lib;$(Console_Libs);%(XboxExtensionsDependencies);%(AdditionalDependencies) true Windows true true Use pch.h $(ProjectDir);Kits\ATGTelemetry\GDK;Kits\LiveTK;Kits\DirectXTK12\Inc;Kits\ATGTK;%(AdditionalIncludeDirectories) MaxSpeed ATG_ENABLE_TELEMETRY;NDEBUG;%(PreprocessorDefinitions) Level4 true true true stdcpp17 5.1 dbghelp.lib;uuid.lib;$(Console_Libs);%(XboxExtensionsDependencies);%(AdditionalDependencies) true Windows true true Use pch.h $(ProjectDir);Kits\ATGTelemetry\GDK;Kits\LiveTK;Kits\DirectXTK12\Inc;Kits\ATGTK;%(AdditionalIncludeDirectories) MaxSpeed ATG_ENABLE_TELEMETRY;PROFILE;%(PreprocessorDefinitions) Level4 true true true stdcpp17 5.1 dbghelp.lib;uuid.lib;$(Console_Libs);%(XboxExtensionsDependencies);%(AdditionalDependencies) true Windows true true Use pch.h $(ProjectDir);Kits\ATGTelemetry\GDK;Kits\LiveTK;Kits\DirectXTK12\Inc;Kits\ATGTK;%(AdditionalIncludeDirectories) MaxSpeed ATG_ENABLE_TELEMETRY;PROFILE;%(PreprocessorDefinitions) Level4 true true true stdcpp17 5.1 dbghelp.lib;uuid.lib;$(Console_Libs);%(XboxExtensionsDependencies);%(AdditionalDependencies) Windows true pch.h Use false $(ProjectDir);Kits\ATGTelemetry\GDK;Kits\LiveTK;Kits\DirectXTK12\Inc;Kits\ATGTK;%(AdditionalIncludeDirectories) Level4 Disabled ATG_ENABLE_TELEMETRY;_DEBUG;%(PreprocessorDefinitions) true stdcpp17 true 5.1 dbghelp.lib;uuid.lib;$(Console_Libs);%(XboxExtensionsDependencies);%(AdditionalDependencies) Windows true pch.h Use false $(ProjectDir);Kits\ATGTelemetry\GDK;Kits\LiveTK;Kits\DirectXTK12\Inc;Kits\ATGTK;%(AdditionalIncludeDirectories) Level4 Disabled ATG_ENABLE_TELEMETRY;_DEBUG;%(PreprocessorDefinitions) true stdcpp17 5.1 true Windows true true dbghelp.lib;uuid.lib;$(Console_Libs);%(AdditionalDependencies) Use pch.h $(ProjectDir);Kits\LiveTK;Kits\DirectXTK12\Inc;Kits\ATGTK;%(AdditionalIncludeDirectories) MaxSpeed NDEBUG;__WRL_NO_DEFAULT_LIB__;%(PreprocessorDefinitions) Level4 true true true stdcpp17 5.1 true Windows true true dbghelp.lib;uuid.lib;$(Console_Libs);%(AdditionalDependencies) Use pch.h $(ProjectDir);Kits\LiveTK;Kits\DirectXTK12\Inc;Kits\ATGTK;%(AdditionalIncludeDirectories) MaxSpeed NDEBUG;__WRL_NO_DEFAULT_LIB__;PROFILE;%(PreprocessorDefinitions) Level4 true true true stdcpp17 5.1 Windows true dbghelp.lib;uuid.lib;$(Console_Libs);%(AdditionalDependencies) pch.h Use false $(ProjectDir);Kits\LiveTK;Kits\DirectXTK12\Inc;Kits\ATGTK;%(AdditionalIncludeDirectories) Level4 Disabled _DEBUG;__WRL_NO_DEFAULT_LIB__;%(PreprocessorDefinitions) true stdcpp17 5.1 Create %(Filename)%(Extension) true %(Filename)%(Extension) true true true true true %(Filename)%(Extension) true true true true true {052c4858-c76f-4cea-8a1a-e8e5559e67c2} true %(Filename)%(Extension) true Assets\%(Filename)%(Extension) true Assets\%(Filename)%(Extension) true Assets\%(Filename)%(Extension) true Assets\%(Filename)%(Extension) true Assets\%(Filename)%(Extension) true Assets\%(Filename)%(Extension) true Assets\%(Filename)%(Extension) true Assets\%(Filename)%(Extension) ================================================ FILE: Tests/GDK/APIRunner.GDK/APIRunner.143.GDK.Src.vcxproj.filters ================================================  916b6ff9-882e-4cf3-93d5-bea41c3e7cd0 0c10bab7-ebd0-49b3-89a5-df53a9e36cf4 ico;cur;bmp;dds;dlg;fbx;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tga;tiff;tif;png;wav;mfcribbon-ms fa3bbbb4-fde4-4785-907e-fbb24dde6491 83010d7a-d0f5-4911-9741-7529fae5301c Common Common ATG Tool Kit ATG Tool Kit ATG Tool Kit ATG Tool Kit ATG Tool Kit ATG Tool Kit ATG Tool Kit ATG Tool Kit ATG Tool Kit Xbox Live Tool Kit Xbox Live Tool Kit Xbox Live Tool Kit ATG Tool Kit Common ATG Tool Kit ATG Tool Kit ATG Tool Kit Xbox Live Tool Kit Xbox Live Tool Kit Xbox Live Tool Kit ATG Tool Kit Assets Assets Assets Assets Assets Assets Assets Assets Assets Assets Assets Assets Assets Assets Assets Assets Assets Assets Assets ================================================ FILE: Tests/GDK/APIRunner.GDK/APIRunner.GDK.Bin.vcxproj ================================================ Debug Gaming.Xbox.Scarlett.x64 Profile Gaming.Xbox.Scarlett.x64 Release Gaming.Xbox.Scarlett.x64 Release Gaming.Xbox.XboxOne.x64 Profile Gaming.Xbox.XboxOne.x64 Debug Gaming.Xbox.XboxOne.x64 Release Gaming.Desktop.x64 Profile Gaming.Desktop.x64 Debug Gaming.Desktop.x64 APIRunner_GDK {46F31A54-4C74-41A8-B294-26B5689E51EF} en-US Win32Proj 14.0 Native Xbox.Services.API.C x64 Application v141 false true Unicode false false Application v141 false true Unicode false false Application v141 false true Unicode false false Application v141 false true Unicode false false Application v141 true Unicode false false Application v141 true Unicode false false Application v141 false true Unicode Application v141 false true Unicode Application v141 true Unicode $(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath) $(Console_SdkLibPath) $(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath) $(Console_SdkIncludeRoot) $(Console_SdkRoot)bin;$(Console_SdkToolPath);$(ExecutablePath) false $(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath) $(Console_SdkLibPath) $(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath) $(Console_SdkIncludeRoot) $(Console_SdkRoot)bin;$(Console_SdkToolPath);$(ExecutablePath) false $(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath) $(Console_SdkLibPath) $(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath) $(Console_SdkIncludeRoot) $(Console_SdkRoot)bin;$(Console_SdkToolPath);$(ExecutablePath) false $(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath) $(Console_SdkLibPath) $(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath) $(Console_SdkIncludeRoot) $(Console_SdkRoot)bin;$(Console_SdkToolPath);$(ExecutablePath) false $(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath) $(Console_SdkLibPath) $(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath) $(Console_SdkIncludeRoot) $(Console_SdkRoot)bin;$(Console_SdkToolPath);$(ExecutablePath) true $(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath) $(Console_SdkLibPath) $(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath) $(Console_SdkIncludeRoot) $(Console_SdkRoot)bin;$(Console_SdkToolPath);$(ExecutablePath) true $(Console_SdkLibPath);$(LibraryPath) $(Console_SdkIncludeRoot);$(IncludePath) $(Console_SdkRoot)bin;$(Console_SdkToolPath);$(ExecutablePath) false $(Console_SdkLibPath);$(LibraryPath) $(Console_SdkIncludeRoot);$(IncludePath) $(Console_SdkRoot)bin;$(Console_SdkToolPath);$(ExecutablePath) false $(Console_SdkLibPath);$(LibraryPath) $(Console_SdkIncludeRoot);$(IncludePath) $(Console_SdkRoot)bin;$(Console_SdkToolPath);$(ExecutablePath) true $(ProjectDir);Kits\DirectXTK12\Inc;Kits\ATGTK;Kits\ATGTelemetry\Gsdk;$(ProjectDir)..\..\APIExplorer\lua\src;$(ProjectDir)..\..\APIExplorer\Shared;$(ProjectDir)..\..\APIExplorer\Include;$(ProjectDir)..\..\..\Include\cpprestinclude\;%(AdditionalIncludeDirectories) SAMPLE_BUILD_WITH_BIN;%(PreprocessorDefinitions) SAMPLE_BUILD_FOR_DESKTOP;%(PreprocessorDefinitions) copy /Y "$(ProjectDir)APIRunnerBin-MicrosoftGame.Config" "$(ProjectDir)MicrosoftGame.Config" xcopy /Y /I /E "$(ProjectDir)Assets\*.*" "$(TargetDir)Assets" & xcopy /Y /I /E "$(ProjectDir)Media\*.*" "$(TargetDir)Media" & xcopy /Y /I /E "$(ProjectDir)Media\Fonts\*.*" "$(TargetDir)" & xcopy /Y /I /E "$(ProjectDir)Media\Textures\*.*" "$(TargetDir)" ..\..\..\External\rapidjson\include;%(AdditionalIncludeDirectories) uuid.lib;$(Console_Libs);%(XboxExtensionsDependencies);%(AdditionalDependencies) true Windows true true Use pch.h $(ProjectDir);Kits\ATGTelemetry\GDK;Kits\LiveTK;Kits\DirectXTK12\Inc;Kits\ATGTK;%(AdditionalIncludeDirectories) MaxSpeed ATG_ENABLE_TELEMETRY;NDEBUG;%(PreprocessorDefinitions) Level4 true true true stdcpp17 5.1 uuid.lib;$(Console_Libs);%(XboxExtensionsDependencies);%(AdditionalDependencies) true Windows true true Use pch.h $(ProjectDir);Kits\ATGTelemetry\GDK;Kits\LiveTK;Kits\DirectXTK12\Inc;Kits\ATGTK;%(AdditionalIncludeDirectories) MaxSpeed ATG_ENABLE_TELEMETRY;NDEBUG;%(PreprocessorDefinitions) Level4 true true true stdcpp17 5.1 uuid.lib;$(Console_Libs);%(XboxExtensionsDependencies);%(AdditionalDependencies) true Windows true true Use pch.h $(ProjectDir);Kits\ATGTelemetry\GDK;Kits\LiveTK;Kits\DirectXTK12\Inc;Kits\ATGTK;%(AdditionalIncludeDirectories) MaxSpeed ATG_ENABLE_TELEMETRY;PROFILE;%(PreprocessorDefinitions) Level4 true true true stdcpp17 5.1 uuid.lib;$(Console_Libs);%(XboxExtensionsDependencies);%(AdditionalDependencies) true Windows true true Use pch.h $(ProjectDir);Kits\ATGTelemetry\GDK;Kits\LiveTK;Kits\DirectXTK12\Inc;Kits\ATGTK;%(AdditionalIncludeDirectories) MaxSpeed ATG_ENABLE_TELEMETRY;PROFILE;%(PreprocessorDefinitions) Level4 true true true stdcpp17 5.1 uuid.lib;$(Console_Libs);%(XboxExtensionsDependencies);%(AdditionalDependencies) Windows true pch.h Use false $(ProjectDir);Kits\ATGTelemetry\GDK;Kits\LiveTK;Kits\DirectXTK12\Inc;Kits\ATGTK;%(AdditionalIncludeDirectories) Level4 Disabled ATG_ENABLE_TELEMETRY;_DEBUG;%(PreprocessorDefinitions) true stdcpp17 5.1 uuid.lib;$(Console_Libs);%(XboxExtensionsDependencies);dbghelp.lib;%(AdditionalDependencies) Windows true pch.h Use false $(ProjectDir);Kits\ATGTelemetry\GDK;Kits\LiveTK;Kits\DirectXTK12\Inc;Kits\ATGTK;%(AdditionalIncludeDirectories) Level4 Disabled ATG_ENABLE_TELEMETRY;_DEBUG;%(PreprocessorDefinitions) true stdcpp17 5.1 true Windows true true uuid.lib;$(Console_Libs);%(AdditionalDependencies) Use pch.h $(ProjectDir);Kits\LiveTK;Kits\DirectXTK12\Inc;Kits\ATGTK;%(AdditionalIncludeDirectories) MaxSpeed NDEBUG;__WRL_NO_DEFAULT_LIB__;%(PreprocessorDefinitions) Level4 true true true stdcpp17 5.1 true Windows true true uuid.lib;$(Console_Libs);%(AdditionalDependencies) Use pch.h $(ProjectDir);Kits\LiveTK;Kits\DirectXTK12\Inc;Kits\ATGTK;%(AdditionalIncludeDirectories) MaxSpeed NDEBUG;__WRL_NO_DEFAULT_LIB__;PROFILE;%(PreprocessorDefinitions) Level4 true true true stdcpp17 5.1 Windows true dbghelp.lib;uuid.lib;$(Console_Libs);%(AdditionalDependencies) pch.h Use false $(ProjectDir);Kits\LiveTK;Kits\DirectXTK12\Inc;Kits\ATGTK;%(AdditionalIncludeDirectories) Level4 Disabled _DEBUG;__WRL_NO_DEFAULT_LIB__;%(PreprocessorDefinitions) true stdcpp17 5.1 Create %(Filename)%(Extension) true %(Filename)%(Extension) true true true true true %(Filename)%(Extension) true true true true true {052c4858-c76f-4cea-8a1a-e8e5559e67c2} true %(Filename)%(Extension) true Assets\%(Filename)%(Extension) true Assets\%(Filename)%(Extension) true Assets\%(Filename)%(Extension) true Assets\%(Filename)%(Extension) true Assets\%(Filename)%(Extension) true Assets\%(Filename)%(Extension) true Assets\%(Filename)%(Extension) true Assets\%(Filename)%(Extension) ================================================ FILE: Tests/GDK/APIRunner.GDK/APIRunner.GDK.Bin.vcxproj.filters ================================================  916b6ff9-882e-4cf3-93d5-bea41c3e7cd0 0c10bab7-ebd0-49b3-89a5-df53a9e36cf4 ico;cur;bmp;dds;dlg;fbx;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tga;tiff;tif;png;wav;mfcribbon-ms fa3bbbb4-fde4-4785-907e-fbb24dde6491 83010d7a-d0f5-4911-9741-7529fae5301c Common Common ATG Tool Kit ATG Tool Kit ATG Tool Kit ATG Tool Kit ATG Tool Kit ATG Tool Kit ATG Tool Kit ATG Tool Kit ATG Tool Kit Xbox Live Tool Kit Xbox Live Tool Kit Xbox Live Tool Kit ATG Tool Kit Common ATG Tool Kit ATG Tool Kit ATG Tool Kit Xbox Live Tool Kit Xbox Live Tool Kit Xbox Live Tool Kit ATG Tool Kit Assets Assets Assets Assets Assets Assets Assets Assets Assets Assets Assets Assets Assets Assets Assets Assets Assets Assets Assets Assets ================================================ FILE: Tests/GDK/APIRunner.GDK/APIRunner.GDK.Src.vcxproj ================================================ {46F31A54-4C74-41A8-B294-26B5689E51EF} Application v141 $(Console_SdkLibPath);$(LibraryPath) $(Console_SdkIncludeRoot);$(IncludePath) $(Console_SdkRoot)bin;$(Console_SdkToolPath);$(ExecutablePath) false $(Console_SdkLibPath);$(LibraryPath) $(Console_SdkIncludeRoot);$(IncludePath) $(Console_SdkRoot)bin;$(Console_SdkToolPath);$(ExecutablePath) true $(ProjectDir);Kits\DirectXTK12\Inc;Kits\ATGTK;Kits\ATGTelemetry\Gsdk;$(ProjectDir)..\..\APIExplorer\lua\src;$(ProjectDir)..\..\APIExplorer\Shared;$(ProjectDir)..\..\APIExplorer\Include;%(AdditionalIncludeDirectories) SAMPLE_BUILD_WITH_CPP;%(PreprocessorDefinitions) SAMPLE_BUILD_FOR_DESKTOP;%(PreprocessorDefinitions) true $([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), xsapi.paths.props))\xsapi.paths.props copy /Y "$(ProjectDir)APIRunnerSrc-MicrosoftGame.Config" "$(ProjectDir)MicrosoftGame.Config" xcopy /Y /I /E "$(ProjectDir)Assets\*.*" "$(TargetDir)Assets" & xcopy /Y /I /E "$(ProjectDir)Media\*.*" "$(TargetDir)Media" & xcopy /Y /I /E "$(ProjectDir)Media\Fonts\*.*" "$(TargetDir)" & xcopy /Y /I /E "$(ProjectDir)Media\Textures\*.*" "$(TargetDir)" dbghelp.lib;uuid.lib;$(Console_Libs);%(XboxExtensionsDependencies);%(AdditionalDependencies) true Windows true true Use pch.h $(ProjectDir);Kits\ATGTelemetry\GDK;Kits\LiveTK;Kits\DirectXTK12\Inc;Kits\ATGTK;%(AdditionalIncludeDirectories) MaxSpeed ATG_ENABLE_TELEMETRY;NDEBUG;%(PreprocessorDefinitions) Level4 true true true stdcpp17 5.1 dbghelp.lib;uuid.lib;$(Console_Libs);%(XboxExtensionsDependencies);%(AdditionalDependencies) true Windows true true Use pch.h $(ProjectDir);Kits\ATGTelemetry\GDK;Kits\LiveTK;Kits\DirectXTK12\Inc;Kits\ATGTK;%(AdditionalIncludeDirectories) MaxSpeed ATG_ENABLE_TELEMETRY;NDEBUG;%(PreprocessorDefinitions) Level4 true true true stdcpp17 5.1 dbghelp.lib;uuid.lib;$(Console_Libs);%(XboxExtensionsDependencies);%(AdditionalDependencies) true Windows true true Use pch.h $(ProjectDir);Kits\ATGTelemetry\GDK;Kits\LiveTK;Kits\DirectXTK12\Inc;Kits\ATGTK;%(AdditionalIncludeDirectories) MaxSpeed ATG_ENABLE_TELEMETRY;PROFILE;%(PreprocessorDefinitions) Level4 true true true stdcpp17 5.1 dbghelp.lib;uuid.lib;$(Console_Libs);%(XboxExtensionsDependencies);%(AdditionalDependencies) true Windows true true Use pch.h $(ProjectDir);Kits\ATGTelemetry\GDK;Kits\LiveTK;Kits\DirectXTK12\Inc;Kits\ATGTK;%(AdditionalIncludeDirectories) MaxSpeed ATG_ENABLE_TELEMETRY;PROFILE;%(PreprocessorDefinitions) Level4 true true true stdcpp17 5.1 dbghelp.lib;uuid.lib;$(Console_Libs);%(XboxExtensionsDependencies);%(AdditionalDependencies) Windows true pch.h Use false $(ProjectDir);Kits\ATGTelemetry\GDK;Kits\LiveTK;Kits\DirectXTK12\Inc;Kits\ATGTK;%(AdditionalIncludeDirectories) Level4 Disabled ATG_ENABLE_TELEMETRY;_DEBUG;%(PreprocessorDefinitions) true stdcpp17 5.1 dbghelp.lib;uuid.lib;$(Console_Libs);%(XboxExtensionsDependencies);%(AdditionalDependencies) Windows true pch.h Use false $(ProjectDir);Kits\ATGTelemetry\GDK;Kits\LiveTK;Kits\DirectXTK12\Inc;Kits\ATGTK;%(AdditionalIncludeDirectories) Level4 Disabled ATG_ENABLE_TELEMETRY;_DEBUG;%(PreprocessorDefinitions) true stdcpp17 5.1 true Windows true true dbghelp.lib;uuid.lib;$(Console_Libs);%(AdditionalDependencies) Use pch.h $(ProjectDir);Kits\LiveTK;Kits\DirectXTK12\Inc;Kits\ATGTK;%(AdditionalIncludeDirectories) MaxSpeed NDEBUG;__WRL_NO_DEFAULT_LIB__;%(PreprocessorDefinitions) Level4 true true true stdcpp17 5.1 true Windows true true dbghelp.lib;uuid.lib;$(Console_Libs);%(AdditionalDependencies) Use pch.h $(ProjectDir);Kits\LiveTK;Kits\DirectXTK12\Inc;Kits\ATGTK;%(AdditionalIncludeDirectories) MaxSpeed NDEBUG;__WRL_NO_DEFAULT_LIB__;PROFILE;%(PreprocessorDefinitions) Level4 true true true stdcpp17 5.1 Windows true dbghelp.lib;uuid.lib;$(Console_Libs);%(AdditionalDependencies) pch.h Use false $(ProjectDir);Kits\LiveTK;Kits\DirectXTK12\Inc;Kits\ATGTK;%(AdditionalIncludeDirectories) Level4 Disabled _DEBUG;__WRL_NO_DEFAULT_LIB__;%(PreprocessorDefinitions) true stdcpp17 5.1 Create %(Filename)%(Extension) true %(Filename)%(Extension) true true true true true %(Filename)%(Extension) true true true true true {052c4858-c76f-4cea-8a1a-e8e5559e67c2} true %(Filename)%(Extension) true Assets\%(Filename)%(Extension) true Assets\%(Filename)%(Extension) true Assets\%(Filename)%(Extension) true Assets\%(Filename)%(Extension) true Assets\%(Filename)%(Extension) true Assets\%(Filename)%(Extension) true Assets\%(Filename)%(Extension) true Assets\%(Filename)%(Extension) ================================================ FILE: Tests/GDK/APIRunner.GDK/APIRunner.GDK.Src.vcxproj.filters ================================================  916b6ff9-882e-4cf3-93d5-bea41c3e7cd0 0c10bab7-ebd0-49b3-89a5-df53a9e36cf4 ico;cur;bmp;dds;dlg;fbx;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tga;tiff;tif;png;wav;mfcribbon-ms fa3bbbb4-fde4-4785-907e-fbb24dde6491 83010d7a-d0f5-4911-9741-7529fae5301c Common Common ATG Tool Kit ATG Tool Kit ATG Tool Kit ATG Tool Kit ATG Tool Kit ATG Tool Kit ATG Tool Kit ATG Tool Kit ATG Tool Kit Xbox Live Tool Kit Xbox Live Tool Kit Xbox Live Tool Kit ATG Tool Kit Common ATG Tool Kit ATG Tool Kit ATG Tool Kit Xbox Live Tool Kit Xbox Live Tool Kit Xbox Live Tool Kit ATG Tool Kit Assets Assets Assets Assets Assets Assets Assets Assets Assets Assets Assets Assets Assets Assets Assets Assets Assets Assets Assets Assets ================================================ FILE: Tests/GDK/APIRunner.GDK/APIRunner.GDK.cpp ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include "pch.h" #include "APIRunner.GDK.h" #include "ATGColors.h" #include "FindMedia.h" #include "Kits\ATGTK\StringUtil.h" #include extern void ExitSample(); using namespace DirectX; using Microsoft::WRL::ComPtr; Sample* g_Sample = nullptr; namespace { const int c_sampleUIPanel = 2000; } Sample::Sample() noexcept(false) : m_frame(0), m_asyncQueue(nullptr) { g_Sample = this; DX::ThrowIfFailed( XTaskQueueCreate(XTaskQueueDispatchMode::ThreadPool, XTaskQueueDispatchMode::Manual, &m_asyncQueue) ); // Renders only 2D, so no need for a depth buffer. m_deviceResources = std::make_unique(DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_UNKNOWN); m_liveResources = std::make_shared(m_asyncQueue); m_liveInfoHUD = std::make_unique("Xbox Live API Runner GDK"); ATG::UIConfig uiconfig; m_ui = std::make_shared(uiconfig); m_log = std::make_unique(); ApiRunnerSetupApiExplorer(); } Sample::~Sample() { if (m_deviceResources) { m_deviceResources->WaitForGpu(); } if (m_asyncQueue) { XTaskQueueCloseHandle(m_asyncQueue); m_asyncQueue = nullptr; } } // Initialize the Direct3D resources required to run. void Sample::Initialize(HWND window, int width, int height) { m_gamePad = std::make_unique(); m_keyboard = std::make_unique(); m_mouse = std::make_unique(); #ifdef _GAMING_DESKTOP m_mouse->SetWindow(window); #endif #if !defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP) // NOTE: When running the app from the Start Menu (required for // Store API's to work) the Current Working Directory will be // returned as C:\Windows\system32 unless you overwrite it. // The sample relies on the font and image files in the .exe's // directory and so we do the following to set the working // directory to what we want. char dir[1024]; GetModuleFileNameA(NULL, dir, 1024); std::string exe = dir; exe = exe.substr(0, exe.find_last_of("\\")); SetCurrentDirectoryA(exe.c_str()); #endif m_ui->LoadLayout(L".\\Assets\\SampleUI.csv", L".\\Assets"); m_deviceResources->SetWindow(window, width, height); m_deviceResources->CreateDeviceResources(); CreateDeviceDependentResources(); m_deviceResources->CreateWindowSizeDependentResources(); CreateWindowSizeDependentResources(); uint32_t titleId = 0; HRESULT hr = XGameGetXboxTitleId(&titleId); if (FAILED(hr)) { m_log->WriteLine(L"Can not get title ID. Ensure you are running inside a app package"); } m_liveResources->SetUserChangedCallback([this](XUserHandle user) { m_liveInfoHUD->SetUser(user, m_asyncQueue); m_ui->FindPanel(c_sampleUIPanel)->Show(); }); m_liveResources->SetUserSignOutCompletedCallback([this](XUserHandle /*user*/) { m_liveInfoHUD->SetUser(nullptr, m_asyncQueue); m_ui->FindPanel(c_sampleUIPanel)->Close(); }); m_liveResources->SetErrorHandler([this](HRESULT error) { if (error == E_XAL_UIREQUIRED) { m_liveResources->SignInWithUI(); } else // Handle other error cases { } }); m_liveResources->Initialize(); m_liveInfoHUD->Initialize(); SetupUI(); // Before we can make an Xbox Live call we need to ensure that the Game OS has initialized the network stack // For sample purposes we block user interaction with the sample. A game should wait for the network to be // initialized before the main menu appears. For samples, we will wait at the end of initialization. while (!m_liveResources->IsNetworkAvailable()) { } } DWORD WINAPI ApiRunnerDoWork(LPVOID ) { if (g_Sample->m_runBVTs) { ApiRunnerRunTests(TestSet::SingleDeviceBVTs); } else { std::string jsonFileContents = ApiRunnerReadFile("cmds.json"); ApiRunnerProcessJsonCmds(jsonFileContents); } g_Sample->m_bRunningTests = false; g_Sample->m_bTestsFinished = true; return 0; } void Sample::StartRunTests() { if (!m_bRunningTests && m_log) { m_bRunningTests = true; m_log->Clear(); m_log->WriteLine(L"Running tests"); CreateThread(nullptr, 0, ApiRunnerDoWork, nullptr, 0, nullptr); } } #pragma region UI Methods void Sample::SetupUI() { using namespace ATG; } #pragma endregion #pragma region Frame Update // Executes basic render loop. void Sample::Tick() { PIXBeginEvent(PIX_COLOR_DEFAULT, L"Frame %I64u", m_frame); m_timer.Tick([&]() { Update(m_timer); }); Render(); PIXEndEvent(); m_frame++; } // Updates the world. void Sample::Update(DX::StepTimer const& timer) { PIXBeginEvent(PIX_COLOR_DEFAULT, L"Update"); float elapsedTime = float(timer.GetElapsedSeconds()); // Auto start tests after a small delay m_startTimer -= elapsedTime; if (m_startTimer < 0.0f && !m_bAutoStarted) { if (m_cmdLine.find(L"/test:") != std::wstring::npos) { std::wstring num = m_cmdLine.substr(m_cmdLine.find(L"/test:") + 6); if (num.find(L" ") != std::wstring::npos) { num = num.substr(0, num.find(L" ")); } int testNum = _wtoi(num.c_str()); ApiRunnerSetRunTestsParams(testNum, 0, 0); } if (m_cmdLine.find(L"/range:") != std::wstring::npos) { std::wstring num = m_cmdLine.substr(m_cmdLine.find(L"/range:") + 7); if (num.find(L" ") != std::wstring::npos) { num = num.substr(0, num.find(L" ")); } std::wstring num1 = num.substr(0, num.find(L":")); std::wstring num2 = num.substr(num.find(L":")+1); int minNum = _wtoi(num1.c_str()); int maxNum = _wtoi(num2.c_str()); ApiRunnerSetRunTestsParams(0, minNum, maxNum); } m_bAutoStarted = true; m_runBVTs = false; StartRunTests(); } if (m_bTestsFinished) { if (m_cmdLine.find(L"/exit") != std::wstring::npos) { ExitSample(); } } auto pad = m_gamePad->GetState(0); if (pad.IsConnected()) { m_gamePadButtons.Update(pad); if (pad.IsViewPressed()) { ExitSample(); } if (m_gamePadButtons.menu == GamePad::ButtonStateTracker::PRESSED) { if (!m_liveResources->IsUserSignedIn()) { m_log->WriteLine(L"Sign in silently"); m_liveResources->SignInSilently(); } else { m_log->WriteLine(L"Sign in with UI"); m_liveResources->SignInWithUI(); } } m_ui->Update(elapsedTime, pad); } else { m_gamePadButtons.Reset(); } auto kb = m_keyboard->GetState(); m_keyboardButtons.Update(kb); if (kb.Enter || kb.Space || kb.B) { m_runBVTs = true; StartRunTests(); } if (kb.A) { m_runBVTs = false; StartRunTests(); } if (kb.Escape) { ExitSample(); } // Process any completed tasks while (XTaskQueueDispatch(m_asyncQueue, XTaskQueuePort::Completion, 0)) { } m_liveInfoHUD->Update(m_deviceResources->GetCommandQueue()); PIXEndEvent(); } #pragma endregion #pragma region Frame Render // Draws the scene. void Sample::Render() { // Don't try to render anything before the first Update. if (m_timer.GetFrameCount() == 0) { return; } // Prepare the command list to render a new frame. m_deviceResources->Prepare(); Clear(); auto commandList = m_deviceResources->GetCommandList(); PIXBeginEvent(commandList, PIX_COLOR_DEFAULT, L"Render"); ID3D12DescriptorHeap* heap = m_resourceDescriptors->Heap(); commandList->SetDescriptorHeaps(1, &heap); m_liveInfoHUD->Render(commandList); if (m_log) m_log->Render(commandList); if (m_ui) m_ui->Render(commandList); PIXEndEvent(commandList); // Show the new frame. PIXBeginEvent(m_deviceResources->GetCommandQueue(), PIX_COLOR_DEFAULT, L"Present"); m_deviceResources->Present(); m_graphicsMemory->Commit(m_deviceResources->GetCommandQueue()); PIXEndEvent(m_deviceResources->GetCommandQueue()); } // Helper method to clear the back buffers. void Sample::Clear() { auto commandList = m_deviceResources->GetCommandList(); PIXBeginEvent(commandList, PIX_COLOR_DEFAULT, L"Clear"); // Clear the views. auto rtvDescriptor = m_deviceResources->GetRenderTargetView(); commandList->OMSetRenderTargets(1, &rtvDescriptor, FALSE, nullptr); commandList->ClearRenderTargetView(rtvDescriptor, ATG::Colors::Background, 0, nullptr); // Set the viewport and scissor rect. auto viewport = m_deviceResources->GetScreenViewport(); auto scissorRect = m_deviceResources->GetScissorRect(); commandList->RSSetViewports(1, &viewport); commandList->RSSetScissorRects(1, &scissorRect); PIXEndEvent(commandList); } #pragma endregion #pragma region Message Handlers // Message handlers void Sample::OnActivated() { } void Sample::OnDeactivated() { } void Sample::OnSuspending() { m_deviceResources->Suspend(); } void Sample::OnResuming() { m_deviceResources->Resume(); m_timer.ResetElapsedTime(); m_gamePadButtons.Reset(); m_keyboardButtons.Reset(); m_liveResources->Refresh(); m_ui->Reset(); m_log.reset(); } void Sample::OnWindowMoved() { auto r = m_deviceResources->GetOutputSize(); m_deviceResources->WindowSizeChanged(r.right, r.bottom); } void Sample::OnWindowSizeChanged(int width, int height) { if (!m_deviceResources->WindowSizeChanged(width, height)) return; CreateWindowSizeDependentResources(); } // Properties void Sample::GetDefaultSize(int& width, int& height) const { width = 1920; height = 1080; } #pragma endregion #pragma region Direct3D Resources // These are the resources that depend on the device. void Sample::CreateDeviceDependentResources() { auto device = m_deviceResources->GetD3DDevice(); m_graphicsMemory = std::make_unique(device); RenderTargetState rtState(m_deviceResources->GetBackBufferFormat(), m_deviceResources->GetDepthBufferFormat()); ResourceUploadBatch resourceUpload(device); resourceUpload.Begin(); m_resourceDescriptors = std::make_unique(device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE, Descriptors::Count, Descriptors::Reserve ); m_liveInfoHUD->RestoreDevice(device, rtState, resourceUpload, *m_resourceDescriptors); m_log->RestoreDevice( device, resourceUpload, rtState, L"Media\\Fonts\\SegoeUI_18.spritefont", L"Assets\\ATGSampleBackground.DDS", m_resourceDescriptors->GetCpuHandle(Descriptors::Font), m_resourceDescriptors->GetGpuHandle(Descriptors::Font), m_resourceDescriptors->GetCpuHandle(Descriptors::Background), m_resourceDescriptors->GetGpuHandle(Descriptors::Background) ); m_ui->RestoreDevice(device, rtState, resourceUpload, *m_resourceDescriptors); auto uploadResourcesFinished = resourceUpload.End(m_deviceResources->GetCommandQueue()); uploadResourcesFinished.wait(); } // Allocate all memory resources that change on a window SizeChanged event. void Sample::CreateWindowSizeDependentResources() { m_liveInfoHUD->SetViewport(m_deviceResources->GetScreenViewport()); auto viewport = m_deviceResources->GetScreenViewport(); static const RECT screenDisplay = { 50, 150, static_cast(viewport.Width-50), static_cast(viewport.Height-150) }; m_log->SetWindow(screenDisplay, false); m_log->SetViewport(viewport); RECT size = m_deviceResources->GetOutputSize(); m_ui->SetWindow(size); } void Sample::OnDeviceLost() { m_graphicsMemory.reset(); m_liveInfoHUD->ReleaseDevice(); m_resourceDescriptors.reset(); } void Sample::OnDeviceRestored() { CreateDeviceDependentResources(); CreateWindowSizeDependentResources(); } #pragma endregion void LogToScreen(_Printf_format_string_ char const* format, ...) { char message[8000] = {}; va_list varArgs{}; va_start(varArgs, format); pal::vsprintf(message, 4096, format, varArgs); va_end(varArgs); if (g_Sample && g_Sample->m_log != nullptr) { std::wstring wstr = DX::Utf8ToWide(message); g_Sample->m_log->WriteLine(wstr.c_str()); } else { std::cout << message; std::cout << "\n"; } LogToFile(message); } ================================================ FILE: Tests/GDK/APIRunner.GDK/APIRunner.GDK.h ================================================ // Copyright (c) Microsoft Corporation // Licensed under the MIT license. See LICENSE file in the project root for full license information. #pragma once #include "DeviceResources.h" #include "Kits\LiveTK\LiveResources.h" #include "Kits\LiveTK\LiveInfoHUD.h" #include "StepTimer.h" #include "SampleGUI.h" #include "Kits\ATGTK\TextConsole.h" class Sample; // A basic sample implementation that creates a D3D12 device and // provides a render loop. class Sample final : public DX::IDeviceNotify { public: Sample() noexcept(false); ~Sample(); // Initialization and management void Initialize(HWND window, int width, int height); // Basic render loop void Tick(); // IDeviceNotify virtual void OnDeviceLost() override; virtual void OnDeviceRestored() override; // Messages void OnActivated(); void OnDeactivated(); void OnSuspending(); void OnResuming(); void OnWindowMoved(); void OnWindowSizeChanged(int width, int height); // Properties void GetDefaultSize(int& width, int& height) const; std::unique_ptr m_log; bool m_bAutoStarted = false; bool m_bRunningTests = false; bool m_bTestsFinished = false; bool m_runBVTs = false; std::wstring m_cmdLine; float m_startTimer = 1.0f; void StartRunTests(); private: void Update(DX::StepTimer const& timer); void Render(); void Clear(); void CreateDeviceDependentResources(); void CreateWindowSizeDependentResources(); void SetupUI(); // Device resources. std::unique_ptr m_deviceResources; // Rendering loop timer. uint64_t m_frame; DX::StepTimer m_timer; // Input devices. std::unique_ptr m_gamePad; std::unique_ptr m_keyboard; std::unique_ptr m_mouse; DirectX::GamePad::ButtonStateTracker m_gamePadButtons; DirectX::Keyboard::KeyboardStateTracker m_keyboardButtons; // DirectXTK objects. std::unique_ptr m_graphicsMemory; std::unique_ptr m_resourceDescriptors; // UI Objects std::shared_ptr m_ui; // Xbox Live objects. std::shared_ptr m_liveResources; std::unique_ptr m_liveInfoHUD; XTaskQueueHandle m_asyncQueue; enum Descriptors { Font, ConsoleFont, Background, ConsoleBackground, Reserve, Count = 32, }; }; ================================================ FILE: Tests/GDK/APIRunner.GDK/APIRunnerBin-MicrosoftGame.Config ================================================ false 000000004C26FED0 76029B4D 1024 ================================================ FILE: Tests/GDK/APIRunner.GDK/APIRunnerSrc-MicrosoftGame.Config ================================================ false 000000004C26FED0 76029B4D 1024 ================================================ FILE: Tests/GDK/APIRunner.GDK/APIRunnerSrc143-MicrosoftGame.Config ================================================ false 000000004C26FED0 76029B4D 1024 ================================================ FILE: Tests/GDK/APIRunner.GDK/APIRunnerStats2017-MicrosoftGame.Config ================================================ false 0000000044296E10 78C0191B 1024 ================================================ FILE: Tests/GDK/APIRunner.GDK/AppxManifest.xml ================================================ APIRunner Src GDK Desktop Sample Xbox Advanced Technology Group Assets/Logo.png **REPLACE** ================================================ FILE: Tests/GDK/APIRunner.GDK/Assets/SampleUI.csv ================================================ #ITEM,ID,X,Y,DX,DY,PARAMETERS CUSTOM_OVERLAY,2000,0,0,1920,1080, LABEL,2002,50,50,50,50,Log,LEFT;SMALL ================================================ FILE: Tests/GDK/APIRunner.GDK/CopyLogFromConsole.cmd ================================================ xbcp xd:\titles\41336MicrosoftATG.XboxLiveE2E_dspnxghe87tn0\apirunner-log.txt ================================================ FILE: Tests/GDK/APIRunner.GDK/DeviceResources.cpp ================================================ // // DeviceResources.cpp - A wrapper for the Direct3D 12/12.X device and swapchain // #include "pch.h" #include "DeviceResources.h" using namespace DirectX; using namespace DX; using Microsoft::WRL::ComPtr; #ifndef _GAMING_XBOX #ifdef __clang__ #pragma clang diagnostic ignored "-Wcovered-switch-default" #endif #pragma warning(disable : 4061) namespace { inline DXGI_FORMAT NoSRGB(DXGI_FORMAT fmt) { switch (fmt) { case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: return DXGI_FORMAT_R8G8B8A8_UNORM; case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: return DXGI_FORMAT_B8G8R8A8_UNORM; case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB: return DXGI_FORMAT_B8G8R8X8_UNORM; default: return fmt; } } } #endif // Constructor for DeviceResources. DeviceResources::DeviceResources( DXGI_FORMAT backBufferFormat, DXGI_FORMAT depthBufferFormat, UINT backBufferCount) noexcept(false) : m_backBufferIndex(0), m_fenceValues{}, #ifdef _GAMING_XBOX m_framePipelineToken{}, #endif m_rtvDescriptorSize(0), m_screenViewport{}, m_scissorRect{}, m_backBufferFormat(backBufferFormat), m_depthBufferFormat(depthBufferFormat), m_backBufferCount(backBufferCount), m_window(nullptr), m_d3dFeatureLevel(D3D_FEATURE_LEVEL_11_0), m_outputSize{ 0, 0, 1, 1 }, m_deviceNotify(nullptr) { if (backBufferCount > MAX_BACK_BUFFER_COUNT) { throw std::out_of_range("backBufferCount too large"); } } // Destructor for DeviceResources. DeviceResources::~DeviceResources() { // Ensure that the GPU is no longer referencing resources that are about to be destroyed. WaitForGpu(); #ifdef _GAMING_XBOX // Ensure we present a blank screen before cleaning up resources. if (m_commandQueue) { (void)m_commandQueue->PresentX(0, nullptr, nullptr); } #endif } // Configures the Direct3D device, and stores handles to it and the device context. void DeviceResources::CreateDeviceResources() { #ifdef _GAMING_XBOX // Create the DX12 API device object. D3D12XBOX_CREATE_DEVICE_PARAMETERS params = {}; params.Version = D3D12_SDK_VERSION; #if defined(_DEBUG) // Enable the debug layer. params.ProcessDebugFlags = D3D12_PROCESS_DEBUG_FLAG_DEBUG_LAYER_ENABLED; #elif defined(PROFILE) // Enable the instrumented driver. params.ProcessDebugFlags = D3D12XBOX_PROCESS_DEBUG_FLAG_INSTRUMENTED; #endif params.GraphicsCommandQueueRingSizeBytes = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); params.GraphicsScratchMemorySizeBytes = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); params.ComputeScratchMemorySizeBytes = static_cast(D3D12XBOX_DEFAULT_SIZE_BYTES); ThrowIfFailed(D3D12XboxCreateDevice( nullptr, ¶ms, IID_GRAPHICS_PPV_ARGS(m_d3dDevice.ReleaseAndGetAddressOf()) )); m_d3dDevice->SetName(L"DeviceResources"); m_d3dFeatureLevel = D3D_FEATURE_LEVEL_12_0; #else DWORD dxgiFactoryFlags = 0; #if defined(_DEBUG) // Enable the debug layer (requires the Graphics Tools "optional feature"). // // NOTE: Enabling the debug layer after device creation will invalidate the active device. //{ // ComPtr debugController; // if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(debugController.GetAddressOf())))) // { // debugController->EnableDebugLayer(); // } // else // { // OutputDebugStringA("WARNING: Direct3D Debug Device is not available\n"); // } // ComPtr dxgiInfoQueue; // if (SUCCEEDED(DXGIGetDebugInterface1(0, IID_PPV_ARGS(dxgiInfoQueue.GetAddressOf())))) // { // dxgiFactoryFlags = DXGI_CREATE_FACTORY_DEBUG; // dxgiInfoQueue->SetBreakOnSeverity(DXGI_DEBUG_ALL, DXGI_INFO_QUEUE_MESSAGE_SEVERITY_ERROR, true); // dxgiInfoQueue->SetBreakOnSeverity(DXGI_DEBUG_ALL, DXGI_INFO_QUEUE_MESSAGE_SEVERITY_CORRUPTION, true); // DXGI_INFO_QUEUE_MESSAGE_ID hide[] = // { // 80 /* IDXGISwapChain::GetContainingOutput: The swapchain's adapter does not control the output on which the swapchain's window resides. */, // }; // DXGI_INFO_QUEUE_FILTER filter = {}; // filter.DenyList.NumIDs = _countof(hide); // filter.DenyList.pIDList = hide; // dxgiInfoQueue->AddStorageFilterEntries(DXGI_DEBUG_DXGI, &filter); // } //} #endif ThrowIfFailed(CreateDXGIFactory2(dxgiFactoryFlags, IID_PPV_ARGS(m_dxgiFactory.ReleaseAndGetAddressOf()))); ComPtr adapter; GetAdapter(adapter.GetAddressOf()); // Create the DX12 API device object. ThrowIfFailed(D3D12CreateDevice( adapter.Get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(m_d3dDevice.ReleaseAndGetAddressOf()) )); m_d3dDevice->SetName(L"DeviceResources"); #ifndef NDEBUG // Configure debug device (if active). ComPtr d3dInfoQueue; if (SUCCEEDED(m_d3dDevice.As(&d3dInfoQueue))) { #ifdef _DEBUG d3dInfoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_CORRUPTION, true); d3dInfoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_ERROR, true); #endif D3D12_MESSAGE_ID hide[] = { D3D12_MESSAGE_ID_MAP_INVALID_NULLRANGE, D3D12_MESSAGE_ID_UNMAP_INVALID_NULLRANGE, D3D12_MESSAGE_ID_EXECUTECOMMANDLISTS_WRONGSWAPCHAINBUFFERREFERENCE }; D3D12_INFO_QUEUE_FILTER filter = {}; filter.DenyList.NumIDs = _countof(hide); filter.DenyList.pIDList = hide; d3dInfoQueue->AddStorageFilterEntries(&filter); } #endif // Determine maximum supported feature level for this device static const D3D_FEATURE_LEVEL s_featureLevels[] = { D3D_FEATURE_LEVEL_12_1, D3D_FEATURE_LEVEL_12_0, D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0, }; D3D12_FEATURE_DATA_FEATURE_LEVELS featLevels = { _countof(s_featureLevels), s_featureLevels, D3D_FEATURE_LEVEL_11_0 }; HRESULT hr = m_d3dDevice->CheckFeatureSupport(D3D12_FEATURE_FEATURE_LEVELS, &featLevels, sizeof(featLevels)); if (SUCCEEDED(hr)) { m_d3dFeatureLevel = featLevels.MaxSupportedFeatureLevel; } else { m_d3dFeatureLevel = D3D_FEATURE_LEVEL_11_0; } #endif // Create the command queue. D3D12_COMMAND_QUEUE_DESC queueDesc = {}; queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; ThrowIfFailed(m_d3dDevice->CreateCommandQueue(&queueDesc, IID_GRAPHICS_PPV_ARGS(m_commandQueue.ReleaseAndGetAddressOf()))); m_commandQueue->SetName(L"DeviceResources"); // Create descriptor heaps for render target views and depth stencil views. D3D12_DESCRIPTOR_HEAP_DESC rtvDescriptorHeapDesc = {}; rtvDescriptorHeapDesc.NumDescriptors = m_backBufferCount; rtvDescriptorHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV; ThrowIfFailed(m_d3dDevice->CreateDescriptorHeap(&rtvDescriptorHeapDesc, IID_GRAPHICS_PPV_ARGS(m_rtvDescriptorHeap.ReleaseAndGetAddressOf()))); m_rtvDescriptorHeap->SetName(L"DeviceResources"); m_rtvDescriptorSize = m_d3dDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV); if (m_depthBufferFormat != DXGI_FORMAT_UNKNOWN) { D3D12_DESCRIPTOR_HEAP_DESC dsvDescriptorHeapDesc = {}; dsvDescriptorHeapDesc.NumDescriptors = 1; dsvDescriptorHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_DSV; ThrowIfFailed(m_d3dDevice->CreateDescriptorHeap(&dsvDescriptorHeapDesc, IID_GRAPHICS_PPV_ARGS(m_dsvDescriptorHeap.ReleaseAndGetAddressOf()))); m_dsvDescriptorHeap->SetName(L"DeviceResources"); } // Create a command allocator for each back buffer that will be rendered to. for (UINT n = 0; n < m_backBufferCount; n++) { ThrowIfFailed(m_d3dDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_GRAPHICS_PPV_ARGS(m_commandAllocators[n].ReleaseAndGetAddressOf()))); wchar_t name[25] = {}; swprintf_s(name, L"Render target %u", n); m_commandAllocators[n]->SetName(name); } // Create a command list for recording graphics commands. ThrowIfFailed(m_d3dDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, m_commandAllocators[0].Get(), nullptr, IID_GRAPHICS_PPV_ARGS(m_commandList.ReleaseAndGetAddressOf()))); ThrowIfFailed(m_commandList->Close()); m_commandList->SetName(L"DeviceResources"); // Create a fence for tracking GPU execution progress. ThrowIfFailed(m_d3dDevice->CreateFence(m_fenceValues[m_backBufferIndex], D3D12_FENCE_FLAG_NONE, IID_GRAPHICS_PPV_ARGS(m_fence.ReleaseAndGetAddressOf()))); m_fenceValues[m_backBufferIndex]++; m_fence->SetName(L"DeviceResources"); m_fenceEvent.Attach(CreateEventEx(nullptr, nullptr, 0, EVENT_MODIFY_STATE | SYNCHRONIZE)); if (!m_fenceEvent.IsValid()) { throw std::exception("CreateEvent"); } #ifdef _GAMING_XBOX RegisterFrameEvents(); #endif } // These resources need to be recreated every time the window size is changed. void DeviceResources::CreateWindowSizeDependentResources() { if (!m_window) { throw std::exception("Call SetWindow with a valid window handle"); } // Wait until all previous GPU work is complete. WaitForGpu(); #ifdef _GAMING_XBOX // Ensure we present a blank screen before cleaning up resources. ThrowIfFailed(m_commandQueue->PresentX(0, nullptr, nullptr)); #endif // Release resources that are tied to the swap chain and update fence values. for (UINT n = 0; n < m_backBufferCount; n++) { m_renderTargets[n].Reset(); m_fenceValues[n] = m_fenceValues[m_backBufferIndex]; } // Determine the render target size in pixels. UINT backBufferWidth = std::max(static_cast(m_outputSize.right - m_outputSize.left), 1u); UINT backBufferHeight = std::max(static_cast(m_outputSize.bottom - m_outputSize.top), 1u); #ifdef _GAMING_XBOX // Obtain the back buffers for this window which will be the final render targets // and create render target views for each of them. CD3DX12_HEAP_PROPERTIES swapChainHeapProperties(D3D12_HEAP_TYPE_DEFAULT); D3D12_RESOURCE_DESC swapChainBufferDesc = CD3DX12_RESOURCE_DESC::Tex2D( m_backBufferFormat, backBufferWidth, backBufferHeight, 1, // This resource has only one texture. 1 // Use a single mipmap level. ); swapChainBufferDesc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET; D3D12_CLEAR_VALUE swapChainOptimizedClearValue = {}; swapChainOptimizedClearValue.Format = m_backBufferFormat; for (UINT n = 0; n < m_backBufferCount; n++) { ThrowIfFailed(m_d3dDevice->CreateCommittedResource( &swapChainHeapProperties, D3D12_HEAP_FLAG_ALLOW_DISPLAY, &swapChainBufferDesc, D3D12_RESOURCE_STATE_PRESENT, &swapChainOptimizedClearValue, IID_GRAPHICS_PPV_ARGS(m_renderTargets[n].GetAddressOf()))); wchar_t name[25] = {}; swprintf_s(name, L"Render target %u", n); m_renderTargets[n]->SetName(name); D3D12_RENDER_TARGET_VIEW_DESC rtvDesc = {}; rtvDesc.Format = m_backBufferFormat; rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptor( m_rtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(), static_cast(n), m_rtvDescriptorSize); m_d3dDevice->CreateRenderTargetView(m_renderTargets[n].Get(), &rtvDesc, rtvDescriptor); } // Reset the index to the current back buffer. m_backBufferIndex = 0; #else DXGI_FORMAT backBufferFormat = NoSRGB(m_backBufferFormat); // If the swap chain already exists, resize it, otherwise create one. if (m_swapChain) { // If the swap chain already exists, resize it. HRESULT hr = m_swapChain->ResizeBuffers( m_backBufferCount, backBufferWidth, backBufferHeight, backBufferFormat, 0 ); if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) { #ifdef _DEBUG char buff[64] = {}; sprintf_s(buff, "Device Lost on ResizeBuffers: Reason code 0x%08X\n", (hr == DXGI_ERROR_DEVICE_REMOVED) ? m_d3dDevice->GetDeviceRemovedReason() : hr); OutputDebugStringA(buff); #endif // If the device was removed for any reason, a new device and swap chain will need to be created. HandleDeviceLost(); // Everything is set up now. Do not continue execution of this method. HandleDeviceLost will reenter this method // and correctly set up the new device. return; } else { ThrowIfFailed(hr); } } else { // Create a descriptor for the swap chain. DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {}; swapChainDesc.Width = backBufferWidth; swapChainDesc.Height = backBufferHeight; swapChainDesc.Format = backBufferFormat; swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; swapChainDesc.BufferCount = m_backBufferCount; swapChainDesc.SampleDesc.Count = 1; swapChainDesc.SampleDesc.Quality = 0; swapChainDesc.Scaling = DXGI_SCALING_STRETCH; swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_IGNORE; DXGI_SWAP_CHAIN_FULLSCREEN_DESC fsSwapChainDesc = {}; fsSwapChainDesc.Windowed = TRUE; // Create a swap chain for the window. ComPtr swapChain; ThrowIfFailed(m_dxgiFactory->CreateSwapChainForHwnd( m_commandQueue.Get(), m_window, &swapChainDesc, &fsSwapChainDesc, nullptr, swapChain.GetAddressOf() )); ThrowIfFailed(swapChain.As(&m_swapChain)); // This class does not support exclusive full-screen mode and prevents DXGI from responding to the ALT+ENTER shortcut ThrowIfFailed(m_dxgiFactory->MakeWindowAssociation(m_window, DXGI_MWA_NO_ALT_ENTER)); } // Obtain the back buffers for this window which will be the final render targets // and create render target views for each of them. for (UINT n = 0; n < m_backBufferCount; n++) { ThrowIfFailed(m_swapChain->GetBuffer(n, IID_PPV_ARGS(m_renderTargets[n].GetAddressOf()))); wchar_t name[25] = {}; swprintf_s(name, L"Render target %u", n); m_renderTargets[n]->SetName(name); D3D12_RENDER_TARGET_VIEW_DESC rtvDesc = {}; rtvDesc.Format = m_backBufferFormat; rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptor( m_rtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(), static_cast(n), m_rtvDescriptorSize); m_d3dDevice->CreateRenderTargetView(m_renderTargets[n].Get(), &rtvDesc, rtvDescriptor); } // Reset the index to the current back buffer. m_backBufferIndex = m_swapChain->GetCurrentBackBufferIndex(); #endif if (m_depthBufferFormat != DXGI_FORMAT_UNKNOWN) { // Allocate a 2-D surface as the depth/stencil buffer and create a depth/stencil view // on this surface. CD3DX12_HEAP_PROPERTIES depthHeapProperties(D3D12_HEAP_TYPE_DEFAULT); D3D12_RESOURCE_DESC depthStencilDesc = CD3DX12_RESOURCE_DESC::Tex2D( m_depthBufferFormat, backBufferWidth, backBufferHeight, 1, // This depth stencil view has only one texture. 1 // Use a single mipmap level. ); depthStencilDesc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL; D3D12_CLEAR_VALUE depthOptimizedClearValue = {}; depthOptimizedClearValue.Format = m_depthBufferFormat; depthOptimizedClearValue.DepthStencil.Depth = 1.0f; depthOptimizedClearValue.DepthStencil.Stencil = 0; ThrowIfFailed(m_d3dDevice->CreateCommittedResource( &depthHeapProperties, D3D12_HEAP_FLAG_NONE, &depthStencilDesc, D3D12_RESOURCE_STATE_DEPTH_WRITE, &depthOptimizedClearValue, IID_GRAPHICS_PPV_ARGS(m_depthStencil.ReleaseAndGetAddressOf()) )); m_depthStencil->SetName(L"Depth stencil"); D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc = {}; dsvDesc.Format = m_depthBufferFormat; dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D; m_d3dDevice->CreateDepthStencilView(m_depthStencil.Get(), &dsvDesc, m_dsvDescriptorHeap->GetCPUDescriptorHandleForHeapStart()); } // Set the 3D rendering viewport and scissor rectangle to target the entire window. m_screenViewport.TopLeftX = m_screenViewport.TopLeftY = 0.f; m_screenViewport.Width = static_cast(backBufferWidth); m_screenViewport.Height = static_cast(backBufferHeight); m_screenViewport.MinDepth = D3D12_MIN_DEPTH; m_screenViewport.MaxDepth = D3D12_MAX_DEPTH; m_scissorRect.left = m_scissorRect.top = 0; m_scissorRect.right = static_cast(backBufferWidth); m_scissorRect.bottom = static_cast(backBufferHeight); } // This method is called when the Win32 window is created (or re-created). void DeviceResources::SetWindow(HWND window, int width, int height) { m_window = window; m_outputSize.left = m_outputSize.top = 0; m_outputSize.right = width; m_outputSize.bottom = height; } // This method is called when the Win32 window changes size. bool DeviceResources::WindowSizeChanged(int width, int height) { RECT newRc; newRc.left = newRc.top = 0; newRc.right = width; newRc.bottom = height; if (newRc.left == m_outputSize.left && newRc.top == m_outputSize.top && newRc.right == m_outputSize.right && newRc.bottom == m_outputSize.bottom) { return false; } m_outputSize = newRc; CreateWindowSizeDependentResources(); return true; } // Recreate all device resources and set them back to the current state. void DeviceResources::HandleDeviceLost() { #ifndef _GAMING_XBOX if (m_deviceNotify) { m_deviceNotify->OnDeviceLost(); } for (UINT n = 0; n < m_backBufferCount; n++) { m_commandAllocators[n].Reset(); m_renderTargets[n].Reset(); } m_depthStencil.Reset(); m_commandQueue.Reset(); m_commandList.Reset(); m_fence.Reset(); m_rtvDescriptorHeap.Reset(); m_dsvDescriptorHeap.Reset(); m_swapChain.Reset(); m_d3dDevice.Reset(); m_dxgiFactory.Reset(); #ifdef _DEBUG { ComPtr dxgiDebug; if (SUCCEEDED(DXGIGetDebugInterface1(0, IID_PPV_ARGS(&dxgiDebug)))) { dxgiDebug->ReportLiveObjects(DXGI_DEBUG_ALL, DXGI_DEBUG_RLO_FLAGS(DXGI_DEBUG_RLO_SUMMARY | DXGI_DEBUG_RLO_IGNORE_INTERNAL)); } } #endif CreateDeviceResources(); CreateWindowSizeDependentResources(); if (m_deviceNotify) { m_deviceNotify->OnDeviceRestored(); } #endif } // Prepare the command list and render target for rendering. void DeviceResources::Prepare(D3D12_RESOURCE_STATES beforeState) { #ifdef _GAMING_XBOX // Wait until frame start is signaled m_framePipelineToken = D3D12XBOX_FRAME_PIPELINE_TOKEN_NULL; ThrowIfFailed(m_d3dDevice->WaitFrameEventX(D3D12XBOX_FRAME_EVENT_ORIGIN, INFINITE, nullptr, D3D12XBOX_WAIT_FRAME_EVENT_FLAG_NONE, &m_framePipelineToken)); #endif // Reset command list and allocator. ThrowIfFailed(m_commandAllocators[m_backBufferIndex]->Reset()); ThrowIfFailed(m_commandList->Reset(m_commandAllocators[m_backBufferIndex].Get(), nullptr)); if (beforeState != D3D12_RESOURCE_STATE_RENDER_TARGET) { // Transition the render target into the correct state to allow for drawing into it. D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_backBufferIndex].Get(), beforeState, D3D12_RESOURCE_STATE_RENDER_TARGET); m_commandList->ResourceBarrier(1, &barrier); } } // Present the contents of the swap chain to the screen. void DeviceResources::Present(D3D12_RESOURCE_STATES beforeState) { if (beforeState != D3D12_RESOURCE_STATE_PRESENT) { // Transition the render target to the state that allows it to be presented to the display. D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_backBufferIndex].Get(), beforeState, D3D12_RESOURCE_STATE_PRESENT); m_commandList->ResourceBarrier(1, &barrier); } // Send the command list off to the GPU for processing. ThrowIfFailed(m_commandList->Close()); m_commandQueue->ExecuteCommandLists(1, CommandListCast(m_commandList.GetAddressOf())); #ifdef _GAMING_XBOX // Present the backbuffer using the PresentX API. D3D12XBOX_PRESENT_PLANE_PARAMETERS planeParameters = {}; planeParameters.Token = m_framePipelineToken; planeParameters.ResourceCount = 1; planeParameters.ppResources = m_renderTargets[m_backBufferIndex].GetAddressOf(); ThrowIfFailed( m_commandQueue->PresentX(1, &planeParameters, nullptr) ); // Xbox One apps do not need to handle DXGI_ERROR_DEVICE_REMOVED or DXGI_ERROR_DEVICE_RESET. #else // The first argument instructs DXGI to block until VSync, putting the application // to sleep until the next VSync. This ensures we don't waste any cycles rendering // frames that will never be displayed to the screen. HRESULT hr = m_swapChain->Present(1, 0); // If the device was reset we must completely reinitialize the renderer. if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) { #ifdef _DEBUG char buff[64] = {}; sprintf_s(buff, "Device Lost on Present: Reason code 0x%08X\n", (hr == DXGI_ERROR_DEVICE_REMOVED) ? m_d3dDevice->GetDeviceRemovedReason() : hr); OutputDebugStringA(buff); #endif HandleDeviceLost(); } else { ThrowIfFailed(hr); } #endif MoveToNextFrame(); } // Handle GPU suspend/resume void DeviceResources::Suspend() { #ifdef _GAMING_XBOX m_commandQueue->SuspendX(0); #endif } void DeviceResources::Resume() { #ifdef _GAMING_XBOX m_commandQueue->ResumeX(); RegisterFrameEvents(); #endif } // Wait for pending GPU work to complete. void DeviceResources::WaitForGpu() noexcept { if (m_commandQueue && m_fence && m_fenceEvent.IsValid()) { // Schedule a Signal command in the GPU queue. UINT64 fenceValue = m_fenceValues[m_backBufferIndex]; if (SUCCEEDED(m_commandQueue->Signal(m_fence.Get(), fenceValue))) { // Wait until the Signal has been processed. if (SUCCEEDED(m_fence->SetEventOnCompletion(fenceValue, m_fenceEvent.Get()))) { WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); // Increment the fence value for the current frame. m_fenceValues[m_backBufferIndex]++; } } } } // Prepare to render the next frame. void DeviceResources::MoveToNextFrame() { // Schedule a Signal command in the queue. const UINT64 currentFenceValue = m_fenceValues[m_backBufferIndex]; ThrowIfFailed(m_commandQueue->Signal(m_fence.Get(), currentFenceValue)); // Update the back buffer index. #ifdef _GAMING_XBOX m_backBufferIndex = (m_backBufferIndex + 1) % m_backBufferCount; #else m_backBufferIndex = m_swapChain->GetCurrentBackBufferIndex(); #endif // If the next frame is not ready to be rendered yet, wait until it is ready. if (m_fence->GetCompletedValue() < m_fenceValues[m_backBufferIndex]) { ThrowIfFailed(m_fence->SetEventOnCompletion(m_fenceValues[m_backBufferIndex], m_fenceEvent.Get())); WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); } // Set the fence value for the next frame. m_fenceValues[m_backBufferIndex] = currentFenceValue + 1; } #ifdef _GAMING_XBOX // Set frame interval and register for frame events void DeviceResources::RegisterFrameEvents() { // First, retrieve the underlying DXGI device from the D3D device. ComPtr dxgiDevice; ThrowIfFailed(m_d3dDevice.As(&dxgiDevice)); // Identify the physical adapter (GPU or card) this device is running on. ComPtr dxgiAdapter; ThrowIfFailed(dxgiDevice->GetAdapter(dxgiAdapter.GetAddressOf())); // Retrieve the outputs for the adapter. ComPtr dxgiOutput; ThrowIfFailed(dxgiAdapter->EnumOutputs(0, dxgiOutput.GetAddressOf())); // Set frame interval and register for frame events ThrowIfFailed(m_d3dDevice->SetFrameIntervalX( dxgiOutput.Get(), D3D12XBOX_FRAME_INTERVAL_60_HZ, 2 /* Allow 2 frames of latency */, D3D12XBOX_FRAME_INTERVAL_FLAG_NONE)); ThrowIfFailed(m_d3dDevice->ScheduleFrameEventX( D3D12XBOX_FRAME_EVENT_ORIGIN, 0U, nullptr, D3D12XBOX_SCHEDULE_FRAME_EVENT_FLAG_NONE)); } #else // This method acquires the first available hardware adapter that supports Direct3D 12. // If no such adapter can be found, try WARP. Otherwise throw an exception. void DeviceResources::GetAdapter(IDXGIAdapter1** ppAdapter) { *ppAdapter = nullptr; ComPtr adapter; for (UINT adapterIndex = 0; SUCCEEDED(m_dxgiFactory->EnumAdapterByGpuPreference( adapterIndex, DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE, IID_PPV_ARGS(adapter.ReleaseAndGetAddressOf()))); adapterIndex++) { DXGI_ADAPTER_DESC1 desc; ThrowIfFailed(adapter->GetDesc1(&desc)); if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) { // Don't select the Basic Render Driver adapter. continue; } // Check to see if the adapter supports Direct3D 12, but don't create the actual device yet. if (SUCCEEDED(D3D12CreateDevice(adapter.Get(), D3D_FEATURE_LEVEL_11_0, _uuidof(ID3D12Device), nullptr))) { #ifdef _DEBUG wchar_t buff[256] = {}; swprintf_s(buff, L"Direct3D Adapter (%u): VID:%04X, PID:%04X - %ls\n", adapterIndex, desc.VendorId, desc.DeviceId, desc.Description); OutputDebugStringW(buff); #endif break; } } #if !defined(NDEBUG) if (!adapter) { // Try WARP12 instead if (FAILED(m_dxgiFactory->EnumWarpAdapter(IID_PPV_ARGS(adapter.ReleaseAndGetAddressOf())))) { throw std::exception("WARP12 not available. Enable the 'Graphics Tools' optional feature"); } OutputDebugStringA("Direct3D Adapter - WARP12\n"); } #endif if (!adapter) { throw std::exception("No Direct3D 12 device found"); } *ppAdapter = adapter.Detach(); } #endif ================================================ FILE: Tests/GDK/APIRunner.GDK/DeviceResources.h ================================================ // // DeviceResources.h - A wrapper for the Direct3D 12/12.X device and swapchain // #pragma once namespace DX { // Provides an interface for an application that owns DeviceResources to be notified of the device being lost or created. interface IDeviceNotify { virtual void OnDeviceLost() = 0; virtual void OnDeviceRestored() = 0; protected: ~IDeviceNotify() = default; }; // Controls all the DirectX device resources. class DeviceResources { public: DeviceResources(DXGI_FORMAT backBufferFormat = DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT depthBufferFormat = DXGI_FORMAT_D32_FLOAT, UINT backBufferCount = 2) noexcept(false); ~DeviceResources(); void CreateDeviceResources(); void CreateWindowSizeDependentResources(); void SetWindow(HWND window, int width, int height); bool WindowSizeChanged(int width, int height); void HandleDeviceLost(); void RegisterDeviceNotify(IDeviceNotify* deviceNotify) { m_deviceNotify = deviceNotify; } void SetWindow(HWND window) { m_window = window; } void Prepare(D3D12_RESOURCE_STATES beforeState = D3D12_RESOURCE_STATE_PRESENT); void Present(D3D12_RESOURCE_STATES beforeState = D3D12_RESOURCE_STATE_RENDER_TARGET); void Suspend(); void Resume(); void WaitForGpu() noexcept; // Device Accessors. RECT GetOutputSize() const { return m_outputSize; } // Direct3D Accessors. ID3D12Device* GetD3DDevice() const { return m_d3dDevice.Get(); } #ifndef _GAMING_XBOX IDXGISwapChain3* GetSwapChain() const { return m_swapChain.Get(); } IDXGIFactory6* GetDXGIFactory() const { return m_dxgiFactory.Get(); } #endif D3D_FEATURE_LEVEL GetDeviceFeatureLevel() const { return m_d3dFeatureLevel; } ID3D12Resource* GetRenderTarget() const { return m_renderTargets[m_backBufferIndex].Get(); } ID3D12Resource* GetDepthStencil() const { return m_depthStencil.Get(); } ID3D12CommandQueue* GetCommandQueue() const { return m_commandQueue.Get(); } ID3D12CommandAllocator* GetCommandAllocator() const { return m_commandAllocators[m_backBufferIndex].Get(); } ID3D12GraphicsCommandList* GetCommandList() const { return m_commandList.Get(); } DXGI_FORMAT GetBackBufferFormat() const { return m_backBufferFormat; } DXGI_FORMAT GetDepthBufferFormat() const { return m_depthBufferFormat; } D3D12_VIEWPORT GetScreenViewport() const { return m_screenViewport; } D3D12_RECT GetScissorRect() const { return m_scissorRect; } UINT GetCurrentFrameIndex() const { return m_backBufferIndex; } UINT GetBackBufferCount() const { return m_backBufferCount; } CD3DX12_CPU_DESCRIPTOR_HANDLE GetRenderTargetView() const { return CD3DX12_CPU_DESCRIPTOR_HANDLE( m_rtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(), static_cast(m_backBufferIndex), m_rtvDescriptorSize); } CD3DX12_CPU_DESCRIPTOR_HANDLE GetDepthStencilView() const { return CD3DX12_CPU_DESCRIPTOR_HANDLE(m_dsvDescriptorHeap->GetCPUDescriptorHandleForHeapStart()); } private: void MoveToNextFrame(); #ifdef _GAMING_XBOX void RegisterFrameEvents(); #else void GetAdapter(IDXGIAdapter1** ppAdapter); #endif static const size_t MAX_BACK_BUFFER_COUNT = 3; UINT m_backBufferIndex; // Direct3D objects. Microsoft::WRL::ComPtr m_d3dDevice; Microsoft::WRL::ComPtr m_commandQueue; Microsoft::WRL::ComPtr m_commandList; Microsoft::WRL::ComPtr m_commandAllocators[MAX_BACK_BUFFER_COUNT]; // Swap chain objects. #ifndef _GAMING_XBOX Microsoft::WRL::ComPtr m_dxgiFactory; Microsoft::WRL::ComPtr m_swapChain; #endif Microsoft::WRL::ComPtr m_renderTargets[MAX_BACK_BUFFER_COUNT]; Microsoft::WRL::ComPtr m_depthStencil; // Presentation fence objects. Microsoft::WRL::ComPtr m_fence; UINT64 m_fenceValues[MAX_BACK_BUFFER_COUNT]; Microsoft::WRL::Wrappers::Event m_fenceEvent; #ifdef _GAMING_XBOX D3D12XBOX_FRAME_PIPELINE_TOKEN m_framePipelineToken; #endif // Direct3D rendering objects. Microsoft::WRL::ComPtr m_rtvDescriptorHeap; Microsoft::WRL::ComPtr m_dsvDescriptorHeap; UINT m_rtvDescriptorSize; D3D12_VIEWPORT m_screenViewport; D3D12_RECT m_scissorRect; // Direct3D properties. DXGI_FORMAT m_backBufferFormat; DXGI_FORMAT m_depthBufferFormat; UINT m_backBufferCount; // Cached device properties. HWND m_window; D3D_FEATURE_LEVEL m_d3dFeatureLevel; RECT m_outputSize; // The IDeviceNotify can be held directly as it owns the DeviceResources. IDeviceNotify* m_deviceNotify; }; } ================================================ FILE: Tests/GDK/APIRunner.GDK/Kits/ATGTK/ATGColors.h ================================================ //-------------------------------------------------------------------------------------- // File: ATGColors.h // // Definitions of the standard ATG color palette. // // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A // PARTICULAR PURPOSE. // // Copyright (c) Microsoft Corporation. All rights reserved. //------------------------------------------------------------------------------------- #pragma once #include namespace ATG { namespace Colors { XMGLOBALCONST DirectX::XMVECTORF32 Background = { { { 0.254901975f, 0.254901975f, 0.254901975f, 1.f } } }; // #414141 XMGLOBALCONST DirectX::XMVECTORF32 Green = { { { 0.062745102f, 0.486274511f, 0.062745102f, 1.f } } }; // #107c10 XMGLOBALCONST DirectX::XMVECTORF32 Blue = { { { 0.019607844f, 0.372549027f, 0.803921580f, 1.f } } }; // #055fcd XMGLOBALCONST DirectX::XMVECTORF32 Orange = { { { 0.764705896f, 0.176470593f, 0.019607844f, 1.f } } }; // #c32d05 XMGLOBALCONST DirectX::XMVECTORF32 DarkGrey = { { { 0.200000003f, 0.200000003f, 0.200000003f, 1.f } } }; // #333333 XMGLOBALCONST DirectX::XMVECTORF32 LightGrey = { { { 0.478431374f, 0.478431374f, 0.478431374f, 1.f } } }; // #7a7a7a XMGLOBALCONST DirectX::XMVECTORF32 OffWhite = { { { 0.635294139f, 0.635294139f, 0.635294139f, 1.f } } }; // #a2a2a2 XMGLOBALCONST DirectX::XMVECTORF32 White = { { { 0.980392158f, 0.980392158f, 0.980392158f, 1.f } } }; // #fafafa }; namespace ColorsLinear { XMGLOBALCONST DirectX::XMVECTORF32 Background = { { { 0.052860655f, 0.052860655f, 0.052860655f, 1.f } } }; XMGLOBALCONST DirectX::XMVECTORF32 Green = { { { 0.005181516f, 0.201556236f, 0.005181516f, 1.f } } }; XMGLOBALCONST DirectX::XMVECTORF32 Blue = { { { 0.001517635f, 0.114435382f, 0.610495627f, 1.f } } }; XMGLOBALCONST DirectX::XMVECTORF32 Orange = { { { 0.545724571f, 0.026241219f, 0.001517635f, 1.f } } }; XMGLOBALCONST DirectX::XMVECTORF32 DarkGrey = { { { 0.033104762f, 0.033104762f, 0.033104762f, 1.f } } }; XMGLOBALCONST DirectX::XMVECTORF32 LightGrey = { { { 0.194617808f, 0.194617808f, 0.194617808f, 1.f } } }; XMGLOBALCONST DirectX::XMVECTORF32 OffWhite = { { { 0.361306787f, 0.361306787f, 0.361306787f, 1.f } } }; XMGLOBALCONST DirectX::XMVECTORF32 White = { { { 0.955973506f, 0.955973506f, 0.955973506f, 1.f } } }; }; namespace ColorsHDR { XMGLOBALCONST DirectX::XMVECTORF32 Background = { { { 0.052860655f * 2.f, 0.052860655f * 2.f, 0.052860655f * 2.f, 1.f } } }; XMGLOBALCONST DirectX::XMVECTORF32 Green = { { { 0.005181516f * 2.f, 0.201556236f * 2.f, 0.005181516f * 2.f, 1.f } } }; XMGLOBALCONST DirectX::XMVECTORF32 Blue = { { { 0.001517635f * 2.f, 0.114435382f * 2.f, 0.610495627f * 2.f, 1.f } } }; XMGLOBALCONST DirectX::XMVECTORF32 Orange = { { { 0.545724571f * 2.f, 0.026241219f * 2.f, 0.001517635f * 2.f, 1.f } } }; XMGLOBALCONST DirectX::XMVECTORF32 DarkGrey = { { { 0.033104762f * 2.f, 0.033104762f * 2.f, 0.033104762f * 2.f, 1.f } } }; XMGLOBALCONST DirectX::XMVECTORF32 LightGrey = { { { 0.194617808f * 2.f, 0.194617808f * 2.f, 0.194617808f * 2.f, 1.f } } }; XMGLOBALCONST DirectX::XMVECTORF32 OffWhite = { { { 0.361306787f * 2.f, 0.361306787f * 2.f, 0.361306787f * 2.f, 1.f } } }; XMGLOBALCONST DirectX::XMVECTORF32 White = { { { 0.955973506f * 2.f, 0.955973506f * 2.f, 0.955973506f * 2.f, 1.f } } }; }; } ================================================ FILE: Tests/GDK/APIRunner.GDK/Kits/ATGTK/CSVReader.h ================================================ //-------------------------------------------------------------------------------------- // File: CSVReader.h // // Simple parser for .csv (Comma-Separated Values) files. // // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. //------------------------------------------------------------------------------------- #pragma once #include #include #include namespace DX { class CSVReader { public: enum class Encoding { UTF16, // File is Unicode UTF-16 UTF8, // File is Unicode UTF-8 }; explicit CSVReader(_In_z_ const wchar_t* fileName, Encoding encoding = Encoding::UTF8, bool ignoreComments = false) : m_end(nullptr), m_currentChar(nullptr), m_currentLine(0), m_ignoreComments(ignoreComments) { assert(fileName != 0); #if (_WIN32_WINNT >= 0x0602 /*_WIN32_WINNT_WIN8*/) ScopedHandle hFile(safe_handle(CreateFile2(fileName, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, nullptr))); #else ScopedHandle hFile(safe_handle(CreateFileW(fileName, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, nullptr))); #endif if (!hFile) { throw std::exception("CreateFile"); } FILE_STANDARD_INFO fileInfo; if (!GetFileInformationByHandleEx(hFile.get(), FileStandardInfo, &fileInfo, sizeof(fileInfo))) { throw std::exception("GetFileInformationByHandleEx"); } if (fileInfo.EndOfFile.HighPart > 0 || fileInfo.EndOfFile.LowPart > 0x7ffffffd) { // File is 2 GBs or larger throw std::exception("CSV too large"); } auto data = std::make_unique(fileInfo.EndOfFile.LowPart + 2); DWORD out; if (!ReadFile(hFile.get(), data.get(), fileInfo.EndOfFile.LowPart, &out, nullptr) || out != fileInfo.EndOfFile.LowPart) { throw std::exception("ReadFile"); } // Ensure buffer is nul terminated data[out] = data[out + 1] = '\0'; // Ignore lines in .csv that start with '#' character // Handle text encoding if (encoding == Encoding::UTF16) { m_data.reset(reinterpret_cast(data.release())); m_end = reinterpret_cast(&data[out]); } else { // If we are not UTF16, we have to convert... int cch = ::MultiByteToWideChar(CP_UTF8, 0, reinterpret_cast(data.get()), -1, nullptr, 0); if (cch <= 0) { throw std::exception("MultiByteToWideChar"); } m_data.reset(new wchar_t[size_t(cch)]); int result = ::MultiByteToWideChar(CP_UTF8, 0, reinterpret_cast(data.get()), -1, m_data.get(), cch); if (result <= 0) { m_data.reset(); throw std::exception("MultiByteToWideChar"); } m_end = &m_data[size_t(result - 1)]; } // Locate the start of lines bool newline = true; for (const wchar_t* ptr = m_data.get(); *ptr != 0 && ptr < m_end; ) { if (*ptr == '\n' || *ptr == '\r') { ++ptr; newline = true; } else if (*ptr == '#' && m_ignoreComments && newline) { // Skip to CR for (; *ptr != 0 && *ptr != '\n' && ptr < m_end; ++ptr) {} } else if (*ptr == '"') { if (newline) { m_lines.push_back(ptr); newline = false; } // Skip to next " (skipping "" escapes) for (ptr++; *ptr != 0 && ptr < m_end; ++ptr) { if (*ptr == '"') { ++ptr; if (*ptr != '"') break; } } } else if (newline) { m_lines.push_back(ptr); newline = false; ++ptr; } else ++ptr; } TopOfFile(); } CSVReader(const CSVReader&) = delete; CSVReader& operator=(const CSVReader&) = delete; // Return number of lines of data in CSV size_t GetRecordCount() const { return m_lines.size(); } // Check for end of file bool EndOfFile() const { return m_currentChar == nullptr; } // Return current record number (0-based) size_t RecordIndex() const { return m_currentLine; } // Set to top of file void TopOfFile() { m_currentChar = (m_lines.empty()) ? nullptr : m_lines[0]; m_currentLine = 0; } // Start processing next record (returns false when out of data) bool NextRecord() { if (!m_currentChar) return false; if (++m_currentLine >= m_lines.size()) { m_currentChar = nullptr; return false; } m_currentChar = m_lines[m_currentLine]; return true; } // Get next item in record (returns false when reached end of record) bool NextItem(_Out_writes_(maxstr) wchar_t* str, _In_ size_t maxstr) { if (!str || !m_currentChar || !maxstr) return false; *str = L'\0'; const wchar_t* end = ((m_currentLine + 1) >= m_lines.size()) ? m_end : (m_lines[(m_currentLine + 1)]); if (m_currentChar >= end) return false; wchar_t* dest = str; wchar_t* edest = str + maxstr; for (const wchar_t* ptr = m_currentChar; ; ) { if (*ptr == 0 || ptr >= end || *ptr == '\n' || *ptr == '\r') { m_currentChar = end; break; } else if (*ptr == ',') { m_currentChar = ptr + 1; break; } else if (*ptr == '\t' || *ptr == ' ') { // Whitespace ++ptr; } else if (*ptr == '"') { // Copy from " to ", respecting "" as double-quotes for (ptr++; *ptr != 0 && ptr < end; ++ptr) { if (*ptr == '"') { ++ptr; if (*ptr != '"') break; else if (dest < edest) *(dest++) = '"'; } else if (dest < edest) *(dest++) = *ptr; } } else { for (; *ptr != 0 && ptr < end; ++ptr) { if (*ptr == '\n' || *ptr == '\r' || *ptr == ',') break; else if (dest < edest) *(dest++) = *ptr; } } } if (dest < edest) *dest = 0; return true; } template bool NextItem(wchar_t(&name)[TNameLength]) { return NextItem(name, TNameLength); } private: struct handle_closer { void operator()(HANDLE h) { if (h) CloseHandle(h); } }; typedef std::unique_ptr ScopedHandle; inline HANDLE safe_handle(HANDLE h) { return (h == INVALID_HANDLE_VALUE) ? nullptr : h; } std::unique_ptr m_data; const wchar_t* m_end; const wchar_t* m_currentChar; size_t m_currentLine; bool m_ignoreComments; std::vector m_lines; }; } ================================================ FILE: Tests/GDK/APIRunner.GDK/Kits/ATGTK/ControllerFont.h ================================================ //-------------------------------------------------------------------------------------- // File: ControllerFont.h // // Class for compositing text with Xbox controller font button sprites // // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. //------------------------------------------------------------------------------------- #pragma once #include "SpriteBatch.h" #include "SpriteFont.h" namespace DX { enum class ControllerFont : wchar_t { LeftThumb = L' ', DPad = L'!', RightThumb = L'\"', View = L'#', Nexus = L'$', Menu = L'%', XButton = L'&', AButton = L'\'', YButton = L'(', BButton = L')', RightShoulder = L'*', RightTrigger = L'+', LeftTrigger = L',', LeftShoulder = L'-', }; inline void XM_CALLCONV DrawControllerString(_In_ DirectX::SpriteBatch* spriteBatch, _In_ DirectX::SpriteFont* textFont, _In_ DirectX::SpriteFont* butnFont, _In_z_ wchar_t const* text, DirectX::XMFLOAT2 const& position, DirectX::FXMVECTOR color = DirectX::Colors::White, float scale = 1) { using namespace DirectX; size_t textLen = wcslen(text); if (textLen >= 4096) { throw std::out_of_range("String is too long"); } float buttonHeight = butnFont->GetLineSpacing(); float buttonScale = (textFont->GetLineSpacing() * scale) / buttonHeight; float offsetY = buttonScale / 2.f; size_t j = 0; wchar_t strBuffer[4096] = {}; bool buttonText = false; XMFLOAT2 outPos = position; for (size_t ch = 0; ch < textLen; ++ch) { if (buttonText) { strBuffer[j++] = text[ch]; if (text[ch] == L']') { wchar_t button[2] = {}; if (_wcsicmp(strBuffer, L"[A]") == 0) { *button = static_cast(ControllerFont::AButton); } else if (_wcsicmp(strBuffer, L"[B]") == 0) { *button = static_cast(ControllerFont::BButton); } else if (_wcsicmp(strBuffer, L"[X]") == 0) { *button = static_cast(ControllerFont::XButton); } else if (_wcsicmp(strBuffer, L"[Y]") == 0) { *button = static_cast(ControllerFont::YButton); } else if (_wcsicmp(strBuffer, L"[DPad]") == 0) { *button = static_cast(ControllerFont::DPad); } else if (_wcsicmp(strBuffer, L"[View]") == 0) { *button = static_cast(ControllerFont::View); } else if (_wcsicmp(strBuffer, L"[Menu]") == 0) { *button = static_cast(ControllerFont::Menu); } else if (_wcsicmp(strBuffer, L"[Nexus]") == 0) { *button = static_cast(ControllerFont::Nexus); } else if (_wcsicmp(strBuffer, L"[RThumb]") == 0) { *button = static_cast(ControllerFont::RightThumb); } else if (_wcsicmp(strBuffer, L"[LThumb]") == 0) { *button = static_cast(ControllerFont::LeftThumb); } else if (_wcsicmp(strBuffer, L"[RB]") == 0) { *button = static_cast(ControllerFont::RightShoulder); } else if (_wcsicmp(strBuffer, L"[LB]") == 0) { *button = static_cast(ControllerFont::LeftShoulder); } else if (_wcsicmp(strBuffer, L"[RT]") == 0) { *button = static_cast(ControllerFont::RightTrigger); } else if (_wcsicmp(strBuffer, L"[LT]") == 0) { *button = static_cast(ControllerFont::LeftTrigger); } if (*button) { float bsize = XMVectorGetX(butnFont->MeasureString(button)); float offsetX = (bsize * buttonScale / 2.f); outPos.x += offsetX; outPos.y -= offsetY; butnFont->DrawString(spriteBatch, button, outPos, Colors::White, 0.f, XMFLOAT2(0.f, 0.f), XMFLOAT2(buttonScale, buttonScale)); outPos.x += bsize * buttonScale; outPos.y += offsetY; } memset(strBuffer, 0, sizeof(strBuffer)); j = 0; buttonText = false; } } else { switch (text[ch]) { case '\r': break; case '[': if (*strBuffer) { textFont->DrawString(spriteBatch, strBuffer, outPos, color, 0.f, XMFLOAT2(0.f, 0.f), XMFLOAT2(scale, scale)); outPos.x += XMVectorGetX(textFont->MeasureString(strBuffer)) * scale; memset(strBuffer, 0, sizeof(strBuffer)); j = 0; } buttonText = true; *strBuffer = L'['; ++j; break; case '\n': if (*strBuffer) { textFont->DrawString(spriteBatch, strBuffer, outPos, color, 0.f, XMFLOAT2(0.f, 0.f), XMFLOAT2(scale, scale)); memset(strBuffer, 0, sizeof(strBuffer)); j = 0; } outPos.x = position.x; outPos.y += textFont->GetLineSpacing() * scale; break; default: strBuffer[j++] = text[ch]; break; } } } if (*strBuffer) { textFont->DrawString(spriteBatch, strBuffer, outPos, color, 0.f, XMFLOAT2(0.f, 0.f), XMFLOAT2(scale, scale)); } } inline RECT XM_CALLCONV MeasureControllerDrawBounds(_In_ DirectX::SpriteFont* textFont, _In_ DirectX::SpriteFont* butnFont, _In_z_ wchar_t const* text, DirectX::XMFLOAT2 const& position, float scale = 1) { using namespace DirectX; size_t textLen = wcslen(text); if (textLen >= 4096) { throw std::out_of_range("String is too long"); } float buttonHeight = butnFont->GetLineSpacing(); float buttonScale = (textFont->GetLineSpacing() * scale) / buttonHeight; float offsetY = buttonScale / 2.f; size_t j = 0; wchar_t strBuffer[4096] = {}; bool buttonText = false; XMFLOAT2 outPos = position; RECT result = { LONG_MAX, LONG_MAX, 0, 0 }; for (size_t ch = 0; ch < textLen; ++ch) { if (buttonText) { strBuffer[j++] = text[ch]; if (text[ch] == L']') { wchar_t button[2] = {}; if (_wcsicmp(strBuffer, L"[A]") == 0) { *button = static_cast(ControllerFont::AButton); } else if (_wcsicmp(strBuffer, L"[B]") == 0) { *button = static_cast(ControllerFont::BButton); } else if (_wcsicmp(strBuffer, L"[X]") == 0) { *button = static_cast(ControllerFont::XButton); } else if (_wcsicmp(strBuffer, L"[Y]") == 0) { *button = static_cast(ControllerFont::YButton); } else if (_wcsicmp(strBuffer, L"[DPad]") == 0) { *button = static_cast(ControllerFont::DPad); } else if (_wcsicmp(strBuffer, L"[View]") == 0) { *button = static_cast(ControllerFont::View); } else if (_wcsicmp(strBuffer, L"[Menu]") == 0) { *button = static_cast(ControllerFont::Menu); } else if (_wcsicmp(strBuffer, L"[Nexus]") == 0) { *button = static_cast(ControllerFont::Nexus); } else if (_wcsicmp(strBuffer, L"[RThumb]") == 0) { *button = static_cast(ControllerFont::RightThumb); } else if (_wcsicmp(strBuffer, L"[LThumb]") == 0) { *button = static_cast(ControllerFont::LeftThumb); } else if (_wcsicmp(strBuffer, L"[RB]") == 0) { *button = static_cast(ControllerFont::RightShoulder); } else if (_wcsicmp(strBuffer, L"[LB]") == 0) { *button = static_cast(ControllerFont::LeftShoulder); } else if (_wcsicmp(strBuffer, L"[RT]") == 0) { *button = static_cast(ControllerFont::RightTrigger); } else if (_wcsicmp(strBuffer, L"[LT]") == 0) { *button = static_cast(ControllerFont::LeftTrigger); } if (*button) { float bsize = XMVectorGetX(butnFont->MeasureString(button)); float offsetX = (bsize * buttonScale / 2.f); if (outPos.x < float(result.left)) result.left = long(outPos.x); if (outPos.y < float(result.top)) result.top = long(outPos.y); outPos.x += offsetX; outPos.y -= offsetY; if (outPos.x < float(result.left)) result.left = long(outPos.x); if (outPos.y < float(result.top)) result.top = long(outPos.y); outPos.x += bsize * buttonScale; outPos.y += offsetY; if (float(result.right) < outPos.x) result.right = long(outPos.x); if (float(result.bottom) < outPos.y) result.bottom = long(outPos.y); } memset(strBuffer, 0, sizeof(strBuffer)); j = 0; buttonText = false; } } else { switch (text[ch]) { case '\r': break; case '[': if (*strBuffer) { if (outPos.x < float(result.left)) result.left = long(outPos.x); if (outPos.y < float(result.top)) result.top = long(outPos.y); outPos.x += XMVectorGetX(textFont->MeasureString(strBuffer)) * scale; if (float(result.right) < outPos.x) result.right = long(outPos.x); if (float(result.bottom) < outPos.y) result.bottom = long(outPos.y); memset(strBuffer, 0, sizeof(strBuffer)); j = 0; } buttonText = true; *strBuffer = L'['; ++j; break; case '\n': if (*strBuffer) { if (outPos.x < float(result.left)) result.left = long(outPos.x); if (outPos.y < float(result.top)) result.top = long(outPos.y); outPos.x += XMVectorGetX(textFont->MeasureString(strBuffer)) * scale; if (float(result.right) < outPos.x) result.right = long(outPos.x); if (float(result.bottom) < outPos.y) result.bottom = long(outPos.y); memset(strBuffer, 0, sizeof(strBuffer)); j = 0; } outPos.x = position.x; outPos.y += textFont->GetLineSpacing() * scale; break; default: strBuffer[j++] = text[ch]; break; } } } if (*strBuffer) { if (outPos.x < float(result.left)) result.left = long(outPos.x); if (outPos.y < float(result.top)) result.top = long(outPos.y); outPos.x += XMVectorGetX(textFont->MeasureString(strBuffer)) * scale; if (float(result.right) < outPos.x) result.right = long(outPos.x); if (float(result.bottom) < outPos.y) result.bottom = long(outPos.y); } if (result.left == LONG_MAX) { result.left = 0; result.top = 0; } return result; } } ================================================ FILE: Tests/GDK/APIRunner.GDK/Kits/ATGTK/FindMedia.h ================================================ //-------------------------------------------------------------------------------------- // File: FindMedia.h // // Helper function to find the location of a media file for Windows desktop apps // since they lack appx packaging support. // // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. //------------------------------------------------------------------------------------- #pragma once #include #include namespace DX { inline void FindMediaFile( _Out_writes_(cchDest) wchar_t* strDestPath, _In_ int cchDest, _In_z_ const wchar_t* strFilename, _In_opt_ const wchar_t* const * searchFolders = nullptr) { if (!strFilename || strFilename[0] == 0 || !strDestPath || cchDest < 10) throw std::invalid_argument("FindMediaFile"); // Check CWD for quick out wcscpy_s(strDestPath, size_t(cchDest), strFilename); if (GetFileAttributesW(strDestPath) != 0xFFFFFFFF) return; // Search CWD with folder names static const wchar_t* s_defSearchFolders[] = { L"Assets", L"Media", L"Media\\Textures", L"Media\\Fonts", L"Media\\Meshes", L"Media\\PBR", L"Media\\CubeMaps", L"Media\\HDR", L"Media\\Sounds", L"Media\\Videos", 0 }; if (!searchFolders) searchFolders = s_defSearchFolders; wchar_t strFullFileName[MAX_PATH] = {}; for (const wchar_t* const * searchFolder = searchFolders; *searchFolder != 0; ++searchFolder) { swprintf_s(strFullFileName, MAX_PATH, L"%ls\\%ls", *searchFolder, strFilename); if (GetFileAttributesW(strFullFileName) != 0xFFFFFFFF) { wcscpy_s(strDestPath, size_t(cchDest), strFullFileName); return; } } // Get the exe name, and exe path wchar_t strExePath[MAX_PATH] = {}; GetModuleFileNameW(nullptr, strExePath, MAX_PATH); strExePath[MAX_PATH - 1] = 0; // Search all parent directories starting at .\ and using strFilename as the leaf name wchar_t strLeafName[MAX_PATH] = {}; wcscpy_s(strLeafName, MAX_PATH, strFilename); wchar_t strFullPath[MAX_PATH] = {}; wchar_t strSearch[MAX_PATH] = {}; wchar_t* strFilePart = nullptr; GetFullPathNameW(strExePath, MAX_PATH, strFullPath, &strFilePart); while (strFilePart && *strFilePart != '\0') { swprintf_s(strFullFileName, MAX_PATH, L"%ls\\%ls", strFullPath, strLeafName); if (GetFileAttributesW(strFullFileName) != 0xFFFFFFFF) { wcscpy_s(strDestPath, size_t(cchDest), strFullFileName); return; } for (const wchar_t* const * searchFolder = searchFolders; *searchFolder != 0; ++searchFolder) { swprintf_s(strFullFileName, MAX_PATH, L"%ls\\%ls\\%ls", strFullPath, *searchFolder, strLeafName); if (GetFileAttributesW(strFullFileName) != 0xFFFFFFFF) { wcscpy_s(strDestPath, size_t(cchDest), strFullFileName); return; } } swprintf_s(strSearch, MAX_PATH, L"%ls\\..", strFullPath); GetFullPathNameW(strSearch, MAX_PATH, strFullPath, &strFilePart); } // On failure, return the file as the path but also throw an error wcscpy_s(strDestPath, size_t(cchDest), strFilename); throw std::exception("File not found"); } } ================================================ FILE: Tests/GDK/APIRunner.GDK/Kits/ATGTK/Json.h ================================================ //-------------------------------------------------------------------------------------- // Json.h // // Header to include the JSON serializer/deserializer found @ https://github.com/nlohmann/json // // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A // PARTICULAR PURPOSE. // // Advanced Technology Group (ATG) // Copyright (C) Microsoft Corporation. All rights reserved. //-------------------------------------------------------------------------------------- #pragma once #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wcovered-switch-default" #endif #pragma warning(push) #pragma warning(disable : 4061) #include "json/json.hpp" using json = nlohmann::json; #pragma warning(pop) #ifdef __clang__ #pragma clang diagnostic pop #endif ================================================ FILE: Tests/GDK/APIRunner.GDK/Kits/ATGTK/SampleGUI.cpp ================================================ //-------------------------------------------------------------------------------------- // File: SampleGUI.cpp // // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A // PARTICULAR PURPOSE. // // Copyright (c) Microsoft Corporation. All rights reserved. //------------------------------------------------------------------------------------- #include "pch.h" #include "SampleGUI.h" #include #include #include #include #include #include #include #include "Effects.h" #include "SpriteBatch.h" #include "SpriteFont.h" #if defined(__d3d12_h__) || defined(__d3d12_x_h__) || defined(__XBOX_D3D12_X__) #include "CommonStates.h" #include "DirectXHelpers.h" #include "RenderTargetState.h" #endif #if !defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP) #include "FindMedia.h" #endif // Other required ATG Tool Kit components #include "ControllerFont.h" #include "CSVReader.h" using namespace DirectX; using namespace ATG; using Microsoft::WRL::ComPtr; namespace { const float c_HoldTimeStart = 1.f; const float c_HoldTimeRepeat = .1f; const float c_KeypressRepeatDelay = .05f; const long c_ScrollWidth = 16; const long c_MinThumbSize = 8; const long c_BorderSize = 6; const long c_MarginSize = 5; inline void DebugTrace(_In_z_ _Printf_format_string_ const char* format, ...) { #ifdef _DEBUG va_list args; va_start(args, format); char buff[1024] = {}; vsprintf_s(buff, format, args); OutputDebugStringA(buff); va_end(args); #else UNREFERENCED_PARAMETER(format); #endif } IControl* InitFocus(std::vector& controls) { IControl *firstCtrl = nullptr; IControl* defaultCtrl = nullptr; for (IControl* it : controls) { assert(it != nullptr); _Analysis_assume_(it != nullptr); if (!it->CanFocus()) continue; if (!firstCtrl) { firstCtrl = it; } if (!defaultCtrl && it->DefaultFocus()) { defaultCtrl = it; break; } } return (defaultCtrl) ? defaultCtrl : firstCtrl; } IControl* NextFocus(_In_opt_ IControl* currentFocus, std::vector& controls) { if (!currentFocus) return nullptr; auto it = std::find(controls.cbegin(), controls.cend(), currentFocus); if (it == controls.cend()) throw std::exception("NextFocus"); for (;;) { ++it; if (it == controls.cend()) it = controls.cbegin(); if (currentFocus == *it) return currentFocus; if ( (*it)->CanFocus()) { currentFocus->OnFocus(false); (*it)->OnFocus(true); return *it; } } } IControl* PrevFocus(_In_opt_ IControl* currentFocus, std::vector& controls) { if (!currentFocus) return nullptr; auto it = std::find(controls.cbegin(), controls.cend(), currentFocus); if (it == controls.cend()) throw std::exception("PrevFocus"); for (;;) { if (it == controls.cbegin()) it = controls.end() - 1; else --it; if (currentFocus == *it) return currentFocus; if ((*it)->CanFocus()) { currentFocus->OnFocus(false); (*it)->OnFocus(true); return *it; } } } IControl* MouseFocus(int x, int y, _In_opt_ IControl* currentFocus, std::vector& controls) { if (!currentFocus) return nullptr; for (auto it : controls) { assert(it != nullptr); _Analysis_assume_(it != nullptr); if (it->CanFocus() && it->Contains(x,y)) { if (currentFocus != it) { if (currentFocus) { currentFocus->OnFocus(false); } it->OnFocus(true); return it; } } } return currentFocus; } IControl* HotKeyFocus(_In_opt_ IControl* currentFocus, std::vector& controls, const Keyboard::KeyboardStateTracker& kbstate) { for (auto it : controls) { assert(it != nullptr); _Analysis_assume_(it != nullptr); unsigned hotkey = it->GetHotKey(); if (hotkey != 0) { if (kbstate.IsKeyPressed(static_cast(hotkey))) { if (currentFocus == it) { return it; } else if (it->CanFocus()) { if (currentFocus) { currentFocus->OnFocus(false); it->OnFocus(true); } return it; } } } } return nullptr; } IControl* SetFocusCtrl(_In_opt_ IControl* currentFocus, _In_ IControl* newFocus) { if (!newFocus) return currentFocus; if (!newFocus->CanFocus()) { DebugTrace("WARNING: SetFocus control (%u) cannot be focus, ignored.\n", newFocus->GetId()); return currentFocus; } if (currentFocus) { currentFocus->OnFocus(false); } newFocus->OnFocus(true); return newFocus; } void ControlSelected(_In_opt_ IControl *ctrl, _In_ IPanel* panel) { if (!ctrl) return; if (ctrl->OnSelected(panel)) { panel->Close(); } } void TrimTrailingWhitespace(_Inout_z_ wchar_t* str) { size_t len = wcslen(str); for(wchar_t *ptr = str + len; len > 0; --len, --ptr) { if (!iswspace( *(ptr-1) )) { *ptr = 0; break; } } } std::wstring WordWrap( _In_z_ const wchar_t* text, _In_ SpriteFont* font, const RECT& rect, std::vector* lineStarts = nullptr) { if (lineStarts) { lineStarts->clear(); if (*text) lineStarts->push_back(0); } std::wstring str; str.reserve(wcslen(text)); size_t line_start = 0; size_t last_line = 0; size_t last_word = 0; size_t extra = 0; const wchar_t *ptr = text; XMFLOAT2 pos(float(rect.left), float(rect.top)); wchar_t prevch = 0; while (*ptr != L'\0') { const wchar_t ch = *ptr; str.push_back(ch); if (iswspace(ch)) { last_word = size_t(ptr - text); } else if (prevch == L'-') { last_word = size_t(ptr - text - 1); } RECT textrect = font->MeasureDrawBounds(str.c_str() + line_start, pos); if (textrect.right > rect.right) { if (last_word > last_line) { str.erase(str.cbegin() + ptrdiff_t(last_word + extra), str.cend()); str.push_back(L'\n'); ++extra; last_line = last_word; ptr = text + last_word + 1; } else { str.erase(str.cend() - 1); str.push_back(L'\n'); ++extra; last_line = last_word = str.length() - extra; } line_start = last_line + extra; if (lineStarts) { lineStarts->push_back(last_line + extra); } prevch = 0; continue; } ++ptr; prevch = ch; } return str; } void HandleEscapeCharacters(_Inout_z_ wchar_t* str) { for (wchar_t*ptr = str; *ptr != 0; ++ptr) { if (*ptr == L'|') *ptr = L'\n'; } } unsigned HandleVirtualKeys(_Inout_z_ wchar_t* str) { static const struct Map { const wchar_t* first; unsigned second; } s_map[] = { { L"F1", VK_F1 }, { L"F2", VK_F2 }, { L"F3", VK_F3 }, { L"F4", VK_F4 }, { L"F5", VK_F5 }, { L"F6", VK_F6 }, { L"F7", VK_F7 }, { L"F8", VK_F8 }, { L"F9", VK_F9 }, { L"F10", VK_F10 }, }; for(size_t j = 0; j < _countof(s_map); ++j) { if (_wcsicmp(s_map[j].first, str) == 0) { return s_map[j].second; } } if (*(str + 1) == 0) { return unsigned(*str); } return 0; } bool UpdateScrollBar(RECT& thumbRect, const RECT& trackRect, int position, int start, int end, int pageSize) { assert(pageSize > 0); if (end - start > pageSize) { int height = (trackRect.bottom - trackRect.top); int thumbHeight = std::max( height * pageSize / (end - start), c_MinThumbSize); int maxPos = end - start - pageSize; thumbRect.top = trackRect.top + (position - start) * (height - thumbHeight) / maxPos; thumbRect.bottom = thumbRect.top + thumbHeight; if (thumbRect.top < trackRect.top) thumbRect.top = trackRect.top; if (thumbRect.bottom > trackRect.bottom) thumbRect.bottom = trackRect.bottom; return true; } else { thumbRect.bottom = thumbRect.top; return false; } } void MessageYesNoCancel(IPanel* panel, unsigned id) { assert(panel != 0); assert(panel->GetUser() != 0); auto cb = reinterpret_cast*>(panel->GetUser()); if (id == 1) { (*cb)(true, false); } else if (id == 2) { (*cb)(false, false); } else { (*cb)(false, true); } } } //===================================================================================== // UIManager //===================================================================================== class UIManager::Impl { public: Impl(const UIConfig& config) : #if defined(__d3d12_h__) || defined(__d3d12_x_h__) || defined(__XBOX_D3D12_X__) m_defaultTexDescriptor{}, m_commandList(nullptr), #endif m_fullscreen{}, m_focusPanel(nullptr), m_overlayPanel(nullptr), m_hudPanel(nullptr), m_heldTimer(0), m_mouseLastX(-1), m_mouseLastY(-1), mConfig(config) { if (s_uiManager) { throw std::exception("UIManager is a singleton"); } s_uiManager = this; } ~Impl() { for (auto& it : m_panels) { delete it.second; } m_panels.clear(); m_focusPanel = nullptr; m_overlayPanel = nullptr; m_hudPanel = nullptr; s_uiManager = nullptr; } void LoadLayout(const wchar_t* layoutFile, const wchar_t* imageDir, unsigned offset) { static const wchar_t* s_seps = L" ,;|"; static wchar_t s_text[4096] = {}; DX::CSVReader reader(layoutFile, DX::CSVReader::Encoding::UTF8, true); IPanel* currentPanel = nullptr; if (imageDir) m_layoutImageDir = imageDir; else m_layoutImageDir.clear(); while (!reader.EndOfFile()) { // Each record starts with ITEM,ID,RECTX,RECTY,DX,DY wchar_t item[1024] = {}; if (!reader.NextItem(item)) { reader.NextRecord(); continue; } TrimTrailingWhitespace(item); unsigned id; { // IPanel object ids need to be globally unique wchar_t tmp[16] = {}; if (!reader.NextItem(tmp)) { DebugTrace("ERROR: Expected an id for record %zu\n", reader.RecordIndex() + 1); throw std::exception("LoadLayout"); } id = static_cast(_wtoi(tmp)); } RECT rct = { 0, 0, 0, 0 }; { wchar_t tmp[16] = {}; if (!reader.NextItem(tmp)) { DebugTrace("ERROR: Expected an rectx for record %zu\n", reader.RecordIndex() + 1); throw std::exception("LoadLayout"); } rct.left = _wtoi(tmp); if (!reader.NextItem(tmp)) { DebugTrace("ERROR: Expected an recty for record %zu\n", reader.RecordIndex() + 1); throw std::exception("LoadLayout"); } rct.top = _wtoi(tmp); if (!reader.NextItem(tmp)) { DebugTrace("ERROR: Expected an dx for record %zu\n", reader.RecordIndex() + 1); throw std::exception("LoadLayout"); } rct.right = rct.left + _wtoi(tmp); if (!reader.NextItem(tmp)) { DebugTrace("ERROR: Expected an dy for record %zu\n", reader.RecordIndex() + 1); throw std::exception("LoadLayout"); } rct.bottom = rct.top + _wtoi(tmp); } // Create item if (_wcsicmp(item, L"POPUP") == 0 || _wcsicmp(item, L"CUSTOM_POPUP") == 0) { id += offset; auto it = m_panels.find(id); if (it != m_panels.end()) { DebugTrace("ERROR: Duplicate panel id found [record %zu]\n", reader.RecordIndex() + 1); throw std::exception("LoadLayout"); } unsigned int styleFlags = (_wcsicmp(item, L"CUSTOM_POPUP") == 0) ? c_styleCustomPanel : 0; wchar_t styleStr[128] = {}; if (reader.NextItem(styleStr)) { _wcsupr_s(styleStr); wchar_t* context = nullptr; const wchar_t* tok = wcstok_s(styleStr, s_seps, &context); while (tok) { if (wcscmp(tok, L"EMPHASIS") == 0) { styleFlags |= c_stylePopupEmphasis; } else if (wcscmp(tok, L"SUPPRESS_CANCEL") == 0) { styleFlags |= c_styleSuppressCancel; } tok = wcstok_s(nullptr, s_seps, &context); } } currentPanel = new Popup(rct, styleFlags); m_panels[id] = currentPanel; } else if (_wcsicmp(item, L"HUD") == 0) { id += offset; auto it = m_panels.find(id); if (it != m_panels.end()) { DebugTrace("ERROR: Duplicate panel id found [record %zu]\n", reader.RecordIndex() + 1); throw std::exception("LoadLayout"); } currentPanel = new HUD(rct); m_panels[id] = currentPanel; if (!m_hudPanel) { currentPanel->Show(); } } else if (_wcsicmp(item, L"OVERLAY") == 0 || _wcsicmp(item, L"CUSTOM_OVERLAY") == 0) { id += offset; auto it = m_panels.find(id); if (it != m_panels.end()) { DebugTrace("ERROR: Duplicate panel id found [record %zu]\n", reader.RecordIndex() + 1); throw std::exception("LoadLayout"); } unsigned int styleFlags = (_wcsicmp(item, L"CUSTOM_OVERLAY") == 0) ? c_styleCustomPanel : 0; wchar_t styleStr[128] = {}; if (reader.NextItem(styleStr)) { _wcsupr_s(styleStr); wchar_t *context = nullptr; const wchar_t *tok = wcstok_s(styleStr, s_seps, &context); while (tok) { if (wcscmp(tok, L"SUPPRESS_CANCEL") == 0) { styleFlags |= c_styleSuppressCancel; } tok = wcstok_s(nullptr, s_seps, &context); } } currentPanel = new Overlay(rct, styleFlags); m_panels[id] = currentPanel; } else if (_wcsicmp(item, L"LABEL") == 0) { if (!currentPanel) { DebugTrace("ERROR: LABEL found outside of panel [record %zu]\n", reader.RecordIndex() + 1); throw std::exception("LoadLayout"); } if (!reader.NextItem(s_text)) { DebugTrace("ERROR: LABEL missing text string [record %zu]\n", reader.RecordIndex() + 1); throw std::exception("LoadLayout"); } HandleEscapeCharacters(s_text); unsigned style = 0; XMVECTOR fgColor = Colors::White; XMVECTOR bgColor = Colors::Transparent; wchar_t styleStr[128] = {}; if (reader.NextItem(styleStr)) { _wcsupr_s(styleStr); wchar_t* context = nullptr; const wchar_t* tok = wcstok_s(styleStr, s_seps, &context); while (tok) { if (wcscmp(tok, L"LEFT") == 0) { style |= TextLabel::c_StyleAlignLeft | TextLabel::c_StyleAlignMiddle; } else if (wcscmp(tok, L"CENTER") == 0) { style |= TextLabel::c_StyleAlignCenter | TextLabel::c_StyleAlignMiddle; } else if (wcscmp(tok, L"RIGHT") == 0) { style |= TextLabel::c_StyleAlignRight | TextLabel::c_StyleAlignMiddle; } else if (wcscmp(tok, L"LARGE") == 0) { style |= TextLabel::c_StyleFontLarge; } else if (wcscmp(tok, L"SMALL") == 0) { style |= TextLabel::c_StyleFontSmall; } else if (wcscmp(tok, L"BOLD") == 0) { style |= TextLabel::c_StyleFontBold; } else if (wcscmp(tok, L"ITALIC") == 0) { style |= TextLabel::c_StyleFontItalic; } else if (wcscmp(tok, L"TRANSPARENT") == 0) { style |= TextLabel::c_StyleTransparent; } else if (wcscmp(tok, L"WORDWRAP") == 0) { style |= TextLabel::c_StyleWordWrap; } tok = wcstok_s(nullptr, s_seps, &context); } wchar_t colorStr[32] = {}; if (reader.NextItem(colorStr)) { _wcsupr_s(colorStr); fgColor = LoadColorItem(colorStr, reader.RecordIndex()); } if (reader.NextItem(colorStr)) { _wcsupr_s(colorStr); bgColor = LoadColorItem(colorStr, reader.RecordIndex()); } } if (id) { // check for duplicates if nonzero ids used if (currentPanel->Find(id)) { DebugTrace("ERROR: Duplicate control id found [record %zu]\n", reader.RecordIndex() + 1); throw std::exception("LoadLayout"); } } auto label = new TextLabel(id, s_text, rct, style); label->SetForegroundColor(fgColor); label->SetBackgroundColor(bgColor); currentPanel->Add(label); } else if (_wcsicmp(item, L"LEGEND") == 0) { if (!currentPanel) { DebugTrace("ERROR: LEGEND found outside of panel [record %zu]\n", reader.RecordIndex() + 1); throw std::exception("LoadLayout"); } if (!reader.NextItem(s_text)) { DebugTrace("ERROR: LEGEND missing text string [record %zu]\n", reader.RecordIndex() + 1); throw std::exception("LoadLayout"); } HandleEscapeCharacters(s_text); unsigned style = 0; XMVECTOR fgColor = Colors::White; XMVECTOR bgColor = Colors::Transparent; wchar_t styleStr[128] = {}; if (reader.NextItem(styleStr)) { _wcsupr_s(styleStr); wchar_t* context = nullptr; const wchar_t* tok = wcstok_s(styleStr, s_seps, &context); while (tok) { if (wcscmp(tok, L"LEFT") == 0) { style |= Legend::c_StyleAlignLeft | Legend::c_StyleAlignMiddle; } else if (wcscmp(tok, L"CENTER") == 0) { style |= Legend::c_StyleAlignCenter | Legend::c_StyleAlignMiddle; } else if (wcscmp(tok, L"RIGHT") == 0) { style |= Legend::c_StyleAlignRight | Legend::c_StyleAlignMiddle; } else if (wcscmp(tok, L"LARGE") == 0) { style |= Legend::c_StyleFontLarge; } else if (wcscmp(tok, L"SMALL") == 0) { style |= Legend::c_StyleFontSmall; } else if (wcscmp(tok, L"BOLD") == 0) { style |= Legend::c_StyleFontBold; } else if (wcscmp(tok, L"ITALIC") == 0) { style |= Legend::c_StyleFontItalic; } else if (wcscmp(tok, L"TRANSPARENT") == 0) { style |= Legend::c_StyleTransparent; } tok = wcstok_s(nullptr, s_seps, &context); } wchar_t colorStr[32] = {}; if (reader.NextItem(colorStr)) { _wcsupr_s(colorStr); fgColor = LoadColorItem(colorStr, reader.RecordIndex()); } if (reader.NextItem(colorStr)) { _wcsupr_s(colorStr); bgColor = LoadColorItem(colorStr, reader.RecordIndex()); } } if (id) { // check for duplicates if nonzero ids used if (currentPanel->Find(id)) { DebugTrace("ERROR: Duplicate control id found [record %zu]\n", reader.RecordIndex() + 1); throw std::exception("LoadLayout"); } } auto legend = new Legend(id, s_text, rct, style); legend->SetForegroundColor(fgColor); legend->SetBackgroundColor(bgColor); currentPanel->Add(legend); } else if (_wcsicmp(item, L"IMAGE") == 0) { if (!currentPanel) { DebugTrace("ERROR: IMAGE found outside of panel [record %zu]\n", reader.RecordIndex() + 1); throw std::exception("LoadLayout"); } unsigned imageId = 0; wchar_t imageFile[MAX_PATH] = {}; if (reader.NextItem(imageFile)) { TrimTrailingWhitespace(imageFile); imageId = LoadImageItem(imageFile); } else { DebugTrace("ERROR: IMAGE missing image file name [record %zu]\n", reader.RecordIndex() + 1); throw std::exception("LoadLayout"); } if (id) { // check for duplicates if nonzero ids used if (currentPanel->Find(id)) { DebugTrace("ERROR: Duplicate control id found [record %zu]\n", reader.RecordIndex() + 1); throw std::exception("LoadLayout"); } } currentPanel->Add(new Image(id, imageId, rct)); } else if (_wcsicmp(item, L"BUTTON") == 0 || _wcsicmp(item, L"EXITBUTTON") == 0 || _wcsicmp(item, L"DEFBUTTON") == 0) { if (!currentPanel) { DebugTrace("ERROR: BUTTON found outside of panel [record %zu]\n", reader.RecordIndex() + 1); throw std::exception("LoadLayout"); } if (!reader.NextItem(s_text)) { DebugTrace("ERROR: BUTTON missing text string [record %zu]\n", reader.RecordIndex() + 1); throw std::exception("LoadLayout"); } unsigned hotkey = 0; wchar_t hotkeyStr[64] = {}; unsigned style = 0; bool showBorder = false; bool noFocusColor = false; bool focusOnText = false; XMVECTOR color = Colors::Black; if (reader.NextItem(hotkeyStr)) { TrimTrailingWhitespace(hotkeyStr); hotkey = HandleVirtualKeys(hotkeyStr); // Optional style wchar_t styleStr[128] = {}; if (reader.NextItem(styleStr)) { _wcsupr_s(styleStr); wchar_t* context = nullptr; const wchar_t* tok = wcstok_s(styleStr, s_seps, &context); while (tok) { if (wcscmp(tok, L"LARGE") == 0) { style |= Button::c_StyleFontLarge; } else if (wcscmp(tok, L"SMALL") == 0) { style |= Button::c_StyleFontSmall; } else if (wcscmp(tok, L"BOLD") == 0) { style |= Button::c_StyleFontBold; } else if (wcscmp(tok, L"ITALIC") == 0) { style |= Button::c_StyleFontItalic; } else if (wcscmp(tok, L"TRANSPARENT") == 0) { style |= Button::c_StyleTransparent; } else if (wcscmp(tok, L"BORDER") == 0) { showBorder = true; } else if (wcscmp(tok, L"NO_FOCUS_COLOR") == 0) { noFocusColor = true; } else if (wcscmp(tok, L"FOCUS_ON_TEXT") == 0) { focusOnText = true; } tok = wcstok_s(nullptr, s_seps, &context); } wchar_t colorStr[32] = {}; if (reader.NextItem(colorStr)) { _wcsupr_s(colorStr); color = LoadColorItem(colorStr, reader.RecordIndex()); } } } if (_wcsicmp(item, L"EXITBUTTON") == 0) { style |= Button::c_StyleExit; } else if (_wcsicmp(item, L"DEFBUTTON") == 0) { style |= Button::c_StyleExit | Button::c_StyleDefault; } // check for duplicate id if (currentPanel->Find(id)) { DebugTrace("ERROR: Duplicate control found [record %zu]\n", reader.RecordIndex() + 1); throw std::exception("LoadLayout"); } auto *butn = new Button(id, s_text, rct); butn->SetStyle(style); butn->SetHotKey(hotkey); butn->SetColor(color); butn->ShowBorder(showBorder); butn->NoFocusColor(noFocusColor); butn->FocusOnText(focusOnText); currentPanel->Add(butn); } else if (_wcsicmp(item, L"IMAGEBUTTON") == 0 || _wcsicmp(item, L"EXITIMAGEBUTTON") == 0 || _wcsicmp(item, L"DEFIMAGEBUTTON") == 0) { if (!currentPanel) { DebugTrace("ERROR: IMAGEBUTTON found outside of panel [record %zu]\n", reader.RecordIndex() + 1); throw std::exception("LoadLayout"); } unsigned imageId = 0; wchar_t imageFile[MAX_PATH] = {}; if (reader.NextItem(imageFile)) { TrimTrailingWhitespace(imageFile); imageId = LoadImageItem(imageFile); } else { DebugTrace("ERROR: IMAGEBUTTON missing image file name [record %zu]\n", reader.RecordIndex() + 1); throw std::exception("LoadLayout"); } unsigned hotkey = 0; wchar_t hotkeyStr[64] = {}; unsigned style = 0; if (reader.NextItem(hotkeyStr)) { TrimTrailingWhitespace(hotkeyStr); hotkey = HandleVirtualKeys(hotkeyStr); // Optional style wchar_t styleStr[128] = {}; if (reader.NextItem(styleStr)) { _wcsupr_s(styleStr); wchar_t* context = nullptr; const wchar_t* tok = wcstok_s(styleStr, s_seps, &context); while (tok) { if (wcscmp(tok, L"BACKGROUND") == 0) { style |= ImageButton::c_StyleBackground; } else if (wcscmp(tok, L"TRANSPARENT") == 0) { style |= ImageButton::c_StyleTransparent | ImageButton::c_StyleBackground; } tok = wcstok_s(nullptr, s_seps, &context); } } } if (_wcsicmp(item, L"EXITIMAGEBUTTON") == 0) { style |= ImageButton::c_StyleExit; } else if (_wcsicmp(item, L"DEFIMAGEBUTTON") == 0) { style |= ImageButton::c_StyleExit | ImageButton::c_StyleDefault; } // check for duplicate id if (currentPanel->Find(id)) { DebugTrace("ERROR: Duplicate control found [record %zu]\n", reader.RecordIndex() + 1); throw std::exception("LoadLayout"); } auto *butn = new ImageButton(id, imageId, rct); butn->SetStyle(style); butn->SetHotKey(hotkey); currentPanel->Add(butn); } else if (_wcsicmp(item, L"CHECKBOX") == 0) { if (!currentPanel) { DebugTrace("ERROR: CHECKBOX found outside of panel [record %zu]", reader.RecordIndex() + 1); throw std::exception("LoadLayout"); } if (!reader.NextItem(s_text)) { DebugTrace("ERROR: CHECKBOX missing text string [record %zu]", reader.RecordIndex() + 1); throw std::exception("LoadLayout"); } unsigned style = 0; bool checked = false; { wchar_t styleStr[128] = {}; if (reader.NextItem(styleStr)) { _wcsupr_s(styleStr); wchar_t* context = nullptr; const wchar_t* tok = wcstok_s(styleStr, s_seps, &context); while (tok) { if (wcscmp(tok, L"LARGE") == 0) { style |= CheckBox::c_StyleFontLarge; } else if (wcscmp(tok, L"SMALL") == 0) { style |= CheckBox::c_StyleFontSmall; } else if (wcscmp(tok, L"BOLD") == 0) { style |= CheckBox::c_StyleFontBold; } else if (wcscmp(tok, L"ITALIC") == 0) { style |= CheckBox::c_StyleFontItalic; } else if (wcscmp(tok, L"CHECKED") == 0) { checked = true; } else if (wcscmp(tok, L"TRANSPARENT") == 0) { style |= CheckBox::c_StyleTransparent; } tok = wcstok_s(nullptr, s_seps, &context); } } } // check for duplicate id if (currentPanel->Find(id)) { DebugTrace("ERROR: Duplicate control id found [record %zu]", reader.RecordIndex() + 1); throw std::exception("LoadLayout"); } auto *box = new CheckBox(id, s_text, rct, checked); box->SetStyle(style); currentPanel->Add(box); } else if (_wcsicmp(item, L"SLIDER") == 0) { if (!currentPanel) { DebugTrace("ERROR: SLIDER found outside of panel [record %zu]\n", reader.RecordIndex() + 1); throw std::exception("LoadLayout"); } // check for duplicate id if (currentPanel->Find(id)) { DebugTrace("ERROR: Duplicate control id found [record %zu]\n", reader.RecordIndex() + 1); throw std::exception("LoadLayout"); } unsigned style = 0; { wchar_t styleStr[128] = {}; if (reader.NextItem(styleStr)) { _wcsupr_s(styleStr); wchar_t* context = nullptr; const wchar_t* tok = wcstok_s(styleStr, s_seps, &context); while (tok) { if (wcscmp(tok, L"TRANSPARENT") == 0) { style |= Slider::c_StyleTransparent; } tok = wcstok_s(nullptr, s_seps, &context); } } } auto slider = new Slider(id, rct); slider->SetStyle(style); currentPanel->Add(slider); } else if (_wcsicmp(item, L"PROGRESSBAR") == 0) { if (!currentPanel) { DebugTrace("ERROR: PROGRESSBAR found outside of panel [record %zu]\n", reader.RecordIndex() + 1); throw std::exception("LoadLayout"); } // check for duplicate id if (currentPanel->Find(id)) { DebugTrace("ERROR: Duplicate control id found [record %zu]\n", reader.RecordIndex() + 1); throw std::exception("LoadLayout"); } currentPanel->Add(new ProgressBar(id, rct)); } else if (_wcsicmp(item, L"TEXTLIST") == 0) { if (!currentPanel) { DebugTrace("ERROR: TEXTLIST found outside of panel [record %zu]\n", reader.RecordIndex() + 1); throw std::exception("LoadLayout"); } // check for duplicate id if (currentPanel->Find(id)) { DebugTrace("ERROR: Duplicate control id found [record %zu]\n", reader.RecordIndex() + 1); throw std::exception("LoadLayout"); } int itemHeight = 0; wchar_t itemHeightStr[64] = {}; unsigned style = 0; if (reader.NextItem(itemHeightStr)) { itemHeight = _wtoi(itemHeightStr); wchar_t styleStr[128] = {}; if (reader.NextItem(styleStr)) { _wcsupr_s(styleStr); wchar_t* context = nullptr; const wchar_t* tok = wcstok_s(styleStr, s_seps, &context); while (tok) { if (wcscmp(tok, L"LARGE") == 0) { style |= ListBox::c_StyleFontLarge; } else if (wcscmp(tok, L"SMALL") == 0) { style |= ListBox::c_StyleFontSmall; } else if (wcscmp(tok, L"BOLD") == 0) { style |= ListBox::c_StyleFontBold; } else if (wcscmp(tok, L"ITALIC") == 0) { style |= ListBox::c_StyleFontItalic; } else if (wcscmp(tok, L"TRANSPARENT") == 0) { style |= ListBox::c_StyleTransparent; } tok = wcstok_s(nullptr, s_seps, &context); } } } currentPanel->Add(new TextList(id, rct, style, itemHeight)); } else if (_wcsicmp(item, L"LISTBOX") == 0) { if (!currentPanel) { DebugTrace("ERROR: LISTBOX found outside of panel [record %zu]\n", reader.RecordIndex() + 1); throw std::exception("LoadLayout"); } // check for duplicate id if (currentPanel->Find(id)) { DebugTrace("ERROR: Duplicate control id found [record %zu]\n", reader.RecordIndex() + 1); throw std::exception("LoadLayout"); } int itemHeight = 0; wchar_t itemHeightStr[64] = {}; unsigned style = 0; if (reader.NextItem(itemHeightStr)) { itemHeight = _wtoi(itemHeightStr); wchar_t styleStr[128] = {}; if (reader.NextItem(styleStr)) { _wcsupr_s(styleStr); wchar_t* context = nullptr; const wchar_t* tok = wcstok_s(styleStr, s_seps, &context); while (tok) { if (wcscmp(tok, L"MULTISELECT") == 0) { style |= ListBox::c_StyleMultiSelection; } else if (wcscmp(tok, L"LARGE") == 0) { style |= ListBox::c_StyleFontLarge; } else if (wcscmp(tok, L"SMALL") == 0) { style |= ListBox::c_StyleFontSmall; } else if (wcscmp(tok, L"BOLD") == 0) { style |= ListBox::c_StyleFontBold; } else if (wcscmp(tok, L"ITALIC") == 0) { style |= ListBox::c_StyleFontItalic; } else if (wcscmp(tok, L"TRANSPARENT") == 0) { style |= ListBox::c_StyleTransparent; } else if (wcscmp(tok, L"SCROLLBAR") == 0) { style |= ListBox::c_StyleScrollBar; } tok = wcstok_s(nullptr, s_seps, &context); } } } currentPanel->Add(new ListBox(id, rct, style, itemHeight)); } else if (_wcsicmp(item, L"TEXTBOX") == 0) { if (!currentPanel) { DebugTrace("ERROR: TEXTBOX found outside of panel [record %zu]\n", reader.RecordIndex() + 1); throw std::exception("LoadLayout"); } if (id) { // check for duplicates if nonzero ids used if (currentPanel->Find(id)) { DebugTrace("ERROR: Duplicate control id found [record %zu]\n", reader.RecordIndex() + 1); throw std::exception("LoadLayout"); } } if (!reader.NextItem(s_text)) { DebugTrace("ERROR: TEXTBOX missing text string [record %zu]\n", reader.RecordIndex() + 1); throw std::exception("LoadLayout"); } HandleEscapeCharacters(s_text); unsigned style = 0; { wchar_t styleStr[128] = {}; if (reader.NextItem(styleStr)) { _wcsupr_s(styleStr); wchar_t* context = nullptr; const wchar_t* tok = wcstok_s(styleStr, s_seps, &context); while (tok) { if (wcscmp(tok, L"LARGE") == 0) { style |= TextBox::c_StyleFontLarge; } else if (wcscmp(tok, L"SMALL") == 0) { style |= TextBox::c_StyleFontSmall; } else if (wcscmp(tok, L"BOLD") == 0) { style |= TextBox::c_StyleFontBold; } else if (wcscmp(tok, L"ITALIC") == 0) { style |= TextBox::c_StyleFontItalic; } else if (wcscmp(tok, L"TRANSPARENT") == 0) { style |= TextBox::c_StyleTransparent; } else if (wcscmp(tok, L"SCROLLBAR") == 0) { style |= TextBox::c_StyleScrollBar; } else if (wcscmp(tok, L"NOBACKGROUND") == 0) { style |= TextBox::c_StyleNoBackground; } tok = wcstok_s(nullptr, s_seps, &context); } } } currentPanel->Add(new TextBox(id, s_text, rct, style)); } reader.NextRecord(); } } bool Update(float elapsedTime, const GamePad::State& pad) { if (m_heldTimer >= 0) { m_heldTimer -= elapsedTime; } m_padButtonState.Update(pad); bool result = false; if (m_focusPanel) { // Focus panel gets input processing result = m_focusPanel->Update(elapsedTime, pad); } for (auto& it : m_panels) { if (it.second == m_overlayPanel || it.second == m_focusPanel) continue; if (it.second->IsVisible()) { it.second->Update(elapsedTime); } } // Overlay gets processed last if (m_overlayPanel) { if (!result) { result = m_overlayPanel->Update(elapsedTime, pad); } else { m_overlayPanel->Update(elapsedTime); } } // m_hudPanel never gets input, but does get Update for time return result; } bool Update(float elapsedTime, Mouse& mouse, Keyboard& kb) { if (m_heldTimer >= 0) { m_heldTimer -= elapsedTime; } auto mstate = mouse.GetState(); m_mouseButtonState.Update(mstate); auto kbstate = kb.GetState(); m_keyboardState.Update(kbstate); bool result = false; if (m_focusPanel) { // Focus panel gets input processing result = m_focusPanel->Update(elapsedTime, mstate, kbstate); } for (auto& it : m_panels) { if (it.second == m_overlayPanel || it.second == m_focusPanel) continue; if (it.second->IsVisible()) { if (!result) { result = it.second->Update(elapsedTime, mstate, kbstate); } else { it.second->Update(elapsedTime); } } } // Overlay is last (it's below all other panels) if (m_overlayPanel) { if (!result) { result = m_overlayPanel->Update(elapsedTime, mstate, kbstate); } else { m_overlayPanel->Update(elapsedTime); } } if (mstate.positionMode == Mouse::MODE_ABSOLUTE) { m_mouseLastX = mstate.x; m_mouseLastY = mstate.y; } else { m_mouseLastX = m_mouseLastY = -1; } // m_hudPanel never gets input, but does get Update for time return result; } void Render() { // HUD panel on buton if (m_hudPanel) { m_hudPanel->Render(); } // Overlay panel next if (m_overlayPanel) { m_overlayPanel->Render(); } // Non-focus visible panels for (auto& it : m_panels) { if (it.second == m_hudPanel || it.second == m_overlayPanel || it.second == m_focusPanel) continue; if (it.second->IsVisible()) { it.second->Render(); } } // Focus panel on top if (m_focusPanel) { m_focusPanel->Render(); } } void ReleaseDevice() { m_images.clear(); m_batch.reset(); m_smallFont.reset(); m_smallItalicFont.reset(); m_smallBoldFont.reset(); m_midFont.reset(); m_midItalicFont.reset(); m_midBoldFont.reset(); m_largeFont.reset(); m_largeItalicFont.reset(); m_largeBoldFont.reset(); m_smallLegend.reset(); m_largeLegend.reset(); m_fxFactory.reset(); m_defaultTex.Reset(); #if defined(__d3d12_h__) || defined(__d3d12_x_h__) || defined(__XBOX_D3D12_X__) #elif defined(__d3d11_h__) || defined(__d3d11_x_h__) m_blendState.Reset(); m_scissorState.Reset(); m_context.Reset(); #endif } #if defined(__d3d12_h__) || defined(__d3d12_x_h__) || defined(__XBOX_D3D12_X__) void RestoreDevice(_In_ ID3D12Device* device, const RenderTargetState& renderTarget, ResourceUploadBatch& resourceUpload, DescriptorPile& pile) { { SpriteBatchPipelineStateDescription pd(renderTarget, (mConfig.pmAlpha) ? &CommonStates::AlphaBlend : &CommonStates::NonPremultiplied); m_batch = std::make_unique(device, resourceUpload, pd); } // Create 1x1 white default texture D3D12_RESOURCE_DESC texDesc = {}; texDesc.Width = 1; texDesc.Height = 1; texDesc.MipLevels = texDesc.DepthOrArraySize = 1; texDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; texDesc.SampleDesc.Count = 1; texDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; CD3DX12_HEAP_PROPERTIES defaultHeapProperties(D3D12_HEAP_TYPE_DEFAULT); DX::ThrowIfFailed( device->CreateCommittedResource( &defaultHeapProperties, D3D12_HEAP_FLAG_NONE, &texDesc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr, IID_GRAPHICS_PPV_ARGS(m_defaultTex.GetAddressOf()))); static const uint32_t s_pixel = 0xffffffff; D3D12_SUBRESOURCE_DATA initData = { &s_pixel, sizeof(uint32_t), 0 }; resourceUpload.Upload(m_defaultTex.Get(), 0, &initData, 1); resourceUpload.Transition( m_defaultTex.Get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); size_t index = pile.Allocate(); device->CreateShaderResourceView(m_defaultTex.Get(), nullptr, pile.GetCpuHandle(index)); m_defaultTexDescriptor = pile.GetGpuHandle(index); // Create fonts #if !defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP) wchar_t buff[MAX_PATH] = {}; DX::FindMediaFile(buff, MAX_PATH, mConfig.smallFontName); index = pile.Allocate(); m_smallFont = std::make_unique(device, resourceUpload, buff, pile.GetCpuHandle(index), pile.GetGpuHandle(index)); DX::FindMediaFile(buff, MAX_PATH, mConfig.smallItalicFontName); index = pile.Allocate(); m_smallItalicFont = std::make_unique(device, resourceUpload, buff, pile.GetCpuHandle(index), pile.GetGpuHandle(index)); DX::FindMediaFile(buff, MAX_PATH, mConfig.smallBoldFontName); index = pile.Allocate(); m_smallBoldFont = std::make_unique(device, resourceUpload, buff, pile.GetCpuHandle(index), pile.GetGpuHandle(index)); DX::FindMediaFile(buff, MAX_PATH, mConfig.midFontName); index = pile.Allocate(); m_midFont = std::make_unique(device, resourceUpload, buff, pile.GetCpuHandle(index), pile.GetGpuHandle(index)); DX::FindMediaFile(buff, MAX_PATH, mConfig.midItalicFontName); index = pile.Allocate(); m_midItalicFont = std::make_unique(device, resourceUpload, buff, pile.GetCpuHandle(index), pile.GetGpuHandle(index)); DX::FindMediaFile(buff, MAX_PATH, mConfig.midBoldFontName); index = pile.Allocate(); m_midBoldFont = std::make_unique(device, resourceUpload, buff, pile.GetCpuHandle(index), pile.GetGpuHandle(index)); DX::FindMediaFile(buff, MAX_PATH, mConfig.largeFontName); index = pile.Allocate(); m_largeFont = std::make_unique(device, resourceUpload, buff, pile.GetCpuHandle(index), pile.GetGpuHandle(index)); DX::FindMediaFile(buff, MAX_PATH, mConfig.largeItalicFontName); index = pile.Allocate(); m_largeItalicFont = std::make_unique(device, resourceUpload, buff, pile.GetCpuHandle(index), pile.GetGpuHandle(index)); DX::FindMediaFile(buff, MAX_PATH, mConfig.largeBoldFontName); index = pile.Allocate(); m_largeBoldFont = std::make_unique(device, resourceUpload, buff, pile.GetCpuHandle(index), pile.GetGpuHandle(index)); DX::FindMediaFile(buff, MAX_PATH, mConfig.largeLegendName); index = pile.Allocate(); m_largeLegend = std::make_unique(device, resourceUpload, buff, pile.GetCpuHandle(index), pile.GetGpuHandle(index)); DX::FindMediaFile(buff, MAX_PATH, mConfig.smallLegendName); index = pile.Allocate(); m_smallLegend = std::make_unique(device, resourceUpload, buff, pile.GetCpuHandle(index), pile.GetGpuHandle(index)); #else index = pile.Allocate(); m_smallFont = std::make_unique(device, resourceUpload, mConfig.smallFontName, pile.GetCpuHandle(index), pile.GetGpuHandle(index)); index = pile.Allocate(); m_smallItalicFont = std::make_unique(device, resourceUpload, mConfig.smallItalicFontName, pile.GetCpuHandle(index), pile.GetGpuHandle(index)); index = pile.Allocate(); m_smallBoldFont = std::make_unique(device, resourceUpload, mConfig.smallBoldFontName, pile.GetCpuHandle(index), pile.GetGpuHandle(index)); index = pile.Allocate(); m_midFont = std::make_unique(device, resourceUpload, mConfig.midFontName, pile.GetCpuHandle(index), pile.GetGpuHandle(index)); index = pile.Allocate(); m_midItalicFont = std::make_unique(device, resourceUpload, mConfig.midItalicFontName, pile.GetCpuHandle(index), pile.GetGpuHandle(index)); index = pile.Allocate(); m_midBoldFont = std::make_unique(device, resourceUpload, mConfig.midBoldFontName, pile.GetCpuHandle(index), pile.GetGpuHandle(index)); index = pile.Allocate(); m_largeFont = std::make_unique(device, resourceUpload, mConfig.largeFontName, pile.GetCpuHandle(index), pile.GetGpuHandle(index)); index = pile.Allocate(); m_largeItalicFont = std::make_unique(device, resourceUpload, mConfig.largeItalicFontName, pile.GetCpuHandle(index), pile.GetGpuHandle(index)); index = pile.Allocate(); m_largeBoldFont = std::make_unique(device, resourceUpload, mConfig.largeBoldFontName, pile.GetCpuHandle(index), pile.GetGpuHandle(index)); index = pile.Allocate(); m_largeLegend = std::make_unique(device, resourceUpload, mConfig.largeLegendName, pile.GetCpuHandle(index), pile.GetGpuHandle(index)); index = pile.Allocate(); m_smallLegend = std::make_unique(device, resourceUpload, mConfig.smallLegendName, pile.GetCpuHandle(index), pile.GetGpuHandle(index)); #endif m_smallFont->SetDefaultCharacter(L'?'); m_smallBoldFont->SetDefaultCharacter(L'?'); m_midFont->SetDefaultCharacter(L'?'); m_midItalicFont->SetDefaultCharacter(L'?'); m_midBoldFont->SetDefaultCharacter(L'?'); m_largeFont->SetDefaultCharacter(L'?'); m_largeItalicFont->SetDefaultCharacter(L'?'); m_largeBoldFont->SetDefaultCharacter(L'?'); // Create factory for images m_fxFactory = std::make_unique(device, resourceUpload, pile.Heap()); m_fxFactory->SetDirectory(m_layoutImageDir.c_str()); m_fxFactory->EnableForceSRGB(mConfig.forceSRGB); unsigned imageId = c_LayoutImageIdStart; if (!m_layoutImages.empty()) { for (auto& it : m_layoutImages) { index = pile.Allocate(); size_t slot = m_fxFactory->CreateTexture(it.c_str(), static_cast(index)); ComPtr tex; m_fxFactory->GetResource(slot, tex.GetAddressOf()); std::pair entry; entry.first = pile.GetGpuHandle(index); entry.second = GetTextureSize(tex.Get()); m_images[imageId] = entry; ++imageId; } } } #else void RestoreDevice(_In_ ID3D11DeviceContext* context) { m_batch = std::make_unique(context); ComPtr device; context->GetDevice(device.GetAddressOf()); // Create 1x1 white default texture { static const uint32_t s_pixel = 0xffffffff; D3D11_SUBRESOURCE_DATA initData = { &s_pixel, sizeof(uint32_t), 0 }; CD3D11_TEXTURE2D_DESC texDesc(DXGI_FORMAT_R8G8B8A8_UNORM, 1, 1, 1, 1, D3D11_BIND_SHADER_RESOURCE, D3D11_USAGE_IMMUTABLE); ComPtr tex; DX::ThrowIfFailed(device->CreateTexture2D(&texDesc, &initData, tex.GetAddressOf())); DX::ThrowIfFailed(device->CreateShaderResourceView(tex.Get(), nullptr, m_defaultTex.ReleaseAndGetAddressOf())); } // Create blend state for sprites { CD3D11_DEFAULT def; CD3D11_BLEND_DESC desc(def); desc.RenderTarget[0].BlendEnable = TRUE; desc.RenderTarget[0].SrcBlend = desc.RenderTarget[0].SrcBlendAlpha = (mConfig.pmAlpha) ? D3D11_BLEND_ONE : D3D11_BLEND_SRC_ALPHA; desc.RenderTarget[0].DestBlend = desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA; DX::ThrowIfFailed(device->CreateBlendState(&desc, m_blendState.ReleaseAndGetAddressOf())); } // Create scissor rasterizer state for sprites { CD3D11_RASTERIZER_DESC rsDesc(D3D11_FILL_SOLID, D3D11_CULL_BACK, FALSE, 0, 0.f, 0.f, TRUE, TRUE, TRUE, FALSE); DX::ThrowIfFailed(device->CreateRasterizerState(&rsDesc, m_scissorState.ReleaseAndGetAddressOf())); } // Create fonts #if !defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP) wchar_t buff[MAX_PATH] = {}; DX::FindMediaFile(buff, MAX_PATH, mConfig.smallFontName); m_smallFont = std::make_unique(device.Get(), buff); DX::FindMediaFile(buff, MAX_PATH, mConfig.smallItalicFontName); m_smallItalicFont = std::make_unique(device.Get(), buff); DX::FindMediaFile(buff, MAX_PATH, mConfig.smallBoldFontName); m_smallBoldFont = std::make_unique(device.Get(), buff); DX::FindMediaFile(buff, MAX_PATH, mConfig.midFontName); m_midFont = std::make_unique(device.Get(), buff); DX::FindMediaFile(buff, MAX_PATH, mConfig.midItalicFontName); m_midItalicFont = std::make_unique(device.Get(), buff); DX::FindMediaFile(buff, MAX_PATH, mConfig.midBoldFontName); m_midBoldFont = std::make_unique(device.Get(), buff); DX::FindMediaFile(buff, MAX_PATH, mConfig.largeFontName); m_largeFont = std::make_unique(device.Get(), buff); DX::FindMediaFile(buff, MAX_PATH, mConfig.largeItalicFontName); m_largeItalicFont = std::make_unique(device.Get(), buff); DX::FindMediaFile(buff, MAX_PATH, mConfig.largeBoldFontName); m_largeBoldFont = std::make_unique(device.Get(), buff); DX::FindMediaFile(buff, MAX_PATH, mConfig.largeLegendName); m_largeLegend = std::make_unique(device.Get(), buff); DX::FindMediaFile(buff, MAX_PATH, mConfig.smallLegendName); m_smallLegend = std::make_unique(device.Get(), buff); #else m_smallFont = std::make_unique(device.Get(), mConfig.smallFontName); m_smallItalicFont = std::make_unique(device.Get(), mConfig.smallItalicFontName); m_smallBoldFont = std::make_unique(device.Get(), mConfig.smallBoldFontName); m_midFont = std::make_unique(device.Get(), mConfig.midFontName); m_midItalicFont = std::make_unique(device.Get(), mConfig.midItalicFontName); m_midBoldFont = std::make_unique(device.Get(), mConfig.midBoldFontName); m_largeFont = std::make_unique(device.Get(), mConfig.largeFontName); m_largeItalicFont = std::make_unique(device.Get(), mConfig.largeItalicFontName); m_largeBoldFont = std::make_unique(device.Get(), mConfig.largeBoldFontName); m_largeLegend = std::make_unique(device.Get(), mConfig.largeLegendName); m_smallLegend = std::make_unique(device.Get(), mConfig.smallLegendName); #endif m_smallFont->SetDefaultCharacter(L'?'); m_smallBoldFont->SetDefaultCharacter(L'?'); m_midFont->SetDefaultCharacter(L'?'); m_midItalicFont->SetDefaultCharacter(L'?'); m_midBoldFont->SetDefaultCharacter(L'?'); m_largeFont->SetDefaultCharacter(L'?'); m_largeItalicFont->SetDefaultCharacter(L'?'); m_largeBoldFont->SetDefaultCharacter(L'?'); // Create factory for images m_fxFactory = std::make_unique(device.Get()); m_fxFactory->SetDirectory(m_layoutImageDir.c_str()); m_fxFactory->EnableForceSRGB(mConfig.forceSRGB); unsigned imageId = c_LayoutImageIdStart; if (!m_layoutImages.empty()) { for (auto& it : m_layoutImages) { ComPtr tex; m_fxFactory->CreateTexture(it.c_str(), context, tex.GetAddressOf()); m_images[imageId] = tex; ++imageId; } } m_context = context; } #endif void Reset() { m_padButtonState.Reset(); m_mouseButtonState.Reset(); m_keyboardState.Reset(); m_mouseLastX = m_mouseLastX = -1; m_heldTimer = 0; } SpriteFont* SelectFont(unsigned style) { static_assert(TextLabel::c_StyleFontSmall == Legend::c_StyleFontSmall, "style flags mismatch"); static_assert(TextLabel::c_StyleFontMid == Legend::c_StyleFontMid, "style flags mismatch"); static_assert(TextLabel::c_StyleFontLarge == Legend::c_StyleFontLarge, "style flags mismatch"); static_assert(TextLabel::c_StyleFontBold == Legend::c_StyleFontBold, "style flags mismatch"); static_assert(TextLabel::c_StyleFontItalic == Legend::c_StyleFontItalic, "style flags mismatch"); static_assert(TextLabel::c_StyleFontSmall == Button::c_StyleFontSmall, "style flags mismatch"); static_assert(TextLabel::c_StyleFontMid == Button::c_StyleFontMid, "style flags mismatch"); static_assert(TextLabel::c_StyleFontLarge == Button::c_StyleFontLarge, "style flags mismatch"); static_assert(TextLabel::c_StyleFontBold == Button::c_StyleFontBold, "style flags mismatch"); static_assert(TextLabel::c_StyleFontItalic == Button::c_StyleFontItalic, "style flags mismatch"); SpriteFont* font = nullptr; if (style & TextLabel::c_StyleFontLarge) { if (style & TextLabel::c_StyleFontBold) { font = m_largeBoldFont.get(); } else if (style & TextLabel::c_StyleFontItalic) { font = m_largeItalicFont.get(); } else { font = m_largeFont.get(); } } else if (style & TextLabel::c_StyleFontSmall) { if (style & TextLabel::c_StyleFontBold) { font = m_smallBoldFont.get(); } else if (style & TextLabel::c_StyleFontItalic) { font = m_smallItalicFont.get(); } else { font = m_smallFont.get(); } } else if (style & TextLabel::c_StyleFontBold) { font = m_midBoldFont.get(); } else if (style & TextLabel::c_StyleFontItalic) { font = m_midItalicFont.get(); } else { font = m_midFont.get(); } assert(font != 0); return font; } // Direct3D resources std::unique_ptr m_batch; std::unique_ptr m_smallFont; std::unique_ptr m_smallItalicFont; std::unique_ptr m_smallBoldFont; std::unique_ptr m_midFont; std::unique_ptr m_midItalicFont; std::unique_ptr m_midBoldFont; std::unique_ptr m_largeFont; std::unique_ptr m_largeItalicFont; std::unique_ptr m_largeBoldFont; std::unique_ptr m_smallLegend; std::unique_ptr m_largeLegend; #if defined(__d3d12_h__) || defined(__d3d12_x_h__) || defined(__XBOX_D3D12_X__) std::unique_ptr m_fxFactory; D3D12_GPU_DESCRIPTOR_HANDLE m_defaultTexDescriptor; ID3D12GraphicsCommandList* m_commandList; ComPtr m_defaultTex; std::map> m_images; #elif defined(__d3d11_h__) || defined(__d3d11_x_h__) std::unique_ptr m_fxFactory; ComPtr m_defaultTex; ComPtr m_blendState; ComPtr m_scissorState; ComPtr m_context; std::map> m_images; #endif // UI objects RECT m_fullscreen; IPanel* m_focusPanel; IPanel* m_overlayPanel; IPanel* m_hudPanel; std::map m_panels; std::vector m_layoutImages; std::wstring m_layoutImageDir; GamePad::ButtonStateTracker m_padButtonState; Mouse::ButtonStateTracker m_mouseButtonState; Keyboard::KeyboardStateTracker m_keyboardState; float m_heldTimer; int m_mouseLastX; int m_mouseLastY; // Common handler objects std::list> m_stdyesno; // Contains configuration data provided on setup const UIConfig mConfig; static UIManager::Impl* s_uiManager; // Helpers void RenderControls(std::vector& controls) { #if defined(__d3d12_h__) || defined(__d3d12_x_h__) || defined(__XBOX_D3D12_X__) if (m_commandList == nullptr || m_batch == nullptr) return; for (auto it : controls) { if (it->IsVisible()) { m_commandList->RSSetScissorRects(1, it->GetRectangle()); m_batch->Begin(m_commandList); it->Render(); m_batch->End(); } } #elif defined(__d3d11_h__) || defined(__d3d11_x_h__) if (m_context == nullptr || m_batch == nullptr) return; for (auto it : controls) { if (it->IsVisible()) { m_batch->Begin(SpriteSortMode_Deferred, m_blendState.Get(), nullptr, nullptr, m_scissorState.Get(), [=]() { m_context->RSSetScissorRects(1, it->GetRectangle()); }); it->Render(); m_batch->End(); } } #endif } void DrawRect(const RECT &rect, FXMVECTOR color) { #if defined(__d3d12_h__) || defined(__d3d12_x_h__) || defined(__XBOX_D3D12_X__) if (m_commandList == nullptr || m_batch == nullptr) return; static const XMUINT2 s_unit(1, 1); m_batch->Draw(m_defaultTexDescriptor, s_unit, rect, color); #elif defined(__d3d11_h__) || defined(__d3d11_x_h__) if (m_context == nullptr || m_batch == nullptr) return; m_batch->Draw(m_defaultTex.Get(), rect, color); #endif } void DrawImage(unsigned id, const RECT &rect, FXMVECTOR color) { #if defined(__d3d12_h__) || defined(__d3d12_x_h__) || defined(__XBOX_D3D12_X__) D3D12_GPU_DESCRIPTOR_HANDLE tex; XMUINT2 texSize; auto it = m_images.find(id); if (it == m_images.end()) { // If not found, use default texture tex = m_defaultTexDescriptor; texSize.x = texSize.y = 1; } else { tex = it->second.first; texSize = it->second.second; } m_batch->Draw(tex, texSize, rect, color); #elif defined(__d3d11_h__) || defined(__d3d11_x_h__) ID3D11ShaderResourceView* tex = nullptr; auto it = m_images.find(id); if (it == m_images.end()) { // If not found, use default texture tex = m_defaultTex.Get(); } else { tex = it->second.Get(); } m_batch->Draw(tex, rect, color); #endif } private: unsigned LoadImageItem(_In_z_ const wchar_t* imageFile) { assert(imageFile != 0); unsigned imageId = 0; std::wstring wname(imageFile); auto it = std::find(m_layoutImages.cbegin(), m_layoutImages.cend(), wname); if (it == m_layoutImages.end()) { imageId = c_LayoutImageIdStart + unsigned(m_layoutImages.size()); m_layoutImages.push_back(wname); } else { imageId = c_LayoutImageIdStart + unsigned(it - m_layoutImages.cbegin()); } return imageId; } XMVECTOR XM_CALLCONV LoadColorItem(_In_z_ const wchar_t* colorStr, size_t index) { assert(colorStr != 0); XMVECTOR color = Colors::White; if (*colorStr == L'#') { unsigned int bgra = 0xFFFFFF; if (swscanf_s(colorStr + 1, L"%x", &bgra) == 1) { using namespace PackedVector; bgra &= 0xFFFFFF; XMVECTOR clr = XMLoadColor(reinterpret_cast(&bgra)); color = XMVectorSelect(g_XMIdentityR3, clr, g_XMSelect1110); if (mConfig.forceSRGB) { color = XMColorSRGBToRGB(color); } } else { DebugTrace("WARNING: Invalid color value [record %zu]\n", index + 1); } } else if (*colorStr) { if (wcscmp(colorStr, L"RED") == 0) { color = XMLoadFloat4(&mConfig.colorDictionary[UIConfig::RED]); } else if (wcscmp(colorStr, L"GREEN") == 0) { color = XMLoadFloat4(&mConfig.colorDictionary[UIConfig::GREEN]); } else if (wcscmp(colorStr, L"BLUE") == 0) { color = XMLoadFloat4(&mConfig.colorDictionary[UIConfig::BLUE]); } else if (wcscmp(colorStr, L"ORANGE") == 0) { color = XMLoadFloat4(&mConfig.colorDictionary[UIConfig::ORANGE]); } else if (wcscmp(colorStr, L"YELLOW") == 0) { color = XMLoadFloat4(&mConfig.colorDictionary[UIConfig::YELLOW]); } else if (wcscmp(colorStr, L"DARKGREY") == 0) { color = XMLoadFloat4(&mConfig.colorDictionary[UIConfig::DARK_GREY]); } else if (wcscmp(colorStr, L"LIGHTGREY") == 0) { color = XMLoadFloat4(&mConfig.colorDictionary[UIConfig::LIGHT_GREY]); } else if (wcscmp(colorStr, L"OFFWHITE") == 0) { color = XMLoadFloat4(&mConfig.colorDictionary[UIConfig::OFF_WHITE]); } else if (wcscmp(colorStr, L"WHITE") == 0) { color = XMLoadFloat4(&mConfig.colorDictionary[UIConfig::WHITE]); } else if (wcscmp(colorStr, L"BLACK") == 0) { color = XMLoadFloat4(&mConfig.colorDictionary[UIConfig::BLACK]); } else if (wcscmp(colorStr, L"MID_GREY") == 0) { color = XMLoadFloat4(&mConfig.colorDictionary[UIConfig::MID_GREY]); } else { DebugTrace("WARNING: Invalid color value [record %zu]\n", index + 1); } } return color; } }; UIManager::Impl* UIManager::Impl::s_uiManager = nullptr; // Public constructors UIManager::UIManager(const UIConfig& config) : pImpl(new Impl(config)) { } #if defined(__d3d12_h__) || defined(__d3d12_x_h__) || defined(__XBOX_D3D12_X__) UIManager::UIManager( _In_ ID3D12Device *device, const RenderTargetState& renderTarget, ResourceUploadBatch& resourceUpload, DescriptorPile& pile, const UIConfig& config) : pImpl(new Impl(config)) { pImpl->RestoreDevice(device, renderTarget, resourceUpload, pile); } #elif defined(__d3d11_h__) || defined(__d3d11_x_h__) UIManager::UIManager(_In_ ID3D11DeviceContext* context, const UIConfig& config) : pImpl(new Impl(config)) { pImpl->RestoreDevice(context); } #endif // Move constructor. UIManager::UIManager(UIManager&& moveFrom) : pImpl(std::move(moveFrom.pImpl)) { } // Move assignment. UIManager& UIManager::operator= (UIManager&& moveFrom) { pImpl = std::move(moveFrom.pImpl); return *this; } // Public destructor. UIManager::~UIManager() { } // Public methods. void UIManager::LoadLayout(const wchar_t* layoutFile, const wchar_t* imageDir, unsigned offset) { pImpl->LoadLayout(layoutFile, imageDir, offset); } void UIManager::Add(unsigned id, _In_ IPanel* panel) { auto it = pImpl->m_panels.find(id); if (it != pImpl->m_panels.end()) { if (pImpl->m_focusPanel == it->second) { // For now, no focus stack pImpl->m_focusPanel = nullptr; } if (pImpl->m_overlayPanel == it->second) { pImpl->m_overlayPanel = nullptr; } if (pImpl->m_hudPanel == it->second) { pImpl->m_hudPanel = nullptr; } delete it->second; } pImpl->m_panels[id] = panel; } IPanel* UIManager::Find(unsigned id) const { auto it = pImpl->m_panels.find(id); if (it != pImpl->m_panels.end()) return it->second; return nullptr; } void UIManager::CloseAll() { std::for_each(pImpl->m_panels.begin(), pImpl->m_panels.end(), [](auto pair) { auto panel = pair.second; if (panel->IsVisible()) panel->Close(); }); } bool UIManager::Update(float elapsedTime, const GamePad::State& pad) { return pImpl->Update(elapsedTime, pad); } bool UIManager::Update(float elapsedTime, Mouse& mouse, Keyboard& kb) { return pImpl->Update(elapsedTime, mouse, kb); } #if defined(__d3d12_h__) || defined(__d3d12_x_h__) || defined(__XBOX_D3D12_X__) void UIManager::Render(_In_ ID3D12GraphicsCommandList* commandList) { pImpl->m_commandList = commandList; pImpl->Render(); pImpl->m_commandList = nullptr; } #else void UIManager::Render() { pImpl->Render(); } #endif void UIManager::SetWindow(const RECT& layout) { pImpl->m_fullscreen = layout; for (auto it : pImpl->m_panels) { if (it.second->IsVisible()) { it.second->OnWindowSize(layout); } } auto width = std::max(layout.right - layout.left, 1); auto height = std::max(layout.bottom - layout.top, 1); #if defined(__d3d12_h__) || defined(__d3d12_x_h__) || defined(__XBOX_D3D12_X__) D3D12_VIEWPORT vp = { 0.0f, 0.0f, static_cast(width), static_cast(height), D3D12_MIN_DEPTH, D3D12_MAX_DEPTH }; #elif defined(__d3d11_h__) || defined(__d3d11_x_h__) D3D11_VIEWPORT vp = { 0.0f, 0.0f, static_cast(width), static_cast(height), D3D11_MIN_DEPTH, D3D11_MAX_DEPTH }; #endif pImpl->m_batch->SetViewport(vp); } void UIManager::SetRotation(DXGI_MODE_ROTATION rotation) { if (pImpl->m_batch) { pImpl->m_batch->SetRotation(rotation); } } #if defined(__d3d12_h__) || defined(__d3d12_x_h__) || defined(__XBOX_D3D12_X__) void UIManager::RegisterImage(unsigned id, D3D12_GPU_DESCRIPTOR_HANDLE tex, XMUINT2 texSize) { std::pair entry; entry.first = tex; entry.second = texSize; pImpl->m_images[id] = entry; } #elif defined(__d3d11_h__) || defined(__d3d11_x_h__) void UIManager::RegisterImage(unsigned id, _In_ ID3D11ShaderResourceView* tex) { pImpl->m_images[id] = tex; } #endif void UIManager::UnregisterImage(unsigned id) { auto it = pImpl->m_images.find(id); if (it != pImpl->m_images.end()) { pImpl->m_images.erase(it); } } void UIManager::UnregisterAllImages() { pImpl->m_images.clear(); } void UIManager::ReleaseDevice() { pImpl->ReleaseDevice(); } #if defined(__d3d12_h__) || defined(__d3d12_x_h__) || defined(__XBOX_D3D12_X__) void UIManager::RestoreDevice( _In_ ID3D12Device* device, const DirectX::RenderTargetState& renderTarget, DirectX::ResourceUploadBatch& resourceUpload, DirectX::DescriptorPile& pile) { pImpl->RestoreDevice(device, renderTarget, resourceUpload, pile); } #elif defined(__d3d11_h__) || defined(__d3d11_x_h__) void UIManager::RestoreDevice(_In_ ID3D11DeviceContext* context) { pImpl->RestoreDevice(context); } #endif void UIManager::Reset() { pImpl->Reset(); } void UIManager::Clear() { UIConfig config = pImpl->mConfig; pImpl.reset(); // have to release singleton first pImpl.reset(new Impl(config)); } void UIManager::Enumerate(std::function enumCallback) { for (auto it = pImpl->m_panels.begin(); it != pImpl->m_panels.end(); ++it) { enumCallback(it->first, it->second); } } // Common callback adapters. void UIManager::CallbackYesNoCancel(_In_ IPanel* panel, std::function callback) { assert(panel != 0); assert(panel->GetUser() == nullptr); pImpl->m_stdyesno.push_back(callback); panel->SetUser(&pImpl->m_stdyesno.back()); panel->SetCallback(MessageYesNoCancel); } //===================================================================================== // Base control //===================================================================================== void IControl::ComputeLayout(const RECT& parent) { m_screenRect.top = m_layoutRect.top + parent.top; m_screenRect.bottom = m_layoutRect.bottom + parent.top; m_screenRect.left = m_layoutRect.left + parent.left; m_screenRect.right = m_layoutRect.right + parent.left; // Constrain to parent bound if (m_screenRect.top < parent.top) m_screenRect.top = parent.top; if (m_screenRect.left < parent.left) m_screenRect.left = parent.left; if (m_screenRect.right > parent.right) m_screenRect.right = parent.right; if (m_screenRect.bottom > parent.bottom) m_screenRect.bottom = parent.bottom; } void IControl::ComputeLayout(const RECT& bounds, float dx, float dy) { m_screenRect.left = long(float(m_layoutRect.left) * dx); m_screenRect.right = long(float(m_layoutRect.right ) * dx); m_screenRect.top = long(float(m_layoutRect.top) * dy); m_screenRect.bottom = long(float(m_layoutRect.bottom) * dy); // Add any offset m_screenRect.top = m_screenRect.top + bounds.top; m_screenRect.bottom = m_screenRect.bottom + bounds.top; m_screenRect.left = m_screenRect.left + bounds.left; m_screenRect.right = m_screenRect.right + bounds.left; // Constrain to bound if (m_screenRect.top < bounds.top) m_screenRect.top = bounds.top; if (m_screenRect.left < bounds.left) m_screenRect.left = bounds.left; if (m_screenRect.right > bounds.right) m_screenRect.right = bounds.right; if (m_screenRect.bottom > bounds.bottom) m_screenRect.bottom = bounds.bottom; } void IControl::SetVisible(bool visible) { #ifdef _DEBUG if (!visible && m_focus) { OutputDebugStringA("WARNING: Control made invisible while it was in focus!\n"); } #endif m_visible = visible; } //===================================================================================== // Text static label control //===================================================================================== _Use_decl_annotations_ TextLabel::TextLabel(unsigned id, const wchar_t* text, const RECT& rect, unsigned style) : IControl(rect, id), m_style(style), m_text(text) { auto mgr = UIManager::Impl::s_uiManager; if (!mgr) { throw std::exception("UIManager"); } m_fgColor = mgr->mConfig.colorNormal; m_bgColor = mgr->mConfig.colorBackground; } void TextLabel::Render() { auto mgr = UIManager::Impl::s_uiManager; if (!mgr) { throw std::exception("UIManager"); } // Determine font SpriteFont* font = mgr->SelectFont(m_style); const wchar_t * text = m_text.c_str(); if (m_style & c_StyleWordWrap) { if (m_wordWrap.empty() && (m_screenRect.right != 0 && m_screenRect.bottom != 0 && m_screenRect.left != m_screenRect.right && m_screenRect.top != m_screenRect.bottom)) { m_wordWrap = WordWrap(text, font, m_screenRect); } text = m_wordWrap.c_str(); } // Determine layout XMFLOAT2 pos(float(m_screenRect.left), float(m_screenRect.top)); if (m_style & (c_StyleAlignCenter | c_StyleAlignRight | c_StyleAlignMiddle | c_StyleAlignBottom)) { XMVECTOR fsize = font->MeasureString(text); if (m_style & c_StyleAlignCenter) { pos.x = float(m_screenRect.left) + float((m_screenRect.right - m_screenRect.left) / 2) - XMVectorGetX(fsize) / 2.f; if (pos.x < float(m_screenRect.left)) pos.x = float(m_screenRect.left); } else if (m_style & c_StyleAlignRight) { pos.x = float(m_screenRect.right - 1) - XMVectorGetX(fsize); if (pos.x < float(m_screenRect.left)) pos.x = float(m_screenRect.left); } if (m_style & c_StyleAlignMiddle) { pos.y = float(m_screenRect.top) + float((m_screenRect.bottom - m_screenRect.top) / 2) - XMVectorGetY(fsize) / 2.f; if (pos.y < float(m_screenRect.top)) pos.y = float(m_screenRect.top); } else if (m_style & c_StyleAlignBottom) { pos.y = float(m_screenRect.bottom - 1) - XMVectorGetY(fsize); if (pos.y < float(m_screenRect.top)) pos.y = float(m_screenRect.top); } } // Draw if (m_bgColor.w != 0.f) { XMVECTOR bgColor = m_style & c_StyleTransparent ? XMLoadFloat4(&mgr->mConfig.colorTransparent) : XMLoadFloat4(&m_bgColor); mgr->DrawRect(m_screenRect, bgColor); } XMVECTOR color = XMLoadFloat4(&m_fgColor); font->DrawString(mgr->m_batch.get(), text, pos, color); } void TextLabel::SetText(const wchar_t* text) { m_text = text; m_wordWrap.clear(); } void TextLabel::ComputeLayout(const RECT& parent) { m_wordWrap.clear(); IControl::ComputeLayout(parent); } void TextLabel::ComputeLayout(const RECT& bounds, float dx, float dy) { m_wordWrap.clear(); IControl::ComputeLayout(bounds, dx, dy); } //===================================================================================== // Image static control //===================================================================================== Image::Image(unsigned id, unsigned imageId, const RECT& rect) : IControl(rect, id), m_imageId(imageId) { } void Image::Render() { auto mgr = UIManager::Impl::s_uiManager; if (!mgr) { throw std::exception("UIManager"); } mgr->DrawImage(m_imageId, m_screenRect, Colors::White); } //===================================================================================== // Static text label that supports the controller font //===================================================================================== _Use_decl_annotations_ Legend::Legend(unsigned id, const wchar_t* text, const RECT& rect, unsigned style) : IControl(rect, id), m_style(style), m_text(text) { auto mgr = UIManager::Impl::s_uiManager; if (!mgr) { throw std::exception("UIManager"); } m_fgColor = mgr->mConfig.colorNormal; m_bgColor = mgr->mConfig.colorBackground; } void Legend::Render() { auto mgr = UIManager::Impl::s_uiManager; if (!mgr) { throw std::exception("UIManager"); } // Determine font SpriteFont* font = mgr->SelectFont(m_style); SpriteFont* ctrlFont = nullptr; if (m_style & c_StyleFontLarge) { ctrlFont = mgr->m_largeLegend.get(); } else if (m_style & c_StyleFontSmall) { ctrlFont = mgr->m_smallLegend.get(); } else { ctrlFont = mgr->m_smallLegend.get(); } assert(ctrlFont != 0); // Determine layout XMFLOAT2 pos(float(m_screenRect.left), float(m_screenRect.top)); if (m_style & (c_StyleAlignCenter | c_StyleAlignRight | c_StyleAlignMiddle | c_StyleAlignBottom)) { RECT rect = DX::MeasureControllerDrawBounds(font, ctrlFont, m_text.c_str(), pos); if (m_style & c_StyleAlignCenter) { pos.x = float(m_screenRect.left) + float((m_screenRect.right - m_screenRect.left) / 2) - float(rect.right - rect.left) / 2.f; if (pos.x < float(m_screenRect.left)) pos.x = float(m_screenRect.left); } else if (m_style & c_StyleAlignRight) { pos.x = float(m_screenRect.right - 1) - float(rect.right - rect.left); if (pos.x < float(m_screenRect.left)) pos.x = float(m_screenRect.left); } if (m_style & c_StyleAlignMiddle) { pos.y = float(m_screenRect.top) + float((m_screenRect.bottom - m_screenRect.top) / 2) - float(rect.bottom - rect.top) / 2.f; if (pos.y < float(m_screenRect.top)) pos.y = float(m_screenRect.top); } else if (m_style & c_StyleAlignBottom) { pos.y = float(m_screenRect.bottom - 1) - float(rect.bottom - rect.top); if (pos.y < float(m_screenRect.top)) pos.y = float(m_screenRect.top); } } // Draw if(m_bgColor.w != 0.f) { XMVECTOR bgColor = m_style & c_StyleTransparent ? XMLoadFloat4(&mgr->mConfig.colorTransparent) : XMLoadFloat4(&m_bgColor); mgr->DrawRect(m_screenRect, bgColor); } XMVECTOR color = XMLoadFloat4(&m_fgColor); DX::DrawControllerString(mgr->m_batch.get(), font, ctrlFont, m_text.c_str(), pos, color); } //===================================================================================== // Button control //===================================================================================== _Use_decl_annotations_ Button::Button(unsigned id, const wchar_t* text, const RECT& rect) : IControl(rect, id), m_enabled(true), m_showBorder(false), m_noFocusColor(false), m_style(c_StyleFontMid), m_text(text) { DirectX::XMStoreFloat4(&m_color, Colors::Black); } void Button::Render() { auto mgr = UIManager::Impl::s_uiManager; if (!mgr) { throw std::exception("UIManager"); } // Determine font SpriteFont* font = mgr->SelectFont(m_style); // Determine layout XMVECTOR fsize = font->MeasureString(m_text.c_str()); XMFLOAT2 pos(float(m_screenRect.left) + float((m_screenRect.right - m_screenRect.left) / 2) - XMVectorGetX(fsize) / 2.f, float(m_screenRect.top) + float((m_screenRect.bottom - m_screenRect.top) / 2) - XMVectorGetY(fsize) / 2.f); if (pos.x < float(m_screenRect.left)) pos.x = float(m_screenRect.left); if (pos.y < float(m_screenRect.top)) pos.y = float(m_screenRect.top); int borderWidth = 5; RECT buttonRect = { m_screenRect.left + borderWidth, m_screenRect.top + borderWidth, m_screenRect.right - borderWidth, m_screenRect.bottom - borderWidth }; auto batch = mgr->m_batch.get(); XMVECTOR buttonColor; XMVECTOR borderColor; XMVECTOR fontColor; buttonColor = (m_focus) ? XMLoadFloat4(&mgr->mConfig.colorFocus) : XMLoadFloat4(&m_color); if (m_focus && !m_noFocusColor) { buttonColor = XMLoadFloat4(&mgr->mConfig.colorFocus); } else { buttonColor = XMLoadFloat4(&m_color); } borderColor = XMLoadFloat4(&mgr->mConfig.colorNormal); fontColor = XMLoadFloat4(&mgr->mConfig.colorNormal); if(!m_enabled) { buttonColor = XMVectorScale(buttonColor, 0.35f); borderColor = XMVectorScale(borderColor, 0.35f); fontColor = XMVectorScale(fontColor, 0.35f); } if (m_focusOnText && m_focus) { fontColor = XMLoadFloat4(&mgr->mConfig.colorFocus); } if (m_showBorder && m_focus) { mgr->DrawRect(m_screenRect, borderColor); mgr->DrawRect(buttonRect, buttonColor); } else { mgr->DrawRect(m_screenRect, buttonColor); } if (m_focus) { float luminance = 0.2126f * mgr->mConfig.colorFocus.x * mgr->mConfig.colorFocus.x + 0.7152f * mgr->mConfig.colorFocus.y * mgr->mConfig.colorFocus.y + 0.0722f * mgr->mConfig.colorFocus.z * mgr->mConfig.colorFocus.z; if (luminance > 0.0722f) { // black would be best contrast with focus color font->DrawString(batch, m_text.c_str(), pos, Colors::Black); } else { font->DrawString(batch, m_text.c_str(), pos, fontColor); } } else { float luminance = 0.2126f * m_color.x * m_color.x + 0.7152f * m_color.y * m_color.y + 0.0722f * m_color.z * m_color.z; if (luminance > 0.0722f) { // black would be best contrast with button color font->DrawString(batch, m_text.c_str(), pos, Colors::Black); } else { fontColor = XMVectorScale(fontColor, 0.6f); font->DrawString(batch, m_text.c_str(), pos, fontColor); } } } bool Button::OnSelected(IPanel* panel) { if (m_callBack) { m_callBack(panel, this); } if (m_style & c_StyleExit) { return true; } return false; } //===================================================================================== // Image button control //===================================================================================== ImageButton::ImageButton(unsigned id, unsigned imageId, const RECT& rect) : IControl(rect, id), m_enabled(true), m_style(0), m_imageId(imageId) { } void ImageButton::Render() { auto mgr = UIManager::Impl::s_uiManager; if (!mgr) { throw std::exception("UIManager"); } if (m_style & c_StyleBackground) { XMVECTOR bgColor; if (m_focus) { bgColor = XMLoadFloat4(&mgr->mConfig.colorFocus); } else { bgColor = XMLoadFloat4((m_style & c_StyleTransparent) ? &mgr->mConfig.colorTransparent : &mgr->mConfig.colorBackground); } mgr->DrawRect(m_screenRect, bgColor); } XMVECTOR fgColor; if (m_focus) { fgColor = XMLoadFloat4(&mgr->mConfig.colorSelected); } else { fgColor = XMLoadFloat4(m_enabled ? &mgr->mConfig.colorNormal : &mgr->mConfig.colorDisabled); } mgr->DrawImage(m_imageId, m_screenRect, fgColor); } bool ImageButton::OnSelected(IPanel* panel) { if (m_callBack) { m_callBack(panel, this); } if (m_style & c_StyleExit) { return true; } return false; } //===================================================================================== // CheckBox control //===================================================================================== _Use_decl_annotations_ CheckBox::CheckBox(unsigned id, const wchar_t* text, const RECT& rect, bool checked) : IControl(rect, id), m_enabled(true), m_checked(checked), m_style(c_StyleFontMid), m_text(text) { } void CheckBox::Render() { auto mgr = UIManager::Impl::s_uiManager; if (!mgr) { throw std::exception("UIManager"); } // Determine font SpriteFont* font = mgr->SelectFont(m_style); float spacing = font->GetLineSpacing(); long boxThickness = std::max(1, long(spacing * 0.1f)); long boxOuter = std::max(2, long(spacing) - boxThickness * 2); // Determine layout RECT chkrct = { m_screenRect.left + boxThickness, m_screenRect.top + boxThickness, m_screenRect.left + boxOuter, m_screenRect.bottom - boxThickness }; XMFLOAT2 pos(float(m_screenRect.left) + boxOuter, float(m_screenRect.top)); // Draw XMVECTOR bgColor; if (m_focus) { bgColor = XMLoadFloat4(&mgr->mConfig.colorFocus); } else { bgColor = XMLoadFloat4((m_style & c_StyleTransparent) ? &mgr->mConfig.colorTransparent : &mgr->mConfig.colorBackground); } auto batch = mgr->m_batch.get(); mgr->DrawRect(m_screenRect, bgColor); XMVECTOR fgColor = XMLoadFloat4(m_enabled ? &mgr->mConfig.colorNormal : &mgr->mConfig.colorDisabled); XMVECTOR ckColor = XMLoadFloat4(&mgr->mConfig.colorBackground); mgr->DrawRect(chkrct, fgColor); chkrct.left += boxThickness; chkrct.top += boxThickness; chkrct.right -= boxThickness; chkrct.bottom -= boxThickness; mgr->DrawRect(chkrct, ckColor); font->DrawString(batch, m_text.c_str(), pos, fgColor); if (m_checked) { chkrct.left += boxThickness; chkrct.top += boxThickness; chkrct.right -= boxThickness; chkrct.bottom -= boxThickness; mgr->DrawRect(chkrct, fgColor); } } bool CheckBox::OnSelected(IPanel* panel) { if (m_enabled) { m_checked = !m_checked; } if (m_callBack) { m_callBack(panel, this); } return false; } //===================================================================================== // Slider control //===================================================================================== Slider::Slider(unsigned id, const RECT& rect, int value, int minValue, int maxValue) : IControl(rect, id), m_enabled(true), m_dragging(false), m_style(0), m_value(value), m_minValue(minValue), m_maxValue(maxValue), m_thumbRect{} { } void Slider::SetValue(int value) { value = std::max(std::min(m_maxValue, value), m_minValue); if (value == m_value) return; m_value = value; if (m_callBack) m_callBack(m_parent, this); } void Slider::SetRange(int minValue, int maxValue) { if (minValue >= maxValue) { throw std::out_of_range("Slider::SetRange"); } m_minValue = minValue; m_maxValue = maxValue; int value = std::max(std::min(m_maxValue, m_value), m_minValue); if (value == m_value) return; m_value = value; if (m_callBack) m_callBack(m_parent, this); } void Slider::Render() { auto mgr = UIManager::Impl::s_uiManager; if (!mgr) { throw std::exception("UIManager"); } // Compute 'thumb' rectangle float dy = float(m_screenRect.right - m_screenRect.left); long boxThickness = std::max(4, long(dy * 0.05f)); int thumbX = static_cast( float( m_value - m_minValue ) * dy / float(m_maxValue - m_minValue)); m_thumbRect.top = m_screenRect.top + 2; m_thumbRect.bottom = m_screenRect.bottom - 2; m_thumbRect.left = m_screenRect.left + thumbX - boxThickness / 2; m_thumbRect.right = m_thumbRect.left + boxThickness; if (m_thumbRect.left < m_screenRect.left) m_thumbRect.left = m_screenRect.left; if (m_thumbRect.right > m_screenRect.right) m_thumbRect.right = m_screenRect.right; // Draw XMVECTOR bgColor; if (m_focus) { bgColor = XMLoadFloat4(&mgr->mConfig.colorFocus); } else { bgColor = XMLoadFloat4((m_style & c_StyleTransparent) ? &mgr->mConfig.colorTransparent : &mgr->mConfig.colorBackground); } mgr->DrawRect(m_screenRect, bgColor); XMVECTOR fgColor = XMLoadFloat4(m_enabled ? &mgr->mConfig.colorHighlight : &mgr->mConfig.colorDisabled); mgr->DrawRect(m_thumbRect, fgColor); } void Slider::OnFocus(bool in) { IControl::OnFocus(in); if (!in) { m_dragging = false; } } bool Slider::Update(float elapsedTime, const DirectX::GamePad::State& pad) { UNREFERENCED_PARAMETER(elapsedTime); auto mgr = UIManager::Impl::s_uiManager; if (!mgr) { throw std::exception("UIManager"); } if (pad.IsLeftThumbStickLeft()) { SetValue(m_value - 1); } else if ( pad.IsLeftThumbStickRight() ) { SetValue(m_value + 1); } if (mgr->m_padButtonState.dpadLeft == GamePad::ButtonStateTracker::PRESSED) { SetValue(m_value - 1); } else if (mgr->m_padButtonState.dpadRight == GamePad::ButtonStateTracker::PRESSED) { SetValue(m_value + 1); } return false; } bool Slider::Update(float elapsedTime, const DirectX::Mouse::State& mstate, const DirectX::Keyboard::State& kbstate) { UNREFERENCED_PARAMETER(elapsedTime); auto mgr = UIManager::Impl::s_uiManager; if (!mgr) { throw std::exception("UIManager"); } // Handle mouse if (m_dragging) { if (!mstate.leftButton) { m_dragging = false; } else { float dy = float(m_screenRect.right - m_screenRect.left); float vpp = float(m_maxValue - m_minValue) / dy; int x = mstate.x; SetValue(int(0.5f + m_minValue + vpp * float(x - m_screenRect.left))); } return true; } else if (mstate.x >= m_screenRect.left && mstate.x < m_screenRect.right && mstate.y >= m_screenRect.top && mstate.y < m_screenRect.bottom) { if (mgr->m_mouseButtonState.leftButton == Mouse::ButtonStateTracker::PRESSED) { if (mstate.x >= m_thumbRect.left && mstate.x < m_thumbRect.right && mstate.y >= m_thumbRect.top && mstate.y < m_thumbRect.bottom) { m_dragging = true; return true; } else if (mstate.x > m_thumbRect.right) { SetValue(m_value + 1); return true; } else if (mstate.x < m_thumbRect.left) { SetValue(m_value - 1); return true; } } } // Handle keyboard if (kbstate.IsKeyDown(Keyboard::Left)) { if (mgr->m_keyboardState.IsKeyPressed(Keyboard::Left)) { SetValue(m_value - 1); } return true; } else if (kbstate.IsKeyDown(Keyboard::Right)) { if (mgr->m_keyboardState.IsKeyPressed(Keyboard::Right)) { SetValue(m_value + 1); } return true; } else if (kbstate.IsKeyDown(Keyboard::Home)) { if (mgr->m_keyboardState.IsKeyPressed(Keyboard::Home)) { SetValue(m_minValue); } return true; } else if (kbstate.IsKeyDown(Keyboard::End)) { if (mgr->m_keyboardState.IsKeyPressed(Keyboard::End)) { SetValue(m_maxValue); } return true; } else if (kbstate.IsKeyDown(Keyboard::PageUp)) { if (mgr->m_keyboardState.IsKeyPressed(Keyboard::PageUp)) { int tenth = std::min( 10, (m_maxValue - m_minValue) / 10 ); SetValue( m_value - tenth); } return true; } else if (kbstate.IsKeyDown(Keyboard::PageDown)) { if (mgr->m_keyboardState.IsKeyPressed(Keyboard::PageDown)) { int tenth = std::min(10, (m_maxValue - m_minValue) / 10); SetValue(m_value + tenth); } return true; } return false; } //===================================================================================== // Progress Bar //===================================================================================== ProgressBar::ProgressBar(unsigned id, const RECT& rect, bool visible, float start) : IControl(rect, id), m_progress(start), m_showPct(false) { m_visible = visible; } ProgressBar::~ProgressBar() { } void ProgressBar::Render() { auto mgr = UIManager::Impl::s_uiManager; if (!mgr) { throw std::exception("UIManager"); } auto batch = mgr->m_batch.get(); auto font = mgr->m_smallFont.get(); int border = 2; long width = m_screenRect.left + long((m_screenRect.right - m_screenRect.left - 2 * border) * m_progress); RECT inner = { m_screenRect.left + border, m_screenRect.top + border, m_screenRect.right - border, m_screenRect.bottom - border }; RECT bar = { m_screenRect.left + border, m_screenRect.top + border, width, m_screenRect.bottom - border }; mgr->DrawRect(m_screenRect, XMLoadFloat4(&mgr->mConfig.colorBackground)); mgr->DrawRect(inner, XMLoadFloat4(&mgr->mConfig.colorProgress)); mgr->DrawRect(bar, XMLoadFloat4(&mgr->mConfig.colorFocus)); if (m_showPct) { XMFLOAT2 pos; pos.x = float(m_screenRect.left + 2); pos.y = float(m_screenRect.top + 2); wchar_t str[32]; swprintf_s(str, 32, L"%.1f%%", m_progress * 100.f); font->DrawString(batch, str, pos, XMLoadFloat4(&mgr->mConfig.colorNormal)); } } //===================================================================================== // List Box //===================================================================================== ListBox::ListBox(unsigned id, const RECT& rect, unsigned style, int itemHeight) : IControl(rect, id), m_enabled(true), m_itemHeight(itemHeight), m_style(style), m_topItem(0), m_focusItem(0), m_itemRect{}, m_scrollRect{}, m_trackRect{}, m_thumbRect{}, m_lastHeight(0) { auto mgr = UIManager::Impl::s_uiManager; if (!mgr) { throw std::exception("UIManager"); } } ListBox::~ListBox() { } _Use_decl_annotations_ void ListBox::AddItem(const wchar_t* text, void *user) { Item item = {}; item.text = text; item.user = user; m_items.emplace_back(item); } _Use_decl_annotations_ void ListBox::InsertItem(int index, const wchar_t* text, void *user) { Item item = {}; item.text = text; item.user = user; int item_size = static_cast(m_items.size()); if ((item_size != 0) && (index >= item_size)) { m_items[size_t(index)] = item; } else { m_items.emplace(m_items.cbegin() + index, item); } } void ListBox::RemoveItem(int index) { if (index < 0 || index >= static_cast(m_items.size())) throw std::out_of_range("RemoveItem"); auto it = m_items.begin() + index; bool selected = it->selected; m_items.erase(it); if (m_topItem >= static_cast(m_items.size())) --m_topItem; if (m_focusItem >= static_cast(m_items.size())) --m_focusItem; if (selected && m_callBack) m_callBack(m_parent, this); } void ListBox::RemoveAllItems() { m_items.clear(); m_topItem = m_focusItem = 0; } int ListBox::GetSelectedItem() const { for(auto it = m_items.cbegin(); it != m_items.cend(); ++it) { if (it->selected) return static_cast(it - m_items.cbegin()); } return -1; } std::vector ListBox::GetSelectedItems() const { std::vector selected; for (auto it = m_items.cbegin(); it != m_items.cend(); ++it) { if (it->selected) selected.push_back(static_cast(it - m_items.cbegin())); } return selected; } void ListBox::ClearSelection() { bool changed = false; for (auto&i : m_items) { if (i.selected) { i.selected = false; changed = true; } } if (changed && m_callBack) m_callBack(m_parent, this); } void ListBox::SelectItem(int index) { if (index < 0 || index >= static_cast(m_items.size())) throw std::out_of_range("SelectItem"); auto it = m_items.begin() + index; bool changed = false; if (m_style & c_StyleMultiSelection) { it->selected = !it->selected; changed = true; } else if (!it->selected) { for (auto& i : m_items) { i.selected = false; } it->selected = true; changed = true; } if (m_callBack && changed) m_callBack(m_parent, this); } void ListBox::Render() { auto mgr = UIManager::Impl::s_uiManager; if (!mgr) { throw std::exception("UIManager"); } m_itemRect.left = m_screenRect.left + c_BorderSize; m_itemRect.top = m_screenRect.top + c_BorderSize; m_itemRect.right = m_screenRect.right - c_BorderSize; if (m_style & c_StyleScrollBar) m_itemRect.right -= c_ScrollWidth; m_itemRect.bottom = m_screenRect.bottom - c_BorderSize; if (m_itemRect.top < m_screenRect.top) m_itemRect.top = m_screenRect.top; if (m_itemRect.left < m_screenRect.left) m_itemRect.left = m_screenRect.left; if (m_itemRect.right > m_screenRect.right) m_itemRect.right = m_screenRect.right; if (m_itemRect.bottom > m_screenRect.bottom) m_itemRect.bottom = m_screenRect.bottom; m_scrollRect.left = m_screenRect.right - c_ScrollWidth - c_BorderSize; m_scrollRect.right = m_screenRect.right - c_BorderSize; m_scrollRect.top = m_screenRect.top + c_BorderSize; m_scrollRect.bottom = m_screenRect.bottom - c_BorderSize; UpdateRects(); auto batch = mgr->m_batch.get(); XMVECTOR bgColor = XMLoadFloat4((m_style & c_StyleTransparent) ? &mgr->mConfig.colorTransparent : &mgr->mConfig.colorBackground); mgr->DrawRect(m_screenRect, bgColor); if (!m_items.empty()) { SpriteFont* font = mgr->SelectFont(m_style); m_lastHeight = (m_itemHeight <= 0) ? static_cast(font->GetLineSpacing()) : m_itemHeight; int maxl = static_cast(float(m_itemRect.bottom - m_itemRect.top - c_MarginSize*2) / float(m_lastHeight)); if (m_focusItem < m_topItem) m_topItem = m_focusItem; if (m_focusItem >= (m_topItem + maxl)) m_topItem = m_focusItem; XMFLOAT2 pos = { float(m_itemRect.left), float(m_itemRect.top) }; RECT selectRect = { m_itemRect.left, m_itemRect.top, m_itemRect.right, m_itemRect.top + m_lastHeight }; for (int j = m_topItem; j < static_cast(m_items.size()); ++j) { if (pos.y + float(m_lastHeight) >= (m_itemRect.bottom - c_MarginSize)) break; auto& item = m_items[size_t(j)]; if (m_focus && m_focusItem == j) { XMVECTOR color = XMLoadFloat4(&mgr->mConfig.colorFocus); XMVECTOR fgColor = XMLoadFloat4((item.selected) ? &mgr->mConfig.colorHighlight : &mgr->mConfig.colorNormal); mgr->DrawRect(selectRect, color); font->DrawString(batch, item.text.c_str(), pos, fgColor); } else { XMVECTOR fgColor = XMLoadFloat4(m_enabled ? &mgr->mConfig.colorNormal : &mgr->mConfig.colorDisabled); if (item.selected) { XMVECTOR color = XMLoadFloat4(&mgr->mConfig.colorSelected); mgr->DrawRect(selectRect, color); } font->DrawString(batch, item.text.c_str(), pos, fgColor); } pos.y += float(m_lastHeight); selectRect.top += m_lastHeight; selectRect.bottom += m_lastHeight; } } if (m_style & c_StyleScrollBar) { XMVECTOR scrollColor = XMLoadFloat4(&mgr->mConfig.colorNormal); mgr->DrawRect(m_scrollRect, scrollColor); mgr->DrawRect(m_trackRect, bgColor); if (m_thumbRect.top != m_thumbRect.bottom) { mgr->DrawRect(m_thumbRect, scrollColor); } } } bool ListBox::Update(float elapsedTime, const DirectX::GamePad::State& pad) { UNREFERENCED_PARAMETER(elapsedTime); UNREFERENCED_PARAMETER(pad); auto mgr = UIManager::Impl::s_uiManager; if (!mgr) { throw std::exception("UIManager"); } assert(m_focusItem >= 0 && m_focusItem < static_cast(m_items.size())); if (mgr->m_padButtonState.a == GamePad::ButtonStateTracker::PRESSED) { if (!m_items.empty()) { SelectItem(m_focusItem); } return true; } else if (mgr->m_padButtonState.y == GamePad::ButtonStateTracker::PRESSED) { if (m_style & c_StyleMultiSelection) { ClearSelection(); } return true; } else if (mgr->m_padButtonState.leftStickUp == GamePad::ButtonStateTracker::PRESSED) { mgr->m_heldTimer = c_HoldTimeStart; if (!m_items.empty()) { --m_focusItem; if (m_focusItem < 0) { m_focusItem = static_cast(m_items.size() - 1); } } return true; } else if (mgr->m_padButtonState.leftStickUp == GamePad::ButtonStateTracker::HELD) { if (!m_items.empty() && mgr->m_heldTimer <= 0.f) { --m_focusItem; if (m_focusItem < 0) { m_focusItem = static_cast(m_items.size() - 1); } mgr->m_heldTimer = c_HoldTimeRepeat; } return true; } else if (mgr->m_padButtonState.leftStickDown == GamePad::ButtonStateTracker::PRESSED) { mgr->m_heldTimer = c_HoldTimeStart; if (!m_items.empty()) { ++m_focusItem; if (m_focusItem >= static_cast(m_items.size())) { m_focusItem = 0; } } return true; } else if (mgr->m_padButtonState.leftStickDown == GamePad::ButtonStateTracker::HELD) { if (!m_items.empty() && mgr->m_heldTimer <= 0.f) { ++m_focusItem; if (m_focusItem >= static_cast(m_items.size())) { m_focusItem = 0; } mgr->m_heldTimer = c_HoldTimeRepeat; } return true; } else if (mgr->m_padButtonState.leftStick == GamePad::ButtonStateTracker::PRESSED) { if (!m_items.empty()) { m_focusItem = 0; } } return false; } bool ListBox::Update(float elapsedTime, const DirectX::Mouse::State& mstate, const DirectX::Keyboard::State& kbstate) { UNREFERENCED_PARAMETER(elapsedTime); auto mgr = UIManager::Impl::s_uiManager; if (!mgr) { throw std::exception("UIManager"); } assert(m_focusItem >= 0 && m_focusItem < static_cast(m_items.size())); // Handle mouse if (mstate.x >= m_itemRect.left && mstate.x < m_itemRect.right && mstate.y >= m_itemRect.top && mstate.y < m_itemRect.bottom) { if (m_lastHeight > 0) { int maxl = static_cast(float(m_itemRect.bottom - m_itemRect.top - c_MarginSize * 2) / float(m_lastHeight)); int item = static_cast(float(mstate.y - m_itemRect.top - c_MarginSize) / float(m_lastHeight)); if (item >= 0 && item < maxl) { item = m_topItem + item; if (item >= 0 && item < static_cast(m_items.size())) { m_focusItem = item; if (mgr->m_mouseButtonState.leftButton == Mouse::ButtonStateTracker::PRESSED) { SelectItem(m_focusItem); } } } } return true; } if ((m_style & c_StyleScrollBar) && (m_thumbRect.top != m_thumbRect.bottom)) { if (mstate.x >= m_scrollRect.left && mstate.x < m_scrollRect.right && mstate.y >= m_scrollRect.top && mstate.y < m_scrollRect.bottom) { if (mgr->m_mouseButtonState.leftButton == Mouse::ButtonStateTracker::PRESSED) { if (mstate.y < m_thumbRect.top) { if (!m_items.empty() && (m_lastHeight > 0)) { int maxl = static_cast(float(m_itemRect.bottom - m_itemRect.top - c_MarginSize * 2) / float(m_lastHeight)); m_focusItem -= maxl; if (m_focusItem < 0) m_focusItem = 0; } } else if (mstate.y > m_thumbRect.bottom) { if (!m_items.empty() && (m_lastHeight > 0)) { int maxl = static_cast(float(m_itemRect.bottom - m_itemRect.top - c_MarginSize * 2) / float(m_lastHeight)); m_focusItem += maxl; if (m_focusItem >= static_cast(m_items.size())) m_focusItem = static_cast(m_items.size() - 1); } } } return true; } } // Handle keyboard if (kbstate.IsKeyDown(Keyboard::Space)) { if (mgr->m_keyboardState.IsKeyPressed(Keyboard::Space)) { if (!m_items.empty()) { SelectItem(m_focusItem); } } return true; } else if (kbstate.IsKeyDown(Keyboard::W)) { if (mgr->m_keyboardState.IsKeyPressed(Keyboard::W)) { if (!m_items.empty()) { --m_focusItem; if (m_focusItem < 0) { m_focusItem = static_cast(m_items.size() - 1); } } } return true; } else if (kbstate.IsKeyDown(Keyboard::S)) { if (mgr->m_keyboardState.IsKeyPressed(Keyboard::S)) { if (!m_items.empty()) { ++m_focusItem; if (m_focusItem >= static_cast(m_items.size())) { m_focusItem = 0; } } } return true; } else if (kbstate.IsKeyDown(Keyboard::Home)) { if (mgr->m_keyboardState.IsKeyPressed(Keyboard::Home)) { m_focusItem = 0; } return true; } else if (kbstate.IsKeyDown(Keyboard::End)) { if (mgr->m_keyboardState.IsKeyPressed(Keyboard::End)) { if (!m_items.empty()) { m_focusItem = static_cast(m_items.size() - 1); } } return true; } else if (kbstate.IsKeyDown(Keyboard::PageUp)) { if (mgr->m_keyboardState.IsKeyPressed(Keyboard::PageUp)) { if (!m_items.empty() && (m_lastHeight > 0)) { int maxl = static_cast(float(m_itemRect.bottom - m_itemRect.top - c_MarginSize * 2) / float(m_lastHeight)); m_focusItem -= maxl; if (m_focusItem < 0) m_focusItem = 0; } } return true; } else if (kbstate.IsKeyDown(Keyboard::PageDown)) { if (mgr->m_keyboardState.IsKeyPressed(Keyboard::PageDown)) { if (!m_items.empty() && (m_lastHeight > 0)) { int maxl = static_cast(float(m_itemRect.bottom - m_itemRect.top - c_MarginSize * 2) / float(m_lastHeight)); m_focusItem += maxl; if (m_focusItem >= static_cast(m_items.size())) m_focusItem = static_cast(m_items.size() - 1); } } return true; } return false; } void ListBox::UpdateRects() { m_trackRect.top = m_scrollRect.top + 1; m_trackRect.left = m_scrollRect.left + 1; m_trackRect.right = m_scrollRect.right - 1; m_trackRect.bottom = m_scrollRect.bottom - 1; m_thumbRect.left = m_trackRect.left; m_thumbRect.right = m_trackRect.right; if (m_items.empty() || !m_lastHeight) { m_thumbRect.bottom = m_thumbRect.top; return; } int maxl = static_cast(float(m_itemRect.bottom - m_itemRect.top - c_MarginSize * 2) / float(m_lastHeight)); UpdateScrollBar(m_thumbRect, m_trackRect, m_topItem, 0, static_cast(m_items.size()), maxl); } //===================================================================================== // Text List //===================================================================================== TextList::TextList(unsigned id, const RECT& rect, unsigned style, int itemHeight) : IControl(rect, id), m_itemHeight(itemHeight), m_style(style), m_topItem(0), m_itemRect{}, m_lastHeight(0) { auto mgr = UIManager::Impl::s_uiManager; if (!mgr) { throw std::exception("UIManager"); } } TextList::~TextList() { } _Use_decl_annotations_ void XM_CALLCONV TextList::AddItem(const wchar_t* text, FXMVECTOR color) { Item item = {}; item.text = text; XMStoreFloat4(&item.color, color); m_items.emplace_back(item); } _Use_decl_annotations_ void XM_CALLCONV TextList::InsertItem(int index, const wchar_t* text, FXMVECTOR color) { Item item = {}; item.text = text; XMStoreFloat4(&item.color, color); int item_size = static_cast(m_items.size()); if ((item_size != 0) && (index >= item_size)) { m_items[size_t(index)] = item; } else { m_items.emplace(m_items.cbegin() + index, item); } } void TextList::RemoveItem(int index) { if (index < 0 || index >= static_cast(m_items.size())) throw std::out_of_range("RemoveItem"); auto it = m_items.begin() + index; m_items.erase(it); if (m_topItem >= static_cast(m_items.size())) --m_topItem; } void TextList::RemoveAllItems() { m_items.clear(); m_topItem = 0; } void TextList::Render() { auto mgr = UIManager::Impl::s_uiManager; if (!mgr) { throw std::exception("UIManager"); } m_itemRect.left = m_screenRect.left + c_BorderSize; m_itemRect.top = m_screenRect.top + c_BorderSize; m_itemRect.right = m_screenRect.right - c_BorderSize; m_itemRect.bottom = m_screenRect.bottom - c_BorderSize; if (m_itemRect.top < m_screenRect.top) m_itemRect.top = m_screenRect.top; if (m_itemRect.left < m_screenRect.left) m_itemRect.left = m_screenRect.left; if (m_itemRect.right > m_screenRect.right) m_itemRect.right = m_screenRect.right; if (m_itemRect.bottom > m_screenRect.bottom) m_itemRect.bottom = m_screenRect.bottom; if (!m_items.empty()) { SpriteFont* font = mgr->SelectFont(m_style); m_lastHeight = (m_itemHeight <= 0) ? static_cast(font->GetLineSpacing()) : m_itemHeight; XMFLOAT2 pos = { float(m_itemRect.left), float(m_itemRect.top) }; RECT selectRect = { m_itemRect.left, m_itemRect.top, m_itemRect.right, m_itemRect.top + m_lastHeight }; for (int j = m_topItem; j < static_cast(m_items.size()); ++j) { if (pos.y + float(m_lastHeight) >= (m_itemRect.bottom - c_MarginSize)) break; auto& item = m_items[size_t(j)]; font->DrawString(mgr->m_batch.get(), item.text.c_str(), pos, XMLoadFloat4(&(item.color))); pos.y += float(m_lastHeight); selectRect.top += m_lastHeight; selectRect.bottom += m_lastHeight; } } } bool TextList::Update(float elapsedTime, const DirectX::GamePad::State& pad) { UNREFERENCED_PARAMETER(elapsedTime); UNREFERENCED_PARAMETER(pad); return false; } bool TextList::Update(float elapsedTime, const DirectX::Mouse::State& mstate, const DirectX::Keyboard::State& kbstate) { UNREFERENCED_PARAMETER(elapsedTime); UNREFERENCED_PARAMETER(mstate); UNREFERENCED_PARAMETER(kbstate); return false; } //===================================================================================== // Text Box //===================================================================================== TextBox::TextBox(unsigned id, _In_z_ const wchar_t* text, const RECT& rect, unsigned style) : IControl(rect, id), m_style(style), m_topLine(0), m_itemRect{}, m_scrollRect{}, m_trackRect{}, m_thumbRect{}, m_lastHeight(0), m_text(text), m_lastWheelValue(0) { auto mgr = UIManager::Impl::s_uiManager; if (!mgr) { throw std::exception("UIManager"); } m_color = mgr->mConfig.colorNormal; } TextBox::~TextBox() { } void TextBox::Render() { auto mgr = UIManager::Impl::s_uiManager; if (!mgr) { throw std::exception("UIManager"); } m_itemRect.left = m_screenRect.left + c_BorderSize; m_itemRect.top = m_screenRect.top + c_BorderSize; m_itemRect.right = m_screenRect.right - c_BorderSize; if (m_style & c_StyleScrollBar) m_itemRect.right -= c_ScrollWidth; m_itemRect.bottom = m_screenRect.bottom - c_BorderSize; if (m_itemRect.top < m_screenRect.top) m_itemRect.top = m_screenRect.top; if (m_itemRect.left < m_screenRect.left) m_itemRect.left = m_screenRect.left; if (m_itemRect.right > m_screenRect.right) m_itemRect.right = m_screenRect.right; if (m_itemRect.bottom > m_screenRect.bottom) m_itemRect.bottom = m_screenRect.bottom; m_scrollRect.left = m_screenRect.right - c_ScrollWidth - c_BorderSize; m_scrollRect.right = m_screenRect.right - c_BorderSize; m_scrollRect.top = m_screenRect.top + c_BorderSize; m_scrollRect.bottom = m_screenRect.bottom - c_BorderSize; UpdateRects(); XMVECTOR bgColor; if (m_focus) { XMVECTOR fgColor = XMLoadFloat4(&mgr->mConfig.colorSelected); bgColor = XMLoadFloat4((m_style & c_StyleTransparent) ? &mgr->mConfig.colorTransparent : &mgr->mConfig.colorBackground); if(!(m_style & c_StyleNoBackground)) { mgr->DrawRect(m_screenRect, fgColor); RECT rect; rect.left = m_screenRect.left + 1; rect.top = m_screenRect.top + 1; rect.right = m_screenRect.right - 1; rect.bottom = m_screenRect.bottom - 1; mgr->DrawRect(rect, bgColor); } } else { bgColor = XMLoadFloat4((m_style & c_StyleTransparent) ? &mgr->mConfig.colorTransparent : &mgr->mConfig.colorBackground); if(!(m_style & c_StyleNoBackground)) { mgr->DrawRect(m_screenRect, bgColor); } } if (!m_text.empty()) { SpriteFont* font = mgr->SelectFont(m_style); m_lastHeight = static_cast(font->GetLineSpacing()); const wchar_t * text = m_text.c_str(); if (m_wordWrap.empty() && (m_itemRect.right != 0 && m_itemRect.bottom != 0 && m_itemRect.left != m_itemRect.right && m_itemRect.top != m_itemRect.bottom)) { m_wordWrap = WordWrap(text, font, m_itemRect, &m_wordWrapLines); } if (m_topLine >= static_cast(m_wordWrapLines.size())) m_topLine = static_cast(m_wordWrapLines.size()) - 1; m_topLine = std::max(m_topLine, 0); XMFLOAT2 pos = { float(m_itemRect.left), float(m_itemRect.top) }; XMVECTOR color = XMLoadFloat4(&m_color); font->DrawString(mgr->m_batch.get(), &m_wordWrap.c_str()[m_wordWrapLines[size_t(m_topLine)]], pos, color); } if (m_style & c_StyleScrollBar) { XMVECTOR scrollColor = XMLoadFloat4(&mgr->mConfig.colorNormal); mgr->DrawRect(m_scrollRect, scrollColor); mgr->DrawRect(m_trackRect, bgColor); if (m_thumbRect.top != m_thumbRect.bottom) { mgr->DrawRect(m_thumbRect, scrollColor); } } } bool TextBox::Update(float elapsedTime, const DirectX::GamePad::State& pad) { UNREFERENCED_PARAMETER(elapsedTime); UNREFERENCED_PARAMETER(pad); auto mgr = UIManager::Impl::s_uiManager; if (!mgr) { throw std::exception("UIManager"); } if (mgr->m_padButtonState.leftStickUp == GamePad::ButtonStateTracker::PRESSED) { mgr->m_heldTimer = c_HoldTimeStart; if (!m_wordWrapLines.empty()) { if (m_topLine > 0) { --m_topLine; } } return true; } else if (mgr->m_padButtonState.leftStickUp == GamePad::ButtonStateTracker::HELD) { if (!m_wordWrapLines.empty() && mgr->m_heldTimer <= 0.f) { if (m_topLine > 0) { --m_topLine; } mgr->m_heldTimer = c_HoldTimeRepeat; } return true; } else if (mgr->m_padButtonState.leftStickDown == GamePad::ButtonStateTracker::PRESSED) { mgr->m_heldTimer = c_HoldTimeStart; if (!m_wordWrapLines.empty()) { ++m_topLine; if (m_topLine >= static_cast(m_wordWrapLines.size())) m_topLine = static_cast(m_wordWrapLines.size() - 1); } return true; } else if (mgr->m_padButtonState.leftStickDown == GamePad::ButtonStateTracker::HELD) { if (!m_wordWrapLines.empty() && mgr->m_heldTimer <= 0.f) { ++m_topLine; if (m_topLine >= static_cast(m_wordWrapLines.size())) m_topLine = static_cast(m_wordWrapLines.size() - 1); mgr->m_heldTimer = c_HoldTimeRepeat; } return true; } else if (mgr->m_padButtonState.leftStick == GamePad::ButtonStateTracker::PRESSED) { if (!m_wordWrapLines.empty()) { m_topLine = 0; } } return false; } bool TextBox::Update(float elapsedTime, const DirectX::Mouse::State& mstate, const DirectX::Keyboard::State& kbstate) { UNREFERENCED_PARAMETER(elapsedTime); auto mgr = UIManager::Impl::s_uiManager; if (!mgr) { throw std::exception("UIManager"); } // Handle mouse if ((m_style & c_StyleScrollBar) && (m_thumbRect.top != m_thumbRect.bottom)) { if (mstate.x >= m_scrollRect.left && mstate.x < m_scrollRect.right && mstate.y >= m_scrollRect.top && mstate.y < m_scrollRect.bottom) { if (mgr->m_mouseButtonState.leftButton == Mouse::ButtonStateTracker::PRESSED) { if (mstate.y < m_thumbRect.top) { if (!m_wordWrapLines.empty() && (m_lastHeight > 0)) { int maxl = static_cast(float(m_itemRect.bottom - m_itemRect.top - c_MarginSize * 2) / float(m_lastHeight)); m_topLine -= maxl; if (m_topLine < 0) m_topLine = 0; } } else if (mstate.y > m_thumbRect.bottom) { if (!m_wordWrapLines.empty() && (m_lastHeight > 0)) { int maxl = static_cast(float(m_itemRect.bottom - m_itemRect.top - c_MarginSize * 2) / float(m_lastHeight)); m_topLine += maxl; if (m_topLine >= static_cast(m_wordWrapLines.size())) m_topLine = static_cast(m_wordWrapLines.size() - 1); } } } return true; } } if (mstate.scrollWheelValue > m_lastWheelValue) { if (!m_wordWrapLines.empty()) { if (m_topLine > 0) { --m_topLine; } } m_lastWheelValue = mstate.scrollWheelValue; return true; } if (mstate.scrollWheelValue < m_lastWheelValue) { if (!m_wordWrapLines.empty()) { ++m_topLine; if (m_topLine >= static_cast(m_wordWrapLines.size())) m_topLine = static_cast(m_wordWrapLines.size() - 1); } m_lastWheelValue = mstate.scrollWheelValue; return true; } // Handle keyboard if (kbstate.IsKeyDown(Keyboard::W) || kbstate.IsKeyDown(Keyboard::Up)) { if (mgr->m_keyboardState.IsKeyPressed(Keyboard::W) || mgr->m_keyboardState.IsKeyPressed(Keyboard::Up)) { mgr->m_heldTimer = c_KeypressRepeatDelay; } if (!m_wordWrapLines.empty() && mgr->m_heldTimer <= 0.f) { if (m_topLine > 0) { --m_topLine; } mgr->m_heldTimer = c_KeypressRepeatDelay; } return true; } else if (kbstate.IsKeyDown(Keyboard::S) || kbstate.IsKeyDown(Keyboard::Down)) { if (mgr->m_keyboardState.IsKeyPressed(Keyboard::S) || mgr->m_keyboardState.IsKeyPressed(Keyboard::Down)) { mgr->m_heldTimer = c_KeypressRepeatDelay; } if (!m_wordWrapLines.empty() && mgr->m_heldTimer <= 0.f) { ++m_topLine; if (m_topLine >= static_cast(m_wordWrapLines.size())) m_topLine = static_cast(m_wordWrapLines.size() - 1); mgr->m_heldTimer = c_KeypressRepeatDelay; } return true; } else if (kbstate.IsKeyDown(Keyboard::Home)) { if (mgr->m_keyboardState.IsKeyPressed(Keyboard::Home)) { m_topLine = 0; } return true; } else if (kbstate.IsKeyDown(Keyboard::End)) { if (mgr->m_keyboardState.IsKeyPressed(Keyboard::End)) { if (!m_wordWrapLines.empty()) { m_topLine = static_cast(m_wordWrapLines.size() - 1); } } return true; } else if (kbstate.IsKeyDown(Keyboard::PageUp)) { if (mgr->m_keyboardState.IsKeyPressed(Keyboard::PageUp)) { mgr->m_heldTimer = c_KeypressRepeatDelay; } if (!m_wordWrapLines.empty() && (m_lastHeight > 0) && mgr->m_heldTimer <= 0.f) { int maxl = static_cast(float(m_itemRect.bottom - m_itemRect.top - c_MarginSize * 2) / float(m_lastHeight)); m_topLine -= maxl; if (m_topLine < 0) m_topLine = 0; mgr->m_heldTimer = c_KeypressRepeatDelay; } return true; } else if (kbstate.IsKeyDown(Keyboard::PageDown)) { if (mgr->m_keyboardState.IsKeyPressed(Keyboard::PageDown)) { mgr->m_heldTimer = c_KeypressRepeatDelay; } if (!m_wordWrapLines.empty() && (m_lastHeight > 0) && mgr->m_heldTimer <= 0.f) { int maxl = static_cast(float(m_itemRect.bottom - m_itemRect.top - c_MarginSize * 2) / float(m_lastHeight)); m_topLine += maxl; if (m_topLine >= static_cast(m_wordWrapLines.size())) m_topLine = static_cast(m_wordWrapLines.size() - 1); mgr->m_heldTimer = c_KeypressRepeatDelay; } return true; } return false; } void TextBox::ComputeLayout(const RECT& parent) { m_wordWrap.clear(); m_wordWrapLines.clear(); IControl::ComputeLayout(parent); } void TextBox::ComputeLayout(const RECT& bounds, float dx, float dy) { m_wordWrap.clear(); m_wordWrapLines.clear(); IControl::ComputeLayout(bounds, dx, dy); } void TextBox::SetText(const wchar_t* text) { m_text = text; m_wordWrapLines.clear(); m_wordWrap.clear(); m_topLine = 0; } void TextBox::UpdateRects() { m_trackRect.top = m_scrollRect.top + 1; m_trackRect.left = m_scrollRect.left + 1; m_trackRect.right = m_scrollRect.right - 1; m_trackRect.bottom = m_scrollRect.bottom - 1; m_thumbRect.left = m_trackRect.left; m_thumbRect.right = m_trackRect.right; if (m_wordWrapLines.empty() || !m_lastHeight) { m_thumbRect.bottom = m_thumbRect.top; return; } int maxl = static_cast(float(m_itemRect.bottom - m_itemRect.top - c_MarginSize * 2) / float(m_lastHeight)); UpdateScrollBar(m_thumbRect, m_trackRect, m_topLine, 0, static_cast(m_wordWrapLines.size()), maxl); } //===================================================================================== // Popup //===================================================================================== Popup::Popup(const RECT& rect, unsigned int styleFlags) : IPanel(rect), m_select(false), m_cancel(false), m_suppressCancel((styleFlags & c_styleSuppressCancel) == c_styleSuppressCancel), m_emphasis((styleFlags & c_stylePopupEmphasis) == c_stylePopupEmphasis), m_custom((styleFlags & c_styleCustomPanel) == c_styleCustomPanel), m_focusControl(nullptr) { } Popup::~Popup() { for (IControl* it : m_controls) { delete it; } m_controls.clear(); m_focusControl = nullptr; } void Popup::Show() { if (m_visible) return; auto mgr = UIManager::Impl::s_uiManager; if (!mgr) { throw std::exception("UIManager"); } OnWindowSize(mgr->m_fullscreen); m_focusControl = InitFocus(m_controls); if (m_focusControl) { m_focusControl->OnFocus(true); } else if (!m_custom) { throw std::exception("No usable controls"); } // Make visible m_visible = true; // Clear any pending action m_select = false; m_cancel = false; // For now, no focus stack. Just grab focus for a popup mgr->m_focusPanel = this; } void Popup::Render() { auto mgr = UIManager::Impl::s_uiManager; if (!mgr) { throw std::exception("UIManager"); } auto batch = mgr->m_batch.get(); #if defined(__d3d12_h__) || defined(__d3d12_x_h__) || defined(__XBOX_D3D12_X__) mgr->m_commandList->RSSetScissorRects(1, &mgr->m_fullscreen); batch->Begin(mgr->m_commandList); #else batch->Begin(SpriteSortMode_Deferred, mgr->m_blendState.Get()); #endif if (m_emphasis) { // fade out all other elements to emphasize popup XMFLOAT4 color = mgr->mConfig.colorBackground; color.w *= 0.5f; mgr->DrawRect(mgr->m_fullscreen, XMLoadFloat4(&color)); } XMVECTOR bgColor = XMLoadFloat4(&mgr->mConfig.colorBackground); mgr->DrawRect(m_screenRect, bgColor); batch->End(); mgr->RenderControls(m_controls); // Restore scissors to full screen #if defined(__d3d12_h__) || defined(__d3d12_x_h__) || defined(__XBOX_D3D12_X__) mgr->m_commandList->RSSetScissorRects(1, &mgr->m_fullscreen); #else mgr->m_context->RSSetScissorRects(1, &mgr->m_fullscreen); #endif } bool Popup::Update(float elapsedTime, const GamePad::State& pad) { auto mgr = UIManager::Impl::s_uiManager; if (!mgr) { throw std::exception("UIManager"); } bool result = false; if (!m_focusControl) { m_focusControl = InitFocus(m_controls); if (m_focusControl) m_focusControl->OnFocus(true); } if (m_focusControl) { result = m_focusControl->Update(elapsedTime, pad); } if (!result) { if (pad.IsDPadDownPressed()) { if (mgr->m_padButtonState.dpadDown == GamePad::ButtonStateTracker::PRESSED) { m_focusControl = NextFocus(m_focusControl, m_controls); } } else if (pad.IsDPadUpPressed()) { if (mgr->m_padButtonState.dpadUp == GamePad::ButtonStateTracker::PRESSED) { m_focusControl = PrevFocus(m_focusControl, m_controls); } } else if (pad.IsBPressed()) { if (mgr->m_padButtonState.b == GamePad::ButtonStateTracker::PRESSED) { Cancel(); } } else if (pad.IsAPressed()) { m_select = true; } else if (m_select && !pad.IsAPressed()) { ControlSelected(m_focusControl, this); m_select = false; } } return true; } bool Popup::Update(float elapsedTime, const Mouse::State& mstate, const Keyboard::State& kbstate) { auto mgr = UIManager::Impl::s_uiManager; if (!mgr) { throw std::exception("UIManager"); } bool result = false; if (!m_focusControl) { m_focusControl = InitFocus(m_controls); if (m_focusControl) m_focusControl->OnFocus(true); } if (m_focusControl) { result = m_focusControl->Update(elapsedTime, mstate, kbstate); } if (!result) { // Handle mouse if (mstate.positionMode == Mouse::MODE_ABSOLUTE) { if ((m_screenRect.left < mstate.x) && (mstate.x < m_screenRect.right) && (m_screenRect.top < mstate.y) && (mstate.y < m_screenRect.bottom)) { if (mgr->m_mouseLastX != mstate.x || mgr->m_mouseLastY != mstate.y) { m_focusControl = MouseFocus(mstate.x, mstate.y, m_focusControl, m_controls); } } if (mgr->m_mouseButtonState.leftButton == Mouse::ButtonStateTracker::PRESSED) { if (m_focusControl && m_focusControl->Contains(mstate.x, mstate.y)) { m_select = true; } } else if (m_select && !mstate.leftButton) { ControlSelected(m_focusControl, this); m_select = false; } } // Handle keyboard if (kbstate.IsKeyDown(Keyboard::Down)) { if (mgr->m_keyboardState.IsKeyPressed(Keyboard::Down)) { m_focusControl = NextFocus(m_focusControl, m_controls); } } else if (kbstate.IsKeyDown(Keyboard::Up)) { if (mgr->m_keyboardState.IsKeyPressed(Keyboard::Up)) { m_focusControl = PrevFocus(m_focusControl, m_controls); } } else if (kbstate.IsKeyDown(Keyboard::Space)) { if (mgr->m_keyboardState.IsKeyPressed(Keyboard::Space)) { ControlSelected(m_focusControl, this); } } else if (kbstate.IsKeyDown(Keyboard::Enter)) { if (mgr->m_keyboardState.IsKeyPressed(Keyboard::Enter)) { ControlSelected(m_focusControl, this); } } else if (kbstate.Escape) { m_cancel = true; } else if (m_cancel && !kbstate.Escape) { Cancel(); m_cancel = false; } else if (m_focusControl) { auto ctrl = HotKeyFocus(m_focusControl, m_controls, mgr->m_keyboardState); if (ctrl) { m_focusControl = ctrl; ControlSelected(ctrl, this); } } } return true; } void Popup::Close() { if (!m_visible) return; unsigned ctrlId = 0; if (m_focusControl) { ctrlId = m_focusControl->GetId(); } if (m_callBack) m_callBack(this, ctrlId); if (m_focusControl) { m_focusControl->OnFocus(false); m_focusControl = nullptr; } m_visible = false; // For now, no focus stack. auto mgr = UIManager::Impl::s_uiManager; if (!mgr) { throw std::exception("UIManager"); } if (mgr->m_focusPanel == this) { mgr->m_focusPanel = nullptr; } } void Popup::Cancel() { if (m_suppressCancel) return; if (m_callBack) m_callBack(this, unsigned(-1)); if (m_focusControl) { m_focusControl->OnFocus(false); m_focusControl = nullptr; } m_visible = false; // For now, no focus stack. auto mgr = UIManager::Impl::s_uiManager; if (!mgr) { throw std::exception("UIManager"); } if (mgr->m_focusPanel == this) { mgr->m_focusPanel = nullptr; } } void Popup::Add(_In_ IControl* ctrl) { if (ctrl) { m_controls.push_back(ctrl); ctrl->SetParent(this); } } IControl* Popup::Find(unsigned id) { for (auto it : m_controls) { if (it->GetId() == id) return it; } return nullptr; } void Popup::SetFocus(_In_ IControl* ctrl) { m_focusControl = ::SetFocusCtrl(m_focusControl, ctrl); } void Popup::OnWindowSize(const RECT& layout) { // Popups should center automatically long dx = (m_layoutRect.right - m_layoutRect.left); long dy = (m_layoutRect.bottom - m_layoutRect.top); m_screenRect.left = layout.left + (layout.right - layout.left) / 2 - dx / 2; m_screenRect.top = layout.top + (layout.bottom - layout.top) / 2 - dy / 2; m_screenRect.right = m_screenRect.left + dx; m_screenRect.bottom = m_screenRect.top + dy; for (IControl* it : m_controls) { it->ComputeLayout(m_screenRect); } } //===================================================================================== // HUD //===================================================================================== HUD::HUD(const RECT& rect) : IPanel(rect) { } HUD::~HUD() { for (IControl* it : m_controls) { delete it; } m_controls.clear(); } void HUD::Show() { if (m_visible) return; auto mgr = UIManager::Impl::s_uiManager; if (!mgr) { throw std::exception("UIManager"); } if (mgr->m_hudPanel) { mgr->m_hudPanel->Close(); } OnWindowSize(mgr->m_fullscreen); mgr->m_hudPanel = this; m_visible = true; } void HUD::Render() { auto mgr = UIManager::Impl::s_uiManager; if (!mgr) { throw std::exception("UIManager"); } mgr->RenderControls(m_controls); } void HUD::Close() { auto mgr = UIManager::Impl::s_uiManager; if (!mgr) { throw std::exception("UIManager"); } if (mgr->m_hudPanel == this) { mgr->m_hudPanel = nullptr; } m_visible = false; } void HUD::Add(_In_ IControl* ctrl) { if (ctrl) { m_controls.push_back(ctrl); ctrl->SetParent(this); } } IControl* HUD::Find(unsigned id) { for (auto it : m_controls) { if (it->GetId() == id) return it; } return nullptr; } void HUD::OnWindowSize(const RECT& layout) { float dx = float(layout.right - layout.left) / float(m_screenRect.right - m_screenRect.left); float dy = float(layout.bottom - layout.top) / float(m_screenRect.bottom - m_screenRect.top); for (IControl* it : m_controls) { it->ComputeLayout(layout, dx, dy); } } //===================================================================================== // Overlay //===================================================================================== Overlay::Overlay(const RECT& rect, unsigned int styleFlags) : IPanel(rect), m_select(false), m_cancel(false), m_suppressCancel((styleFlags & c_styleSuppressCancel) == c_styleSuppressCancel), m_custom((styleFlags & c_styleCustomPanel) == c_styleCustomPanel), m_focusControl(nullptr) { } Overlay::~Overlay() { for (IControl* it : m_controls) { delete it; } m_controls.clear(); m_focusControl = nullptr; } void Overlay::Show() { if (m_visible) return; auto mgr = UIManager::Impl::s_uiManager; if (!mgr) { throw std::exception("UIManager"); } if (mgr->m_overlayPanel) { mgr->m_overlayPanel->Close(); } OnWindowSize(mgr->m_fullscreen); m_focusControl = InitFocus(m_controls); if (m_focusControl) { m_focusControl->OnFocus(true); } else if (!m_custom) { throw std::exception("No usable controls"); } // Make visible m_visible = true; // Clear any pending action m_select = false; m_cancel = false; mgr->m_overlayPanel = this; } void Overlay::Render() { auto mgr = UIManager::Impl::s_uiManager; if (!mgr) { throw std::exception("UIManager"); } mgr->RenderControls(m_controls); } bool Overlay::Update(float elapsedTime, const GamePad::State& pad) { auto mgr = UIManager::Impl::s_uiManager; if (!mgr) { throw std::exception("UIManager"); } bool result = false; if (!m_focusControl) { m_focusControl = InitFocus(m_controls); if (m_focusControl) m_focusControl->OnFocus(true); } if (m_focusControl) { result = m_focusControl->Update(elapsedTime, pad); } if (!result) { if (pad.IsDPadDownPressed()) { if (mgr->m_padButtonState.dpadDown == GamePad::ButtonStateTracker::PRESSED) { m_focusControl = NextFocus(m_focusControl, m_controls); } } else if (pad.IsDPadUpPressed()) { if (mgr->m_padButtonState.dpadUp == GamePad::ButtonStateTracker::PRESSED) { m_focusControl = PrevFocus(m_focusControl, m_controls); } } else if (pad.IsBPressed()) { if (mgr->m_padButtonState.b == GamePad::ButtonStateTracker::PRESSED) { Cancel(); } } else if (pad.IsAPressed()) { m_select = true; } else if (m_select && !pad.IsAPressed()) { ControlSelected(m_focusControl, this); m_select = false; } } return result; } bool Overlay::Update(float elapsedTime, const Mouse::State& mstate, const Keyboard::State& kbstate) { auto mgr = UIManager::Impl::s_uiManager; if (!mgr) { throw std::exception("UIManager"); } bool result = false; if (!m_focusControl) { m_focusControl = InitFocus(m_controls); if (m_focusControl) m_focusControl->OnFocus(true); } if (m_focusControl) { result = m_focusControl->Update(elapsedTime, mstate, kbstate); } if (!result) { // Handle mouse if (mstate.positionMode == Mouse::MODE_ABSOLUTE) { if ((m_screenRect.left < mstate.x) && (mstate.x < m_screenRect.right) && (m_screenRect.top < mstate.y) && (mstate.y < m_screenRect.bottom)) { if (mgr->m_mouseLastX != mstate.x || mgr->m_mouseLastY != mstate.y) { m_focusControl = MouseFocus(mstate.x, mstate.y, m_focusControl, m_controls); } } if (mgr->m_mouseButtonState.leftButton == Mouse::ButtonStateTracker::PRESSED) { if (m_focusControl && m_focusControl->Contains(mstate.x, mstate.y)) { m_select = true; } } else if (m_select && !mstate.leftButton) { ControlSelected(m_focusControl, this); m_select = false; } } // Handle keyboard if (kbstate.IsKeyDown(Keyboard::Down)) { if (mgr->m_keyboardState.IsKeyPressed(Keyboard::Down)) { m_focusControl = NextFocus(m_focusControl, m_controls); } } else if (kbstate.IsKeyDown(Keyboard::Up)) { if (mgr->m_keyboardState.IsKeyPressed(Keyboard::Up)) { m_focusControl = PrevFocus(m_focusControl, m_controls); } } else if (kbstate.IsKeyDown(Keyboard::Space)) { if (mgr->m_keyboardState.IsKeyPressed(Keyboard::Space)) { ControlSelected(m_focusControl, this); } } else if (kbstate.IsKeyDown(Keyboard::Enter)) { if (mgr->m_keyboardState.IsKeyPressed(Keyboard::Enter)) { ControlSelected(m_focusControl, this); } } else if (kbstate.Escape) { m_cancel = true; } else if (m_cancel && !kbstate.Escape) { Cancel(); m_cancel = false; } else if (m_focusControl) { auto ctrl = HotKeyFocus(m_focusControl, m_controls, mgr->m_keyboardState); if (ctrl) { m_focusControl = ctrl; ControlSelected(ctrl, this); } } } return true; } void Overlay::Close() { if (!m_visible) return; unsigned ctrlId = 0; if (m_focusControl) { ctrlId = m_focusControl->GetId(); } if (m_callBack) m_callBack(this, ctrlId); if (m_focusControl) { m_focusControl->OnFocus(false); m_focusControl = nullptr; } m_visible = false; auto mgr = UIManager::Impl::s_uiManager; if (!mgr) { throw std::exception("UIManager"); } if (mgr->m_overlayPanel == this) { mgr->m_overlayPanel = nullptr; } } void Overlay::Cancel() { if (m_suppressCancel) return; if (m_callBack) m_callBack(this, unsigned(-1)); if (m_focusControl) { m_focusControl->OnFocus(false); m_focusControl = nullptr; } m_visible = false; // For now, no focus stack. auto mgr = UIManager::Impl::s_uiManager; if (!mgr) { throw std::exception("UIManager"); } if (mgr->m_overlayPanel == this) { mgr->m_overlayPanel = nullptr; } } void Overlay::Add(_In_ IControl* ctrl) { if (ctrl) { m_controls.push_back(ctrl); ctrl->SetParent(this); } } IControl* Overlay::Find(unsigned id) { for (auto it : m_controls) { if (it->GetId() == id) return it; } return nullptr; } void Overlay::SetFocus(_In_ IControl* ctrl) { m_focusControl = ::SetFocusCtrl(m_focusControl, ctrl); } void Overlay::OnWindowSize(const RECT& layout) { float dx = float(layout.right - layout.left) / float(m_screenRect.right - m_screenRect.left); float dy = float(layout.bottom - layout.top) / float(m_screenRect.bottom - m_screenRect.top); for (IControl* it : m_controls) { it->ComputeLayout(layout, dx, dy); } } ================================================ FILE: Tests/GDK/APIRunner.GDK/Kits/ATGTK/SampleGUI.h ================================================ //-------------------------------------------------------------------------------------- // File: SampleGUI.h // // A simple set of UI widgets for use in ATG samples // // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A // PARTICULAR PURPOSE. // // Copyright (c) Microsoft Corporation. All rights reserved. //------------------------------------------------------------------------------------- #pragma once #include "GamePad.h" #include "Keyboard.h" #include "Mouse.h" #if defined(__d3d12_h__) || defined(__d3d12_x_h__) || defined(__XBOX_D3D12_X__) #include "DescriptorHeap.h" #include "ResourceUploadBatch.h" #endif #include #include #include #include #include #include #ifndef _CPPRTTI #error ATG Sample GUI requires RTTI #endif namespace ATG { class IPanel; //---------------------------------------------------------------------------------- // A control is an individual UI element class IControl { public: virtual ~IControl() {} // Methods virtual void Render() = 0; virtual bool Contains(long x, long y) const { return (m_screenRect.left <= x) && (x < m_screenRect.right) && (m_screenRect.top <= y) && (y < m_screenRect.bottom); } virtual void ComputeLayout(const RECT& parent); virtual void ComputeLayout(const RECT& bounds, float dx, float dy); virtual bool CanFocus() const { return false; } virtual bool DefaultFocus() const { return false; } virtual void OnFocus(bool in) { m_focus = in; if (in && m_focusCb) { m_focusCb(nullptr, this); } } virtual bool OnSelected(IPanel*) { return false; } virtual bool Update(float /*elapsedTime*/, const DirectX::GamePad::State&) { return false; } virtual bool Update(float /*elapsedTime*/, const DirectX::Mouse::State&, const DirectX::Keyboard::State&) { return false; } // Properties using callback_t = std::function; void SetCallback(_In_opt_ callback_t callback) { m_callBack = callback; } void SetFocusCb(_In_opt_ callback_t callback) { m_focusCb = callback; } unsigned GetHotKey() const { return m_hotKey; } void SetHotKey(unsigned hotkey) { m_hotKey = hotkey; } void SetId(unsigned id) { m_id = id; } unsigned GetId() const { return m_id; } void SetUser(void* user) { m_user = user; } void* GetUser() const { return m_user; } void SetParent(IPanel* panel) { m_parent = panel; } void SetVisible(bool visible = true); bool IsVisible() const { return m_visible; } const RECT* GetRectangle() const { return &m_screenRect; } protected: IControl(const RECT& rect, unsigned id) : m_visible(true), m_focus(false), m_layoutRect(rect), m_screenRect(rect), m_hotKey(0), m_id(id), m_user(nullptr), m_parent(nullptr) { } bool m_visible; bool m_focus; RECT m_layoutRect; RECT m_screenRect; callback_t m_callBack; callback_t m_focusCb; unsigned m_hotKey; unsigned m_id; void* m_user; IPanel* m_parent; }; // Static text label class TextLabel : public IControl { public: TextLabel(unsigned id, _In_z_ const wchar_t* text, const RECT& rect, unsigned style = 0); // Properties void XM_CALLCONV SetForegroundColor(DirectX::FXMVECTOR color) { DirectX::XMStoreFloat4(&m_fgColor, color); } void XM_CALLCONV SetBackgroundColor(DirectX::FXMVECTOR color) { DirectX::XMStoreFloat4(&m_bgColor, color); } static const unsigned c_StyleAlignLeft = 0; static const unsigned c_StyleAlignCenter = 0x1; static const unsigned c_StyleAlignRight = 0x2; static const unsigned c_StyleAlignTop = 0x0; static const unsigned c_StyleAlignMiddle = 0x4; static const unsigned c_StyleAlignBottom = 0x8; static const unsigned c_StyleTransparent = 0x10; static const unsigned c_StyleWordWrap = 0x20; static const unsigned c_StyleFontSmall = 0x10000; static const unsigned c_StyleFontMid = 0; static const unsigned c_StyleFontLarge = 0x20000; static const unsigned c_StyleFontBold = 0x40000; static const unsigned c_StyleFontItalic = 0x80000; void SetStyle(unsigned style) { m_style = style; } unsigned GetStyle() const { return m_style; } void SetText(const wchar_t* text); const wchar_t* GetText() const { return m_text.c_str(); } // IControl void Render() override; void ComputeLayout(const RECT& parent) override; void ComputeLayout(const RECT& bounds, float dx, float dy) override; bool Contains(long, long) const override { return false; } private: unsigned m_style; DirectX::XMFLOAT4 m_fgColor; DirectX::XMFLOAT4 m_bgColor; std::wstring m_text; std::wstring m_wordWrap; }; // Static image class Image : public IControl { public: Image(unsigned id, unsigned imageId, const RECT& rect); void SetImageId(unsigned imageId) { m_imageId = imageId; } unsigned GetImageId() const { return m_imageId; } // IControl void Render() override; bool Contains(long, long) const override { return false; } private: unsigned m_imageId; }; // Static text label that supports the controller font class Legend : public IControl { public: Legend(unsigned id, _In_z_ const wchar_t* text, const RECT& rect, unsigned style = 0); // Properties void XM_CALLCONV SetForegroundColor(DirectX::FXMVECTOR color) { DirectX::XMStoreFloat4(&m_fgColor, color); } void XM_CALLCONV SetBackgroundColor(DirectX::FXMVECTOR color) { DirectX::XMStoreFloat4(&m_bgColor, color); } static const unsigned c_StyleAlignLeft = 0; static const unsigned c_StyleAlignCenter = 0x1; static const unsigned c_StyleAlignRight = 0x2; static const unsigned c_StyleAlignTop = 0x0; static const unsigned c_StyleAlignMiddle = 0x4; static const unsigned c_StyleAlignBottom = 0x8; static const unsigned c_StyleTransparent = 0x10; static const unsigned c_StyleFontSmall = 0x10000; static const unsigned c_StyleFontMid = 0; static const unsigned c_StyleFontLarge = 0x20000; static const unsigned c_StyleFontBold = 0x40000; static const unsigned c_StyleFontItalic = 0x80000; void SetStyle(unsigned style) { m_style = style; } unsigned GetStyle() const { return m_style; } void SetText(const wchar_t* text) { m_text = text; } const wchar_t* GetText() const { return m_text.c_str(); } // IControl void Render() override; bool Contains(long, long) const override { return false; } private: unsigned m_style; DirectX::XMFLOAT4 m_bgColor; DirectX::XMFLOAT4 m_fgColor; std::wstring m_text; }; // Pressable button class Button : public IControl { public: Button(unsigned id, _In_z_ const wchar_t* text, const RECT& rect); // Properties void ShowBorder(bool show = true) { m_showBorder = show; } void NoFocusColor(bool noFocusColor = true) { m_noFocusColor = noFocusColor; } void FocusOnText(bool focusOnText = true ) { m_focusOnText = focusOnText; } void SetEnabled(bool enabled = true) { m_enabled = enabled; } bool IsEnabled() const { return m_enabled; } void XM_CALLCONV SetColor(DirectX::FXMVECTOR color) { DirectX::XMStoreFloat4(&m_color, color); } static const unsigned c_StyleExit = 0x1; static const unsigned c_StyleDefault = 0x2; static const unsigned c_StyleTransparent = 0x4; static const unsigned c_StyleFontSmall = 0x10000; static const unsigned c_StyleFontMid = 0; static const unsigned c_StyleFontLarge = 0x20000; static const unsigned c_StyleFontBold = 0x40000; static const unsigned c_StyleFontItalic = 0x80000; void SetStyle(unsigned style) { m_style = style; } unsigned GetStyle() const { return m_style; } void SetText(const wchar_t* text) { m_text = text; } const wchar_t* GetText() const { return m_text.c_str(); } // IControl void Render() override; bool CanFocus() const override { return m_enabled; } bool DefaultFocus() const override { return (m_style & c_StyleDefault) != 0; } bool OnSelected(IPanel* panel) override; private: bool m_enabled; bool m_showBorder; bool m_noFocusColor; bool m_focusOnText; unsigned m_style; std::wstring m_text; DirectX::XMFLOAT4 m_color; }; // Pressable image class ImageButton : public IControl { public: ImageButton(unsigned id, unsigned imageId, const RECT& rect); // Properties void SetEnabled(bool enabled = true) { m_enabled = enabled; } bool IsEnabled() const { return m_enabled; } static const unsigned c_StyleExit = 0x1; static const unsigned c_StyleDefault = 0x2; static const unsigned c_StyleBackground = 0x4; static const unsigned c_StyleTransparent = 0x8; void SetStyle(unsigned style) { m_style = style; } unsigned GetStyle() const { return m_style; } void SetImageId(unsigned imageId) { m_imageId = imageId; } unsigned GetImageId() const { return m_imageId; } // IControl void Render() override; bool CanFocus() const override { return m_enabled; } bool DefaultFocus() const override { return (m_style & c_StyleDefault) != 0; } bool OnSelected(IPanel* panel) override; private: bool m_enabled; unsigned m_style; unsigned m_imageId; }; // Two-state check box class CheckBox : public IControl { public: CheckBox(unsigned id, _In_z_ const wchar_t* text, const RECT& rect, bool checked = false); // Properties void SetEnabled(bool enabled = true) { m_enabled = enabled; } bool IsEnabled() const { return m_enabled; } void SetChecked(bool checked = true) { m_checked = checked; } bool IsChecked() const { return m_checked; } static const unsigned c_StyleTransparent = 0x1; static const unsigned c_StyleFontSmall = 0x10000; static const unsigned c_StyleFontMid = 0; static const unsigned c_StyleFontLarge = 0x20000; static const unsigned c_StyleFontBold = 0x40000; static const unsigned c_StyleFontItalic = 0x80000; void SetStyle(unsigned style) { m_style = style; } unsigned GetStyle() const { return m_style; } void SetText(const wchar_t* text) { m_text = text; } const wchar_t* GetText() const { return m_text.c_str(); } // IControl void Render() override; bool CanFocus() const override { return m_enabled; } bool OnSelected(IPanel* panel) override; private: bool m_enabled; bool m_checked; unsigned m_style; std::wstring m_text; }; // Slider class Slider : public IControl { public: Slider(unsigned id, const RECT& rect, int value = 50, int minValue = 0, int maxValue = 100); // Properties void SetEnabled(bool enabled = true) { m_enabled = enabled; if (!enabled) m_dragging = false; } bool IsEnabled() const { return m_enabled; } static const unsigned c_StyleTransparent = 0x1; void SetStyle(unsigned style) { m_style = style; } unsigned GetStyle() const { return m_style; } void SetValue(int value); int GetValue() const { return m_value; } void SetRange(int minValue, int maxValue); void GetRange(int& minValue, int& maxValue) const { minValue = m_minValue; maxValue = m_maxValue; } // IControl void Render() override; bool CanFocus() const override { return m_enabled; } void OnFocus(bool in) override; bool Update(float elapsedTime, const DirectX::GamePad::State& pad) override; bool Update(float elapsedTime, const DirectX::Mouse::State& mstate, const DirectX::Keyboard::State& kbstate) override; private: bool m_enabled; bool m_dragging; unsigned m_style; int m_value; int m_minValue; int m_maxValue; RECT m_thumbRect; }; // Progress bar that goes from 0.0 to 1.0 class ProgressBar : public IControl { public: ProgressBar(unsigned id, const RECT& rect, bool visible = false, float start = 0.f); ~ProgressBar(); // Properties void SetProgress(float progress) { m_progress = std::min( std::max(progress, 0.f), 1.f); } float GetProgress() const { return m_progress; } void ShowPercentage(bool show = true) { m_showPct = show; } // IControl void Render() override; private: float m_progress; bool m_showPct; }; // TextList class TextList : public IControl { public: TextList(unsigned id, const RECT& rect, unsigned style = 0, int itemHeight = 0); ~TextList(); // Items struct Item { std::wstring text; DirectX::XMFLOAT4 color; }; void XM_CALLCONV AddItem(_In_z_ const wchar_t* text, DirectX::FXMVECTOR color = DirectX::Colors::White); void XM_CALLCONV InsertItem(int index, _In_z_ const wchar_t* text, DirectX::FXMVECTOR color = DirectX::Colors::White); void RemoveItem(int index); void RemoveAllItems(); // IControl void Render() override; bool CanFocus() const override { return false; } bool Update(float elapsedTime, const DirectX::GamePad::State& pad) override; bool Update(float elapsedTime, const DirectX::Mouse::State& mstate, const DirectX::Keyboard::State& kbstate) override; private: int m_itemHeight; unsigned m_style; int m_topItem; RECT m_itemRect; int m_lastHeight; std::vector m_items; }; // List box class ListBox : public IControl { public: ListBox(unsigned id, const RECT& rect, unsigned style = 0, int itemHeight = 0); ~ListBox(); // Items struct Item { std::wstring text; void* user; bool selected; Item() : user(nullptr), selected(false) {} // TODO - add optional image }; void AddItem(_In_z_ const wchar_t* text, _In_opt_ void *user = nullptr); void InsertItem(int index, _In_z_ const wchar_t* text, _In_opt_ void *user = nullptr); void RemoveItem(int index); void RemoveAllItems(); int GetSelectedItem() const; std::vector GetSelectedItems() const; void ClearSelection(); void SelectItem(int index); const Item* GetItem(int index) const { return &m_items[size_t(index)]; } Item* GetItem(int index) { return &m_items[size_t(index)]; } // Properties void SetEnabled(bool enabled = true) { m_enabled = enabled; } bool IsEnabled() const { return m_enabled; } static const unsigned c_StyleMultiSelection = 0x1; static const unsigned c_StyleTransparent = 0x2; static const unsigned c_StyleScrollBar = 0x4; static const unsigned c_StyleFontSmall = 0x10000; static const unsigned c_StyleFontMid = 0; static const unsigned c_StyleFontLarge = 0x20000; static const unsigned c_StyleFontBold = 0x40000; static const unsigned c_StyleFontItalic = 0x80000; void SetStyle(unsigned style) { m_style = style; } unsigned GetStyle() const { return m_style; } // IControl void Render() override; bool CanFocus() const override { return m_enabled; } bool Update(float elapsedTime, const DirectX::GamePad::State& pad) override; bool Update(float elapsedTime, const DirectX::Mouse::State& mstate, const DirectX::Keyboard::State& kbstate) override; private: bool m_enabled; int m_itemHeight; unsigned m_style; int m_topItem; int m_focusItem; RECT m_itemRect; RECT m_scrollRect; RECT m_trackRect; RECT m_thumbRect; int m_lastHeight; std::vector m_items; void UpdateRects(); }; // Text box class TextBox : public IControl { public: TextBox(unsigned id, _In_z_ const wchar_t* text, const RECT& rect, unsigned style = 0); ~TextBox(); // Properties void XM_CALLCONV SetForegroundColor(DirectX::FXMVECTOR color) { DirectX::XMStoreFloat4(&m_color, color); } static const unsigned c_StyleTransparent = 0x1; static const unsigned c_StyleScrollBar = 0x2; static const unsigned c_StyleNoBackground = 0x4; static const unsigned c_StyleFontSmall = 0x10000; static const unsigned c_StyleFontMid = 0; static const unsigned c_StyleFontLarge = 0x20000; static const unsigned c_StyleFontBold = 0x40000; static const unsigned c_StyleFontItalic = 0x80000; void SetStyle(unsigned style) { m_style = style; } unsigned GetStyle() const { return m_style; } void SetText(const wchar_t* text); const wchar_t* GetText() const { return m_text.c_str(); } // IControl void Render() override; void ComputeLayout(const RECT& parent) override; void ComputeLayout(const RECT& bounds, float dx, float dy) override; bool CanFocus() const override { return true; } bool Update(float elapsedTime, const DirectX::GamePad::State& pad) override; bool Update(float elapsedTime, const DirectX::Mouse::State& mstate, const DirectX::Keyboard::State& kbstate) override; private: unsigned m_style; int m_topLine; RECT m_itemRect; RECT m_scrollRect; RECT m_trackRect; RECT m_thumbRect; int m_lastHeight; DirectX::XMFLOAT4 m_color; std::wstring m_text; std::wstring m_wordWrap; std::vector m_wordWrapLines; int m_lastWheelValue; void UpdateRects(); }; //---------------------------------------------------------------------------------- // A panel is a container for UI controls class IPanel { public: virtual ~IPanel() {} // Methods virtual void Show() = 0; virtual void Render() = 0; virtual bool Update(float elapsedTime, const DirectX::GamePad::State& pad) = 0; virtual bool Update(float elapsedTime, const DirectX::Mouse::State& mstate, const DirectX::Keyboard::State& kbstate) = 0; virtual void Update(float /*elapsedTime*/) { }; virtual void Close() = 0; virtual void Cancel() {} virtual void Add(_In_ IControl* ctrl) = 0; virtual IControl* Find(unsigned id) = 0; virtual void SetFocus(_In_ IControl*) {} virtual void OnWindowSize(const RECT& layout) = 0; // Properties using callback_t = std::function; void SetCallback(_In_opt_ callback_t callback) { m_callBack = callback; } void SetUser(void* user) { m_user = user; } void* GetUser() const { return m_user; } bool IsVisible() const { return m_visible; } protected: IPanel(const RECT& rect) : m_visible(false), m_layoutRect(rect), m_screenRect(rect), m_user(nullptr) { } bool m_visible; RECT m_layoutRect; RECT m_screenRect; callback_t m_callBack; void* m_user; }; // Style flags for Popup and Overlay const unsigned int c_styleCustomPanel = 1; // Use this if you want a custom panel where you add controls programatically const unsigned int c_stylePopupEmphasis = 2; // Fades out other UI elements when rendering the popup in order to give it emphasis const unsigned int c_styleSuppressCancel = 4; // Suppress the default cancel behavior that would normally occur when 'B' is pressed class Popup : public IPanel { public: Popup(const RECT& rect, unsigned int styleFlags = 0); ~Popup(); // IPanel void Show() override; void Render() override; bool Update(float elapsedTime, const DirectX::GamePad::State& pad) override; bool Update(float elapsedTime, const DirectX::Mouse::State& mstate, const DirectX::Keyboard::State& kbstate) override; void Update(float) override {} void Close() override; void Cancel() override; void Add(_In_ IControl* ctrl) override; IControl* Find(unsigned id) override; void SetFocus(_In_ IControl* ctrl) override; void OnWindowSize(const RECT& layout) override; private: bool m_select; bool m_cancel; bool m_suppressCancel; bool m_emphasis; const bool m_custom; IControl* m_focusControl; std::vector m_controls; }; class HUD : public IPanel { public: HUD(const RECT& rect); ~HUD(); // IPanel void Show() override; void Render() override; bool Update(float, const DirectX::GamePad::State&) override { return false; } bool Update(float, const DirectX::Mouse::State&, const DirectX::Keyboard::State&) override { return false; } void Update(float) override {} void Close() override; void Add(_In_ IControl* ctrl) override; IControl* Find(unsigned id) override; void OnWindowSize(const RECT& layout) override; private: std::vector m_controls; }; class Overlay : public IPanel { public: Overlay(const RECT& rect, unsigned int styleFlags = 0); ~Overlay(); // IPanel void Show() override; void Render() override; bool Update(float elapsedTime, const DirectX::GamePad::State& pad) override; bool Update(float elapsedTime, const DirectX::Mouse::State& mstate, const DirectX::Keyboard::State& kbstate) override; void Update(float) override {} void Close() override; void Cancel() override; void Add(_In_ IControl* ctrl) override; IControl* Find(unsigned id) override; void SetFocus(_In_ IControl* ctrl) override; void OnWindowSize(const RECT& layout) override; private: bool m_select; bool m_cancel; bool m_suppressCancel; const bool m_custom; IControl* m_focusControl; std::vector m_controls; }; //---------------------------------------------------------------------------------- struct UIConfig { bool forceSRGB; bool pmAlpha; wchar_t largeFontName[MAX_PATH]; wchar_t largeItalicFontName[MAX_PATH]; wchar_t largeBoldFontName[MAX_PATH]; wchar_t midFontName[MAX_PATH]; wchar_t midItalicFontName[MAX_PATH]; wchar_t midBoldFontName[MAX_PATH]; wchar_t smallFontName[MAX_PATH]; wchar_t smallItalicFontName[MAX_PATH]; wchar_t smallBoldFontName[MAX_PATH]; wchar_t largeLegendName[MAX_PATH]; wchar_t smallLegendName[MAX_PATH]; DirectX::XMFLOAT4 colorNormal; DirectX::XMFLOAT4 colorDisabled; DirectX::XMFLOAT4 colorHighlight; DirectX::XMFLOAT4 colorSelected; DirectX::XMFLOAT4 colorFocus; DirectX::XMFLOAT4 colorBackground; DirectX::XMFLOAT4 colorTransparent; DirectX::XMFLOAT4 colorProgress; enum COLORS { RED, GREEN, BLUE, ORANGE, YELLOW, DARK_GREY, MID_GREY, LIGHT_GREY, OFF_WHITE, WHITE, BLACK, MAX_COLORS, }; DirectX::XMFLOAT4 colorDictionary[MAX_COLORS]; UIConfig(bool linear = false, bool pmalpha = true) : forceSRGB(linear), pmAlpha(pmalpha) { using DirectX::XMFLOAT4; wcscpy_s(largeFontName, L"SegoeUI_36.spritefont"); wcscpy_s(largeItalicFontName, L"SegoeUI_36_Italic.spritefont"); wcscpy_s(largeBoldFontName, L"SegoeUI_36_Bold.spritefont"); wcscpy_s(midFontName, L"SegoeUI_22.spritefont"); wcscpy_s(midItalicFontName, L"SegoeUI_22_Italic.spritefont"); wcscpy_s(midBoldFontName, L"SegoeUI_22_Bold.spritefont"); wcscpy_s(smallFontName, L"SegoeUI_18.spritefont"); wcscpy_s(smallItalicFontName, L"SegoeUI_18_Italic.spritefont"); wcscpy_s(smallBoldFontName, L"SegoeUI_18_Bold.spritefont"); wcscpy_s(largeLegendName, L"XboxOneControllerLegend.spritefont"); wcscpy_s(smallLegendName, L"XboxOneControllerLegendSmall.spritefont"); if (linear) { colorNormal = XMFLOAT4(0.361306787f, 0.361306787f, 0.361306787f, 1.f); // OffWhite colorDisabled = XMFLOAT4(0.194617808f, 0.194617808f, 0.194617808f, 1.f); // LightGrey colorHighlight = XMFLOAT4(0.545724571f, 0.026241219f, 0.001517635f, 1.f); // Orange colorSelected = XMFLOAT4(0.955973506f, 0.955973506f, 0.955973506f, 1.f); // White colorFocus = XMFLOAT4(0.005181516f, 0.201556236f, 0.005181516f, 1.f); // Green colorBackground = XMFLOAT4(0.f, 0.f, 0.f, 1.f); // Black colorTransparent = XMFLOAT4(0.033105f, 0.033105f, 0.033105f, 0.5f); colorProgress = XMFLOAT4(0.5f, 0.5f, 0.5f, 1.f); // MidGrey colorDictionary[RED] = XMFLOAT4(1.f, 0.f, 0.f, 1.f); colorDictionary[GREEN] = XMFLOAT4(0.005181516f, 0.201556236f, 0.005181516f, 1.f); colorDictionary[BLUE] = XMFLOAT4(0.001517635f, 0.114435382f, 0.610495627f, 1.f); colorDictionary[ORANGE] = XMFLOAT4(0.545724571f, 0.026241219f, 0.001517635f, 1.f); colorDictionary[YELLOW] = XMFLOAT4(1.f, 1.f, 0.f, 1.f); colorDictionary[DARK_GREY] = XMFLOAT4(0.033104762f, 0.033104762f, 0.033104762f, 1.f); colorDictionary[MID_GREY] = XMFLOAT4(0.113861285f, 0.113861285f, 0.113861285f, 1.f); colorDictionary[LIGHT_GREY] = XMFLOAT4(0.194617808f, 0.194617808f, 0.194617808f, 1.f); colorDictionary[OFF_WHITE] = XMFLOAT4(0.361306787f, 0.361306787f, 0.361306787f, 1.f); colorDictionary[WHITE] = XMFLOAT4(0.955973506f, 0.955973506f, 0.955973506f, 1.f); colorDictionary[BLACK] = XMFLOAT4(0.f, 0.f, 0.f, 1.f); } else { colorNormal = XMFLOAT4(0.635294139f, 0.635294139f, 0.635294139f, 1.f); // OffWhite colorDisabled = XMFLOAT4(0.478431374f, 0.478431374f, 0.478431374f, 1.f); // LightGrey colorHighlight = XMFLOAT4(0.764705896f, 0.176470593f, 0.019607844f, 1.f); // Orange colorSelected = XMFLOAT4(0.980392158f, 0.980392158f, 0.980392158f, 1.f); // White colorFocus = XMFLOAT4(0.062745102f, 0.486274511f, 0.062745102f, 1.f); // Green colorBackground = XMFLOAT4(0.f, 0.f, 0.f, 1.f); // Black colorTransparent = XMFLOAT4(0.2f, 0.2f, 0.2f, 0.5f); colorProgress = XMFLOAT4(0.5f, 0.5f, 0.5f, 1.f); // MidGrey colorDictionary[RED] = XMFLOAT4(1.f, 0.f, 0.f, 1.f); colorDictionary[GREEN] = XMFLOAT4(0.062745102f, 0.486274511f, 0.062745102f, 1.f); colorDictionary[BLUE] = XMFLOAT4(0.019607844f, 0.372549027f, 0.803921580f, 1.f); colorDictionary[ORANGE] = XMFLOAT4(0.764705896f, 0.176470593f, 0.019607844f, 1.f); colorDictionary[YELLOW] = XMFLOAT4(1.f, 1.f, 0.f, 1.f); colorDictionary[DARK_GREY] = XMFLOAT4(0.200000003f, 0.200000003f, 0.200000003f, 1.f); colorDictionary[MID_GREY] = XMFLOAT4(0.371653974f, 0.371653974f, 0.371653974f, 1.f); colorDictionary[LIGHT_GREY] = XMFLOAT4(0.478431374f, 0.478431374f, 0.478431374f, 1.f); colorDictionary[OFF_WHITE] = XMFLOAT4(0.635294139f, 0.635294139f, 0.635294139f, 1.f); colorDictionary[WHITE] = XMFLOAT4(0.980392158f, 0.980392158f, 0.980392158f, 1.f); colorDictionary[BLACK] = XMFLOAT4(0.f, 0.f, 0.f, 1.f); } } }; class UIManager { public: UIManager(const UIConfig& config); #if defined(__d3d12_h__) || defined(__d3d12_x_h__) || defined(__XBOX_D3D12_X__) UIManager(_In_ ID3D12Device *device, const DirectX::RenderTargetState& renderTarget, DirectX::ResourceUploadBatch& resourceUpload, DirectX::DescriptorPile& pile, const UIConfig& config); #elif defined(__d3d11_h__) || defined(__d3d11_x_h__) UIManager(_In_ ID3D11DeviceContext* context, const UIConfig& config); #else # error Please #include or #endif UIManager(UIManager&& moveFrom); UIManager& operator= (UIManager&& moveFrom); UIManager(UIManager const&) = delete; UIManager& operator=(UIManager const&) = delete; virtual ~UIManager(); // Load UI layout from disk void LoadLayout(const wchar_t* layoutFile, const wchar_t* imageDir = nullptr, unsigned offset = 0); // Add a panel (takes ownership) void Add(unsigned id, _In_ IPanel* panel); // Find a panel IPanel* Find(unsigned id) const; template T* FindPanel(unsigned id) const { auto panel = dynamic_cast(Find(id)); if (panel) { return panel; } throw std::exception("Find (panel)"); } // Find a control template T* FindControl(unsigned panelId, unsigned ctrlId) const { auto panel = Find(panelId); if (panel) { auto ctrl = dynamic_cast(panel->Find(ctrlId)); if (ctrl) return ctrl; } throw std::exception("Find (control)"); } // Close all visible panels void CloseAll(); // Process user input for gamepad controls bool Update(float elapsedTime, const DirectX::GamePad::State& pad); // Process user input for keyboard & mouse controls bool Update(float elapsedTime, DirectX::Mouse& mouse, DirectX::Keyboard& kb); // Render the visible UI panels #if defined(__d3d12_h__) || defined(__d3d12_x_h__) || defined(__XBOX_D3D12_X__) void Render(_In_ ID3D12GraphicsCommandList* commandList); #elif defined(__d3d11_h__) || defined(__d3d11_x_h__) void Render(); #endif // Set the screen viewport void SetWindow(const RECT& layout); // Set view rotation void SetRotation(DXGI_MODE_ROTATION rotation); // Texture registry for images (used by controls) static const unsigned c_LayoutImageIdStart = 0x10000; #if defined(__d3d12_h__) || defined(__d3d12_x_h__) || defined(__XBOX_D3D12_X__) void RegisterImage(unsigned id, D3D12_GPU_DESCRIPTOR_HANDLE tex, DirectX::XMUINT2 texSize); #elif defined(__d3d11_h__) || defined(__d3d11_x_h__) void RegisterImage(unsigned id, _In_ ID3D11ShaderResourceView* tex); #endif void UnregisterImage(unsigned id); void UnregisterAllImages(); // Direct3D device management void ReleaseDevice(); #if defined(__d3d12_h__) || defined(__d3d12_x_h__) || defined(__XBOX_D3D12_X__) void RestoreDevice(_In_ ID3D12Device* device, const DirectX::RenderTargetState& renderTarget, DirectX::ResourceUploadBatch& resourceUpload, DirectX::DescriptorPile& pile); #elif defined(__d3d11_h__) || defined(__d3d11_x_h__) void RestoreDevice(_In_ ID3D11DeviceContext* context); #endif // Reset UI state (such as coming back from suspend) void Reset(); // Release all objects void Clear(); // Enumerators void Enumerate(std::function enumCallback); // Common callback adapters void CallbackYesNoCancel(_In_ IPanel* panel, std::function yesnocallback); private: // Private implementation. class Impl; std::unique_ptr pImpl; friend class IControl; friend class TextLabel; friend class Image; friend class Legend; friend class Button; friend class ImageButton; friend class CheckBox; friend class Slider; friend class ProgressBar; friend class ListBox; friend class TextBox; friend class TextList; friend class Popup; friend class HUD; friend class Overlay; }; } ================================================ FILE: Tests/GDK/APIRunner.GDK/Kits/ATGTK/StringUtil.cpp ================================================ //-------------------------------------------------------------------------------------- // StringUtil.cpp // // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. //-------------------------------------------------------------------------------------- #include "pch.h" #include "StringUtil.h" #include #include namespace { constexpr DWORD MBConversionFlags = MB_ERR_INVALID_CHARS; constexpr DWORD WCConversionFlags = WC_ERR_INVALID_CHARS; } // Get the wchar length of a utf8 string size_t DX::GetWideLength(const char* utf8String, size_t utf8Length) { const int newLen = ::MultiByteToWideChar( CP_UTF8, MBConversionFlags, utf8String, static_cast(utf8Length), nullptr, 0 ); return static_cast(newLen); } // Get the utf8 length of a wchar string size_t DX::GetUtf8Length(const wchar_t* wideString, size_t wideLength) { const int newLen = ::WideCharToMultiByte( CP_UTF8, WCConversionFlags, wideString, static_cast(wideLength), nullptr, 0, nullptr, nullptr ); return static_cast(newLen); } std::wstring DX::Utf8ToWide(const char* utf8String, size_t utf8Length) { std::wstring dest; const size_t wideLength = GetWideLength(utf8String, utf8Length); dest.resize(wideLength); ::MultiByteToWideChar( CP_UTF8, MBConversionFlags, utf8String, static_cast(utf8Length), &dest[0], static_cast(wideLength) ); return dest; } std::string DX::WideToUtf8(const wchar_t* wideString, size_t wideLength) { std::string dest; const size_t utf8Length = GetUtf8Length(wideString, wideLength); dest.resize(utf8Length); ::WideCharToMultiByte( CP_UTF8, WCConversionFlags, wideString, static_cast(wideLength), &dest[0], static_cast(utf8Length), nullptr, nullptr ); return dest; } std::wstring DX::Utf8ToWide(const std::string& utf8String) { return Utf8ToWide(utf8String.c_str(), utf8String.length()); } std::string DX::WideToUtf8(const std::wstring& wideString) { return WideToUtf8(wideString.c_str(), wideString.length()); } std::string DX::ToLower(const std::string & utf8String) { std::string lower = utf8String; ToLowerInPlace(lower); return lower; } void DX::ToLowerInPlace(std::string& utf8String) { std::transform( utf8String.begin(), utf8String.end(), utf8String.begin(), [](char c) { return static_cast(std::tolower(static_cast(c))); } ); } std::wstring DX::ToLower(const std::wstring & wideString) { std::wstring lower = wideString; ToLowerInPlace(lower); return lower; } void DX::ToLowerInPlace(std::wstring & wideString) { std::transform( wideString.begin(), wideString.end(), wideString.begin(), [](wchar_t c) { return static_cast(std::tolower(static_cast(c))); } ); } std::string DX::ToUpper(const std::string & utf8String) { std::string lower = utf8String; ToUpperInPlace(lower); return lower; } void DX::ToUpperInPlace(std::string& utf8String) { std::transform( utf8String.begin(), utf8String.end(), utf8String.begin(), [](char c) { return static_cast(std::toupper(static_cast(c))); } ); } std::wstring DX::ToUpper(const std::wstring & wideString) { std::wstring lower = wideString; ToUpperInPlace(lower); return lower; } void DX::ToUpperInPlace(std::wstring & wideString) { std::transform( wideString.begin(), wideString.end(), wideString.begin(), [](wchar_t c) { return static_cast(std::toupper(static_cast(c))); } ); } ================================================ FILE: Tests/GDK/APIRunner.GDK/Kits/ATGTK/StringUtil.h ================================================ //-------------------------------------------------------------------------------------- // StringUtil.h // // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. //-------------------------------------------------------------------------------------- #pragma once #include namespace DX { size_t GetWideLength(const char* utf8String, size_t utf8Length); size_t GetUtf8Length(const wchar_t* wideString, size_t wideLength); std::wstring Utf8ToWide(const char* utf8String, size_t utf8Length); std::string WideToUtf8(const wchar_t* wideString, size_t wideLength); std::wstring Utf8ToWide(const std::string& utf8String); std::string WideToUtf8(const std::wstring& wideString); std::string ToLower(const std::string& utf8String); void ToLowerInPlace(std::string& utf8String); std::wstring ToLower(const std::wstring& wideString); void ToLowerInPlace(std::wstring& wideString); std::string ToUpper(const std::string& utf8String); void ToUpperInPlace(std::string& utf8String); std::wstring ToUpper(const std::wstring& wideString); void ToUpperInPlace(std::wstring& wideString); } ================================================ FILE: Tests/GDK/APIRunner.GDK/Kits/ATGTK/TextConsole.cpp ================================================ //-------------------------------------------------------------------------------------- // File: TextConsole.cpp // // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. //-------------------------------------------------------------------------------------- #include "pch.h" #include "TextConsole.h" #include "SimpleMath.h" #include "DDSTextureLoader.h" #include "WICTextureLoader.h" #include using Microsoft::WRL::ComPtr; using namespace DirectX; using namespace DX; const XMVECTORF32 TextConsole::Line::s_defaultColor = Colors::Transparent; TextConsole::TextConsole() noexcept : m_layout{}, m_foregroundColor(1.f, 1.f, 1.f, 1.f), m_debugOutput(false), m_columns(0), m_rows(0) { Clear(); } #if defined(__d3d12_h__) || defined(__d3d12_x_h__) || defined(__XBOX_D3D12_X__) _Use_decl_annotations_ TextConsole::TextConsole( ID3D12Device* device, ResourceUploadBatch& upload, const RenderTargetState& rtState, const wchar_t* fontName, D3D12_CPU_DESCRIPTOR_HANDLE cpuDescriptor, D3D12_GPU_DESCRIPTOR_HANDLE gpuDescriptor) noexcept(false) : m_layout{}, m_foregroundColor(1.f, 1.f, 1.f, 1.f), m_debugOutput(false), m_columns(0), m_rows(0) { RestoreDevice(device, upload, rtState, fontName, cpuDescriptor, gpuDescriptor); Clear(); } #else _Use_decl_annotations_ TextConsole::TextConsole(ID3D11DeviceContext* context, const wchar_t* fontName) noexcept(false) : m_layout{}, m_foregroundColor(1.f, 1.f, 1.f, 1.f), m_debugOutput(false), m_columns(0), m_rows(0) { RestoreDevice(context, fontName); Clear(); } #endif #if defined(__d3d12_h__) || defined(__d3d12_x_h__) || defined(__XBOX_D3D12_X__) void TextConsole::Render(_In_ ID3D12GraphicsCommandList* commandList) #else void TextConsole::Render() #endif { if (!m_lines) return; std::lock_guard lock(m_mutex); float lineSpacing = m_font->GetLineSpacing(); float x = float(m_layout.left); float y = float(m_layout.top); XMVECTOR foregroundColor = XMLoadFloat4(&m_foregroundColor); #if defined(__d3d12_h__) || defined(__d3d12_x_h__) || defined(__XBOX_D3D12_X__) m_batch->Begin(commandList); #else m_batch->Begin(); #endif auto textLine = static_cast(m_currentLine + 1) % m_rows; for (unsigned int line = 0; line < m_rows; ++line) { XMFLOAT2 pos(x, y + lineSpacing * float(line)); if (*(m_lines[textLine].m_text)) { XMVECTOR lineColor = XMLoadFloat4(&m_lines[textLine].m_textColor); m_font->DrawString(m_batch.get(), m_lines[textLine].m_text, pos, XMColorEqual(lineColor, Line::s_defaultColor) ? foregroundColor : lineColor); } textLine = static_cast(textLine + 1) % m_rows; } m_batch->End(); } void TextConsole::Clear() noexcept { std::lock_guard lock(m_mutex); if (m_buffer) { memset(m_buffer.get(), 0, sizeof(wchar_t) * (m_columns + 1) * m_rows); } if (m_lines) { for (unsigned int line = 0; line < m_rows; ++line) { m_lines[line].SetColor(); } } m_currentColumn = m_currentLine = 0; } _Use_decl_annotations_ void TextConsole::Write(const wchar_t* str) { Write(Line::s_defaultColor, str); } _Use_decl_annotations_ void XM_CALLCONV TextConsole::Write(FXMVECTOR color, const wchar_t* str) { std::lock_guard lock(m_mutex); ProcessString(color, str); #ifndef NDEBUG if (m_debugOutput) { OutputDebugStringW(str); } #endif } _Use_decl_annotations_ void TextConsole::WriteLine(const wchar_t* str) { WriteLine(Line::s_defaultColor, str); } _Use_decl_annotations_ void XM_CALLCONV TextConsole::WriteLine(FXMVECTOR color, const wchar_t* str) { std::lock_guard lock(m_mutex); ProcessString(color, str); IncrementLine(); #ifndef NDEBUG if (m_debugOutput) { OutputDebugStringW(str); OutputDebugStringW(L"\n"); } #endif } _Use_decl_annotations_ void TextConsole::Format(const wchar_t* strFormat, ...) { va_list argList; va_start(argList, strFormat); FormatImpl(Line::s_defaultColor, strFormat, argList); va_end(argList); } _Use_decl_annotations_ void TextConsole::Format(CXMVECTOR color, const wchar_t* strFormat, ...) { va_list argList; va_start(argList, strFormat); FormatImpl(color, strFormat, argList); va_end(argList); } _Use_decl_annotations_ void TextConsole::FormatImpl(CXMVECTOR color, const wchar_t* strFormat, va_list args) { std::lock_guard lock(m_mutex); auto len = size_t(_vscwprintf(strFormat, args) + 1); if (m_tempBuffer.size() < len) m_tempBuffer.resize(len); memset(m_tempBuffer.data(), 0, sizeof(wchar_t) * len); vswprintf_s(m_tempBuffer.data(), m_tempBuffer.size(), strFormat, args); ProcessString(color, m_tempBuffer.data()); #ifndef NDEBUG if (m_debugOutput) { OutputDebugStringW(m_tempBuffer.data()); } #endif } void TextConsole::SetWindow(const RECT& layout) { std::lock_guard lock(m_mutex); m_layout = layout; assert(m_font != nullptr); float lineSpacing = m_font->GetLineSpacing(); unsigned int rows = std::max(1, static_cast(float(layout.bottom - layout.top) / lineSpacing)); RECT fontLayout = m_font->MeasureDrawBounds(L"X", XMFLOAT2(0, 0)); unsigned int columns = std::max(1, static_cast(float(layout.right - layout.left) / float(fontLayout.right - fontLayout.left))); auto buffer = std::make_unique((columns + 1) * rows); memset(buffer.get(), 0, sizeof(wchar_t) * (columns + 1) * rows); auto lines = std::make_unique(rows); for (unsigned int line = 0; line < rows; ++line) { lines[line].m_text = buffer.get() + (columns + 1) * line; } if (m_lines) { unsigned int c = std::min(columns, m_columns); unsigned int r = std::min(rows, m_rows); for (unsigned int line = 0; line < r; ++line) { memcpy(lines[line].m_text, m_lines[line].m_text, c * sizeof(wchar_t)); lines[line].m_textColor = m_lines[line].m_textColor; } } std::swap(columns, m_columns); std::swap(rows, m_rows); std::swap(buffer, m_buffer); std::swap(lines, m_lines); if ((m_currentColumn >= m_columns) || (m_currentLine >= m_rows)) { IncrementLine(); } } void TextConsole::ReleaseDevice() noexcept { m_batch.reset(); m_font.reset(); #if defined(__d3d11_h__) || defined(__d3d11_x_h__) m_context.Reset(); #endif } #if defined(__d3d12_h__) || defined(__d3d12_x_h__) || defined(__XBOX_D3D12_X__) _Use_decl_annotations_ void TextConsole::RestoreDevice( ID3D12Device* device, ResourceUploadBatch& upload, const RenderTargetState& rtState, const wchar_t* fontName, D3D12_CPU_DESCRIPTOR_HANDLE cpuDescriptor, D3D12_GPU_DESCRIPTOR_HANDLE gpuDescriptor) { { SpriteBatchPipelineStateDescription pd(rtState); m_batch = std::make_unique(device, upload, pd); } m_font = std::make_unique(device, upload, fontName, cpuDescriptor, gpuDescriptor); m_font->SetDefaultCharacter(L' '); } void TextConsole::SetViewport(const D3D12_VIEWPORT& viewPort) { if (m_batch) { m_batch->SetViewport(viewPort); } } #else void TextConsole::RestoreDevice(ID3D11DeviceContext* context, const wchar_t* fontName) { m_context = context; m_batch = std::make_unique(context); ComPtr device; context->GetDevice(device.GetAddressOf()); m_font = std::make_unique(device.Get(), fontName); m_font->SetDefaultCharacter(L' '); } void TextConsole::SetViewport(const D3D11_VIEWPORT& viewPort) { if (m_batch) { m_batch->SetViewport(viewPort); } } #endif void TextConsole::SetRotation(DXGI_MODE_ROTATION rotation) { if (m_batch) { m_batch->SetRotation(rotation); } } _Use_decl_annotations_ void TextConsole::ProcessString(FXMVECTOR color, const wchar_t* str) { if (!m_lines) return; m_lines[m_currentLine].SetColor(color); float width = float(m_layout.right - m_layout.left); for (const wchar_t* ch = str; *ch != 0; ++ch) { if (*ch == '\n') { IncrementLine(); m_lines[m_currentLine].SetColor(color); continue; } bool increment = false; if (m_currentColumn >= m_columns) { increment = true; } else { m_lines[m_currentLine].m_text[m_currentColumn] = *ch; auto fontSize = m_font->MeasureString(m_lines[m_currentLine].m_text); if (XMVectorGetX(fontSize) > width) { m_lines[m_currentLine].m_text[m_currentColumn] = L'\0'; increment = true; } } if (increment) { IncrementLine(); m_lines[m_currentLine].m_text[0] = *ch; m_lines[m_currentLine].SetColor(color); } ++m_currentColumn; } } void TextConsole::IncrementLine() { if (!m_lines) return; m_currentLine = (m_currentLine + 1) % m_rows; m_currentColumn = 0; memset(m_lines[m_currentLine].m_text, 0, sizeof(wchar_t) * (m_columns + 1)); } //-------------------------------------------------------------------------------------- #if defined(__d3d12_h__) || defined(__d3d12_x_h__) || defined(__XBOX_D3D12_X__) TextConsoleImage::TextConsoleImage() noexcept : TextConsole(), m_bgGpuDescriptor{}, m_bgSize{} { } #else TextConsoleImage::TextConsoleImage() noexcept : TextConsole() { } #endif #if defined(__d3d12_h__) || defined(__d3d12_x_h__) || defined(__XBOX_D3D12_X__) _Use_decl_annotations_ TextConsoleImage::TextConsoleImage( ID3D12Device* device, ResourceUploadBatch& upload, const RenderTargetState& rtState, const wchar_t* fontName, const wchar_t* image, D3D12_CPU_DESCRIPTOR_HANDLE cpuDescriptorFont, D3D12_GPU_DESCRIPTOR_HANDLE gpuDescriptorFont, D3D12_CPU_DESCRIPTOR_HANDLE cpuDescriptorImage, D3D12_GPU_DESCRIPTOR_HANDLE gpuDescriptorImage) noexcept(false) : TextConsole(), m_bgGpuDescriptor{}, m_bgSize{} { RestoreDevice(device, upload, rtState, fontName, image, cpuDescriptorFont, gpuDescriptorFont, cpuDescriptorImage, gpuDescriptorImage); } #else _Use_decl_annotations_ TextConsoleImage::TextConsoleImage(ID3D11DeviceContext* context, const wchar_t* fontName, const wchar_t* image) noexcept(false) : TextConsole() { RestoreDevice(context, fontName, image); } #endif #if defined(__d3d12_h__) || defined(__d3d12_x_h__) || defined(__XBOX_D3D12_X__) void TextConsoleImage::Render(_In_ ID3D12GraphicsCommandList* commandList) { m_batch->Begin(commandList); m_batch->Draw(m_bgGpuDescriptor, m_bgSize, m_fullscreen); m_batch->End(); TextConsole::Render(commandList); } #else void TextConsoleImage::Render() { m_batch->Begin(); m_batch->Draw(m_background.Get(), m_fullscreen); m_batch->End(); TextConsole::Render(); } #endif void TextConsoleImage::SetWindow(const RECT& fullscreen, bool useSafeRect) { m_fullscreen = fullscreen; if (useSafeRect) { TextConsole::SetWindow( SimpleMath::Viewport::ComputeTitleSafeArea(UINT(fullscreen.right - fullscreen.left), UINT(fullscreen.bottom - fullscreen.top))); } else { TextConsole::SetWindow(fullscreen); } auto width = UINT(std::max(fullscreen.right - fullscreen.left, 1)); auto height = UINT(std::max(fullscreen.bottom - fullscreen.top, 1)); #if defined(__d3d12_h__) || defined(__d3d12_x_h__) || defined(__XBOX_D3D12_X__) D3D12_VIEWPORT vp = { 0.0f, 0.0f, static_cast(width), static_cast(height), D3D12_DEFAULT_VIEWPORT_MIN_DEPTH, D3D12_DEFAULT_VIEWPORT_MAX_DEPTH }; m_batch->SetViewport(vp); #else auto vp = CD3D11_VIEWPORT(0.0f, 0.0f, static_cast(width), static_cast(height)); m_batch->SetViewport(vp); #endif } void TextConsoleImage::ReleaseDevice() noexcept { TextConsole::ReleaseDevice(); m_background.Reset(); } #if defined(__d3d12_h__) || defined(__d3d12_x_h__) || defined(__XBOX_D3D12_X__) _Use_decl_annotations_ void TextConsoleImage::RestoreDevice( ID3D12Device* device, DirectX::ResourceUploadBatch& upload, const DirectX::RenderTargetState& rtState, const wchar_t* fontName, const wchar_t* image, D3D12_CPU_DESCRIPTOR_HANDLE cpuDescriptorFont, D3D12_GPU_DESCRIPTOR_HANDLE gpuDescriptorFont, D3D12_CPU_DESCRIPTOR_HANDLE cpuDescriptorImage, D3D12_GPU_DESCRIPTOR_HANDLE gpuDescriptorImage) { TextConsole::RestoreDevice(device, upload, rtState, fontName, cpuDescriptorFont, gpuDescriptorFont); wchar_t ext[_MAX_EXT]; _wsplitpath_s(image, nullptr, 0, nullptr, 0, nullptr, 0, ext, _MAX_EXT); if (_wcsicmp(ext, L".dds") == 0) { DX::ThrowIfFailed(CreateDDSTextureFromFile(device, upload, image, m_background.ReleaseAndGetAddressOf())); } else { DX::ThrowIfFailed(CreateWICTextureFromFile(device, upload, image, m_background.ReleaseAndGetAddressOf())); } auto desc = m_background->GetDesc(); if (desc.Dimension != D3D12_RESOURCE_DIMENSION_TEXTURE2D) { throw std::exception("Only supports 2D images"); } D3D12_SHADER_RESOURCE_VIEW_DESC SRVDesc = {}; SRVDesc.Format = desc.Format; SRVDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; SRVDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; SRVDesc.Texture2D.MipLevels = (!desc.MipLevels) ? UINT(-1) : desc.MipLevels; device->CreateShaderResourceView(m_background.Get(), &SRVDesc, cpuDescriptorImage); m_bgGpuDescriptor = gpuDescriptorImage; m_bgSize = XMUINT2(static_cast(desc.Width), desc.Height); } #else _Use_decl_annotations_ void TextConsoleImage::RestoreDevice(ID3D11DeviceContext* context, const wchar_t* fontName, const wchar_t* image) { TextConsole::RestoreDevice(context, fontName); ComPtr device; context->GetDevice(device.GetAddressOf()); wchar_t ext[_MAX_EXT]; _wsplitpath_s(image, nullptr, 0, nullptr, 0, nullptr, 0, ext, _MAX_EXT); if (_wcsicmp(ext, L".dds") == 0) { DX::ThrowIfFailed(CreateDDSTextureFromFile(device.Get(), image, nullptr, m_background.ReleaseAndGetAddressOf())); } else { DX::ThrowIfFailed(CreateWICTextureFromFile(device.Get(), image, nullptr, m_background.ReleaseAndGetAddressOf())); } } #endif ================================================ FILE: Tests/GDK/APIRunner.GDK/Kits/ATGTK/TextConsole.h ================================================ //-------------------------------------------------------------------------------------- // File: TextConsole.h // // Renders a simple on screen console where you can output text information on a // Direct3D surface // // Note: This is best used with monospace rather than proportional fonts // // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. //-------------------------------------------------------------------------------------- #pragma once #if defined(__d3d12_h__) || defined(__d3d12_x_h__) || defined(__XBOX_D3D12_X__) #include "RenderTargetState.h" #include "ResourceUploadBatch.h" #endif #include "SpriteBatch.h" #include "SpriteFont.h" #include #include #include namespace DX { class TextConsole { public: TextConsole() noexcept; #if defined(__d3d12_h__) || defined(__d3d12_x_h__) || defined(__XBOX_D3D12_X__) TextConsole( _In_ ID3D12Device* device, DirectX::ResourceUploadBatch& upload, const DirectX::RenderTargetState& rtState, _In_z_ const wchar_t* fontName, D3D12_CPU_DESCRIPTOR_HANDLE cpuDescriptor, D3D12_GPU_DESCRIPTOR_HANDLE gpuDescriptor) noexcept(false); #elif defined(__d3d11_h__) || defined(__d3d11_x_h__) TextConsole(_In_ ID3D11DeviceContext* context, _In_z_ const wchar_t* fontName) noexcept(false); #else # error Please #include or #endif TextConsole(TextConsole&&) = delete; TextConsole& operator= (TextConsole&&) = delete; TextConsole(TextConsole const&) = delete; TextConsole& operator= (TextConsole const&) = delete; #if defined(__d3d12_h__) || defined(__d3d12_x_h__) || defined(__XBOX_D3D12_X__) void Render(_In_ ID3D12GraphicsCommandList* commandList); #else void Render(); #endif void Clear() noexcept; void Write(_In_z_ const wchar_t* str); void XM_CALLCONV Write(DirectX::FXMVECTOR color, _In_z_ const wchar_t* str); void WriteLine(_In_z_ const wchar_t* str); void XM_CALLCONV WriteLine(DirectX::FXMVECTOR color, _In_z_ const wchar_t* str); void Format(_In_z_ _Printf_format_string_ const wchar_t* strFormat, ...); void Format(DirectX::CXMVECTOR color, _In_z_ _Printf_format_string_ const wchar_t* strFormat, ...); void SetWindow(const RECT& layout); void XM_CALLCONV SetForegroundColor(DirectX::FXMVECTOR color) { DirectX::XMStoreFloat4(&m_foregroundColor, color); } void SetDebugOutput(bool debug) { m_debugOutput = debug; } void ReleaseDevice() noexcept; #if defined(__d3d12_h__) || defined(__d3d12_x_h__) || defined(__XBOX_D3D12_X__) void RestoreDevice( _In_ ID3D12Device* device, DirectX::ResourceUploadBatch& upload, const DirectX::RenderTargetState& rtState, _In_z_ const wchar_t* fontName, D3D12_CPU_DESCRIPTOR_HANDLE cpuDescriptor, D3D12_GPU_DESCRIPTOR_HANDLE gpuDescriptor); void SetViewport(const D3D12_VIEWPORT& viewPort); #else void RestoreDevice(ID3D11DeviceContext* context, const wchar_t* fontName); void SetViewport(const D3D11_VIEWPORT& viewPort); #endif void SetRotation(DXGI_MODE_ROTATION rotation); protected: void FormatImpl(DirectX::CXMVECTOR color, _In_z_ _Printf_format_string_ const wchar_t* strFormat, va_list args); void XM_CALLCONV ProcessString(DirectX::FXMVECTOR color, _In_z_ const wchar_t* str); void IncrementLine(); struct Line { wchar_t* m_text; DirectX::XMFLOAT4 m_textColor; static const DirectX::XMVECTORF32 s_defaultColor; Line() : m_text(nullptr), m_textColor(s_defaultColor) {} void XM_CALLCONV SetColor(DirectX::FXMVECTOR color = s_defaultColor) { XMStoreFloat4(&m_textColor, color); } }; RECT m_layout; DirectX::XMFLOAT4 m_foregroundColor; bool m_debugOutput; unsigned int m_columns; unsigned int m_rows; unsigned int m_currentColumn; unsigned int m_currentLine; std::unique_ptr m_buffer; std::unique_ptr m_lines; std::vector m_tempBuffer; std::unique_ptr m_batch; std::unique_ptr m_font; #if defined(__d3d11_h__) || defined(__d3d11_x_h__) Microsoft::WRL::ComPtr m_context; #endif std::mutex m_mutex; }; class TextConsoleImage : public TextConsole { public: TextConsoleImage() noexcept; #if defined(__d3d12_h__) || defined(__d3d12_x_h__) || defined(__XBOX_D3D12_X__) TextConsoleImage( _In_ ID3D12Device* device, DirectX::ResourceUploadBatch& upload, const DirectX::RenderTargetState& rtState, _In_z_ const wchar_t* fontName, _In_z_ const wchar_t* image, D3D12_CPU_DESCRIPTOR_HANDLE cpuDescriptorFont, D3D12_GPU_DESCRIPTOR_HANDLE gpuDescriptorFont, D3D12_CPU_DESCRIPTOR_HANDLE cpuDescriptorImage, D3D12_GPU_DESCRIPTOR_HANDLE gpuDescriptorImage) noexcept(false); #else TextConsoleImage(_In_ ID3D11DeviceContext* context, _In_z_ const wchar_t* fontName, _In_z_ const wchar_t* image) noexcept(false); #endif TextConsoleImage(TextConsoleImage&&) = delete; TextConsoleImage& operator= (TextConsoleImage&&) = delete; TextConsoleImage(TextConsoleImage const&) = delete; TextConsoleImage& operator= (TextConsoleImage const&) = delete; #if defined(__d3d12_h__) || defined(__d3d12_x_h__) || defined(__XBOX_D3D12_X__) void Render(_In_ ID3D12GraphicsCommandList* commandList); #else void Render(); #endif void SetWindow(const RECT& layout) = delete; void SetWindow(const RECT& fullscreen, bool useSafeRect); void ReleaseDevice() noexcept; #if defined(__d3d12_h__) || defined(__d3d12_x_h__) || defined(__XBOX_D3D12_X__) void RestoreDevice( _In_ ID3D12Device* device, DirectX::ResourceUploadBatch& upload, const DirectX::RenderTargetState& rtState, _In_z_ const wchar_t* fontName, D3D12_CPU_DESCRIPTOR_HANDLE cpuDescriptor, D3D12_GPU_DESCRIPTOR_HANDLE gpuDescriptor) = delete; void RestoreDevice( _In_ ID3D12Device* device, DirectX::ResourceUploadBatch& upload, const DirectX::RenderTargetState& rtState, _In_z_ const wchar_t* fontName, _In_z_ const wchar_t* image, D3D12_CPU_DESCRIPTOR_HANDLE cpuDescriptorFont, D3D12_GPU_DESCRIPTOR_HANDLE gpuDescriptorFont, D3D12_CPU_DESCRIPTOR_HANDLE cpuDescriptorImage, D3D12_GPU_DESCRIPTOR_HANDLE gpuDescriptorImage); #else void RestoreDevice(_In_ ID3D11DeviceContext* context, _In_z_ const wchar_t* fontName) = delete; void RestoreDevice(_In_ ID3D11DeviceContext* context, _In_z_ const wchar_t* fontName, _In_z_ const wchar_t* image); #endif private: #if defined(__d3d12_h__) || defined(__d3d12_x_h__) || defined(__XBOX_D3D12_X__) D3D12_GPU_DESCRIPTOR_HANDLE m_bgGpuDescriptor; DirectX::XMUINT2 m_bgSize; Microsoft::WRL::ComPtr m_background; #else Microsoft::WRL::ComPtr m_background; #endif RECT m_fullscreen; }; } ================================================ FILE: Tests/GDK/APIRunner.GDK/Kits/ATGTK/d3dx12.h ================================================ //********************************************************* // // Copyright (c) Microsoft. All rights reserved. // This code is licensed under the MIT License (MIT). // THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF // ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY // IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR // PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. // //********************************************************* #ifndef __D3DX12_H__ #define __D3DX12_H__ #include "d3d12.h" #if defined( __cplusplus ) struct CD3DX12_DEFAULT {}; extern const DECLSPEC_SELECTANY CD3DX12_DEFAULT D3D12_DEFAULT; //------------------------------------------------------------------------------------------------ inline bool operator==( const D3D12_VIEWPORT& l, const D3D12_VIEWPORT& r ) noexcept { return l.TopLeftX == r.TopLeftX && l.TopLeftY == r.TopLeftY && l.Width == r.Width && l.Height == r.Height && l.MinDepth == r.MinDepth && l.MaxDepth == r.MaxDepth; } //------------------------------------------------------------------------------------------------ inline bool operator!=( const D3D12_VIEWPORT& l, const D3D12_VIEWPORT& r ) noexcept { return !( l == r ); } //------------------------------------------------------------------------------------------------ struct CD3DX12_RECT : public D3D12_RECT { CD3DX12_RECT() = default; explicit CD3DX12_RECT( const D3D12_RECT& o ) noexcept : D3D12_RECT( o ) {} explicit CD3DX12_RECT( LONG Left, LONG Top, LONG Right, LONG Bottom ) noexcept { left = Left; top = Top; right = Right; bottom = Bottom; } }; //------------------------------------------------------------------------------------------------ struct CD3DX12_VIEWPORT : public D3D12_VIEWPORT { CD3DX12_VIEWPORT() = default; explicit CD3DX12_VIEWPORT( const D3D12_VIEWPORT& o ) noexcept : D3D12_VIEWPORT( o ) {} explicit CD3DX12_VIEWPORT( FLOAT topLeftX, FLOAT topLeftY, FLOAT width, FLOAT height, FLOAT minDepth = D3D12_MIN_DEPTH, FLOAT maxDepth = D3D12_MAX_DEPTH ) noexcept { TopLeftX = topLeftX; TopLeftY = topLeftY; Width = width; Height = height; MinDepth = minDepth; MaxDepth = maxDepth; } explicit CD3DX12_VIEWPORT( _In_ ID3D12Resource* pResource, UINT mipSlice = 0, FLOAT topLeftX = 0.0f, FLOAT topLeftY = 0.0f, FLOAT minDepth = D3D12_MIN_DEPTH, FLOAT maxDepth = D3D12_MAX_DEPTH ) noexcept { auto Desc = pResource->GetDesc(); const UINT64 SubresourceWidth = Desc.Width >> mipSlice; const UINT64 SubresourceHeight = Desc.Height >> mipSlice; switch (Desc.Dimension) { case D3D12_RESOURCE_DIMENSION_BUFFER: TopLeftX = topLeftX; TopLeftY = 0.0f; Width = float(Desc.Width) - topLeftX; Height = 1.0f; break; case D3D12_RESOURCE_DIMENSION_TEXTURE1D: TopLeftX = topLeftX; TopLeftY = 0.0f; Width = (SubresourceWidth ? float(SubresourceWidth) : 1.0f) - topLeftX; Height = 1.0f; break; case D3D12_RESOURCE_DIMENSION_TEXTURE2D: case D3D12_RESOURCE_DIMENSION_TEXTURE3D: TopLeftX = topLeftX; TopLeftY = topLeftY; Width = (SubresourceWidth ? float(SubresourceWidth) : 1.0f) - topLeftX; Height = (SubresourceHeight ? float(SubresourceHeight) : 1.0f) - topLeftY; break; default: break; } MinDepth = minDepth; MaxDepth = maxDepth; } }; //------------------------------------------------------------------------------------------------ struct CD3DX12_BOX : public D3D12_BOX { CD3DX12_BOX() = default; explicit CD3DX12_BOX( const D3D12_BOX& o ) noexcept : D3D12_BOX( o ) {} explicit CD3DX12_BOX( LONG Left, LONG Right ) noexcept { left = static_cast(Left); top = 0; front = 0; right = static_cast(Right); bottom = 1; back = 1; } explicit CD3DX12_BOX( LONG Left, LONG Top, LONG Right, LONG Bottom ) noexcept { left = static_cast(Left); top = static_cast(Top); front = 0; right = static_cast(Right); bottom = static_cast(Bottom); back = 1; } explicit CD3DX12_BOX( LONG Left, LONG Top, LONG Front, LONG Right, LONG Bottom, LONG Back ) noexcept { left = static_cast(Left); top = static_cast(Top); front = static_cast(Front); right = static_cast(Right); bottom = static_cast(Bottom); back = static_cast(Back); } }; inline bool operator==( const D3D12_BOX& l, const D3D12_BOX& r ) noexcept { return l.left == r.left && l.top == r.top && l.front == r.front && l.right == r.right && l.bottom == r.bottom && l.back == r.back; } inline bool operator!=( const D3D12_BOX& l, const D3D12_BOX& r ) noexcept { return !( l == r ); } //------------------------------------------------------------------------------------------------ struct CD3DX12_DEPTH_STENCIL_DESC : public D3D12_DEPTH_STENCIL_DESC { CD3DX12_DEPTH_STENCIL_DESC() = default; explicit CD3DX12_DEPTH_STENCIL_DESC( const D3D12_DEPTH_STENCIL_DESC& o ) noexcept : D3D12_DEPTH_STENCIL_DESC( o ) {} explicit CD3DX12_DEPTH_STENCIL_DESC( CD3DX12_DEFAULT ) noexcept { DepthEnable = TRUE; DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL; DepthFunc = D3D12_COMPARISON_FUNC_LESS; StencilEnable = FALSE; StencilReadMask = D3D12_DEFAULT_STENCIL_READ_MASK; StencilWriteMask = D3D12_DEFAULT_STENCIL_WRITE_MASK; const D3D12_DEPTH_STENCILOP_DESC defaultStencilOp = { D3D12_STENCIL_OP_KEEP, D3D12_STENCIL_OP_KEEP, D3D12_STENCIL_OP_KEEP, D3D12_COMPARISON_FUNC_ALWAYS }; FrontFace = defaultStencilOp; BackFace = defaultStencilOp; } explicit CD3DX12_DEPTH_STENCIL_DESC( BOOL depthEnable, D3D12_DEPTH_WRITE_MASK depthWriteMask, D3D12_COMPARISON_FUNC depthFunc, BOOL stencilEnable, UINT8 stencilReadMask, UINT8 stencilWriteMask, D3D12_STENCIL_OP frontStencilFailOp, D3D12_STENCIL_OP frontStencilDepthFailOp, D3D12_STENCIL_OP frontStencilPassOp, D3D12_COMPARISON_FUNC frontStencilFunc, D3D12_STENCIL_OP backStencilFailOp, D3D12_STENCIL_OP backStencilDepthFailOp, D3D12_STENCIL_OP backStencilPassOp, D3D12_COMPARISON_FUNC backStencilFunc ) noexcept { DepthEnable = depthEnable; DepthWriteMask = depthWriteMask; DepthFunc = depthFunc; StencilEnable = stencilEnable; StencilReadMask = stencilReadMask; StencilWriteMask = stencilWriteMask; FrontFace.StencilFailOp = frontStencilFailOp; FrontFace.StencilDepthFailOp = frontStencilDepthFailOp; FrontFace.StencilPassOp = frontStencilPassOp; FrontFace.StencilFunc = frontStencilFunc; BackFace.StencilFailOp = backStencilFailOp; BackFace.StencilDepthFailOp = backStencilDepthFailOp; BackFace.StencilPassOp = backStencilPassOp; BackFace.StencilFunc = backStencilFunc; } }; //------------------------------------------------------------------------------------------------ // Requires the Windows 10 Creators Update SDK (15063) #if defined(NTDDI_WIN10_RS2) && (NTDDI_VERSION >= NTDDI_WIN10_RS2) struct CD3DX12_DEPTH_STENCIL_DESC1 : public D3D12_DEPTH_STENCIL_DESC1 { CD3DX12_DEPTH_STENCIL_DESC1() = default; explicit CD3DX12_DEPTH_STENCIL_DESC1( const D3D12_DEPTH_STENCIL_DESC1& o ) noexcept : D3D12_DEPTH_STENCIL_DESC1( o ) {} explicit CD3DX12_DEPTH_STENCIL_DESC1( const D3D12_DEPTH_STENCIL_DESC& o ) noexcept { DepthEnable = o.DepthEnable; DepthWriteMask = o.DepthWriteMask; DepthFunc = o.DepthFunc; StencilEnable = o.StencilEnable; StencilReadMask = o.StencilReadMask; StencilWriteMask = o.StencilWriteMask; FrontFace.StencilFailOp = o.FrontFace.StencilFailOp; FrontFace.StencilDepthFailOp = o.FrontFace.StencilDepthFailOp; FrontFace.StencilPassOp = o.FrontFace.StencilPassOp; FrontFace.StencilFunc = o.FrontFace.StencilFunc; BackFace.StencilFailOp = o.BackFace.StencilFailOp; BackFace.StencilDepthFailOp = o.BackFace.StencilDepthFailOp; BackFace.StencilPassOp = o.BackFace.StencilPassOp; BackFace.StencilFunc = o.BackFace.StencilFunc; DepthBoundsTestEnable = FALSE; } explicit CD3DX12_DEPTH_STENCIL_DESC1( CD3DX12_DEFAULT ) noexcept { DepthEnable = TRUE; DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL; DepthFunc = D3D12_COMPARISON_FUNC_LESS; StencilEnable = FALSE; StencilReadMask = D3D12_DEFAULT_STENCIL_READ_MASK; StencilWriteMask = D3D12_DEFAULT_STENCIL_WRITE_MASK; const D3D12_DEPTH_STENCILOP_DESC defaultStencilOp = { D3D12_STENCIL_OP_KEEP, D3D12_STENCIL_OP_KEEP, D3D12_STENCIL_OP_KEEP, D3D12_COMPARISON_FUNC_ALWAYS }; FrontFace = defaultStencilOp; BackFace = defaultStencilOp; DepthBoundsTestEnable = FALSE; } explicit CD3DX12_DEPTH_STENCIL_DESC1( BOOL depthEnable, D3D12_DEPTH_WRITE_MASK depthWriteMask, D3D12_COMPARISON_FUNC depthFunc, BOOL stencilEnable, UINT8 stencilReadMask, UINT8 stencilWriteMask, D3D12_STENCIL_OP frontStencilFailOp, D3D12_STENCIL_OP frontStencilDepthFailOp, D3D12_STENCIL_OP frontStencilPassOp, D3D12_COMPARISON_FUNC frontStencilFunc, D3D12_STENCIL_OP backStencilFailOp, D3D12_STENCIL_OP backStencilDepthFailOp, D3D12_STENCIL_OP backStencilPassOp, D3D12_COMPARISON_FUNC backStencilFunc, BOOL depthBoundsTestEnable ) noexcept { DepthEnable = depthEnable; DepthWriteMask = depthWriteMask; DepthFunc = depthFunc; StencilEnable = stencilEnable; StencilReadMask = stencilReadMask; StencilWriteMask = stencilWriteMask; FrontFace.StencilFailOp = frontStencilFailOp; FrontFace.StencilDepthFailOp = frontStencilDepthFailOp; FrontFace.StencilPassOp = frontStencilPassOp; FrontFace.StencilFunc = frontStencilFunc; BackFace.StencilFailOp = backStencilFailOp; BackFace.StencilDepthFailOp = backStencilDepthFailOp; BackFace.StencilPassOp = backStencilPassOp; BackFace.StencilFunc = backStencilFunc; DepthBoundsTestEnable = depthBoundsTestEnable; } operator D3D12_DEPTH_STENCIL_DESC() const noexcept { D3D12_DEPTH_STENCIL_DESC D; D.DepthEnable = DepthEnable; D.DepthWriteMask = DepthWriteMask; D.DepthFunc = DepthFunc; D.StencilEnable = StencilEnable; D.StencilReadMask = StencilReadMask; D.StencilWriteMask = StencilWriteMask; D.FrontFace.StencilFailOp = FrontFace.StencilFailOp; D.FrontFace.StencilDepthFailOp = FrontFace.StencilDepthFailOp; D.FrontFace.StencilPassOp = FrontFace.StencilPassOp; D.FrontFace.StencilFunc = FrontFace.StencilFunc; D.BackFace.StencilFailOp = BackFace.StencilFailOp; D.BackFace.StencilDepthFailOp = BackFace.StencilDepthFailOp; D.BackFace.StencilPassOp = BackFace.StencilPassOp; D.BackFace.StencilFunc = BackFace.StencilFunc; return D; } }; #endif // NTDDI_WIN10_RS2 //------------------------------------------------------------------------------------------------ struct CD3DX12_BLEND_DESC : public D3D12_BLEND_DESC { CD3DX12_BLEND_DESC() = default; explicit CD3DX12_BLEND_DESC( const D3D12_BLEND_DESC& o ) noexcept : D3D12_BLEND_DESC( o ) {} explicit CD3DX12_BLEND_DESC( CD3DX12_DEFAULT ) noexcept { AlphaToCoverageEnable = FALSE; IndependentBlendEnable = FALSE; const D3D12_RENDER_TARGET_BLEND_DESC defaultRenderTargetBlendDesc = { FALSE,FALSE, D3D12_BLEND_ONE, D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD, D3D12_BLEND_ONE, D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD, D3D12_LOGIC_OP_NOOP, D3D12_COLOR_WRITE_ENABLE_ALL, }; for (UINT i = 0; i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; ++i) RenderTarget[ i ] = defaultRenderTargetBlendDesc; } }; //------------------------------------------------------------------------------------------------ struct CD3DX12_RASTERIZER_DESC : public D3D12_RASTERIZER_DESC { CD3DX12_RASTERIZER_DESC() = default; explicit CD3DX12_RASTERIZER_DESC( const D3D12_RASTERIZER_DESC& o ) noexcept : D3D12_RASTERIZER_DESC( o ) {} explicit CD3DX12_RASTERIZER_DESC( CD3DX12_DEFAULT ) noexcept { FillMode = D3D12_FILL_MODE_SOLID; CullMode = D3D12_CULL_MODE_BACK; FrontCounterClockwise = FALSE; DepthBias = D3D12_DEFAULT_DEPTH_BIAS; DepthBiasClamp = D3D12_DEFAULT_DEPTH_BIAS_CLAMP; SlopeScaledDepthBias = D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS; DepthClipEnable = TRUE; MultisampleEnable = FALSE; AntialiasedLineEnable = FALSE; ForcedSampleCount = 0; ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF; } explicit CD3DX12_RASTERIZER_DESC( D3D12_FILL_MODE fillMode, D3D12_CULL_MODE cullMode, BOOL frontCounterClockwise, INT depthBias, FLOAT depthBiasClamp, FLOAT slopeScaledDepthBias, BOOL depthClipEnable, BOOL multisampleEnable, BOOL antialiasedLineEnable, UINT forcedSampleCount, D3D12_CONSERVATIVE_RASTERIZATION_MODE conservativeRaster) noexcept { FillMode = fillMode; CullMode = cullMode; FrontCounterClockwise = frontCounterClockwise; DepthBias = depthBias; DepthBiasClamp = depthBiasClamp; SlopeScaledDepthBias = slopeScaledDepthBias; DepthClipEnable = depthClipEnable; MultisampleEnable = multisampleEnable; AntialiasedLineEnable = antialiasedLineEnable; ForcedSampleCount = forcedSampleCount; ConservativeRaster = conservativeRaster; } }; //------------------------------------------------------------------------------------------------ struct CD3DX12_RESOURCE_ALLOCATION_INFO : public D3D12_RESOURCE_ALLOCATION_INFO { CD3DX12_RESOURCE_ALLOCATION_INFO() = default; explicit CD3DX12_RESOURCE_ALLOCATION_INFO( const D3D12_RESOURCE_ALLOCATION_INFO& o ) noexcept : D3D12_RESOURCE_ALLOCATION_INFO( o ) {} CD3DX12_RESOURCE_ALLOCATION_INFO( UINT64 size, UINT64 alignment ) noexcept { SizeInBytes = size; Alignment = alignment; } }; //------------------------------------------------------------------------------------------------ struct CD3DX12_HEAP_PROPERTIES : public D3D12_HEAP_PROPERTIES { CD3DX12_HEAP_PROPERTIES() = default; explicit CD3DX12_HEAP_PROPERTIES(const D3D12_HEAP_PROPERTIES &o) noexcept : D3D12_HEAP_PROPERTIES(o) {} CD3DX12_HEAP_PROPERTIES( D3D12_CPU_PAGE_PROPERTY cpuPageProperty, D3D12_MEMORY_POOL memoryPoolPreference, UINT creationNodeMask = 1, UINT nodeMask = 1 ) noexcept { Type = D3D12_HEAP_TYPE_CUSTOM; CPUPageProperty = cpuPageProperty; MemoryPoolPreference = memoryPoolPreference; CreationNodeMask = creationNodeMask; VisibleNodeMask = nodeMask; } explicit CD3DX12_HEAP_PROPERTIES( D3D12_HEAP_TYPE type, UINT creationNodeMask = 1, UINT nodeMask = 1 ) noexcept { Type = type; CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; CreationNodeMask = creationNodeMask; VisibleNodeMask = nodeMask; } bool IsCPUAccessible() const noexcept { return Type == D3D12_HEAP_TYPE_UPLOAD || Type == D3D12_HEAP_TYPE_READBACK || (Type == D3D12_HEAP_TYPE_CUSTOM && (CPUPageProperty == D3D12_CPU_PAGE_PROPERTY_WRITE_COMBINE || CPUPageProperty == D3D12_CPU_PAGE_PROPERTY_WRITE_BACK)); } }; inline bool operator==( const D3D12_HEAP_PROPERTIES& l, const D3D12_HEAP_PROPERTIES& r ) noexcept { return l.Type == r.Type && l.CPUPageProperty == r.CPUPageProperty && l.MemoryPoolPreference == r.MemoryPoolPreference && l.CreationNodeMask == r.CreationNodeMask && l.VisibleNodeMask == r.VisibleNodeMask; } inline bool operator!=( const D3D12_HEAP_PROPERTIES& l, const D3D12_HEAP_PROPERTIES& r ) noexcept { return !( l == r ); } //------------------------------------------------------------------------------------------------ struct CD3DX12_HEAP_DESC : public D3D12_HEAP_DESC { CD3DX12_HEAP_DESC() = default; explicit CD3DX12_HEAP_DESC(const D3D12_HEAP_DESC &o) noexcept : D3D12_HEAP_DESC(o) {} CD3DX12_HEAP_DESC( UINT64 size, D3D12_HEAP_PROPERTIES properties, UINT64 alignment = 0, D3D12_HEAP_FLAGS flags = D3D12_HEAP_FLAG_NONE ) noexcept { SizeInBytes = size; Properties = properties; Alignment = alignment; Flags = flags; } CD3DX12_HEAP_DESC( UINT64 size, D3D12_HEAP_TYPE type, UINT64 alignment = 0, D3D12_HEAP_FLAGS flags = D3D12_HEAP_FLAG_NONE ) noexcept { SizeInBytes = size; Properties = CD3DX12_HEAP_PROPERTIES( type ); Alignment = alignment; Flags = flags; } CD3DX12_HEAP_DESC( UINT64 size, D3D12_CPU_PAGE_PROPERTY cpuPageProperty, D3D12_MEMORY_POOL memoryPoolPreference, UINT64 alignment = 0, D3D12_HEAP_FLAGS flags = D3D12_HEAP_FLAG_NONE ) noexcept { SizeInBytes = size; Properties = CD3DX12_HEAP_PROPERTIES( cpuPageProperty, memoryPoolPreference ); Alignment = alignment; Flags = flags; } CD3DX12_HEAP_DESC( const D3D12_RESOURCE_ALLOCATION_INFO& resAllocInfo, D3D12_HEAP_PROPERTIES properties, D3D12_HEAP_FLAGS flags = D3D12_HEAP_FLAG_NONE ) noexcept { SizeInBytes = resAllocInfo.SizeInBytes; Properties = properties; Alignment = resAllocInfo.Alignment; Flags = flags; } CD3DX12_HEAP_DESC( const D3D12_RESOURCE_ALLOCATION_INFO& resAllocInfo, D3D12_HEAP_TYPE type, D3D12_HEAP_FLAGS flags = D3D12_HEAP_FLAG_NONE ) noexcept { SizeInBytes = resAllocInfo.SizeInBytes; Properties = CD3DX12_HEAP_PROPERTIES( type ); Alignment = resAllocInfo.Alignment; Flags = flags; } CD3DX12_HEAP_DESC( const D3D12_RESOURCE_ALLOCATION_INFO& resAllocInfo, D3D12_CPU_PAGE_PROPERTY cpuPageProperty, D3D12_MEMORY_POOL memoryPoolPreference, D3D12_HEAP_FLAGS flags = D3D12_HEAP_FLAG_NONE ) noexcept { SizeInBytes = resAllocInfo.SizeInBytes; Properties = CD3DX12_HEAP_PROPERTIES( cpuPageProperty, memoryPoolPreference ); Alignment = resAllocInfo.Alignment; Flags = flags; } bool IsCPUAccessible() const noexcept { return static_cast< const CD3DX12_HEAP_PROPERTIES* >( &Properties )->IsCPUAccessible(); } }; inline bool operator==( const D3D12_HEAP_DESC& l, const D3D12_HEAP_DESC& r ) noexcept { return l.SizeInBytes == r.SizeInBytes && l.Properties == r.Properties && l.Alignment == r.Alignment && l.Flags == r.Flags; } inline bool operator!=( const D3D12_HEAP_DESC& l, const D3D12_HEAP_DESC& r ) noexcept { return !( l == r ); } //------------------------------------------------------------------------------------------------ struct CD3DX12_CLEAR_VALUE : public D3D12_CLEAR_VALUE { CD3DX12_CLEAR_VALUE() = default; explicit CD3DX12_CLEAR_VALUE(const D3D12_CLEAR_VALUE &o) noexcept : D3D12_CLEAR_VALUE(o) {} CD3DX12_CLEAR_VALUE( DXGI_FORMAT format, const FLOAT color[4] ) noexcept { Format = format; memcpy( Color, color, sizeof( Color ) ); } CD3DX12_CLEAR_VALUE( DXGI_FORMAT format, FLOAT depth, UINT8 stencil ) noexcept { Format = format; memset( &Color, 0, sizeof( Color ) ); /* Use memcpy to preserve NAN values */ memcpy( &DepthStencil.Depth, &depth, sizeof( depth ) ); DepthStencil.Stencil = stencil; } }; //------------------------------------------------------------------------------------------------ struct CD3DX12_RANGE : public D3D12_RANGE { CD3DX12_RANGE() = default; explicit CD3DX12_RANGE(const D3D12_RANGE &o) noexcept : D3D12_RANGE(o) {} CD3DX12_RANGE( SIZE_T begin, SIZE_T end ) noexcept { Begin = begin; End = end; } }; //------------------------------------------------------------------------------------------------ #if defined(NTDDI_WIN10_RS2) && (NTDDI_VERSION >= NTDDI_WIN10_RS2) struct CD3DX12_RANGE_UINT64 : public D3D12_RANGE_UINT64 { CD3DX12_RANGE_UINT64() = default; explicit CD3DX12_RANGE_UINT64(const D3D12_RANGE_UINT64 &o) noexcept : D3D12_RANGE_UINT64(o) {} CD3DX12_RANGE_UINT64( UINT64 begin, UINT64 end ) noexcept { Begin = begin; End = end; } }; //------------------------------------------------------------------------------------------------ struct CD3DX12_SUBRESOURCE_RANGE_UINT64 : public D3D12_SUBRESOURCE_RANGE_UINT64 { CD3DX12_SUBRESOURCE_RANGE_UINT64() = default; explicit CD3DX12_SUBRESOURCE_RANGE_UINT64(const D3D12_SUBRESOURCE_RANGE_UINT64 &o) noexcept : D3D12_SUBRESOURCE_RANGE_UINT64(o) {} CD3DX12_SUBRESOURCE_RANGE_UINT64( UINT subresource, const D3D12_RANGE_UINT64& range ) noexcept { Subresource = subresource; Range = range; } CD3DX12_SUBRESOURCE_RANGE_UINT64( UINT subresource, UINT64 begin, UINT64 end ) noexcept { Subresource = subresource; Range.Begin = begin; Range.End = end; } }; #endif // NTDDI_WIN10_RS2 //------------------------------------------------------------------------------------------------ struct CD3DX12_SHADER_BYTECODE : public D3D12_SHADER_BYTECODE { CD3DX12_SHADER_BYTECODE() = default; explicit CD3DX12_SHADER_BYTECODE(const D3D12_SHADER_BYTECODE &o) noexcept : D3D12_SHADER_BYTECODE(o) {} CD3DX12_SHADER_BYTECODE( _In_ ID3DBlob* pShaderBlob ) noexcept { pShaderBytecode = pShaderBlob->GetBufferPointer(); BytecodeLength = pShaderBlob->GetBufferSize(); } CD3DX12_SHADER_BYTECODE( const void* _pShaderBytecode, SIZE_T bytecodeLength ) noexcept { pShaderBytecode = _pShaderBytecode; BytecodeLength = bytecodeLength; } }; //------------------------------------------------------------------------------------------------ struct CD3DX12_TILED_RESOURCE_COORDINATE : public D3D12_TILED_RESOURCE_COORDINATE { CD3DX12_TILED_RESOURCE_COORDINATE() = default; explicit CD3DX12_TILED_RESOURCE_COORDINATE(const D3D12_TILED_RESOURCE_COORDINATE &o) noexcept : D3D12_TILED_RESOURCE_COORDINATE(o) {} CD3DX12_TILED_RESOURCE_COORDINATE( UINT x, UINT y, UINT z, UINT subresource ) noexcept { X = x; Y = y; Z = z; Subresource = subresource; } }; //------------------------------------------------------------------------------------------------ struct CD3DX12_TILE_REGION_SIZE : public D3D12_TILE_REGION_SIZE { CD3DX12_TILE_REGION_SIZE() = default; explicit CD3DX12_TILE_REGION_SIZE(const D3D12_TILE_REGION_SIZE &o) noexcept : D3D12_TILE_REGION_SIZE(o) {} CD3DX12_TILE_REGION_SIZE( UINT numTiles, BOOL useBox, UINT width, UINT16 height, UINT16 depth ) noexcept { NumTiles = numTiles; UseBox = useBox; Width = width; Height = height; Depth = depth; } }; //------------------------------------------------------------------------------------------------ struct CD3DX12_SUBRESOURCE_TILING : public D3D12_SUBRESOURCE_TILING { CD3DX12_SUBRESOURCE_TILING() = default; explicit CD3DX12_SUBRESOURCE_TILING(const D3D12_SUBRESOURCE_TILING &o) noexcept : D3D12_SUBRESOURCE_TILING(o) {} CD3DX12_SUBRESOURCE_TILING( UINT widthInTiles, UINT16 heightInTiles, UINT16 depthInTiles, UINT startTileIndexInOverallResource ) noexcept { WidthInTiles = widthInTiles; HeightInTiles = heightInTiles; DepthInTiles = depthInTiles; StartTileIndexInOverallResource = startTileIndexInOverallResource; } }; //------------------------------------------------------------------------------------------------ struct CD3DX12_TILE_SHAPE : public D3D12_TILE_SHAPE { CD3DX12_TILE_SHAPE() = default; explicit CD3DX12_TILE_SHAPE(const D3D12_TILE_SHAPE &o) noexcept : D3D12_TILE_SHAPE(o) {} CD3DX12_TILE_SHAPE( UINT widthInTexels, UINT heightInTexels, UINT depthInTexels ) noexcept { WidthInTexels = widthInTexels; HeightInTexels = heightInTexels; DepthInTexels = depthInTexels; } }; //------------------------------------------------------------------------------------------------ struct CD3DX12_RESOURCE_BARRIER : public D3D12_RESOURCE_BARRIER { CD3DX12_RESOURCE_BARRIER() = default; explicit CD3DX12_RESOURCE_BARRIER(const D3D12_RESOURCE_BARRIER &o) noexcept : D3D12_RESOURCE_BARRIER(o) {} static inline CD3DX12_RESOURCE_BARRIER Transition( _In_ ID3D12Resource* pResource, D3D12_RESOURCE_STATES stateBefore, D3D12_RESOURCE_STATES stateAfter, UINT subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, D3D12_RESOURCE_BARRIER_FLAGS flags = D3D12_RESOURCE_BARRIER_FLAG_NONE) noexcept { CD3DX12_RESOURCE_BARRIER result = {}; D3D12_RESOURCE_BARRIER &barrier = result; result.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; result.Flags = flags; barrier.Transition.pResource = pResource; barrier.Transition.StateBefore = stateBefore; barrier.Transition.StateAfter = stateAfter; barrier.Transition.Subresource = subresource; return result; } static inline CD3DX12_RESOURCE_BARRIER Aliasing( _In_ ID3D12Resource* pResourceBefore, _In_ ID3D12Resource* pResourceAfter) noexcept { CD3DX12_RESOURCE_BARRIER result = {}; D3D12_RESOURCE_BARRIER &barrier = result; result.Type = D3D12_RESOURCE_BARRIER_TYPE_ALIASING; barrier.Aliasing.pResourceBefore = pResourceBefore; barrier.Aliasing.pResourceAfter = pResourceAfter; return result; } static inline CD3DX12_RESOURCE_BARRIER UAV( _In_ ID3D12Resource* pResource) noexcept { CD3DX12_RESOURCE_BARRIER result = {}; D3D12_RESOURCE_BARRIER &barrier = result; result.Type = D3D12_RESOURCE_BARRIER_TYPE_UAV; barrier.UAV.pResource = pResource; return result; } }; //------------------------------------------------------------------------------------------------ struct CD3DX12_PACKED_MIP_INFO : public D3D12_PACKED_MIP_INFO { CD3DX12_PACKED_MIP_INFO() = default; explicit CD3DX12_PACKED_MIP_INFO(const D3D12_PACKED_MIP_INFO &o) noexcept : D3D12_PACKED_MIP_INFO(o) {} CD3DX12_PACKED_MIP_INFO( UINT8 numStandardMips, UINT8 numPackedMips, UINT numTilesForPackedMips, UINT startTileIndexInOverallResource ) noexcept { NumStandardMips = numStandardMips; NumPackedMips = numPackedMips; NumTilesForPackedMips = numTilesForPackedMips; StartTileIndexInOverallResource = startTileIndexInOverallResource; } }; //------------------------------------------------------------------------------------------------ struct CD3DX12_SUBRESOURCE_FOOTPRINT : public D3D12_SUBRESOURCE_FOOTPRINT { CD3DX12_SUBRESOURCE_FOOTPRINT() = default; explicit CD3DX12_SUBRESOURCE_FOOTPRINT(const D3D12_SUBRESOURCE_FOOTPRINT &o) noexcept : D3D12_SUBRESOURCE_FOOTPRINT(o) {} CD3DX12_SUBRESOURCE_FOOTPRINT( DXGI_FORMAT format, UINT width, UINT height, UINT depth, UINT rowPitch ) noexcept { Format = format; Width = width; Height = height; Depth = depth; RowPitch = rowPitch; } explicit CD3DX12_SUBRESOURCE_FOOTPRINT( const D3D12_RESOURCE_DESC& resDesc, UINT rowPitch ) noexcept { Format = resDesc.Format; Width = UINT( resDesc.Width ); Height = resDesc.Height; Depth = (resDesc.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE3D ? resDesc.DepthOrArraySize : 1); RowPitch = rowPitch; } }; //------------------------------------------------------------------------------------------------ struct CD3DX12_TEXTURE_COPY_LOCATION : public D3D12_TEXTURE_COPY_LOCATION { CD3DX12_TEXTURE_COPY_LOCATION() = default; explicit CD3DX12_TEXTURE_COPY_LOCATION(const D3D12_TEXTURE_COPY_LOCATION &o) noexcept : D3D12_TEXTURE_COPY_LOCATION(o) {} CD3DX12_TEXTURE_COPY_LOCATION(_In_ ID3D12Resource* pRes) noexcept { pResource = pRes; Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; PlacedFootprint = {}; } CD3DX12_TEXTURE_COPY_LOCATION(_In_ ID3D12Resource* pRes, D3D12_PLACED_SUBRESOURCE_FOOTPRINT const& Footprint) noexcept { pResource = pRes; Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; PlacedFootprint = Footprint; } CD3DX12_TEXTURE_COPY_LOCATION(_In_ ID3D12Resource* pRes, UINT Sub) noexcept { pResource = pRes; Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; PlacedFootprint = {}; SubresourceIndex = Sub; } }; //------------------------------------------------------------------------------------------------ struct CD3DX12_DESCRIPTOR_RANGE : public D3D12_DESCRIPTOR_RANGE { CD3DX12_DESCRIPTOR_RANGE() = default; explicit CD3DX12_DESCRIPTOR_RANGE(const D3D12_DESCRIPTOR_RANGE &o) noexcept : D3D12_DESCRIPTOR_RANGE(o) {} CD3DX12_DESCRIPTOR_RANGE( D3D12_DESCRIPTOR_RANGE_TYPE rangeType, UINT numDescriptors, UINT baseShaderRegister, UINT registerSpace = 0, UINT offsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND) noexcept { Init(rangeType, numDescriptors, baseShaderRegister, registerSpace, offsetInDescriptorsFromTableStart); } inline void Init( D3D12_DESCRIPTOR_RANGE_TYPE rangeType, UINT numDescriptors, UINT baseShaderRegister, UINT registerSpace = 0, UINT offsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND) noexcept { Init(*this, rangeType, numDescriptors, baseShaderRegister, registerSpace, offsetInDescriptorsFromTableStart); } static inline void Init( _Out_ D3D12_DESCRIPTOR_RANGE &range, D3D12_DESCRIPTOR_RANGE_TYPE rangeType, UINT numDescriptors, UINT baseShaderRegister, UINT registerSpace = 0, UINT offsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND) noexcept { range.RangeType = rangeType; range.NumDescriptors = numDescriptors; range.BaseShaderRegister = baseShaderRegister; range.RegisterSpace = registerSpace; range.OffsetInDescriptorsFromTableStart = offsetInDescriptorsFromTableStart; } }; //------------------------------------------------------------------------------------------------ struct CD3DX12_ROOT_DESCRIPTOR_TABLE : public D3D12_ROOT_DESCRIPTOR_TABLE { CD3DX12_ROOT_DESCRIPTOR_TABLE() = default; explicit CD3DX12_ROOT_DESCRIPTOR_TABLE(const D3D12_ROOT_DESCRIPTOR_TABLE &o) noexcept : D3D12_ROOT_DESCRIPTOR_TABLE(o) {} CD3DX12_ROOT_DESCRIPTOR_TABLE( UINT numDescriptorRanges, _In_reads_opt_(numDescriptorRanges) const D3D12_DESCRIPTOR_RANGE* _pDescriptorRanges) noexcept { Init(numDescriptorRanges, _pDescriptorRanges); } inline void Init( UINT numDescriptorRanges, _In_reads_opt_(numDescriptorRanges) const D3D12_DESCRIPTOR_RANGE* _pDescriptorRanges) noexcept { Init(*this, numDescriptorRanges, _pDescriptorRanges); } static inline void Init( _Out_ D3D12_ROOT_DESCRIPTOR_TABLE &rootDescriptorTable, UINT numDescriptorRanges, _In_reads_opt_(numDescriptorRanges) const D3D12_DESCRIPTOR_RANGE* _pDescriptorRanges) noexcept { rootDescriptorTable.NumDescriptorRanges = numDescriptorRanges; rootDescriptorTable.pDescriptorRanges = _pDescriptorRanges; } }; //------------------------------------------------------------------------------------------------ struct CD3DX12_ROOT_CONSTANTS : public D3D12_ROOT_CONSTANTS { CD3DX12_ROOT_CONSTANTS() = default; explicit CD3DX12_ROOT_CONSTANTS(const D3D12_ROOT_CONSTANTS &o) noexcept : D3D12_ROOT_CONSTANTS(o) {} CD3DX12_ROOT_CONSTANTS( UINT num32BitValues, UINT shaderRegister, UINT registerSpace = 0) noexcept { Init(num32BitValues, shaderRegister, registerSpace); } inline void Init( UINT num32BitValues, UINT shaderRegister, UINT registerSpace = 0) noexcept { Init(*this, num32BitValues, shaderRegister, registerSpace); } static inline void Init( _Out_ D3D12_ROOT_CONSTANTS &rootConstants, UINT num32BitValues, UINT shaderRegister, UINT registerSpace = 0) noexcept { rootConstants.Num32BitValues = num32BitValues; rootConstants.ShaderRegister = shaderRegister; rootConstants.RegisterSpace = registerSpace; } }; //------------------------------------------------------------------------------------------------ struct CD3DX12_ROOT_DESCRIPTOR : public D3D12_ROOT_DESCRIPTOR { CD3DX12_ROOT_DESCRIPTOR() = default; explicit CD3DX12_ROOT_DESCRIPTOR(const D3D12_ROOT_DESCRIPTOR &o) noexcept : D3D12_ROOT_DESCRIPTOR(o) {} CD3DX12_ROOT_DESCRIPTOR( UINT shaderRegister, UINT registerSpace = 0) noexcept { Init(shaderRegister, registerSpace); } inline void Init( UINT shaderRegister, UINT registerSpace = 0) noexcept { Init(*this, shaderRegister, registerSpace); } static inline void Init(_Out_ D3D12_ROOT_DESCRIPTOR &table, UINT shaderRegister, UINT registerSpace = 0) noexcept { table.ShaderRegister = shaderRegister; table.RegisterSpace = registerSpace; } }; //------------------------------------------------------------------------------------------------ struct CD3DX12_ROOT_PARAMETER : public D3D12_ROOT_PARAMETER { CD3DX12_ROOT_PARAMETER() = default; explicit CD3DX12_ROOT_PARAMETER(const D3D12_ROOT_PARAMETER &o) noexcept : D3D12_ROOT_PARAMETER(o) {} static inline void InitAsDescriptorTable( _Out_ D3D12_ROOT_PARAMETER &rootParam, UINT numDescriptorRanges, _In_reads_(numDescriptorRanges) const D3D12_DESCRIPTOR_RANGE* pDescriptorRanges, D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) noexcept { rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; rootParam.ShaderVisibility = visibility; CD3DX12_ROOT_DESCRIPTOR_TABLE::Init(rootParam.DescriptorTable, numDescriptorRanges, pDescriptorRanges); } static inline void InitAsConstants( _Out_ D3D12_ROOT_PARAMETER &rootParam, UINT num32BitValues, UINT shaderRegister, UINT registerSpace = 0, D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) noexcept { rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS; rootParam.ShaderVisibility = visibility; CD3DX12_ROOT_CONSTANTS::Init(rootParam.Constants, num32BitValues, shaderRegister, registerSpace); } static inline void InitAsConstantBufferView( _Out_ D3D12_ROOT_PARAMETER &rootParam, UINT shaderRegister, UINT registerSpace = 0, D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) noexcept { rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV; rootParam.ShaderVisibility = visibility; CD3DX12_ROOT_DESCRIPTOR::Init(rootParam.Descriptor, shaderRegister, registerSpace); } static inline void InitAsShaderResourceView( _Out_ D3D12_ROOT_PARAMETER &rootParam, UINT shaderRegister, UINT registerSpace = 0, D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) noexcept { rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_SRV; rootParam.ShaderVisibility = visibility; CD3DX12_ROOT_DESCRIPTOR::Init(rootParam.Descriptor, shaderRegister, registerSpace); } static inline void InitAsUnorderedAccessView( _Out_ D3D12_ROOT_PARAMETER &rootParam, UINT shaderRegister, UINT registerSpace = 0, D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) noexcept { rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_UAV; rootParam.ShaderVisibility = visibility; CD3DX12_ROOT_DESCRIPTOR::Init(rootParam.Descriptor, shaderRegister, registerSpace); } inline void InitAsDescriptorTable( UINT numDescriptorRanges, _In_reads_(numDescriptorRanges) const D3D12_DESCRIPTOR_RANGE* pDescriptorRanges, D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) noexcept { InitAsDescriptorTable(*this, numDescriptorRanges, pDescriptorRanges, visibility); } inline void InitAsConstants( UINT num32BitValues, UINT shaderRegister, UINT registerSpace = 0, D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) noexcept { InitAsConstants(*this, num32BitValues, shaderRegister, registerSpace, visibility); } inline void InitAsConstantBufferView( UINT shaderRegister, UINT registerSpace = 0, D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) noexcept { InitAsConstantBufferView(*this, shaderRegister, registerSpace, visibility); } inline void InitAsShaderResourceView( UINT shaderRegister, UINT registerSpace = 0, D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) noexcept { InitAsShaderResourceView(*this, shaderRegister, registerSpace, visibility); } inline void InitAsUnorderedAccessView( UINT shaderRegister, UINT registerSpace = 0, D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) noexcept { InitAsUnorderedAccessView(*this, shaderRegister, registerSpace, visibility); } }; //------------------------------------------------------------------------------------------------ struct CD3DX12_STATIC_SAMPLER_DESC : public D3D12_STATIC_SAMPLER_DESC { CD3DX12_STATIC_SAMPLER_DESC() = default; explicit CD3DX12_STATIC_SAMPLER_DESC(const D3D12_STATIC_SAMPLER_DESC &o) noexcept : D3D12_STATIC_SAMPLER_DESC(o) {} CD3DX12_STATIC_SAMPLER_DESC( UINT shaderRegister, D3D12_FILTER filter = D3D12_FILTER_ANISOTROPIC, D3D12_TEXTURE_ADDRESS_MODE addressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP, D3D12_TEXTURE_ADDRESS_MODE addressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP, D3D12_TEXTURE_ADDRESS_MODE addressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP, FLOAT mipLODBias = 0, UINT maxAnisotropy = 16, D3D12_COMPARISON_FUNC comparisonFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL, D3D12_STATIC_BORDER_COLOR borderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_WHITE, FLOAT minLOD = 0.f, FLOAT maxLOD = D3D12_FLOAT32_MAX, D3D12_SHADER_VISIBILITY shaderVisibility = D3D12_SHADER_VISIBILITY_ALL, UINT registerSpace = 0) noexcept { Init( shaderRegister, filter, addressU, addressV, addressW, mipLODBias, maxAnisotropy, comparisonFunc, borderColor, minLOD, maxLOD, shaderVisibility, registerSpace); } static inline void Init( _Out_ D3D12_STATIC_SAMPLER_DESC &samplerDesc, UINT shaderRegister, D3D12_FILTER filter = D3D12_FILTER_ANISOTROPIC, D3D12_TEXTURE_ADDRESS_MODE addressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP, D3D12_TEXTURE_ADDRESS_MODE addressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP, D3D12_TEXTURE_ADDRESS_MODE addressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP, FLOAT mipLODBias = 0, UINT maxAnisotropy = 16, D3D12_COMPARISON_FUNC comparisonFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL, D3D12_STATIC_BORDER_COLOR borderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_WHITE, FLOAT minLOD = 0.f, FLOAT maxLOD = D3D12_FLOAT32_MAX, D3D12_SHADER_VISIBILITY shaderVisibility = D3D12_SHADER_VISIBILITY_ALL, UINT registerSpace = 0) noexcept { samplerDesc.ShaderRegister = shaderRegister; samplerDesc.Filter = filter; samplerDesc.AddressU = addressU; samplerDesc.AddressV = addressV; samplerDesc.AddressW = addressW; samplerDesc.MipLODBias = mipLODBias; samplerDesc.MaxAnisotropy = maxAnisotropy; samplerDesc.ComparisonFunc = comparisonFunc; samplerDesc.BorderColor = borderColor; samplerDesc.MinLOD = minLOD; samplerDesc.MaxLOD = maxLOD; samplerDesc.ShaderVisibility = shaderVisibility; samplerDesc.RegisterSpace = registerSpace; } inline void Init( UINT shaderRegister, D3D12_FILTER filter = D3D12_FILTER_ANISOTROPIC, D3D12_TEXTURE_ADDRESS_MODE addressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP, D3D12_TEXTURE_ADDRESS_MODE addressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP, D3D12_TEXTURE_ADDRESS_MODE addressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP, FLOAT mipLODBias = 0, UINT maxAnisotropy = 16, D3D12_COMPARISON_FUNC comparisonFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL, D3D12_STATIC_BORDER_COLOR borderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_WHITE, FLOAT minLOD = 0.f, FLOAT maxLOD = D3D12_FLOAT32_MAX, D3D12_SHADER_VISIBILITY shaderVisibility = D3D12_SHADER_VISIBILITY_ALL, UINT registerSpace = 0) noexcept { Init( *this, shaderRegister, filter, addressU, addressV, addressW, mipLODBias, maxAnisotropy, comparisonFunc, borderColor, minLOD, maxLOD, shaderVisibility, registerSpace); } }; //------------------------------------------------------------------------------------------------ struct CD3DX12_ROOT_SIGNATURE_DESC : public D3D12_ROOT_SIGNATURE_DESC { CD3DX12_ROOT_SIGNATURE_DESC() = default; explicit CD3DX12_ROOT_SIGNATURE_DESC(const D3D12_ROOT_SIGNATURE_DESC &o) noexcept : D3D12_ROOT_SIGNATURE_DESC(o) {} CD3DX12_ROOT_SIGNATURE_DESC( UINT numParameters, _In_reads_opt_(numParameters) const D3D12_ROOT_PARAMETER* _pParameters, UINT numStaticSamplers = 0, _In_reads_opt_(numStaticSamplers) const D3D12_STATIC_SAMPLER_DESC* _pStaticSamplers = nullptr, D3D12_ROOT_SIGNATURE_FLAGS flags = D3D12_ROOT_SIGNATURE_FLAG_NONE) noexcept { Init(numParameters, _pParameters, numStaticSamplers, _pStaticSamplers, flags); } CD3DX12_ROOT_SIGNATURE_DESC(CD3DX12_DEFAULT) noexcept { Init(0, nullptr, 0, nullptr, D3D12_ROOT_SIGNATURE_FLAG_NONE); } inline void Init( UINT numParameters, _In_reads_opt_(numParameters) const D3D12_ROOT_PARAMETER* _pParameters, UINT numStaticSamplers = 0, _In_reads_opt_(numStaticSamplers) const D3D12_STATIC_SAMPLER_DESC* _pStaticSamplers = nullptr, D3D12_ROOT_SIGNATURE_FLAGS flags = D3D12_ROOT_SIGNATURE_FLAG_NONE) noexcept { Init(*this, numParameters, _pParameters, numStaticSamplers, _pStaticSamplers, flags); } static inline void Init( _Out_ D3D12_ROOT_SIGNATURE_DESC &desc, UINT numParameters, _In_reads_opt_(numParameters) const D3D12_ROOT_PARAMETER* _pParameters, UINT numStaticSamplers = 0, _In_reads_opt_(numStaticSamplers) const D3D12_STATIC_SAMPLER_DESC* _pStaticSamplers = nullptr, D3D12_ROOT_SIGNATURE_FLAGS flags = D3D12_ROOT_SIGNATURE_FLAG_NONE) noexcept { desc.NumParameters = numParameters; desc.pParameters = _pParameters; desc.NumStaticSamplers = numStaticSamplers; desc.pStaticSamplers = _pStaticSamplers; desc.Flags = flags; } }; //------------------------------------------------------------------------------------------------ struct CD3DX12_DESCRIPTOR_RANGE1 : public D3D12_DESCRIPTOR_RANGE1 { CD3DX12_DESCRIPTOR_RANGE1() = default; explicit CD3DX12_DESCRIPTOR_RANGE1(const D3D12_DESCRIPTOR_RANGE1 &o) noexcept : D3D12_DESCRIPTOR_RANGE1(o) {} CD3DX12_DESCRIPTOR_RANGE1( D3D12_DESCRIPTOR_RANGE_TYPE rangeType, UINT numDescriptors, UINT baseShaderRegister, UINT registerSpace = 0, D3D12_DESCRIPTOR_RANGE_FLAGS flags = D3D12_DESCRIPTOR_RANGE_FLAG_NONE, UINT offsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND) noexcept { Init(rangeType, numDescriptors, baseShaderRegister, registerSpace, flags, offsetInDescriptorsFromTableStart); } inline void Init( D3D12_DESCRIPTOR_RANGE_TYPE rangeType, UINT numDescriptors, UINT baseShaderRegister, UINT registerSpace = 0, D3D12_DESCRIPTOR_RANGE_FLAGS flags = D3D12_DESCRIPTOR_RANGE_FLAG_NONE, UINT offsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND) noexcept { Init(*this, rangeType, numDescriptors, baseShaderRegister, registerSpace, flags, offsetInDescriptorsFromTableStart); } static inline void Init( _Out_ D3D12_DESCRIPTOR_RANGE1 &range, D3D12_DESCRIPTOR_RANGE_TYPE rangeType, UINT numDescriptors, UINT baseShaderRegister, UINT registerSpace = 0, D3D12_DESCRIPTOR_RANGE_FLAGS flags = D3D12_DESCRIPTOR_RANGE_FLAG_NONE, UINT offsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND) noexcept { range.RangeType = rangeType; range.NumDescriptors = numDescriptors; range.BaseShaderRegister = baseShaderRegister; range.RegisterSpace = registerSpace; range.Flags = flags; range.OffsetInDescriptorsFromTableStart = offsetInDescriptorsFromTableStart; } }; //------------------------------------------------------------------------------------------------ struct CD3DX12_ROOT_DESCRIPTOR_TABLE1 : public D3D12_ROOT_DESCRIPTOR_TABLE1 { CD3DX12_ROOT_DESCRIPTOR_TABLE1() = default; explicit CD3DX12_ROOT_DESCRIPTOR_TABLE1(const D3D12_ROOT_DESCRIPTOR_TABLE1 &o) noexcept : D3D12_ROOT_DESCRIPTOR_TABLE1(o) {} CD3DX12_ROOT_DESCRIPTOR_TABLE1( UINT numDescriptorRanges, _In_reads_opt_(numDescriptorRanges) const D3D12_DESCRIPTOR_RANGE1* _pDescriptorRanges) noexcept { Init(numDescriptorRanges, _pDescriptorRanges); } inline void Init( UINT numDescriptorRanges, _In_reads_opt_(numDescriptorRanges) const D3D12_DESCRIPTOR_RANGE1* _pDescriptorRanges) noexcept { Init(*this, numDescriptorRanges, _pDescriptorRanges); } static inline void Init( _Out_ D3D12_ROOT_DESCRIPTOR_TABLE1 &rootDescriptorTable, UINT numDescriptorRanges, _In_reads_opt_(numDescriptorRanges) const D3D12_DESCRIPTOR_RANGE1* _pDescriptorRanges) noexcept { rootDescriptorTable.NumDescriptorRanges = numDescriptorRanges; rootDescriptorTable.pDescriptorRanges = _pDescriptorRanges; } }; //------------------------------------------------------------------------------------------------ struct CD3DX12_ROOT_DESCRIPTOR1 : public D3D12_ROOT_DESCRIPTOR1 { CD3DX12_ROOT_DESCRIPTOR1() = default; explicit CD3DX12_ROOT_DESCRIPTOR1(const D3D12_ROOT_DESCRIPTOR1 &o) noexcept : D3D12_ROOT_DESCRIPTOR1(o) {} CD3DX12_ROOT_DESCRIPTOR1( UINT shaderRegister, UINT registerSpace = 0, D3D12_ROOT_DESCRIPTOR_FLAGS flags = D3D12_ROOT_DESCRIPTOR_FLAG_NONE) noexcept { Init(shaderRegister, registerSpace, flags); } inline void Init( UINT shaderRegister, UINT registerSpace = 0, D3D12_ROOT_DESCRIPTOR_FLAGS flags = D3D12_ROOT_DESCRIPTOR_FLAG_NONE) noexcept { Init(*this, shaderRegister, registerSpace, flags); } static inline void Init( _Out_ D3D12_ROOT_DESCRIPTOR1 &table, UINT shaderRegister, UINT registerSpace = 0, D3D12_ROOT_DESCRIPTOR_FLAGS flags = D3D12_ROOT_DESCRIPTOR_FLAG_NONE) noexcept { table.ShaderRegister = shaderRegister; table.RegisterSpace = registerSpace; table.Flags = flags; } }; //------------------------------------------------------------------------------------------------ struct CD3DX12_ROOT_PARAMETER1 : public D3D12_ROOT_PARAMETER1 { CD3DX12_ROOT_PARAMETER1() = default; explicit CD3DX12_ROOT_PARAMETER1(const D3D12_ROOT_PARAMETER1 &o) noexcept : D3D12_ROOT_PARAMETER1(o) {} static inline void InitAsDescriptorTable( _Out_ D3D12_ROOT_PARAMETER1 &rootParam, UINT numDescriptorRanges, _In_reads_(numDescriptorRanges) const D3D12_DESCRIPTOR_RANGE1* pDescriptorRanges, D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) noexcept { rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; rootParam.ShaderVisibility = visibility; CD3DX12_ROOT_DESCRIPTOR_TABLE1::Init(rootParam.DescriptorTable, numDescriptorRanges, pDescriptorRanges); } static inline void InitAsConstants( _Out_ D3D12_ROOT_PARAMETER1 &rootParam, UINT num32BitValues, UINT shaderRegister, UINT registerSpace = 0, D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) noexcept { rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS; rootParam.ShaderVisibility = visibility; CD3DX12_ROOT_CONSTANTS::Init(rootParam.Constants, num32BitValues, shaderRegister, registerSpace); } static inline void InitAsConstantBufferView( _Out_ D3D12_ROOT_PARAMETER1 &rootParam, UINT shaderRegister, UINT registerSpace = 0, D3D12_ROOT_DESCRIPTOR_FLAGS flags = D3D12_ROOT_DESCRIPTOR_FLAG_NONE, D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) noexcept { rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV; rootParam.ShaderVisibility = visibility; CD3DX12_ROOT_DESCRIPTOR1::Init(rootParam.Descriptor, shaderRegister, registerSpace, flags); } static inline void InitAsShaderResourceView( _Out_ D3D12_ROOT_PARAMETER1 &rootParam, UINT shaderRegister, UINT registerSpace = 0, D3D12_ROOT_DESCRIPTOR_FLAGS flags = D3D12_ROOT_DESCRIPTOR_FLAG_NONE, D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) noexcept { rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_SRV; rootParam.ShaderVisibility = visibility; CD3DX12_ROOT_DESCRIPTOR1::Init(rootParam.Descriptor, shaderRegister, registerSpace, flags); } static inline void InitAsUnorderedAccessView( _Out_ D3D12_ROOT_PARAMETER1 &rootParam, UINT shaderRegister, UINT registerSpace = 0, D3D12_ROOT_DESCRIPTOR_FLAGS flags = D3D12_ROOT_DESCRIPTOR_FLAG_NONE, D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) noexcept { rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_UAV; rootParam.ShaderVisibility = visibility; CD3DX12_ROOT_DESCRIPTOR1::Init(rootParam.Descriptor, shaderRegister, registerSpace, flags); } inline void InitAsDescriptorTable( UINT numDescriptorRanges, _In_reads_(numDescriptorRanges) const D3D12_DESCRIPTOR_RANGE1* pDescriptorRanges, D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) noexcept { InitAsDescriptorTable(*this, numDescriptorRanges, pDescriptorRanges, visibility); } inline void InitAsConstants( UINT num32BitValues, UINT shaderRegister, UINT registerSpace = 0, D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) noexcept { InitAsConstants(*this, num32BitValues, shaderRegister, registerSpace, visibility); } inline void InitAsConstantBufferView( UINT shaderRegister, UINT registerSpace = 0, D3D12_ROOT_DESCRIPTOR_FLAGS flags = D3D12_ROOT_DESCRIPTOR_FLAG_NONE, D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) noexcept { InitAsConstantBufferView(*this, shaderRegister, registerSpace, flags, visibility); } inline void InitAsShaderResourceView( UINT shaderRegister, UINT registerSpace = 0, D3D12_ROOT_DESCRIPTOR_FLAGS flags = D3D12_ROOT_DESCRIPTOR_FLAG_NONE, D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) noexcept { InitAsShaderResourceView(*this, shaderRegister, registerSpace, flags, visibility); } inline void InitAsUnorderedAccessView( UINT shaderRegister, UINT registerSpace = 0, D3D12_ROOT_DESCRIPTOR_FLAGS flags = D3D12_ROOT_DESCRIPTOR_FLAG_NONE, D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) noexcept { InitAsUnorderedAccessView(*this, shaderRegister, registerSpace, flags, visibility); } }; //------------------------------------------------------------------------------------------------ struct CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC : public D3D12_VERSIONED_ROOT_SIGNATURE_DESC { CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC() = default; explicit CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC(const D3D12_VERSIONED_ROOT_SIGNATURE_DESC &o) noexcept : D3D12_VERSIONED_ROOT_SIGNATURE_DESC(o) {} explicit CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC(const D3D12_ROOT_SIGNATURE_DESC &o) noexcept { Version = D3D_ROOT_SIGNATURE_VERSION_1_0; Desc_1_0 = o; } explicit CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC(const D3D12_ROOT_SIGNATURE_DESC1 &o) noexcept { Version = D3D_ROOT_SIGNATURE_VERSION_1_1; Desc_1_1 = o; } CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC( UINT numParameters, _In_reads_opt_(numParameters) const D3D12_ROOT_PARAMETER* _pParameters, UINT numStaticSamplers = 0, _In_reads_opt_(numStaticSamplers) const D3D12_STATIC_SAMPLER_DESC* _pStaticSamplers = nullptr, D3D12_ROOT_SIGNATURE_FLAGS flags = D3D12_ROOT_SIGNATURE_FLAG_NONE) noexcept { Init_1_0(numParameters, _pParameters, numStaticSamplers, _pStaticSamplers, flags); } CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC( UINT numParameters, _In_reads_opt_(numParameters) const D3D12_ROOT_PARAMETER1* _pParameters, UINT numStaticSamplers = 0, _In_reads_opt_(numStaticSamplers) const D3D12_STATIC_SAMPLER_DESC* _pStaticSamplers = nullptr, D3D12_ROOT_SIGNATURE_FLAGS flags = D3D12_ROOT_SIGNATURE_FLAG_NONE) noexcept { Init_1_1(numParameters, _pParameters, numStaticSamplers, _pStaticSamplers, flags); } CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC(CD3DX12_DEFAULT) noexcept { Init_1_1(0, nullptr, 0, nullptr, D3D12_ROOT_SIGNATURE_FLAG_NONE); } inline void Init_1_0( UINT numParameters, _In_reads_opt_(numParameters) const D3D12_ROOT_PARAMETER* _pParameters, UINT numStaticSamplers = 0, _In_reads_opt_(numStaticSamplers) const D3D12_STATIC_SAMPLER_DESC* _pStaticSamplers = nullptr, D3D12_ROOT_SIGNATURE_FLAGS flags = D3D12_ROOT_SIGNATURE_FLAG_NONE) noexcept { Init_1_0(*this, numParameters, _pParameters, numStaticSamplers, _pStaticSamplers, flags); } static inline void Init_1_0( _Out_ D3D12_VERSIONED_ROOT_SIGNATURE_DESC &desc, UINT numParameters, _In_reads_opt_(numParameters) const D3D12_ROOT_PARAMETER* _pParameters, UINT numStaticSamplers = 0, _In_reads_opt_(numStaticSamplers) const D3D12_STATIC_SAMPLER_DESC* _pStaticSamplers = nullptr, D3D12_ROOT_SIGNATURE_FLAGS flags = D3D12_ROOT_SIGNATURE_FLAG_NONE) noexcept { desc.Version = D3D_ROOT_SIGNATURE_VERSION_1_0; desc.Desc_1_0.NumParameters = numParameters; desc.Desc_1_0.pParameters = _pParameters; desc.Desc_1_0.NumStaticSamplers = numStaticSamplers; desc.Desc_1_0.pStaticSamplers = _pStaticSamplers; desc.Desc_1_0.Flags = flags; } inline void Init_1_1( UINT numParameters, _In_reads_opt_(numParameters) const D3D12_ROOT_PARAMETER1* _pParameters, UINT numStaticSamplers = 0, _In_reads_opt_(numStaticSamplers) const D3D12_STATIC_SAMPLER_DESC* _pStaticSamplers = nullptr, D3D12_ROOT_SIGNATURE_FLAGS flags = D3D12_ROOT_SIGNATURE_FLAG_NONE) noexcept { Init_1_1(*this, numParameters, _pParameters, numStaticSamplers, _pStaticSamplers, flags); } static inline void Init_1_1( _Out_ D3D12_VERSIONED_ROOT_SIGNATURE_DESC &desc, UINT numParameters, _In_reads_opt_(numParameters) const D3D12_ROOT_PARAMETER1* _pParameters, UINT numStaticSamplers = 0, _In_reads_opt_(numStaticSamplers) const D3D12_STATIC_SAMPLER_DESC* _pStaticSamplers = nullptr, D3D12_ROOT_SIGNATURE_FLAGS flags = D3D12_ROOT_SIGNATURE_FLAG_NONE) noexcept { desc.Version = D3D_ROOT_SIGNATURE_VERSION_1_1; desc.Desc_1_1.NumParameters = numParameters; desc.Desc_1_1.pParameters = _pParameters; desc.Desc_1_1.NumStaticSamplers = numStaticSamplers; desc.Desc_1_1.pStaticSamplers = _pStaticSamplers; desc.Desc_1_1.Flags = flags; } }; //------------------------------------------------------------------------------------------------ struct CD3DX12_CPU_DESCRIPTOR_HANDLE : public D3D12_CPU_DESCRIPTOR_HANDLE { CD3DX12_CPU_DESCRIPTOR_HANDLE() = default; explicit CD3DX12_CPU_DESCRIPTOR_HANDLE(const D3D12_CPU_DESCRIPTOR_HANDLE &o) noexcept : D3D12_CPU_DESCRIPTOR_HANDLE(o) {} CD3DX12_CPU_DESCRIPTOR_HANDLE(CD3DX12_DEFAULT) noexcept { ptr = 0; } CD3DX12_CPU_DESCRIPTOR_HANDLE(_In_ const D3D12_CPU_DESCRIPTOR_HANDLE &other, INT offsetScaledByIncrementSize) noexcept { InitOffsetted(other, offsetScaledByIncrementSize); } CD3DX12_CPU_DESCRIPTOR_HANDLE(_In_ const D3D12_CPU_DESCRIPTOR_HANDLE &other, INT offsetInDescriptors, UINT descriptorIncrementSize) noexcept { InitOffsetted(other, offsetInDescriptors, descriptorIncrementSize); } CD3DX12_CPU_DESCRIPTOR_HANDLE& Offset(INT offsetInDescriptors, UINT descriptorIncrementSize) noexcept { ptr = SIZE_T(INT64(ptr) + INT64(offsetInDescriptors) * INT64(descriptorIncrementSize)); return *this; } CD3DX12_CPU_DESCRIPTOR_HANDLE& Offset(INT offsetScaledByIncrementSize) noexcept { ptr = SIZE_T(INT64(ptr) + INT64(offsetScaledByIncrementSize)); return *this; } bool operator==(_In_ const D3D12_CPU_DESCRIPTOR_HANDLE& other) const noexcept { return (ptr == other.ptr); } bool operator!=(_In_ const D3D12_CPU_DESCRIPTOR_HANDLE& other) const noexcept { return (ptr != other.ptr); } CD3DX12_CPU_DESCRIPTOR_HANDLE &operator=(const D3D12_CPU_DESCRIPTOR_HANDLE &other) noexcept { ptr = other.ptr; return *this; } inline void InitOffsetted(_In_ const D3D12_CPU_DESCRIPTOR_HANDLE &base, INT offsetScaledByIncrementSize) noexcept { InitOffsetted(*this, base, offsetScaledByIncrementSize); } inline void InitOffsetted(_In_ const D3D12_CPU_DESCRIPTOR_HANDLE &base, INT offsetInDescriptors, UINT descriptorIncrementSize) noexcept { InitOffsetted(*this, base, offsetInDescriptors, descriptorIncrementSize); } static inline void InitOffsetted(_Out_ D3D12_CPU_DESCRIPTOR_HANDLE &handle, _In_ const D3D12_CPU_DESCRIPTOR_HANDLE &base, INT offsetScaledByIncrementSize) noexcept { handle.ptr = SIZE_T(INT64(base.ptr) + INT64(offsetScaledByIncrementSize)); } static inline void InitOffsetted(_Out_ D3D12_CPU_DESCRIPTOR_HANDLE &handle, _In_ const D3D12_CPU_DESCRIPTOR_HANDLE &base, INT offsetInDescriptors, UINT descriptorIncrementSize) noexcept { handle.ptr = SIZE_T(INT64(base.ptr) + INT64(offsetInDescriptors) * INT64(descriptorIncrementSize)); } }; //------------------------------------------------------------------------------------------------ struct CD3DX12_GPU_DESCRIPTOR_HANDLE : public D3D12_GPU_DESCRIPTOR_HANDLE { CD3DX12_GPU_DESCRIPTOR_HANDLE() = default; explicit CD3DX12_GPU_DESCRIPTOR_HANDLE(const D3D12_GPU_DESCRIPTOR_HANDLE &o) noexcept : D3D12_GPU_DESCRIPTOR_HANDLE(o) {} CD3DX12_GPU_DESCRIPTOR_HANDLE(CD3DX12_DEFAULT) noexcept { ptr = 0; } CD3DX12_GPU_DESCRIPTOR_HANDLE(_In_ const D3D12_GPU_DESCRIPTOR_HANDLE &other, INT offsetScaledByIncrementSize) noexcept { InitOffsetted(other, offsetScaledByIncrementSize); } CD3DX12_GPU_DESCRIPTOR_HANDLE(_In_ const D3D12_GPU_DESCRIPTOR_HANDLE &other, INT offsetInDescriptors, UINT descriptorIncrementSize) noexcept { InitOffsetted(other, offsetInDescriptors, descriptorIncrementSize); } CD3DX12_GPU_DESCRIPTOR_HANDLE& Offset(INT offsetInDescriptors, UINT descriptorIncrementSize) noexcept { ptr = UINT64(INT64(ptr) + INT64(offsetInDescriptors) * INT64(descriptorIncrementSize)); return *this; } CD3DX12_GPU_DESCRIPTOR_HANDLE& Offset(INT offsetScaledByIncrementSize) noexcept { ptr = UINT64(INT64(ptr) + INT64(offsetScaledByIncrementSize)); return *this; } inline bool operator==(_In_ const D3D12_GPU_DESCRIPTOR_HANDLE& other) const noexcept { return (ptr == other.ptr); } inline bool operator!=(_In_ const D3D12_GPU_DESCRIPTOR_HANDLE& other) const noexcept { return (ptr != other.ptr); } CD3DX12_GPU_DESCRIPTOR_HANDLE &operator=(const D3D12_GPU_DESCRIPTOR_HANDLE &other) noexcept { ptr = other.ptr; return *this; } inline void InitOffsetted(_In_ const D3D12_GPU_DESCRIPTOR_HANDLE &base, INT offsetScaledByIncrementSize) noexcept { InitOffsetted(*this, base, offsetScaledByIncrementSize); } inline void InitOffsetted(_In_ const D3D12_GPU_DESCRIPTOR_HANDLE &base, INT offsetInDescriptors, UINT descriptorIncrementSize) noexcept { InitOffsetted(*this, base, offsetInDescriptors, descriptorIncrementSize); } static inline void InitOffsetted(_Out_ D3D12_GPU_DESCRIPTOR_HANDLE &handle, _In_ const D3D12_GPU_DESCRIPTOR_HANDLE &base, INT offsetScaledByIncrementSize) noexcept { handle.ptr = UINT64(INT64(base.ptr) + INT64(offsetScaledByIncrementSize)); } static inline void InitOffsetted(_Out_ D3D12_GPU_DESCRIPTOR_HANDLE &handle, _In_ const D3D12_GPU_DESCRIPTOR_HANDLE &base, INT offsetInDescriptors, UINT descriptorIncrementSize) noexcept { handle.ptr = UINT64(INT64(base.ptr) + INT64(offsetInDescriptors) * INT64(descriptorIncrementSize)); } }; //------------------------------------------------------------------------------------------------ inline constexpr UINT D3D12CalcSubresource( UINT MipSlice, UINT ArraySlice, UINT PlaneSlice, UINT MipLevels, UINT ArraySize ) noexcept { return MipSlice + ArraySlice * MipLevels + PlaneSlice * MipLevels * ArraySize; } //------------------------------------------------------------------------------------------------ template inline void D3D12DecomposeSubresource( UINT Subresource, UINT MipLevels, UINT ArraySize, _Out_ T& MipSlice, _Out_ U& ArraySlice, _Out_ V& PlaneSlice ) noexcept { MipSlice = static_cast(Subresource % MipLevels); ArraySlice = static_cast((Subresource / MipLevels) % ArraySize); PlaneSlice = static_cast(Subresource / (MipLevels * ArraySize)); } //------------------------------------------------------------------------------------------------ inline UINT8 D3D12GetFormatPlaneCount( _In_ ID3D12Device* pDevice, DXGI_FORMAT Format ) noexcept { D3D12_FEATURE_DATA_FORMAT_INFO formatInfo = { Format, 0 }; if (FAILED(pDevice->CheckFeatureSupport(D3D12_FEATURE_FORMAT_INFO, &formatInfo, sizeof(formatInfo)))) { return 0; } return formatInfo.PlaneCount; } //------------------------------------------------------------------------------------------------ struct CD3DX12_RESOURCE_DESC : public D3D12_RESOURCE_DESC { CD3DX12_RESOURCE_DESC() = default; explicit CD3DX12_RESOURCE_DESC( const D3D12_RESOURCE_DESC& o ) noexcept : D3D12_RESOURCE_DESC( o ) {} CD3DX12_RESOURCE_DESC( D3D12_RESOURCE_DIMENSION dimension, UINT64 alignment, UINT64 width, UINT height, UINT16 depthOrArraySize, UINT16 mipLevels, DXGI_FORMAT format, UINT sampleCount, UINT sampleQuality, D3D12_TEXTURE_LAYOUT layout, D3D12_RESOURCE_FLAGS flags ) noexcept { Dimension = dimension; Alignment = alignment; Width = width; Height = height; DepthOrArraySize = depthOrArraySize; MipLevels = mipLevels; Format = format; SampleDesc.Count = sampleCount; SampleDesc.Quality = sampleQuality; Layout = layout; Flags = flags; } static inline CD3DX12_RESOURCE_DESC Buffer( const D3D12_RESOURCE_ALLOCATION_INFO& resAllocInfo, D3D12_RESOURCE_FLAGS flags = D3D12_RESOURCE_FLAG_NONE ) noexcept { return CD3DX12_RESOURCE_DESC( D3D12_RESOURCE_DIMENSION_BUFFER, resAllocInfo.Alignment, resAllocInfo.SizeInBytes, 1, 1, 1, DXGI_FORMAT_UNKNOWN, 1, 0, D3D12_TEXTURE_LAYOUT_ROW_MAJOR, flags ); } static inline CD3DX12_RESOURCE_DESC Buffer( UINT64 width, D3D12_RESOURCE_FLAGS flags = D3D12_RESOURCE_FLAG_NONE, UINT64 alignment = 0 ) noexcept { return CD3DX12_RESOURCE_DESC( D3D12_RESOURCE_DIMENSION_BUFFER, alignment, width, 1, 1, 1, DXGI_FORMAT_UNKNOWN, 1, 0, D3D12_TEXTURE_LAYOUT_ROW_MAJOR, flags ); } static inline CD3DX12_RESOURCE_DESC Tex1D( DXGI_FORMAT format, UINT64 width, UINT16 arraySize = 1, UINT16 mipLevels = 0, D3D12_RESOURCE_FLAGS flags = D3D12_RESOURCE_FLAG_NONE, D3D12_TEXTURE_LAYOUT layout = D3D12_TEXTURE_LAYOUT_UNKNOWN, UINT64 alignment = 0 ) noexcept { return CD3DX12_RESOURCE_DESC( D3D12_RESOURCE_DIMENSION_TEXTURE1D, alignment, width, 1, arraySize, mipLevels, format, 1, 0, layout, flags ); } static inline CD3DX12_RESOURCE_DESC Tex2D( DXGI_FORMAT format, UINT64 width, UINT height, UINT16 arraySize = 1, UINT16 mipLevels = 0, UINT sampleCount = 1, UINT sampleQuality = 0, D3D12_RESOURCE_FLAGS flags = D3D12_RESOURCE_FLAG_NONE, D3D12_TEXTURE_LAYOUT layout = D3D12_TEXTURE_LAYOUT_UNKNOWN, UINT64 alignment = 0 ) noexcept { return CD3DX12_RESOURCE_DESC( D3D12_RESOURCE_DIMENSION_TEXTURE2D, alignment, width, height, arraySize, mipLevels, format, sampleCount, sampleQuality, layout, flags ); } static inline CD3DX12_RESOURCE_DESC Tex3D( DXGI_FORMAT format, UINT64 width, UINT height, UINT16 depth, UINT16 mipLevels = 0, D3D12_RESOURCE_FLAGS flags = D3D12_RESOURCE_FLAG_NONE, D3D12_TEXTURE_LAYOUT layout = D3D12_TEXTURE_LAYOUT_UNKNOWN, UINT64 alignment = 0 ) noexcept { return CD3DX12_RESOURCE_DESC( D3D12_RESOURCE_DIMENSION_TEXTURE3D, alignment, width, height, depth, mipLevels, format, 1, 0, layout, flags ); } inline UINT16 Depth() const noexcept { return (Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE3D ? DepthOrArraySize : 1); } inline UINT16 ArraySize() const noexcept { return (Dimension != D3D12_RESOURCE_DIMENSION_TEXTURE3D ? DepthOrArraySize : 1); } inline UINT8 PlaneCount(_In_ ID3D12Device* pDevice) const noexcept { return D3D12GetFormatPlaneCount(pDevice, Format); } inline UINT Subresources(_In_ ID3D12Device* pDevice) const noexcept { return MipLevels * ArraySize() * PlaneCount(pDevice); } inline UINT CalcSubresource(UINT MipSlice, UINT ArraySlice, UINT PlaneSlice) noexcept { return D3D12CalcSubresource(MipSlice, ArraySlice, PlaneSlice, MipLevels, ArraySize()); } }; inline bool operator==( const D3D12_RESOURCE_DESC& l, const D3D12_RESOURCE_DESC& r ) noexcept { return l.Dimension == r.Dimension && l.Alignment == r.Alignment && l.Width == r.Width && l.Height == r.Height && l.DepthOrArraySize == r.DepthOrArraySize && l.MipLevels == r.MipLevels && l.Format == r.Format && l.SampleDesc.Count == r.SampleDesc.Count && l.SampleDesc.Quality == r.SampleDesc.Quality && l.Layout == r.Layout && l.Flags == r.Flags; } inline bool operator!=( const D3D12_RESOURCE_DESC& l, const D3D12_RESOURCE_DESC& r ) noexcept { return !( l == r ); } //------------------------------------------------------------------------------------------------ // Requires the Windows 10 SDK (19041) #if defined(NTDDI_WIN10_VB) && (NTDDI_VERSION >= NTDDI_WIN10_VB) struct CD3DX12_RESOURCE_DESC1 : public D3D12_RESOURCE_DESC1 { CD3DX12_RESOURCE_DESC1() = default; explicit CD3DX12_RESOURCE_DESC1( const D3D12_RESOURCE_DESC1& o ) noexcept : D3D12_RESOURCE_DESC1( o ) {} CD3DX12_RESOURCE_DESC1( D3D12_RESOURCE_DIMENSION dimension, UINT64 alignment, UINT64 width, UINT height, UINT16 depthOrArraySize, UINT16 mipLevels, DXGI_FORMAT format, UINT sampleCount, UINT sampleQuality, D3D12_TEXTURE_LAYOUT layout, D3D12_RESOURCE_FLAGS flags, UINT samplerFeedbackMipRegionWidth = 0, UINT samplerFeedbackMipRegionHeight = 0, UINT samplerFeedbackMipRegionDepth = 0) noexcept { Dimension = dimension; Alignment = alignment; Width = width; Height = height; DepthOrArraySize = depthOrArraySize; MipLevels = mipLevels; Format = format; SampleDesc.Count = sampleCount; SampleDesc.Quality = sampleQuality; Layout = layout; Flags = flags; SamplerFeedbackMipRegion.Width = samplerFeedbackMipRegionWidth; SamplerFeedbackMipRegion.Height = samplerFeedbackMipRegionHeight; SamplerFeedbackMipRegion.Depth = samplerFeedbackMipRegionDepth; } static inline CD3DX12_RESOURCE_DESC1 Buffer( const D3D12_RESOURCE_ALLOCATION_INFO& resAllocInfo, D3D12_RESOURCE_FLAGS flags = D3D12_RESOURCE_FLAG_NONE ) noexcept { return CD3DX12_RESOURCE_DESC1( D3D12_RESOURCE_DIMENSION_BUFFER, resAllocInfo.Alignment, resAllocInfo.SizeInBytes, 1, 1, 1, DXGI_FORMAT_UNKNOWN, 1, 0, D3D12_TEXTURE_LAYOUT_ROW_MAJOR, flags, 0, 0, 0 ); } static inline CD3DX12_RESOURCE_DESC1 Buffer( UINT64 width, D3D12_RESOURCE_FLAGS flags = D3D12_RESOURCE_FLAG_NONE, UINT64 alignment = 0 ) noexcept { return CD3DX12_RESOURCE_DESC1( D3D12_RESOURCE_DIMENSION_BUFFER, alignment, width, 1, 1, 1, DXGI_FORMAT_UNKNOWN, 1, 0, D3D12_TEXTURE_LAYOUT_ROW_MAJOR, flags, 0, 0, 0 ); } static inline CD3DX12_RESOURCE_DESC1 Tex1D( DXGI_FORMAT format, UINT64 width, UINT16 arraySize = 1, UINT16 mipLevels = 0, D3D12_RESOURCE_FLAGS flags = D3D12_RESOURCE_FLAG_NONE, D3D12_TEXTURE_LAYOUT layout = D3D12_TEXTURE_LAYOUT_UNKNOWN, UINT64 alignment = 0 ) noexcept { return CD3DX12_RESOURCE_DESC1( D3D12_RESOURCE_DIMENSION_TEXTURE1D, alignment, width, 1, arraySize, mipLevels, format, 1, 0, layout, flags, 0, 0, 0 ); } static inline CD3DX12_RESOURCE_DESC1 Tex2D( DXGI_FORMAT format, UINT64 width, UINT height, UINT16 arraySize = 1, UINT16 mipLevels = 0, UINT sampleCount = 1, UINT sampleQuality = 0, D3D12_RESOURCE_FLAGS flags = D3D12_RESOURCE_FLAG_NONE, D3D12_TEXTURE_LAYOUT layout = D3D12_TEXTURE_LAYOUT_UNKNOWN, UINT64 alignment = 0, UINT samplerFeedbackMipRegionWidth = 0, UINT samplerFeedbackMipRegionHeight = 0, UINT samplerFeedbackMipRegionDepth = 0) noexcept { return CD3DX12_RESOURCE_DESC1( D3D12_RESOURCE_DIMENSION_TEXTURE2D, alignment, width, height, arraySize, mipLevels, format, sampleCount, sampleQuality, layout, flags, samplerFeedbackMipRegionWidth, samplerFeedbackMipRegionHeight, samplerFeedbackMipRegionDepth ); } static inline CD3DX12_RESOURCE_DESC1 Tex3D( DXGI_FORMAT format, UINT64 width, UINT height, UINT16 depth, UINT16 mipLevels = 0, D3D12_RESOURCE_FLAGS flags = D3D12_RESOURCE_FLAG_NONE, D3D12_TEXTURE_LAYOUT layout = D3D12_TEXTURE_LAYOUT_UNKNOWN, UINT64 alignment = 0 ) noexcept { return CD3DX12_RESOURCE_DESC1( D3D12_RESOURCE_DIMENSION_TEXTURE3D, alignment, width, height, depth, mipLevels, format, 1, 0, layout, flags, 0, 0, 0 ); } inline UINT16 Depth() const noexcept { return (Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE3D ? DepthOrArraySize : 1); } inline UINT16 ArraySize() const noexcept { return (Dimension != D3D12_RESOURCE_DIMENSION_TEXTURE3D ? DepthOrArraySize : 1); } inline UINT8 PlaneCount(_In_ ID3D12Device* pDevice) const noexcept { return D3D12GetFormatPlaneCount(pDevice, Format); } inline UINT Subresources(_In_ ID3D12Device* pDevice) const noexcept { return MipLevels * ArraySize() * PlaneCount(pDevice); } inline UINT CalcSubresource(UINT MipSlice, UINT ArraySlice, UINT PlaneSlice) noexcept { return D3D12CalcSubresource(MipSlice, ArraySlice, PlaneSlice, MipLevels, ArraySize()); } }; inline bool operator==( const D3D12_RESOURCE_DESC1& l, const D3D12_RESOURCE_DESC1& r ) noexcept { return l.Dimension == r.Dimension && l.Alignment == r.Alignment && l.Width == r.Width && l.Height == r.Height && l.DepthOrArraySize == r.DepthOrArraySize && l.MipLevels == r.MipLevels && l.Format == r.Format && l.SampleDesc.Count == r.SampleDesc.Count && l.SampleDesc.Quality == r.SampleDesc.Quality && l.Layout == r.Layout && l.Flags == r.Flags && l.SamplerFeedbackMipRegion.Width == r.SamplerFeedbackMipRegion.Width && l.SamplerFeedbackMipRegion.Height == r.SamplerFeedbackMipRegion.Height && l.SamplerFeedbackMipRegion.Depth == r.SamplerFeedbackMipRegion.Depth; } inline bool operator!=( const D3D12_RESOURCE_DESC1& l, const D3D12_RESOURCE_DESC1& r ) noexcept { return !( l == r ); } #endif // NTDDI_WIN10_VB //------------------------------------------------------------------------------------------------ // Requires the Windows 10 Fall Creators Update SDK (16299) #if defined(NTDDI_WIN10_RS3) && (NTDDI_VERSION >= NTDDI_WIN10_RS3) struct CD3DX12_VIEW_INSTANCING_DESC : public D3D12_VIEW_INSTANCING_DESC { CD3DX12_VIEW_INSTANCING_DESC() = default; explicit CD3DX12_VIEW_INSTANCING_DESC( const D3D12_VIEW_INSTANCING_DESC& o ) noexcept : D3D12_VIEW_INSTANCING_DESC( o ) {} explicit CD3DX12_VIEW_INSTANCING_DESC( CD3DX12_DEFAULT ) noexcept { ViewInstanceCount = 0; pViewInstanceLocations = nullptr; Flags = D3D12_VIEW_INSTANCING_FLAG_NONE; } explicit CD3DX12_VIEW_INSTANCING_DESC( UINT InViewInstanceCount, const D3D12_VIEW_INSTANCE_LOCATION* InViewInstanceLocations, D3D12_VIEW_INSTANCING_FLAGS InFlags) noexcept { ViewInstanceCount = InViewInstanceCount; pViewInstanceLocations = InViewInstanceLocations; Flags = InFlags; } }; #endif // NTDDI_WIN10_RS3 //------------------------------------------------------------------------------------------------ // Row-by-row memcpy inline void MemcpySubresource( _In_ const D3D12_MEMCPY_DEST* pDest, _In_ const D3D12_SUBRESOURCE_DATA* pSrc, SIZE_T RowSizeInBytes, UINT NumRows, UINT NumSlices) noexcept { for (UINT z = 0; z < NumSlices; ++z) { auto pDestSlice = static_cast(pDest->pData) + pDest->SlicePitch * z; auto pSrcSlice = static_cast(pSrc->pData) + pSrc->SlicePitch * LONG_PTR(z); for (UINT y = 0; y < NumRows; ++y) { memcpy(pDestSlice + pDest->RowPitch * y, pSrcSlice + pSrc->RowPitch * LONG_PTR(y), RowSizeInBytes); } } } //------------------------------------------------------------------------------------------------ // Returns required size of a buffer to be used for data upload inline UINT64 GetRequiredIntermediateSize( _In_ ID3D12Resource* pDestinationResource, _In_range_(0,D3D12_REQ_SUBRESOURCES) UINT FirstSubresource, _In_range_(0,D3D12_REQ_SUBRESOURCES-FirstSubresource) UINT NumSubresources) noexcept { auto Desc = pDestinationResource->GetDesc(); UINT64 RequiredSize = 0; ID3D12Device* pDevice = nullptr; pDestinationResource->GetDevice(IID_ID3D12Device, reinterpret_cast(&pDevice)); pDevice->GetCopyableFootprints(&Desc, FirstSubresource, NumSubresources, 0, nullptr, nullptr, nullptr, &RequiredSize); pDevice->Release(); return RequiredSize; } //------------------------------------------------------------------------------------------------ // All arrays must be populated (e.g. by calling GetCopyableFootprints) inline UINT64 UpdateSubresources( _In_ ID3D12GraphicsCommandList* pCmdList, _In_ ID3D12Resource* pDestinationResource, _In_ ID3D12Resource* pIntermediate, _In_range_(0,D3D12_REQ_SUBRESOURCES) UINT FirstSubresource, _In_range_(0,D3D12_REQ_SUBRESOURCES-FirstSubresource) UINT NumSubresources, UINT64 RequiredSize, _In_reads_(NumSubresources) const D3D12_PLACED_SUBRESOURCE_FOOTPRINT* pLayouts, _In_reads_(NumSubresources) const UINT* pNumRows, _In_reads_(NumSubresources) const UINT64* pRowSizesInBytes, _In_reads_(NumSubresources) const D3D12_SUBRESOURCE_DATA* pSrcData) noexcept { // Minor validation auto IntermediateDesc = pIntermediate->GetDesc(); auto DestinationDesc = pDestinationResource->GetDesc(); if (IntermediateDesc.Dimension != D3D12_RESOURCE_DIMENSION_BUFFER || IntermediateDesc.Width < RequiredSize + pLayouts[0].Offset || RequiredSize > SIZE_T(-1) || (DestinationDesc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER && (FirstSubresource != 0 || NumSubresources != 1))) { return 0; } BYTE* pData; HRESULT hr = pIntermediate->Map(0, nullptr, reinterpret_cast(&pData)); if (FAILED(hr)) { return 0; } for (UINT i = 0; i < NumSubresources; ++i) { if (pRowSizesInBytes[i] > SIZE_T(-1)) return 0; D3D12_MEMCPY_DEST DestData = { pData + pLayouts[i].Offset, pLayouts[i].Footprint.RowPitch, SIZE_T(pLayouts[i].Footprint.RowPitch) * SIZE_T(pNumRows[i]) }; MemcpySubresource(&DestData, &pSrcData[i], static_cast(pRowSizesInBytes[i]), pNumRows[i], pLayouts[i].Footprint.Depth); } pIntermediate->Unmap(0, nullptr); if (DestinationDesc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER) { pCmdList->CopyBufferRegion( pDestinationResource, 0, pIntermediate, pLayouts[0].Offset, pLayouts[0].Footprint.Width); } else { for (UINT i = 0; i < NumSubresources; ++i) { CD3DX12_TEXTURE_COPY_LOCATION Dst(pDestinationResource, i + FirstSubresource); CD3DX12_TEXTURE_COPY_LOCATION Src(pIntermediate, pLayouts[i]); pCmdList->CopyTextureRegion(&Dst, 0, 0, 0, &Src, nullptr); } } return RequiredSize; } //------------------------------------------------------------------------------------------------ // Heap-allocating UpdateSubresources implementation inline UINT64 UpdateSubresources( _In_ ID3D12GraphicsCommandList* pCmdList, _In_ ID3D12Resource* pDestinationResource, _In_ ID3D12Resource* pIntermediate, UINT64 IntermediateOffset, _In_range_(0,D3D12_REQ_SUBRESOURCES) UINT FirstSubresource, _In_range_(0,D3D12_REQ_SUBRESOURCES-FirstSubresource) UINT NumSubresources, _In_reads_(NumSubresources) const D3D12_SUBRESOURCE_DATA* pSrcData) noexcept { UINT64 RequiredSize = 0; UINT64 MemToAlloc = static_cast(sizeof(D3D12_PLACED_SUBRESOURCE_FOOTPRINT) + sizeof(UINT) + sizeof(UINT64)) * NumSubresources; if (MemToAlloc > SIZE_MAX) { return 0; } void* pMem = HeapAlloc(GetProcessHeap(), 0, static_cast(MemToAlloc)); if (pMem == nullptr) { return 0; } auto pLayouts = static_cast(pMem); UINT64* pRowSizesInBytes = reinterpret_cast(pLayouts + NumSubresources); UINT* pNumRows = reinterpret_cast(pRowSizesInBytes + NumSubresources); auto Desc = pDestinationResource->GetDesc(); ID3D12Device* pDevice = nullptr; pDestinationResource->GetDevice(IID_ID3D12Device, reinterpret_cast(&pDevice)); pDevice->GetCopyableFootprints(&Desc, FirstSubresource, NumSubresources, IntermediateOffset, pLayouts, pNumRows, pRowSizesInBytes, &RequiredSize); pDevice->Release(); UINT64 Result = UpdateSubresources(pCmdList, pDestinationResource, pIntermediate, FirstSubresource, NumSubresources, RequiredSize, pLayouts, pNumRows, pRowSizesInBytes, pSrcData); HeapFree(GetProcessHeap(), 0, pMem); return Result; } //------------------------------------------------------------------------------------------------ // Stack-allocating UpdateSubresources implementation template inline UINT64 UpdateSubresources( _In_ ID3D12GraphicsCommandList* pCmdList, _In_ ID3D12Resource* pDestinationResource, _In_ ID3D12Resource* pIntermediate, UINT64 IntermediateOffset, _In_range_(0, MaxSubresources) UINT FirstSubresource, _In_range_(1, MaxSubresources - FirstSubresource) UINT NumSubresources, _In_reads_(NumSubresources) const D3D12_SUBRESOURCE_DATA* pSrcData) noexcept { UINT64 RequiredSize = 0; D3D12_PLACED_SUBRESOURCE_FOOTPRINT Layouts[MaxSubresources]; UINT NumRows[MaxSubresources]; UINT64 RowSizesInBytes[MaxSubresources]; auto Desc = pDestinationResource->GetDesc(); ID3D12Device* pDevice = nullptr; pDestinationResource->GetDevice(IID_ID3D12Device, reinterpret_cast(&pDevice)); pDevice->GetCopyableFootprints(&Desc, FirstSubresource, NumSubresources, IntermediateOffset, Layouts, NumRows, RowSizesInBytes, &RequiredSize); pDevice->Release(); return UpdateSubresources(pCmdList, pDestinationResource, pIntermediate, FirstSubresource, NumSubresources, RequiredSize, Layouts, NumRows, RowSizesInBytes, pSrcData); } //------------------------------------------------------------------------------------------------ inline constexpr bool D3D12IsLayoutOpaque( D3D12_TEXTURE_LAYOUT Layout ) noexcept { return Layout == D3D12_TEXTURE_LAYOUT_UNKNOWN || Layout == D3D12_TEXTURE_LAYOUT_64KB_UNDEFINED_SWIZZLE; } //------------------------------------------------------------------------------------------------ template inline ID3D12CommandList * const * CommandListCast(t_CommandListType * const * pp) noexcept { // This cast is useful for passing strongly typed command list pointers into // ExecuteCommandLists. // This cast is valid as long as the const-ness is respected. D3D12 APIs do // respect the const-ness of their arguments. return reinterpret_cast(pp); } //------------------------------------------------------------------------------------------------ // D3D12 exports a new method for serializing root signatures in the Windows 10 Anniversary Update. // To help enable root signature 1.1 features when they are available and not require maintaining // two code paths for building root signatures, this helper method reconstructs a 1.0 signature when // 1.1 is not supported. inline HRESULT D3DX12SerializeVersionedRootSignature( _In_ const D3D12_VERSIONED_ROOT_SIGNATURE_DESC* pRootSignatureDesc, D3D_ROOT_SIGNATURE_VERSION MaxVersion, _Outptr_ ID3DBlob** ppBlob, _Always_(_Outptr_opt_result_maybenull_) ID3DBlob** ppErrorBlob) noexcept { if (ppErrorBlob != nullptr) { *ppErrorBlob = nullptr; } switch (MaxVersion) { case D3D_ROOT_SIGNATURE_VERSION_1_0: switch (pRootSignatureDesc->Version) { case D3D_ROOT_SIGNATURE_VERSION_1_0: return D3D12SerializeRootSignature(&pRootSignatureDesc->Desc_1_0, D3D_ROOT_SIGNATURE_VERSION_1, ppBlob, ppErrorBlob); case D3D_ROOT_SIGNATURE_VERSION_1_1: { HRESULT hr = S_OK; const D3D12_ROOT_SIGNATURE_DESC1& desc_1_1 = pRootSignatureDesc->Desc_1_1; const SIZE_T ParametersSize = sizeof(D3D12_ROOT_PARAMETER) * desc_1_1.NumParameters; void* pParameters = (ParametersSize > 0) ? HeapAlloc(GetProcessHeap(), 0, ParametersSize) : nullptr; if (ParametersSize > 0 && pParameters == nullptr) { hr = E_OUTOFMEMORY; } auto pParameters_1_0 = static_cast(pParameters); if (SUCCEEDED(hr)) { for (UINT n = 0; n < desc_1_1.NumParameters; n++) { __analysis_assume(ParametersSize == sizeof(D3D12_ROOT_PARAMETER) * desc_1_1.NumParameters); pParameters_1_0[n].ParameterType = desc_1_1.pParameters[n].ParameterType; pParameters_1_0[n].ShaderVisibility = desc_1_1.pParameters[n].ShaderVisibility; switch (desc_1_1.pParameters[n].ParameterType) { case D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS: pParameters_1_0[n].Constants.Num32BitValues = desc_1_1.pParameters[n].Constants.Num32BitValues; pParameters_1_0[n].Constants.RegisterSpace = desc_1_1.pParameters[n].Constants.RegisterSpace; pParameters_1_0[n].Constants.ShaderRegister = desc_1_1.pParameters[n].Constants.ShaderRegister; break; case D3D12_ROOT_PARAMETER_TYPE_CBV: case D3D12_ROOT_PARAMETER_TYPE_SRV: case D3D12_ROOT_PARAMETER_TYPE_UAV: pParameters_1_0[n].Descriptor.RegisterSpace = desc_1_1.pParameters[n].Descriptor.RegisterSpace; pParameters_1_0[n].Descriptor.ShaderRegister = desc_1_1.pParameters[n].Descriptor.ShaderRegister; break; case D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE: const D3D12_ROOT_DESCRIPTOR_TABLE1& table_1_1 = desc_1_1.pParameters[n].DescriptorTable; const SIZE_T DescriptorRangesSize = sizeof(D3D12_DESCRIPTOR_RANGE) * table_1_1.NumDescriptorRanges; void* pDescriptorRanges = (DescriptorRangesSize > 0 && SUCCEEDED(hr)) ? HeapAlloc(GetProcessHeap(), 0, DescriptorRangesSize) : nullptr; if (DescriptorRangesSize > 0 && pDescriptorRanges == nullptr) { hr = E_OUTOFMEMORY; } auto pDescriptorRanges_1_0 = static_cast(pDescriptorRanges); if (SUCCEEDED(hr)) { for (UINT x = 0; x < table_1_1.NumDescriptorRanges; x++) { __analysis_assume(DescriptorRangesSize == sizeof(D3D12_DESCRIPTOR_RANGE) * table_1_1.NumDescriptorRanges); pDescriptorRanges_1_0[x].BaseShaderRegister = table_1_1.pDescriptorRanges[x].BaseShaderRegister; pDescriptorRanges_1_0[x].NumDescriptors = table_1_1.pDescriptorRanges[x].NumDescriptors; pDescriptorRanges_1_0[x].OffsetInDescriptorsFromTableStart = table_1_1.pDescriptorRanges[x].OffsetInDescriptorsFromTableStart; pDescriptorRanges_1_0[x].RangeType = table_1_1.pDescriptorRanges[x].RangeType; pDescriptorRanges_1_0[x].RegisterSpace = table_1_1.pDescriptorRanges[x].RegisterSpace; } } D3D12_ROOT_DESCRIPTOR_TABLE& table_1_0 = pParameters_1_0[n].DescriptorTable; table_1_0.NumDescriptorRanges = table_1_1.NumDescriptorRanges; table_1_0.pDescriptorRanges = pDescriptorRanges_1_0; } } } if (SUCCEEDED(hr)) { CD3DX12_ROOT_SIGNATURE_DESC desc_1_0(desc_1_1.NumParameters, pParameters_1_0, desc_1_1.NumStaticSamplers, desc_1_1.pStaticSamplers, desc_1_1.Flags); hr = D3D12SerializeRootSignature(&desc_1_0, D3D_ROOT_SIGNATURE_VERSION_1, ppBlob, ppErrorBlob); } if (pParameters) { for (UINT n = 0; n < desc_1_1.NumParameters; n++) { if (desc_1_1.pParameters[n].ParameterType == D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE) { HeapFree(GetProcessHeap(), 0, reinterpret_cast(const_cast(pParameters_1_0[n].DescriptorTable.pDescriptorRanges))); } } HeapFree(GetProcessHeap(), 0, pParameters); } return hr; } } break; case D3D_ROOT_SIGNATURE_VERSION_1_1: return D3D12SerializeVersionedRootSignature(pRootSignatureDesc, ppBlob, ppErrorBlob); } return E_INVALIDARG; } //------------------------------------------------------------------------------------------------ #if defined(NTDDI_WIN10_RS2) && (NTDDI_VERSION >= NTDDI_WIN10_RS2) struct CD3DX12_RT_FORMAT_ARRAY : public D3D12_RT_FORMAT_ARRAY { CD3DX12_RT_FORMAT_ARRAY() = default; explicit CD3DX12_RT_FORMAT_ARRAY(const D3D12_RT_FORMAT_ARRAY& o) noexcept : D3D12_RT_FORMAT_ARRAY(o) {} explicit CD3DX12_RT_FORMAT_ARRAY(_In_reads_(NumFormats) const DXGI_FORMAT* pFormats, UINT NumFormats) noexcept { NumRenderTargets = NumFormats; memcpy(RTFormats, pFormats, sizeof(RTFormats)); // assumes ARRAY_SIZE(pFormats) == ARRAY_SIZE(RTFormats) } }; //------------------------------------------------------------------------------------------------ // Pipeline State Stream Helpers //------------------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------------------ // Stream Subobjects, i.e. elements of a stream struct DefaultSampleMask { operator UINT() noexcept { return UINT_MAX; } }; struct DefaultSampleDesc { operator DXGI_SAMPLE_DESC() noexcept { return DXGI_SAMPLE_DESC{1, 0}; } }; #pragma warning(push) #pragma warning(disable : 4324) template class alignas(void*) CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT { private: D3D12_PIPELINE_STATE_SUBOBJECT_TYPE _Type; InnerStructType _Inner; public: CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT() noexcept : _Type(Type), _Inner(DefaultArg()) {} CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT(InnerStructType const& i) noexcept : _Type(Type), _Inner(i) {} CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT& operator=(InnerStructType const& i) noexcept { _Type = Type; _Inner = i; return *this; } operator InnerStructType const&() const noexcept { return _Inner; } operator InnerStructType&() noexcept { return _Inner; } InnerStructType* operator&() noexcept { return &_Inner; } InnerStructType const* operator&() const noexcept { return &_Inner; } }; #pragma warning(pop) typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< D3D12_PIPELINE_STATE_FLAGS, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_FLAGS> CD3DX12_PIPELINE_STATE_STREAM_FLAGS; typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< UINT, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_NODE_MASK> CD3DX12_PIPELINE_STATE_STREAM_NODE_MASK; typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< ID3D12RootSignature*, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_ROOT_SIGNATURE> CD3DX12_PIPELINE_STATE_STREAM_ROOT_SIGNATURE; typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< D3D12_INPUT_LAYOUT_DESC, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_INPUT_LAYOUT> CD3DX12_PIPELINE_STATE_STREAM_INPUT_LAYOUT; typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< D3D12_INDEX_BUFFER_STRIP_CUT_VALUE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_IB_STRIP_CUT_VALUE> CD3DX12_PIPELINE_STATE_STREAM_IB_STRIP_CUT_VALUE; typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< D3D12_PRIMITIVE_TOPOLOGY_TYPE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_PRIMITIVE_TOPOLOGY> CD3DX12_PIPELINE_STATE_STREAM_PRIMITIVE_TOPOLOGY; typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< D3D12_SHADER_BYTECODE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_VS> CD3DX12_PIPELINE_STATE_STREAM_VS; typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< D3D12_SHADER_BYTECODE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_GS> CD3DX12_PIPELINE_STATE_STREAM_GS; typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< D3D12_STREAM_OUTPUT_DESC, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_STREAM_OUTPUT> CD3DX12_PIPELINE_STATE_STREAM_STREAM_OUTPUT; typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< D3D12_SHADER_BYTECODE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_HS> CD3DX12_PIPELINE_STATE_STREAM_HS; typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< D3D12_SHADER_BYTECODE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DS> CD3DX12_PIPELINE_STATE_STREAM_DS; typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< D3D12_SHADER_BYTECODE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_PS> CD3DX12_PIPELINE_STATE_STREAM_PS; #if defined(NTDDI_WIN10_VB) && (NTDDI_VERSION >= NTDDI_WIN10_VB) typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< D3D12_SHADER_BYTECODE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_AS> CD3DX12_PIPELINE_STATE_STREAM_AS; typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< D3D12_SHADER_BYTECODE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_MS> CD3DX12_PIPELINE_STATE_STREAM_MS; #endif typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< D3D12_SHADER_BYTECODE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_CS> CD3DX12_PIPELINE_STATE_STREAM_CS; typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< CD3DX12_BLEND_DESC, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_BLEND, CD3DX12_DEFAULT> CD3DX12_PIPELINE_STATE_STREAM_BLEND_DESC; typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< CD3DX12_DEPTH_STENCIL_DESC, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DEPTH_STENCIL, CD3DX12_DEFAULT> CD3DX12_PIPELINE_STATE_STREAM_DEPTH_STENCIL; typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< CD3DX12_DEPTH_STENCIL_DESC1, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DEPTH_STENCIL1, CD3DX12_DEFAULT> CD3DX12_PIPELINE_STATE_STREAM_DEPTH_STENCIL1; typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< DXGI_FORMAT, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DEPTH_STENCIL_FORMAT> CD3DX12_PIPELINE_STATE_STREAM_DEPTH_STENCIL_FORMAT; typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< CD3DX12_RASTERIZER_DESC, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_RASTERIZER, CD3DX12_DEFAULT> CD3DX12_PIPELINE_STATE_STREAM_RASTERIZER; typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< D3D12_RT_FORMAT_ARRAY, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_RENDER_TARGET_FORMATS> CD3DX12_PIPELINE_STATE_STREAM_RENDER_TARGET_FORMATS; typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< DXGI_SAMPLE_DESC, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_SAMPLE_DESC, DefaultSampleDesc> CD3DX12_PIPELINE_STATE_STREAM_SAMPLE_DESC; typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< UINT, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_SAMPLE_MASK, DefaultSampleMask> CD3DX12_PIPELINE_STATE_STREAM_SAMPLE_MASK; typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< D3D12_CACHED_PIPELINE_STATE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_CACHED_PSO> CD3DX12_PIPELINE_STATE_STREAM_CACHED_PSO; #if defined(NTDDI_WIN10_RS3) && (NTDDI_VERSION >= NTDDI_WIN10_RS3) typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< CD3DX12_VIEW_INSTANCING_DESC, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_VIEW_INSTANCING, CD3DX12_DEFAULT> CD3DX12_PIPELINE_STATE_STREAM_VIEW_INSTANCING; #endif //------------------------------------------------------------------------------------------------ // Stream Parser Helpers struct ID3DX12PipelineParserCallbacks { // Subobject Callbacks virtual void FlagsCb(D3D12_PIPELINE_STATE_FLAGS) {} virtual void NodeMaskCb(UINT) {} virtual void RootSignatureCb(ID3D12RootSignature*) {} virtual void InputLayoutCb(const D3D12_INPUT_LAYOUT_DESC&) {} virtual void IBStripCutValueCb(D3D12_INDEX_BUFFER_STRIP_CUT_VALUE) {} virtual void PrimitiveTopologyTypeCb(D3D12_PRIMITIVE_TOPOLOGY_TYPE) {} virtual void VSCb(const D3D12_SHADER_BYTECODE&) {} virtual void GSCb(const D3D12_SHADER_BYTECODE&) {} virtual void StreamOutputCb(const D3D12_STREAM_OUTPUT_DESC&) {} virtual void HSCb(const D3D12_SHADER_BYTECODE&) {} virtual void DSCb(const D3D12_SHADER_BYTECODE&) {} virtual void PSCb(const D3D12_SHADER_BYTECODE&) {} virtual void CSCb(const D3D12_SHADER_BYTECODE&) {} #if defined(NTDDI_WIN10_VB) && (NTDDI_VERSION >= NTDDI_WIN10_VB) virtual void ASCb(const D3D12_SHADER_BYTECODE&) {} virtual void MSCb(const D3D12_SHADER_BYTECODE&) {} #endif virtual void BlendStateCb(const D3D12_BLEND_DESC&) {} virtual void DepthStencilStateCb(const D3D12_DEPTH_STENCIL_DESC&) {} virtual void DepthStencilState1Cb(const D3D12_DEPTH_STENCIL_DESC1&) {} virtual void DSVFormatCb(DXGI_FORMAT) {} virtual void RasterizerStateCb(const D3D12_RASTERIZER_DESC&) {} virtual void RTVFormatsCb(const D3D12_RT_FORMAT_ARRAY&) {} virtual void SampleDescCb(const DXGI_SAMPLE_DESC&) {} virtual void SampleMaskCb(UINT) {} #if defined(NTDDI_WIN10_RS3) && (NTDDI_VERSION >= NTDDI_WIN10_RS3) virtual void ViewInstancingCb(const D3D12_VIEW_INSTANCING_DESC&) {} #endif virtual void CachedPSOCb(const D3D12_CACHED_PIPELINE_STATE&) {} // Error Callbacks virtual void ErrorBadInputParameter(UINT /*ParameterIndex*/) {} virtual void ErrorDuplicateSubobject(D3D12_PIPELINE_STATE_SUBOBJECT_TYPE /*DuplicateType*/) {} virtual void ErrorUnknownSubobject(UINT /*UnknownTypeValue*/) {} virtual ~ID3DX12PipelineParserCallbacks() = default; }; #if defined(NTDDI_WIN10_VB) && (NTDDI_VERSION >= NTDDI_WIN10_VB) struct D3DX12_MESH_SHADER_PIPELINE_STATE_DESC { ID3D12RootSignature* pRootSignature; D3D12_SHADER_BYTECODE AS; D3D12_SHADER_BYTECODE MS; D3D12_SHADER_BYTECODE PS; D3D12_BLEND_DESC BlendState; UINT SampleMask; D3D12_RASTERIZER_DESC RasterizerState; D3D12_DEPTH_STENCIL_DESC DepthStencilState; D3D12_PRIMITIVE_TOPOLOGY_TYPE PrimitiveTopologyType; UINT NumRenderTargets; DXGI_FORMAT RTVFormats[ D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT ]; DXGI_FORMAT DSVFormat; DXGI_SAMPLE_DESC SampleDesc; UINT NodeMask; D3D12_CACHED_PIPELINE_STATE CachedPSO; D3D12_PIPELINE_STATE_FLAGS Flags; }; // CD3DX12_PIPELINE_STATE_STREAM2 Works on OS Build 19041+ (where there is a new mesh shader pipeline). // Use CD3DX12_PIPELINE_STATE_STREAM1 for OS Build 16299+ (where there is a new view instancing subobject). // Use CD3DX12_PIPELINE_STATE_STREAM for OS Build 15063+ support. struct CD3DX12_PIPELINE_STATE_STREAM2 { CD3DX12_PIPELINE_STATE_STREAM2() = default; // Mesh and amplification shaders must be set manually, since they do not have representation in D3D12_GRAPHICS_PIPELINE_STATE_DESC CD3DX12_PIPELINE_STATE_STREAM2(const D3D12_GRAPHICS_PIPELINE_STATE_DESC& Desc) noexcept : Flags(Desc.Flags) , NodeMask(Desc.NodeMask) , pRootSignature(Desc.pRootSignature) , InputLayout(Desc.InputLayout) , IBStripCutValue(Desc.IBStripCutValue) , PrimitiveTopologyType(Desc.PrimitiveTopologyType) , VS(Desc.VS) , GS(Desc.GS) , StreamOutput(Desc.StreamOutput) , HS(Desc.HS) , DS(Desc.DS) , PS(Desc.PS) , BlendState(CD3DX12_BLEND_DESC(Desc.BlendState)) , DepthStencilState(CD3DX12_DEPTH_STENCIL_DESC1(Desc.DepthStencilState)) , DSVFormat(Desc.DSVFormat) , RasterizerState(CD3DX12_RASTERIZER_DESC(Desc.RasterizerState)) , RTVFormats(CD3DX12_RT_FORMAT_ARRAY(Desc.RTVFormats, Desc.NumRenderTargets)) , SampleDesc(Desc.SampleDesc) , SampleMask(Desc.SampleMask) , CachedPSO(Desc.CachedPSO) , ViewInstancingDesc(CD3DX12_VIEW_INSTANCING_DESC(CD3DX12_DEFAULT())) {} CD3DX12_PIPELINE_STATE_STREAM2(const D3DX12_MESH_SHADER_PIPELINE_STATE_DESC& Desc) noexcept : Flags(Desc.Flags) , NodeMask(Desc.NodeMask) , pRootSignature(Desc.pRootSignature) , PrimitiveTopologyType(Desc.PrimitiveTopologyType) , PS(Desc.PS) , AS(Desc.AS) , MS(Desc.MS) , BlendState(CD3DX12_BLEND_DESC(Desc.BlendState)) , DepthStencilState(CD3DX12_DEPTH_STENCIL_DESC1(Desc.DepthStencilState)) , DSVFormat(Desc.DSVFormat) , RasterizerState(CD3DX12_RASTERIZER_DESC(Desc.RasterizerState)) , RTVFormats(CD3DX12_RT_FORMAT_ARRAY(Desc.RTVFormats, Desc.NumRenderTargets)) , SampleDesc(Desc.SampleDesc) , SampleMask(Desc.SampleMask) , CachedPSO(Desc.CachedPSO) , ViewInstancingDesc(CD3DX12_VIEW_INSTANCING_DESC(CD3DX12_DEFAULT())) {} CD3DX12_PIPELINE_STATE_STREAM2(const D3D12_COMPUTE_PIPELINE_STATE_DESC& Desc) noexcept : Flags(Desc.Flags) , NodeMask(Desc.NodeMask) , pRootSignature(Desc.pRootSignature) , CS(CD3DX12_SHADER_BYTECODE(Desc.CS)) , CachedPSO(Desc.CachedPSO) { static_cast(DepthStencilState).DepthEnable = false; } CD3DX12_PIPELINE_STATE_STREAM_FLAGS Flags; CD3DX12_PIPELINE_STATE_STREAM_NODE_MASK NodeMask; CD3DX12_PIPELINE_STATE_STREAM_ROOT_SIGNATURE pRootSignature; CD3DX12_PIPELINE_STATE_STREAM_INPUT_LAYOUT InputLayout; CD3DX12_PIPELINE_STATE_STREAM_IB_STRIP_CUT_VALUE IBStripCutValue; CD3DX12_PIPELINE_STATE_STREAM_PRIMITIVE_TOPOLOGY PrimitiveTopologyType; CD3DX12_PIPELINE_STATE_STREAM_VS VS; CD3DX12_PIPELINE_STATE_STREAM_GS GS; CD3DX12_PIPELINE_STATE_STREAM_STREAM_OUTPUT StreamOutput; CD3DX12_PIPELINE_STATE_STREAM_HS HS; CD3DX12_PIPELINE_STATE_STREAM_DS DS; CD3DX12_PIPELINE_STATE_STREAM_PS PS; CD3DX12_PIPELINE_STATE_STREAM_AS AS; CD3DX12_PIPELINE_STATE_STREAM_MS MS; CD3DX12_PIPELINE_STATE_STREAM_CS CS; CD3DX12_PIPELINE_STATE_STREAM_BLEND_DESC BlendState; CD3DX12_PIPELINE_STATE_STREAM_DEPTH_STENCIL1 DepthStencilState; CD3DX12_PIPELINE_STATE_STREAM_DEPTH_STENCIL_FORMAT DSVFormat; CD3DX12_PIPELINE_STATE_STREAM_RASTERIZER RasterizerState; CD3DX12_PIPELINE_STATE_STREAM_RENDER_TARGET_FORMATS RTVFormats; CD3DX12_PIPELINE_STATE_STREAM_SAMPLE_DESC SampleDesc; CD3DX12_PIPELINE_STATE_STREAM_SAMPLE_MASK SampleMask; CD3DX12_PIPELINE_STATE_STREAM_CACHED_PSO CachedPSO; CD3DX12_PIPELINE_STATE_STREAM_VIEW_INSTANCING ViewInstancingDesc; D3D12_GRAPHICS_PIPELINE_STATE_DESC GraphicsDescV0() const noexcept { D3D12_GRAPHICS_PIPELINE_STATE_DESC D; D.Flags = this->Flags; D.NodeMask = this->NodeMask; D.pRootSignature = this->pRootSignature; D.InputLayout = this->InputLayout; D.IBStripCutValue = this->IBStripCutValue; D.PrimitiveTopologyType = this->PrimitiveTopologyType; D.VS = this->VS; D.GS = this->GS; D.StreamOutput = this->StreamOutput; D.HS = this->HS; D.DS = this->DS; D.PS = this->PS; D.BlendState = this->BlendState; D.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC1(D3D12_DEPTH_STENCIL_DESC1(this->DepthStencilState)); D.DSVFormat = this->DSVFormat; D.RasterizerState = this->RasterizerState; D.NumRenderTargets = D3D12_RT_FORMAT_ARRAY(this->RTVFormats).NumRenderTargets; memcpy(D.RTVFormats, D3D12_RT_FORMAT_ARRAY(this->RTVFormats).RTFormats, sizeof(D.RTVFormats)); D.SampleDesc = this->SampleDesc; D.SampleMask = this->SampleMask; D.CachedPSO = this->CachedPSO; return D; } D3D12_COMPUTE_PIPELINE_STATE_DESC ComputeDescV0() const noexcept { D3D12_COMPUTE_PIPELINE_STATE_DESC D; D.Flags = this->Flags; D.NodeMask = this->NodeMask; D.pRootSignature = this->pRootSignature; D.CS = this->CS; D.CachedPSO = this->CachedPSO; return D; } }; #endif // NTDDI_WIN10_VB #if defined(NTDDI_WIN10_RS3) && (NTDDI_VERSION >= NTDDI_WIN10_RS3) // CD3DX12_PIPELINE_STATE_STREAM1 Works on OS Build 16299+ (where there is a new view instancing subobject). // Use CD3DX12_PIPELINE_STATE_STREAM for OS Build 15063+ support. struct CD3DX12_PIPELINE_STATE_STREAM1 { CD3DX12_PIPELINE_STATE_STREAM1() = default; // Mesh and amplification shaders must be set manually, since they do not have representation in D3D12_GRAPHICS_PIPELINE_STATE_DESC CD3DX12_PIPELINE_STATE_STREAM1(const D3D12_GRAPHICS_PIPELINE_STATE_DESC& Desc) noexcept : Flags(Desc.Flags) , NodeMask(Desc.NodeMask) , pRootSignature(Desc.pRootSignature) , InputLayout(Desc.InputLayout) , IBStripCutValue(Desc.IBStripCutValue) , PrimitiveTopologyType(Desc.PrimitiveTopologyType) , VS(Desc.VS) , GS(Desc.GS) , StreamOutput(Desc.StreamOutput) , HS(Desc.HS) , DS(Desc.DS) , PS(Desc.PS) , BlendState(CD3DX12_BLEND_DESC(Desc.BlendState)) , DepthStencilState(CD3DX12_DEPTH_STENCIL_DESC1(Desc.DepthStencilState)) , DSVFormat(Desc.DSVFormat) , RasterizerState(CD3DX12_RASTERIZER_DESC(Desc.RasterizerState)) , RTVFormats(CD3DX12_RT_FORMAT_ARRAY(Desc.RTVFormats, Desc.NumRenderTargets)) , SampleDesc(Desc.SampleDesc) , SampleMask(Desc.SampleMask) , CachedPSO(Desc.CachedPSO) , ViewInstancingDesc(CD3DX12_VIEW_INSTANCING_DESC(CD3DX12_DEFAULT())) {} #if defined(NTDDI_WIN10_VB) && (NTDDI_VERSION >= NTDDI_WIN10_VB) CD3DX12_PIPELINE_STATE_STREAM1(const D3DX12_MESH_SHADER_PIPELINE_STATE_DESC& Desc) noexcept : Flags(Desc.Flags) , NodeMask(Desc.NodeMask) , pRootSignature(Desc.pRootSignature) , PrimitiveTopologyType(Desc.PrimitiveTopologyType) , PS(Desc.PS) , BlendState(CD3DX12_BLEND_DESC(Desc.BlendState)) , DepthStencilState(CD3DX12_DEPTH_STENCIL_DESC1(Desc.DepthStencilState)) , DSVFormat(Desc.DSVFormat) , RasterizerState(CD3DX12_RASTERIZER_DESC(Desc.RasterizerState)) , RTVFormats(CD3DX12_RT_FORMAT_ARRAY(Desc.RTVFormats, Desc.NumRenderTargets)) , SampleDesc(Desc.SampleDesc) , SampleMask(Desc.SampleMask) , CachedPSO(Desc.CachedPSO) , ViewInstancingDesc(CD3DX12_VIEW_INSTANCING_DESC(CD3DX12_DEFAULT())) {} #endif CD3DX12_PIPELINE_STATE_STREAM1(const D3D12_COMPUTE_PIPELINE_STATE_DESC& Desc) noexcept : Flags(Desc.Flags) , NodeMask(Desc.NodeMask) , pRootSignature(Desc.pRootSignature) , CS(CD3DX12_SHADER_BYTECODE(Desc.CS)) , CachedPSO(Desc.CachedPSO) { static_cast(DepthStencilState).DepthEnable = false; } CD3DX12_PIPELINE_STATE_STREAM_FLAGS Flags; CD3DX12_PIPELINE_STATE_STREAM_NODE_MASK NodeMask; CD3DX12_PIPELINE_STATE_STREAM_ROOT_SIGNATURE pRootSignature; CD3DX12_PIPELINE_STATE_STREAM_INPUT_LAYOUT InputLayout; CD3DX12_PIPELINE_STATE_STREAM_IB_STRIP_CUT_VALUE IBStripCutValue; CD3DX12_PIPELINE_STATE_STREAM_PRIMITIVE_TOPOLOGY PrimitiveTopologyType; CD3DX12_PIPELINE_STATE_STREAM_VS VS; CD3DX12_PIPELINE_STATE_STREAM_GS GS; CD3DX12_PIPELINE_STATE_STREAM_STREAM_OUTPUT StreamOutput; CD3DX12_PIPELINE_STATE_STREAM_HS HS; CD3DX12_PIPELINE_STATE_STREAM_DS DS; CD3DX12_PIPELINE_STATE_STREAM_PS PS; CD3DX12_PIPELINE_STATE_STREAM_CS CS; CD3DX12_PIPELINE_STATE_STREAM_BLEND_DESC BlendState; CD3DX12_PIPELINE_STATE_STREAM_DEPTH_STENCIL1 DepthStencilState; CD3DX12_PIPELINE_STATE_STREAM_DEPTH_STENCIL_FORMAT DSVFormat; CD3DX12_PIPELINE_STATE_STREAM_RASTERIZER RasterizerState; CD3DX12_PIPELINE_STATE_STREAM_RENDER_TARGET_FORMATS RTVFormats; CD3DX12_PIPELINE_STATE_STREAM_SAMPLE_DESC SampleDesc; CD3DX12_PIPELINE_STATE_STREAM_SAMPLE_MASK SampleMask; CD3DX12_PIPELINE_STATE_STREAM_CACHED_PSO CachedPSO; CD3DX12_PIPELINE_STATE_STREAM_VIEW_INSTANCING ViewInstancingDesc; D3D12_GRAPHICS_PIPELINE_STATE_DESC GraphicsDescV0() const noexcept { D3D12_GRAPHICS_PIPELINE_STATE_DESC D; D.Flags = this->Flags; D.NodeMask = this->NodeMask; D.pRootSignature = this->pRootSignature; D.InputLayout = this->InputLayout; D.IBStripCutValue = this->IBStripCutValue; D.PrimitiveTopologyType = this->PrimitiveTopologyType; D.VS = this->VS; D.GS = this->GS; D.StreamOutput = this->StreamOutput; D.HS = this->HS; D.DS = this->DS; D.PS = this->PS; D.BlendState = this->BlendState; D.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC1(D3D12_DEPTH_STENCIL_DESC1(this->DepthStencilState)); D.DSVFormat = this->DSVFormat; D.RasterizerState = this->RasterizerState; D.NumRenderTargets = D3D12_RT_FORMAT_ARRAY(this->RTVFormats).NumRenderTargets; memcpy(D.RTVFormats, D3D12_RT_FORMAT_ARRAY(this->RTVFormats).RTFormats, sizeof(D.RTVFormats)); D.SampleDesc = this->SampleDesc; D.SampleMask = this->SampleMask; D.CachedPSO = this->CachedPSO; return D; } D3D12_COMPUTE_PIPELINE_STATE_DESC ComputeDescV0() const noexcept { D3D12_COMPUTE_PIPELINE_STATE_DESC D; D.Flags = this->Flags; D.NodeMask = this->NodeMask; D.pRootSignature = this->pRootSignature; D.CS = this->CS; D.CachedPSO = this->CachedPSO; return D; } }; #endif // NTDDI_WIN10_RS3 #if defined(NTDDI_WIN10_VB) && (NTDDI_VERSION >= NTDDI_WIN10_VB) struct CD3DX12_PIPELINE_MESH_STATE_STREAM { CD3DX12_PIPELINE_MESH_STATE_STREAM() = default; CD3DX12_PIPELINE_MESH_STATE_STREAM(const D3DX12_MESH_SHADER_PIPELINE_STATE_DESC& Desc) noexcept : Flags(Desc.Flags) , NodeMask(Desc.NodeMask) , pRootSignature(Desc.pRootSignature) , PS(Desc.PS) , AS(Desc.AS) , MS(Desc.MS) , BlendState(CD3DX12_BLEND_DESC(Desc.BlendState)) , DepthStencilState(CD3DX12_DEPTH_STENCIL_DESC1(Desc.DepthStencilState)) , DSVFormat(Desc.DSVFormat) , RasterizerState(CD3DX12_RASTERIZER_DESC(Desc.RasterizerState)) , RTVFormats(CD3DX12_RT_FORMAT_ARRAY(Desc.RTVFormats, Desc.NumRenderTargets)) , SampleDesc(Desc.SampleDesc) , SampleMask(Desc.SampleMask) , CachedPSO(Desc.CachedPSO) , ViewInstancingDesc(CD3DX12_VIEW_INSTANCING_DESC(CD3DX12_DEFAULT())) {} CD3DX12_PIPELINE_STATE_STREAM_FLAGS Flags; CD3DX12_PIPELINE_STATE_STREAM_NODE_MASK NodeMask; CD3DX12_PIPELINE_STATE_STREAM_ROOT_SIGNATURE pRootSignature; CD3DX12_PIPELINE_STATE_STREAM_PS PS; CD3DX12_PIPELINE_STATE_STREAM_AS AS; CD3DX12_PIPELINE_STATE_STREAM_MS MS; CD3DX12_PIPELINE_STATE_STREAM_BLEND_DESC BlendState; CD3DX12_PIPELINE_STATE_STREAM_DEPTH_STENCIL1 DepthStencilState; CD3DX12_PIPELINE_STATE_STREAM_DEPTH_STENCIL_FORMAT DSVFormat; CD3DX12_PIPELINE_STATE_STREAM_RASTERIZER RasterizerState; CD3DX12_PIPELINE_STATE_STREAM_RENDER_TARGET_FORMATS RTVFormats; CD3DX12_PIPELINE_STATE_STREAM_SAMPLE_DESC SampleDesc; CD3DX12_PIPELINE_STATE_STREAM_SAMPLE_MASK SampleMask; CD3DX12_PIPELINE_STATE_STREAM_CACHED_PSO CachedPSO; CD3DX12_PIPELINE_STATE_STREAM_VIEW_INSTANCING ViewInstancingDesc; D3DX12_MESH_SHADER_PIPELINE_STATE_DESC MeshShaderDescV0() const noexcept { D3DX12_MESH_SHADER_PIPELINE_STATE_DESC D; D.Flags = this->Flags; D.NodeMask = this->NodeMask; D.pRootSignature = this->pRootSignature; D.PS = this->PS; D.AS = this->AS; D.MS = this->MS; D.BlendState = this->BlendState; D.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC1(D3D12_DEPTH_STENCIL_DESC1(this->DepthStencilState)); D.DSVFormat = this->DSVFormat; D.RasterizerState = this->RasterizerState; D.NumRenderTargets = D3D12_RT_FORMAT_ARRAY(this->RTVFormats).NumRenderTargets; memcpy(D.RTVFormats, D3D12_RT_FORMAT_ARRAY(this->RTVFormats).RTFormats, sizeof(D.RTVFormats)); D.SampleDesc = this->SampleDesc; D.SampleMask = this->SampleMask; D.CachedPSO = this->CachedPSO; return D; } }; #endif // NTDDI_WIN10_VB // CD3DX12_PIPELINE_STATE_STREAM works on OS Build 15063+ but does not support new subobject(s) added in OS Build 16299+. // See CD3DX12_PIPELINE_STATE_STREAM1 for instance. struct CD3DX12_PIPELINE_STATE_STREAM { CD3DX12_PIPELINE_STATE_STREAM() = default; CD3DX12_PIPELINE_STATE_STREAM(const D3D12_GRAPHICS_PIPELINE_STATE_DESC& Desc) noexcept : Flags(Desc.Flags) , NodeMask(Desc.NodeMask) , pRootSignature(Desc.pRootSignature) , InputLayout(Desc.InputLayout) , IBStripCutValue(Desc.IBStripCutValue) , PrimitiveTopologyType(Desc.PrimitiveTopologyType) , VS(Desc.VS) , GS(Desc.GS) , StreamOutput(Desc.StreamOutput) , HS(Desc.HS) , DS(Desc.DS) , PS(Desc.PS) , BlendState(CD3DX12_BLEND_DESC(Desc.BlendState)) , DepthStencilState(CD3DX12_DEPTH_STENCIL_DESC1(Desc.DepthStencilState)) , DSVFormat(Desc.DSVFormat) , RasterizerState(CD3DX12_RASTERIZER_DESC(Desc.RasterizerState)) , RTVFormats(CD3DX12_RT_FORMAT_ARRAY(Desc.RTVFormats, Desc.NumRenderTargets)) , SampleDesc(Desc.SampleDesc) , SampleMask(Desc.SampleMask) , CachedPSO(Desc.CachedPSO) {} CD3DX12_PIPELINE_STATE_STREAM(const D3D12_COMPUTE_PIPELINE_STATE_DESC& Desc) noexcept : Flags(Desc.Flags) , NodeMask(Desc.NodeMask) , pRootSignature(Desc.pRootSignature) , CS(CD3DX12_SHADER_BYTECODE(Desc.CS)) , CachedPSO(Desc.CachedPSO) {} CD3DX12_PIPELINE_STATE_STREAM_FLAGS Flags; CD3DX12_PIPELINE_STATE_STREAM_NODE_MASK NodeMask; CD3DX12_PIPELINE_STATE_STREAM_ROOT_SIGNATURE pRootSignature; CD3DX12_PIPELINE_STATE_STREAM_INPUT_LAYOUT InputLayout; CD3DX12_PIPELINE_STATE_STREAM_IB_STRIP_CUT_VALUE IBStripCutValue; CD3DX12_PIPELINE_STATE_STREAM_PRIMITIVE_TOPOLOGY PrimitiveTopologyType; CD3DX12_PIPELINE_STATE_STREAM_VS VS; CD3DX12_PIPELINE_STATE_STREAM_GS GS; CD3DX12_PIPELINE_STATE_STREAM_STREAM_OUTPUT StreamOutput; CD3DX12_PIPELINE_STATE_STREAM_HS HS; CD3DX12_PIPELINE_STATE_STREAM_DS DS; CD3DX12_PIPELINE_STATE_STREAM_PS PS; CD3DX12_PIPELINE_STATE_STREAM_CS CS; CD3DX12_PIPELINE_STATE_STREAM_BLEND_DESC BlendState; CD3DX12_PIPELINE_STATE_STREAM_DEPTH_STENCIL1 DepthStencilState; CD3DX12_PIPELINE_STATE_STREAM_DEPTH_STENCIL_FORMAT DSVFormat; CD3DX12_PIPELINE_STATE_STREAM_RASTERIZER RasterizerState; CD3DX12_PIPELINE_STATE_STREAM_RENDER_TARGET_FORMATS RTVFormats; CD3DX12_PIPELINE_STATE_STREAM_SAMPLE_DESC SampleDesc; CD3DX12_PIPELINE_STATE_STREAM_SAMPLE_MASK SampleMask; CD3DX12_PIPELINE_STATE_STREAM_CACHED_PSO CachedPSO; D3D12_GRAPHICS_PIPELINE_STATE_DESC GraphicsDescV0() const noexcept { D3D12_GRAPHICS_PIPELINE_STATE_DESC D; D.Flags = this->Flags; D.NodeMask = this->NodeMask; D.pRootSignature = this->pRootSignature; D.InputLayout = this->InputLayout; D.IBStripCutValue = this->IBStripCutValue; D.PrimitiveTopologyType = this->PrimitiveTopologyType; D.VS = this->VS; D.GS = this->GS; D.StreamOutput = this->StreamOutput; D.HS = this->HS; D.DS = this->DS; D.PS = this->PS; D.BlendState = this->BlendState; D.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC1(D3D12_DEPTH_STENCIL_DESC1(this->DepthStencilState)); D.DSVFormat = this->DSVFormat; D.RasterizerState = this->RasterizerState; D.NumRenderTargets = D3D12_RT_FORMAT_ARRAY(this->RTVFormats).NumRenderTargets; memcpy(D.RTVFormats, D3D12_RT_FORMAT_ARRAY(this->RTVFormats).RTFormats, sizeof(D.RTVFormats)); D.SampleDesc = this->SampleDesc; D.SampleMask = this->SampleMask; D.CachedPSO = this->CachedPSO; return D; } D3D12_COMPUTE_PIPELINE_STATE_DESC ComputeDescV0() const noexcept { D3D12_COMPUTE_PIPELINE_STATE_DESC D; D.Flags = this->Flags; D.NodeMask = this->NodeMask; D.pRootSignature = this->pRootSignature; D.CS = this->CS; D.CachedPSO = this->CachedPSO; return D; } }; #if defined(NTDDI_WIN10_VB) && (NTDDI_VERSION >= NTDDI_WIN10_VB) struct CD3DX12_PIPELINE_STATE_STREAM2_PARSE_HELPER : public ID3DX12PipelineParserCallbacks { CD3DX12_PIPELINE_STATE_STREAM2 PipelineStream; CD3DX12_PIPELINE_STATE_STREAM2_PARSE_HELPER() noexcept : SeenDSS(false) { // Adjust defaults to account for absent members. PipelineStream.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; // Depth disabled if no DSV format specified. static_cast(PipelineStream.DepthStencilState).DepthEnable = false; } // ID3DX12PipelineParserCallbacks void FlagsCb(D3D12_PIPELINE_STATE_FLAGS Flags) override {PipelineStream.Flags = Flags;} void NodeMaskCb(UINT NodeMask) override {PipelineStream.NodeMask = NodeMask;} void RootSignatureCb(ID3D12RootSignature* pRootSignature) override {PipelineStream.pRootSignature = pRootSignature;} void InputLayoutCb(const D3D12_INPUT_LAYOUT_DESC& InputLayout) override {PipelineStream.InputLayout = InputLayout;} void IBStripCutValueCb(D3D12_INDEX_BUFFER_STRIP_CUT_VALUE IBStripCutValue) override {PipelineStream.IBStripCutValue = IBStripCutValue;} void PrimitiveTopologyTypeCb(D3D12_PRIMITIVE_TOPOLOGY_TYPE PrimitiveTopologyType) override {PipelineStream.PrimitiveTopologyType = PrimitiveTopologyType;} void VSCb(const D3D12_SHADER_BYTECODE& VS) override {PipelineStream.VS = VS;} void GSCb(const D3D12_SHADER_BYTECODE& GS) override {PipelineStream.GS = GS;} void StreamOutputCb(const D3D12_STREAM_OUTPUT_DESC& StreamOutput) override {PipelineStream.StreamOutput = StreamOutput;} void HSCb(const D3D12_SHADER_BYTECODE& HS) override {PipelineStream.HS = HS;} void DSCb(const D3D12_SHADER_BYTECODE& DS) override {PipelineStream.DS = DS;} void PSCb(const D3D12_SHADER_BYTECODE& PS) override {PipelineStream.PS = PS;} void CSCb(const D3D12_SHADER_BYTECODE& CS) override {PipelineStream.CS = CS;} void ASCb(const D3D12_SHADER_BYTECODE& AS) override {PipelineStream.AS = AS;} void MSCb(const D3D12_SHADER_BYTECODE& MS) override {PipelineStream.MS = MS;} void BlendStateCb(const D3D12_BLEND_DESC& BlendState) override {PipelineStream.BlendState = CD3DX12_BLEND_DESC(BlendState);} void DepthStencilStateCb(const D3D12_DEPTH_STENCIL_DESC& DepthStencilState) override { PipelineStream.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC1(DepthStencilState); SeenDSS = true; } void DepthStencilState1Cb(const D3D12_DEPTH_STENCIL_DESC1& DepthStencilState) override { PipelineStream.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC1(DepthStencilState); SeenDSS = true; } void DSVFormatCb(DXGI_FORMAT DSVFormat) override { PipelineStream.DSVFormat = DSVFormat; if (!SeenDSS && DSVFormat != DXGI_FORMAT_UNKNOWN) { // Re-enable depth for the default state. static_cast(PipelineStream.DepthStencilState).DepthEnable = true; } } void RasterizerStateCb(const D3D12_RASTERIZER_DESC& RasterizerState) override {PipelineStream.RasterizerState = CD3DX12_RASTERIZER_DESC(RasterizerState);} void RTVFormatsCb(const D3D12_RT_FORMAT_ARRAY& RTVFormats) override {PipelineStream.RTVFormats = RTVFormats;} void SampleDescCb(const DXGI_SAMPLE_DESC& SampleDesc) override {PipelineStream.SampleDesc = SampleDesc;} void SampleMaskCb(UINT SampleMask) override {PipelineStream.SampleMask = SampleMask;} void ViewInstancingCb(const D3D12_VIEW_INSTANCING_DESC& ViewInstancingDesc) override {PipelineStream.ViewInstancingDesc = CD3DX12_VIEW_INSTANCING_DESC(ViewInstancingDesc);} void CachedPSOCb(const D3D12_CACHED_PIPELINE_STATE& CachedPSO) override {PipelineStream.CachedPSO = CachedPSO;} private: bool SeenDSS; }; #endif // NTDDI_WIN10_VB struct CD3DX12_PIPELINE_STATE_STREAM_PARSE_HELPER : public ID3DX12PipelineParserCallbacks { #if defined(NTDDI_WIN10_RS3) && (NTDDI_VERSION >= NTDDI_WIN10_RS3) CD3DX12_PIPELINE_STATE_STREAM1 PipelineStream; #else CD3DX12_PIPELINE_STATE_STREAM PipelineStream; #endif CD3DX12_PIPELINE_STATE_STREAM_PARSE_HELPER() noexcept : SeenDSS(false) { // Adjust defaults to account for absent members. PipelineStream.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; // Depth disabled if no DSV format specified. static_cast(PipelineStream.DepthStencilState).DepthEnable = false; } // ID3DX12PipelineParserCallbacks void FlagsCb(D3D12_PIPELINE_STATE_FLAGS Flags) override {PipelineStream.Flags = Flags;} void NodeMaskCb(UINT NodeMask) override {PipelineStream.NodeMask = NodeMask;} void RootSignatureCb(ID3D12RootSignature* pRootSignature) override {PipelineStream.pRootSignature = pRootSignature;} void InputLayoutCb(const D3D12_INPUT_LAYOUT_DESC& InputLayout) override {PipelineStream.InputLayout = InputLayout;} void IBStripCutValueCb(D3D12_INDEX_BUFFER_STRIP_CUT_VALUE IBStripCutValue) override {PipelineStream.IBStripCutValue = IBStripCutValue;} void PrimitiveTopologyTypeCb(D3D12_PRIMITIVE_TOPOLOGY_TYPE PrimitiveTopologyType) override {PipelineStream.PrimitiveTopologyType = PrimitiveTopologyType;} void VSCb(const D3D12_SHADER_BYTECODE& VS) override {PipelineStream.VS = VS;} void GSCb(const D3D12_SHADER_BYTECODE& GS) override {PipelineStream.GS = GS;} void StreamOutputCb(const D3D12_STREAM_OUTPUT_DESC& StreamOutput) override {PipelineStream.StreamOutput = StreamOutput;} void HSCb(const D3D12_SHADER_BYTECODE& HS) override {PipelineStream.HS = HS;} void DSCb(const D3D12_SHADER_BYTECODE& DS) override {PipelineStream.DS = DS;} void PSCb(const D3D12_SHADER_BYTECODE& PS) override {PipelineStream.PS = PS;} void CSCb(const D3D12_SHADER_BYTECODE& CS) override {PipelineStream.CS = CS;} void BlendStateCb(const D3D12_BLEND_DESC& BlendState) override {PipelineStream.BlendState = CD3DX12_BLEND_DESC(BlendState);} void DepthStencilStateCb(const D3D12_DEPTH_STENCIL_DESC& DepthStencilState) override { PipelineStream.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC1(DepthStencilState); SeenDSS = true; } void DepthStencilState1Cb(const D3D12_DEPTH_STENCIL_DESC1& DepthStencilState) override { PipelineStream.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC1(DepthStencilState); SeenDSS = true; } void DSVFormatCb(DXGI_FORMAT DSVFormat) override { PipelineStream.DSVFormat = DSVFormat; if (!SeenDSS && DSVFormat != DXGI_FORMAT_UNKNOWN) { // Re-enable depth for the default state. static_cast(PipelineStream.DepthStencilState).DepthEnable = true; } } void RasterizerStateCb(const D3D12_RASTERIZER_DESC& RasterizerState) override {PipelineStream.RasterizerState = CD3DX12_RASTERIZER_DESC(RasterizerState);} void RTVFormatsCb(const D3D12_RT_FORMAT_ARRAY& RTVFormats) override {PipelineStream.RTVFormats = RTVFormats;} void SampleDescCb(const DXGI_SAMPLE_DESC& SampleDesc) override {PipelineStream.SampleDesc = SampleDesc;} void SampleMaskCb(UINT SampleMask) override {PipelineStream.SampleMask = SampleMask;} #if defined(NTDDI_WIN10_RS3) && (NTDDI_VERSION >= NTDDI_WIN10_RS3) void ViewInstancingCb(const D3D12_VIEW_INSTANCING_DESC& ViewInstancingDesc) override {PipelineStream.ViewInstancingDesc = CD3DX12_VIEW_INSTANCING_DESC(ViewInstancingDesc);} #endif void CachedPSOCb(const D3D12_CACHED_PIPELINE_STATE& CachedPSO) override {PipelineStream.CachedPSO = CachedPSO;} private: bool SeenDSS; }; inline D3D12_PIPELINE_STATE_SUBOBJECT_TYPE D3DX12GetBaseSubobjectType(D3D12_PIPELINE_STATE_SUBOBJECT_TYPE SubobjectType) noexcept { switch (SubobjectType) { case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DEPTH_STENCIL1: return D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DEPTH_STENCIL; default: return SubobjectType; } } inline HRESULT D3DX12ParsePipelineStream(const D3D12_PIPELINE_STATE_STREAM_DESC& Desc, ID3DX12PipelineParserCallbacks* pCallbacks) { if (pCallbacks == nullptr) { return E_INVALIDARG; } if (Desc.SizeInBytes == 0 || Desc.pPipelineStateSubobjectStream == nullptr) { pCallbacks->ErrorBadInputParameter(1); // first parameter issue return E_INVALIDARG; } bool SubobjectSeen[D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_MAX_VALID] = {}; for (SIZE_T CurOffset = 0, SizeOfSubobject = 0; CurOffset < Desc.SizeInBytes; CurOffset += SizeOfSubobject) { BYTE* pStream = static_cast(Desc.pPipelineStateSubobjectStream)+CurOffset; auto SubobjectType = *reinterpret_cast(pStream); if (SubobjectType < 0 || SubobjectType >= D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_MAX_VALID) { pCallbacks->ErrorUnknownSubobject(SubobjectType); return E_INVALIDARG; } if (SubobjectSeen[D3DX12GetBaseSubobjectType(SubobjectType)]) { pCallbacks->ErrorDuplicateSubobject(SubobjectType); return E_INVALIDARG; // disallow subobject duplicates in a stream } SubobjectSeen[SubobjectType] = true; switch (SubobjectType) { case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_ROOT_SIGNATURE: pCallbacks->RootSignatureCb(*reinterpret_cast(pStream)); SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::pRootSignature); break; case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_VS: pCallbacks->VSCb(*reinterpret_cast(pStream)); SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::VS); break; case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_PS: pCallbacks->PSCb(*reinterpret_cast(pStream)); SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::PS); break; case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DS: pCallbacks->DSCb(*reinterpret_cast(pStream)); SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::DS); break; case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_HS: pCallbacks->HSCb(*reinterpret_cast(pStream)); SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::HS); break; case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_GS: pCallbacks->GSCb(*reinterpret_cast(pStream)); SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::GS); break; case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_CS: pCallbacks->CSCb(*reinterpret_cast(pStream)); SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::CS); break; #if defined(NTDDI_WIN10_VB) && (NTDDI_VERSION >= NTDDI_WIN10_VB) case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_AS: pCallbacks->ASCb(*reinterpret_cast(pStream)); SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM2::AS); break; case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_MS: pCallbacks->MSCb(*reinterpret_cast(pStream)); SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM2::MS); break; #endif case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_STREAM_OUTPUT: pCallbacks->StreamOutputCb(*reinterpret_cast(pStream)); SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::StreamOutput); break; case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_BLEND: pCallbacks->BlendStateCb(*reinterpret_cast(pStream)); SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::BlendState); break; case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_SAMPLE_MASK: pCallbacks->SampleMaskCb(*reinterpret_cast(pStream)); SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::SampleMask); break; case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_RASTERIZER: pCallbacks->RasterizerStateCb(*reinterpret_cast(pStream)); SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::RasterizerState); break; case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DEPTH_STENCIL: pCallbacks->DepthStencilStateCb(*reinterpret_cast(pStream)); SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM_DEPTH_STENCIL); break; case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DEPTH_STENCIL1: pCallbacks->DepthStencilState1Cb(*reinterpret_cast(pStream)); SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::DepthStencilState); break; case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_INPUT_LAYOUT: pCallbacks->InputLayoutCb(*reinterpret_cast(pStream)); SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::InputLayout); break; case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_IB_STRIP_CUT_VALUE: pCallbacks->IBStripCutValueCb(*reinterpret_cast(pStream)); SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::IBStripCutValue); break; case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_PRIMITIVE_TOPOLOGY: pCallbacks->PrimitiveTopologyTypeCb(*reinterpret_cast(pStream)); SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::PrimitiveTopologyType); break; case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_RENDER_TARGET_FORMATS: pCallbacks->RTVFormatsCb(*reinterpret_cast(pStream)); SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::RTVFormats); break; case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DEPTH_STENCIL_FORMAT: pCallbacks->DSVFormatCb(*reinterpret_cast(pStream)); SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::DSVFormat); break; case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_SAMPLE_DESC: pCallbacks->SampleDescCb(*reinterpret_cast(pStream)); SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::SampleDesc); break; case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_NODE_MASK: pCallbacks->NodeMaskCb(*reinterpret_cast(pStream)); SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::NodeMask); break; case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_CACHED_PSO: pCallbacks->CachedPSOCb(*reinterpret_cast(pStream)); SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::CachedPSO); break; case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_FLAGS: pCallbacks->FlagsCb(*reinterpret_cast(pStream)); SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::Flags); break; #if defined(NTDDI_WIN10_RS3) && (NTDDI_VERSION >= NTDDI_WIN10_RS3) case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_VIEW_INSTANCING: pCallbacks->ViewInstancingCb(*reinterpret_cast(pStream)); SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM1::ViewInstancingDesc); break; #endif default: pCallbacks->ErrorUnknownSubobject(SubobjectType); return E_INVALIDARG; } } return S_OK; } #endif // NTDDI_WIN10_RS2 // Requires the Windows 10 October 2018 Update SDK (17763) #if defined(NTDDI_WIN10_RS5) && (NTDDI_VERSION >= NTDDI_WIN10_RS5) //------------------------------------------------------------------------------------------------ inline bool operator==( const D3D12_CLEAR_VALUE &a, const D3D12_CLEAR_VALUE &b) noexcept { if (a.Format != b.Format) return false; if (a.Format == DXGI_FORMAT_D24_UNORM_S8_UINT || a.Format == DXGI_FORMAT_D16_UNORM || a.Format == DXGI_FORMAT_D32_FLOAT || a.Format == DXGI_FORMAT_D32_FLOAT_S8X24_UINT) { return (a.DepthStencil.Depth == b.DepthStencil.Depth) && (a.DepthStencil.Stencil == b.DepthStencil.Stencil); } else { return (a.Color[0] == b.Color[0]) && (a.Color[1] == b.Color[1]) && (a.Color[2] == b.Color[2]) && (a.Color[3] == b.Color[3]); } } inline bool operator==( const D3D12_RENDER_PASS_BEGINNING_ACCESS_CLEAR_PARAMETERS &a, const D3D12_RENDER_PASS_BEGINNING_ACCESS_CLEAR_PARAMETERS &b) noexcept { return a.ClearValue == b.ClearValue; } inline bool operator==( const D3D12_RENDER_PASS_ENDING_ACCESS_RESOLVE_PARAMETERS &a, const D3D12_RENDER_PASS_ENDING_ACCESS_RESOLVE_PARAMETERS &b) noexcept { if (a.pSrcResource != b.pSrcResource) return false; if (a.pDstResource != b.pDstResource) return false; if (a.SubresourceCount != b.SubresourceCount) return false; if (a.Format != b.Format) return false; if (a.ResolveMode != b.ResolveMode) return false; if (a.PreserveResolveSource != b.PreserveResolveSource) return false; return true; } inline bool operator==( const D3D12_RENDER_PASS_BEGINNING_ACCESS &a, const D3D12_RENDER_PASS_BEGINNING_ACCESS &b) noexcept { if (a.Type != b.Type) return false; if (a.Type == D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_CLEAR && !(a.Clear == b.Clear)) return false; return true; } inline bool operator==( const D3D12_RENDER_PASS_ENDING_ACCESS &a, const D3D12_RENDER_PASS_ENDING_ACCESS &b) noexcept { if (a.Type != b.Type) return false; if (a.Type == D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_RESOLVE && !(a.Resolve == b.Resolve)) return false; return true; } inline bool operator==( const D3D12_RENDER_PASS_RENDER_TARGET_DESC &a, const D3D12_RENDER_PASS_RENDER_TARGET_DESC &b) noexcept { if (a.cpuDescriptor.ptr != b.cpuDescriptor.ptr) return false; if (!(a.BeginningAccess == b.BeginningAccess)) return false; if (!(a.EndingAccess == b.EndingAccess)) return false; return true; } inline bool operator==( const D3D12_RENDER_PASS_DEPTH_STENCIL_DESC &a, const D3D12_RENDER_PASS_DEPTH_STENCIL_DESC &b) noexcept { if (a.cpuDescriptor.ptr != b.cpuDescriptor.ptr) return false; if (!(a.DepthBeginningAccess == b.DepthBeginningAccess)) return false; if (!(a.StencilBeginningAccess == b.StencilBeginningAccess)) return false; if (!(a.DepthEndingAccess == b.DepthEndingAccess)) return false; if (!(a.StencilEndingAccess == b.StencilEndingAccess)) return false; return true; } #ifndef D3DX12_NO_STATE_OBJECT_HELPERS //================================================================================================ // D3DX12 State Object Creation Helpers // // Helper classes for creating new style state objects out of an arbitrary set of subobjects. // Uses STL // // Start by instantiating CD3DX12_STATE_OBJECT_DESC (see it's public methods). // One of its methods is CreateSubobject(), which has a comment showing a couple of options for // defining subobjects using the helper classes for each subobject (CD3DX12_DXIL_LIBRARY_SUBOBJECT // etc.). The subobject helpers each have methods specific to the subobject for configuring it's // contents. // //================================================================================================ #include #include #include #include #include //------------------------------------------------------------------------------------------------ class CD3DX12_STATE_OBJECT_DESC { public: CD3DX12_STATE_OBJECT_DESC() noexcept { Init(D3D12_STATE_OBJECT_TYPE_COLLECTION); } CD3DX12_STATE_OBJECT_DESC(D3D12_STATE_OBJECT_TYPE Type) noexcept { Init(Type); } void SetStateObjectType(D3D12_STATE_OBJECT_TYPE Type) noexcept { m_Desc.Type = Type; } operator const D3D12_STATE_OBJECT_DESC&() { // Do final preparation work m_RepointedAssociations.clear(); m_SubobjectArray.clear(); m_SubobjectArray.reserve(m_Desc.NumSubobjects); // Flatten subobjects into an array (each flattened subobject still has a // member that's a pointer to it's desc that's not flattened) for (auto Iter = m_SubobjectList.begin(); Iter != m_SubobjectList.end(); Iter++) { m_SubobjectArray.push_back(*Iter); // Store new location in array so we can redirect pointers contained in subobjects Iter->pSubobjectArrayLocation = &m_SubobjectArray.back(); } // For subobjects with pointer fields, create a new copy of those subobject definitions // with fixed pointers for (UINT i = 0; i < m_Desc.NumSubobjects; i++) { if (m_SubobjectArray[i].Type == D3D12_STATE_SUBOBJECT_TYPE_SUBOBJECT_TO_EXPORTS_ASSOCIATION) { auto pOriginalSubobjectAssociation = static_cast(m_SubobjectArray[i].pDesc); D3D12_SUBOBJECT_TO_EXPORTS_ASSOCIATION Repointed = *pOriginalSubobjectAssociation; auto pWrapper = static_cast(pOriginalSubobjectAssociation->pSubobjectToAssociate); Repointed.pSubobjectToAssociate = pWrapper->pSubobjectArrayLocation; m_RepointedAssociations.push_back(Repointed); m_SubobjectArray[i].pDesc = &m_RepointedAssociations.back(); } } // Below: using ugly way to get pointer in case .data() is not defined m_Desc.pSubobjects = m_Desc.NumSubobjects ? &m_SubobjectArray[0] : nullptr; return m_Desc; } operator const D3D12_STATE_OBJECT_DESC*() { // Cast calls the above final preparation work return &static_cast(*this); } // CreateSubobject creates a sububject helper (e.g. CD3DX12_HIT_GROUP_SUBOBJECT) // whose lifetime is owned by this class. // e.g. // // CD3DX12_STATE_OBJECT_DESC Collection1(D3D12_STATE_OBJECT_TYPE_COLLECTION); // auto Lib0 = Collection1.CreateSubobject(); // Lib0->SetDXILLibrary(&pMyAppDxilLibs[0]); // Lib0->DefineExport(L"rayGenShader0"); // in practice these export listings might be // // data/engine driven // etc. // // Alternatively, users can instantiate sububject helpers explicitly, such as via local // variables instead, passing the state object desc that should point to it into the helper // constructor (or call mySubobjectHelper.AddToStateObject(Collection1)). // In this alternative scenario, the user must keep the subobject alive as long as the state // object it is associated with is alive, else it's pointer references will be stale. // e.g. // // CD3DX12_STATE_OBJECT_DESC RaytracingState2(D3D12_STATE_OBJECT_TYPE_RAYTRACING_PIPELINE); // CD3DX12_DXIL_LIBRARY_SUBOBJECT LibA(RaytracingState2); // LibA.SetDXILLibrary(&pMyAppDxilLibs[4]); // not manually specifying exports // // - meaning all exports in the libraries // // are exported // etc. template T* CreateSubobject() { T* pSubobject = new T(*this); m_OwnedSubobjectHelpers.emplace_back(pSubobject); return pSubobject; } private: D3D12_STATE_SUBOBJECT* TrackSubobject(D3D12_STATE_SUBOBJECT_TYPE Type, void* pDesc) { SUBOBJECT_WRAPPER Subobject; Subobject.pSubobjectArrayLocation = nullptr; Subobject.Type = Type; Subobject.pDesc = pDesc; m_SubobjectList.push_back(Subobject); m_Desc.NumSubobjects++; return &m_SubobjectList.back(); } void Init(D3D12_STATE_OBJECT_TYPE Type) noexcept { SetStateObjectType(Type); m_Desc.pSubobjects = nullptr; m_Desc.NumSubobjects = 0; m_SubobjectList.clear(); m_SubobjectArray.clear(); m_RepointedAssociations.clear(); } typedef struct SUBOBJECT_WRAPPER : public D3D12_STATE_SUBOBJECT { D3D12_STATE_SUBOBJECT* pSubobjectArrayLocation; // new location when flattened into array // for repointing pointers in subobjects } SUBOBJECT_WRAPPER; D3D12_STATE_OBJECT_DESC m_Desc; std::list m_SubobjectList; // Pointers to list nodes handed out so // these can be edited live std::vector m_SubobjectArray; // Built at the end, copying list contents std::list m_RepointedAssociations; // subobject type that contains pointers to other subobjects, // repointed to flattened array class StringContainer { public: LPCWSTR LocalCopy(LPCWSTR string, bool bSingleString = false) { if (string) { if (bSingleString) { m_Strings.clear(); m_Strings.push_back(string); } else { m_Strings.push_back(string); } return m_Strings.back().c_str(); } else { return nullptr; } } void clear() noexcept { m_Strings.clear(); } private: std::list m_Strings; }; class SUBOBJECT_HELPER_BASE { public: SUBOBJECT_HELPER_BASE() noexcept { Init(); } virtual ~SUBOBJECT_HELPER_BASE() = default; virtual D3D12_STATE_SUBOBJECT_TYPE Type() const noexcept = 0; void AddToStateObject(CD3DX12_STATE_OBJECT_DESC& ContainingStateObject) { m_pSubobject = ContainingStateObject.TrackSubobject(Type(), Data()); } protected: virtual void* Data() noexcept = 0; void Init() noexcept { m_pSubobject = nullptr; } D3D12_STATE_SUBOBJECT* m_pSubobject; }; #if(__cplusplus >= 201103L) std::list> m_OwnedSubobjectHelpers; #else class OWNED_HELPER { public: OWNED_HELPER(const SUBOBJECT_HELPER_BASE* pHelper) noexcept { m_pHelper = pHelper; } ~OWNED_HELPER() { delete m_pHelper; } const SUBOBJECT_HELPER_BASE* m_pHelper; }; std::list m_OwnedSubobjectHelpers; #endif friend class CD3DX12_DXIL_LIBRARY_SUBOBJECT; friend class CD3DX12_EXISTING_COLLECTION_SUBOBJECT; friend class CD3DX12_SUBOBJECT_TO_EXPORTS_ASSOCIATION_SUBOBJECT; friend class CD3DX12_DXIL_SUBOBJECT_TO_EXPORTS_ASSOCIATION; friend class CD3DX12_HIT_GROUP_SUBOBJECT; friend class CD3DX12_RAYTRACING_SHADER_CONFIG_SUBOBJECT; friend class CD3DX12_RAYTRACING_PIPELINE_CONFIG_SUBOBJECT; #if defined(NTDDI_WIN10_VB) && (NTDDI_VERSION >= NTDDI_WIN10_VB) friend class CD3DX12_RAYTRACING_PIPELINE_CONFIG1_SUBOBJECT; #endif friend class CD3DX12_GLOBAL_ROOT_SIGNATURE_SUBOBJECT; friend class CD3DX12_LOCAL_ROOT_SIGNATURE_SUBOBJECT; friend class CD3DX12_STATE_OBJECT_CONFIG_SUBOBJECT; friend class CD3DX12_NODE_MASK_SUBOBJECT; }; //------------------------------------------------------------------------------------------------ class CD3DX12_DXIL_LIBRARY_SUBOBJECT : public CD3DX12_STATE_OBJECT_DESC::SUBOBJECT_HELPER_BASE { public: CD3DX12_DXIL_LIBRARY_SUBOBJECT() noexcept { Init(); } CD3DX12_DXIL_LIBRARY_SUBOBJECT(CD3DX12_STATE_OBJECT_DESC& ContainingStateObject) { Init(); AddToStateObject(ContainingStateObject); } void SetDXILLibrary(const D3D12_SHADER_BYTECODE* pCode) noexcept { static const D3D12_SHADER_BYTECODE Default = {}; m_Desc.DXILLibrary = pCode ? *pCode : Default; } void DefineExport( LPCWSTR Name, LPCWSTR ExportToRename = nullptr, D3D12_EXPORT_FLAGS Flags = D3D12_EXPORT_FLAG_NONE) { D3D12_EXPORT_DESC Export; Export.Name = m_Strings.LocalCopy(Name); Export.ExportToRename = m_Strings.LocalCopy(ExportToRename); Export.Flags = Flags; m_Exports.push_back(Export); m_Desc.pExports = &m_Exports[0]; // using ugly way to get pointer in case .data() is not defined m_Desc.NumExports = static_cast(m_Exports.size()); } template void DefineExports(LPCWSTR(&Exports)[N]) { for (UINT i = 0; i < N; i++) { DefineExport(Exports[i]); } } void DefineExports(const LPCWSTR* Exports, UINT N) { for (UINT i = 0; i < N; i++) { DefineExport(Exports[i]); } } D3D12_STATE_SUBOBJECT_TYPE Type() const noexcept override { return D3D12_STATE_SUBOBJECT_TYPE_DXIL_LIBRARY; } operator const D3D12_STATE_SUBOBJECT&() const noexcept { return *m_pSubobject; } operator const D3D12_DXIL_LIBRARY_DESC&() const noexcept { return m_Desc; } private: void Init() noexcept { SUBOBJECT_HELPER_BASE::Init(); m_Desc = {}; m_Strings.clear(); m_Exports.clear(); } void* Data() noexcept override { return &m_Desc; } D3D12_DXIL_LIBRARY_DESC m_Desc; CD3DX12_STATE_OBJECT_DESC::StringContainer m_Strings; std::vector m_Exports; }; //------------------------------------------------------------------------------------------------ class CD3DX12_EXISTING_COLLECTION_SUBOBJECT : public CD3DX12_STATE_OBJECT_DESC::SUBOBJECT_HELPER_BASE { public: CD3DX12_EXISTING_COLLECTION_SUBOBJECT() noexcept { Init(); } CD3DX12_EXISTING_COLLECTION_SUBOBJECT(CD3DX12_STATE_OBJECT_DESC& ContainingStateObject) { Init(); AddToStateObject(ContainingStateObject); } void SetExistingCollection(ID3D12StateObject*pExistingCollection) noexcept { m_Desc.pExistingCollection = pExistingCollection; m_CollectionRef = pExistingCollection; } void DefineExport( LPCWSTR Name, LPCWSTR ExportToRename = nullptr, D3D12_EXPORT_FLAGS Flags = D3D12_EXPORT_FLAG_NONE) { D3D12_EXPORT_DESC Export; Export.Name = m_Strings.LocalCopy(Name); Export.ExportToRename = m_Strings.LocalCopy(ExportToRename); Export.Flags = Flags; m_Exports.push_back(Export); m_Desc.pExports = &m_Exports[0]; // using ugly way to get pointer in case .data() is not defined m_Desc.NumExports = static_cast(m_Exports.size()); } template void DefineExports(LPCWSTR(&Exports)[N]) { for (UINT i = 0; i < N; i++) { DefineExport(Exports[i]); } } void DefineExports(const LPCWSTR* Exports, UINT N) { for (UINT i = 0; i < N; i++) { DefineExport(Exports[i]); } } D3D12_STATE_SUBOBJECT_TYPE Type() const noexcept override { return D3D12_STATE_SUBOBJECT_TYPE_EXISTING_COLLECTION; } operator const D3D12_STATE_SUBOBJECT&() const noexcept { return *m_pSubobject; } operator const D3D12_EXISTING_COLLECTION_DESC&() const noexcept { return m_Desc; } private: void Init() noexcept { SUBOBJECT_HELPER_BASE::Init(); m_Desc = {}; m_CollectionRef = nullptr; m_Strings.clear(); m_Exports.clear(); } void* Data() noexcept override { return &m_Desc; } D3D12_EXISTING_COLLECTION_DESC m_Desc; Microsoft::WRL::ComPtr m_CollectionRef; CD3DX12_STATE_OBJECT_DESC::StringContainer m_Strings; std::vector m_Exports; }; //------------------------------------------------------------------------------------------------ class CD3DX12_SUBOBJECT_TO_EXPORTS_ASSOCIATION_SUBOBJECT : public CD3DX12_STATE_OBJECT_DESC::SUBOBJECT_HELPER_BASE { public: CD3DX12_SUBOBJECT_TO_EXPORTS_ASSOCIATION_SUBOBJECT() noexcept { Init(); } CD3DX12_SUBOBJECT_TO_EXPORTS_ASSOCIATION_SUBOBJECT(CD3DX12_STATE_OBJECT_DESC& ContainingStateObject) { Init(); AddToStateObject(ContainingStateObject); } void SetSubobjectToAssociate(const D3D12_STATE_SUBOBJECT& SubobjectToAssociate) noexcept { m_Desc.pSubobjectToAssociate = &SubobjectToAssociate; } void AddExport(LPCWSTR Export) { m_Desc.NumExports++; m_Exports.push_back(m_Strings.LocalCopy(Export)); m_Desc.pExports = &m_Exports[0]; // using ugly way to get pointer in case .data() is not defined } template void AddExports(LPCWSTR (&Exports)[N]) { for (UINT i = 0; i < N; i++) { AddExport(Exports[i]); } } void AddExports(const LPCWSTR* Exports, UINT N) { for (UINT i = 0; i < N; i++) { AddExport(Exports[i]); } } D3D12_STATE_SUBOBJECT_TYPE Type() const noexcept override { return D3D12_STATE_SUBOBJECT_TYPE_SUBOBJECT_TO_EXPORTS_ASSOCIATION; } operator const D3D12_STATE_SUBOBJECT&() const noexcept { return *m_pSubobject; } operator const D3D12_SUBOBJECT_TO_EXPORTS_ASSOCIATION&() const noexcept { return m_Desc; } private: void Init() noexcept { SUBOBJECT_HELPER_BASE::Init(); m_Desc = {}; m_Strings.clear(); m_Exports.clear(); } void* Data() noexcept override { return &m_Desc; } D3D12_SUBOBJECT_TO_EXPORTS_ASSOCIATION m_Desc; CD3DX12_STATE_OBJECT_DESC::StringContainer m_Strings; std::vector m_Exports; }; //------------------------------------------------------------------------------------------------ class CD3DX12_DXIL_SUBOBJECT_TO_EXPORTS_ASSOCIATION : public CD3DX12_STATE_OBJECT_DESC::SUBOBJECT_HELPER_BASE { public: CD3DX12_DXIL_SUBOBJECT_TO_EXPORTS_ASSOCIATION() noexcept { Init(); } CD3DX12_DXIL_SUBOBJECT_TO_EXPORTS_ASSOCIATION(CD3DX12_STATE_OBJECT_DESC& ContainingStateObject) { Init(); AddToStateObject(ContainingStateObject); } void SetSubobjectNameToAssociate(LPCWSTR SubobjectToAssociate) { m_Desc.SubobjectToAssociate = m_SubobjectName.LocalCopy(SubobjectToAssociate, true); } void AddExport(LPCWSTR Export) { m_Desc.NumExports++; m_Exports.push_back(m_Strings.LocalCopy(Export)); m_Desc.pExports = &m_Exports[0]; // using ugly way to get pointer in case .data() is not defined } template void AddExports(LPCWSTR (&Exports)[N]) { for (UINT i = 0; i < N; i++) { AddExport(Exports[i]); } } void AddExports(const LPCWSTR* Exports, UINT N) { for (UINT i = 0; i < N; i++) { AddExport(Exports[i]); } } D3D12_STATE_SUBOBJECT_TYPE Type() const noexcept override { return D3D12_STATE_SUBOBJECT_TYPE_DXIL_SUBOBJECT_TO_EXPORTS_ASSOCIATION; } operator const D3D12_STATE_SUBOBJECT&() const noexcept { return *m_pSubobject; } operator const D3D12_DXIL_SUBOBJECT_TO_EXPORTS_ASSOCIATION&() const noexcept { return m_Desc; } private: void Init() noexcept { SUBOBJECT_HELPER_BASE::Init(); m_Desc = {}; m_Strings.clear(); m_SubobjectName.clear(); m_Exports.clear(); } void* Data() noexcept override { return &m_Desc; } D3D12_DXIL_SUBOBJECT_TO_EXPORTS_ASSOCIATION m_Desc; CD3DX12_STATE_OBJECT_DESC::StringContainer m_Strings; CD3DX12_STATE_OBJECT_DESC::StringContainer m_SubobjectName; std::vector m_Exports; }; //------------------------------------------------------------------------------------------------ class CD3DX12_HIT_GROUP_SUBOBJECT : public CD3DX12_STATE_OBJECT_DESC::SUBOBJECT_HELPER_BASE { public: CD3DX12_HIT_GROUP_SUBOBJECT() noexcept { Init(); } CD3DX12_HIT_GROUP_SUBOBJECT(CD3DX12_STATE_OBJECT_DESC& ContainingStateObject) { Init(); AddToStateObject(ContainingStateObject); } void SetHitGroupExport(LPCWSTR exportName) { m_Desc.HitGroupExport = m_Strings[0].LocalCopy(exportName, true); } void SetHitGroupType(D3D12_HIT_GROUP_TYPE Type) noexcept { m_Desc.Type = Type; } void SetAnyHitShaderImport(LPCWSTR importName) { m_Desc.AnyHitShaderImport = m_Strings[1].LocalCopy(importName, true); } void SetClosestHitShaderImport(LPCWSTR importName) { m_Desc.ClosestHitShaderImport = m_Strings[2].LocalCopy(importName, true); } void SetIntersectionShaderImport(LPCWSTR importName) { m_Desc.IntersectionShaderImport = m_Strings[3].LocalCopy(importName, true); } D3D12_STATE_SUBOBJECT_TYPE Type() const noexcept override { return D3D12_STATE_SUBOBJECT_TYPE_HIT_GROUP; } operator const D3D12_STATE_SUBOBJECT&() const noexcept { return *m_pSubobject; } operator const D3D12_HIT_GROUP_DESC&() const noexcept { return m_Desc; } private: void Init() noexcept { SUBOBJECT_HELPER_BASE::Init(); m_Desc = {}; for (UINT i = 0; i < m_NumStrings; i++) { m_Strings[i].clear(); } } void* Data() noexcept override { return &m_Desc; } D3D12_HIT_GROUP_DESC m_Desc; static const UINT m_NumStrings = 4; CD3DX12_STATE_OBJECT_DESC::StringContainer m_Strings[m_NumStrings]; // one string for every entrypoint name }; //------------------------------------------------------------------------------------------------ class CD3DX12_RAYTRACING_SHADER_CONFIG_SUBOBJECT : public CD3DX12_STATE_OBJECT_DESC::SUBOBJECT_HELPER_BASE { public: CD3DX12_RAYTRACING_SHADER_CONFIG_SUBOBJECT() noexcept { Init(); } CD3DX12_RAYTRACING_SHADER_CONFIG_SUBOBJECT(CD3DX12_STATE_OBJECT_DESC& ContainingStateObject) { Init(); AddToStateObject(ContainingStateObject); } void Config(UINT MaxPayloadSizeInBytes, UINT MaxAttributeSizeInBytes) noexcept { m_Desc.MaxPayloadSizeInBytes = MaxPayloadSizeInBytes; m_Desc.MaxAttributeSizeInBytes = MaxAttributeSizeInBytes; } D3D12_STATE_SUBOBJECT_TYPE Type() const noexcept override { return D3D12_STATE_SUBOBJECT_TYPE_RAYTRACING_SHADER_CONFIG; } operator const D3D12_STATE_SUBOBJECT&() const noexcept { return *m_pSubobject; } operator const D3D12_RAYTRACING_SHADER_CONFIG&() const noexcept { return m_Desc; } private: void Init() noexcept { SUBOBJECT_HELPER_BASE::Init(); m_Desc = {}; } void* Data() noexcept override { return &m_Desc; } D3D12_RAYTRACING_SHADER_CONFIG m_Desc; }; //------------------------------------------------------------------------------------------------ class CD3DX12_RAYTRACING_PIPELINE_CONFIG_SUBOBJECT : public CD3DX12_STATE_OBJECT_DESC::SUBOBJECT_HELPER_BASE { public: CD3DX12_RAYTRACING_PIPELINE_CONFIG_SUBOBJECT() noexcept { Init(); } CD3DX12_RAYTRACING_PIPELINE_CONFIG_SUBOBJECT(CD3DX12_STATE_OBJECT_DESC& ContainingStateObject) { Init(); AddToStateObject(ContainingStateObject); } void Config(UINT MaxTraceRecursionDepth) noexcept { m_Desc.MaxTraceRecursionDepth = MaxTraceRecursionDepth; } D3D12_STATE_SUBOBJECT_TYPE Type() const noexcept override { return D3D12_STATE_SUBOBJECT_TYPE_RAYTRACING_PIPELINE_CONFIG; } operator const D3D12_STATE_SUBOBJECT&() const noexcept { return *m_pSubobject; } operator const D3D12_RAYTRACING_PIPELINE_CONFIG&() const noexcept { return m_Desc; } private: void Init() noexcept { SUBOBJECT_HELPER_BASE::Init(); m_Desc = {}; } void* Data() noexcept override { return &m_Desc; } D3D12_RAYTRACING_PIPELINE_CONFIG m_Desc; }; //------------------------------------------------------------------------------------------------ #if defined(NTDDI_WIN10_VB) && (NTDDI_VERSION >= NTDDI_WIN10_VB) class CD3DX12_RAYTRACING_PIPELINE_CONFIG1_SUBOBJECT : public CD3DX12_STATE_OBJECT_DESC::SUBOBJECT_HELPER_BASE { public: CD3DX12_RAYTRACING_PIPELINE_CONFIG1_SUBOBJECT() noexcept { Init(); } CD3DX12_RAYTRACING_PIPELINE_CONFIG1_SUBOBJECT(CD3DX12_STATE_OBJECT_DESC& ContainingStateObject) { Init(); AddToStateObject(ContainingStateObject); } void Config(UINT MaxTraceRecursionDepth, D3D12_RAYTRACING_PIPELINE_FLAGS Flags) noexcept { m_Desc.MaxTraceRecursionDepth = MaxTraceRecursionDepth; m_Desc.Flags = Flags; } D3D12_STATE_SUBOBJECT_TYPE Type() const noexcept override { return D3D12_STATE_SUBOBJECT_TYPE_RAYTRACING_PIPELINE_CONFIG1; } operator const D3D12_STATE_SUBOBJECT&() const noexcept { return *m_pSubobject; } operator const D3D12_RAYTRACING_PIPELINE_CONFIG1&() const noexcept { return m_Desc; } private: void Init() noexcept { SUBOBJECT_HELPER_BASE::Init(); m_Desc = {}; } void* Data() noexcept override { return &m_Desc; } D3D12_RAYTRACING_PIPELINE_CONFIG1 m_Desc; }; #endif // NTDDI_WIN10_VB //------------------------------------------------------------------------------------------------ class CD3DX12_GLOBAL_ROOT_SIGNATURE_SUBOBJECT : public CD3DX12_STATE_OBJECT_DESC::SUBOBJECT_HELPER_BASE { public: CD3DX12_GLOBAL_ROOT_SIGNATURE_SUBOBJECT() noexcept { Init(); } CD3DX12_GLOBAL_ROOT_SIGNATURE_SUBOBJECT(CD3DX12_STATE_OBJECT_DESC& ContainingStateObject) { Init(); AddToStateObject(ContainingStateObject); } void SetRootSignature(ID3D12RootSignature* pRootSig) noexcept { m_pRootSig = pRootSig; } D3D12_STATE_SUBOBJECT_TYPE Type() const noexcept override { return D3D12_STATE_SUBOBJECT_TYPE_GLOBAL_ROOT_SIGNATURE; } operator const D3D12_STATE_SUBOBJECT&() const noexcept { return *m_pSubobject; } operator ID3D12RootSignature*() const noexcept { return m_pRootSig.Get(); } private: void Init() noexcept { SUBOBJECT_HELPER_BASE::Init(); m_pRootSig = nullptr; } void* Data() noexcept override { return m_pRootSig.GetAddressOf(); } Microsoft::WRL::ComPtr m_pRootSig; }; //------------------------------------------------------------------------------------------------ class CD3DX12_LOCAL_ROOT_SIGNATURE_SUBOBJECT : public CD3DX12_STATE_OBJECT_DESC::SUBOBJECT_HELPER_BASE { public: CD3DX12_LOCAL_ROOT_SIGNATURE_SUBOBJECT() noexcept { Init(); } CD3DX12_LOCAL_ROOT_SIGNATURE_SUBOBJECT(CD3DX12_STATE_OBJECT_DESC& ContainingStateObject) { Init(); AddToStateObject(ContainingStateObject); } void SetRootSignature(ID3D12RootSignature* pRootSig) noexcept { m_pRootSig = pRootSig; } D3D12_STATE_SUBOBJECT_TYPE Type() const noexcept override { return D3D12_STATE_SUBOBJECT_TYPE_LOCAL_ROOT_SIGNATURE; } operator const D3D12_STATE_SUBOBJECT&() const noexcept { return *m_pSubobject; } operator ID3D12RootSignature*() const noexcept { return m_pRootSig.Get(); } private: void Init() noexcept { SUBOBJECT_HELPER_BASE::Init(); m_pRootSig = nullptr; } void* Data() noexcept override { return m_pRootSig.GetAddressOf(); } Microsoft::WRL::ComPtr m_pRootSig; }; //------------------------------------------------------------------------------------------------ class CD3DX12_STATE_OBJECT_CONFIG_SUBOBJECT : public CD3DX12_STATE_OBJECT_DESC::SUBOBJECT_HELPER_BASE { public: CD3DX12_STATE_OBJECT_CONFIG_SUBOBJECT() noexcept { Init(); } CD3DX12_STATE_OBJECT_CONFIG_SUBOBJECT(CD3DX12_STATE_OBJECT_DESC& ContainingStateObject) { Init(); AddToStateObject(ContainingStateObject); } void SetFlags(D3D12_STATE_OBJECT_FLAGS Flags) noexcept { m_Desc.Flags = Flags; } D3D12_STATE_SUBOBJECT_TYPE Type() const noexcept override { return D3D12_STATE_SUBOBJECT_TYPE_STATE_OBJECT_CONFIG; } operator const D3D12_STATE_SUBOBJECT&() const noexcept { return *m_pSubobject; } operator const D3D12_STATE_OBJECT_CONFIG&() const noexcept { return m_Desc; } private: void Init() noexcept { SUBOBJECT_HELPER_BASE::Init(); m_Desc = {}; } void* Data() noexcept override { return &m_Desc; } D3D12_STATE_OBJECT_CONFIG m_Desc; }; //------------------------------------------------------------------------------------------------ class CD3DX12_NODE_MASK_SUBOBJECT : public CD3DX12_STATE_OBJECT_DESC::SUBOBJECT_HELPER_BASE { public: CD3DX12_NODE_MASK_SUBOBJECT() noexcept { Init(); } CD3DX12_NODE_MASK_SUBOBJECT(CD3DX12_STATE_OBJECT_DESC& ContainingStateObject) { Init(); AddToStateObject(ContainingStateObject); } void SetNodeMask(UINT NodeMask) noexcept { m_Desc.NodeMask = NodeMask; } D3D12_STATE_SUBOBJECT_TYPE Type() const noexcept override { return D3D12_STATE_SUBOBJECT_TYPE_NODE_MASK; } operator const D3D12_STATE_SUBOBJECT&() const noexcept { return *m_pSubobject; } operator const D3D12_NODE_MASK&() const noexcept { return m_Desc; } private: void Init() noexcept { SUBOBJECT_HELPER_BASE::Init(); m_Desc = {}; } void* Data() noexcept override { return &m_Desc; } D3D12_NODE_MASK m_Desc; }; #endif // #ifndef D3DX12_NO_STATE_OBJECT_HELPERS #endif // NTDDI_WIN10_RS5 #endif // defined( __cplusplus ) #endif //__D3DX12_H__ ================================================ FILE: Tests/GDK/APIRunner.GDK/Kits/ATGTK/json/LICENSE.MIT ================================================ MIT License Copyright (c) 2013-2019 Niels Lohmann Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: Tests/GDK/APIRunner.GDK/Kits/ATGTK/json/commit_id.txt ================================================ e6e6805c6c80d6ec82ba719afd435fd2280535da ================================================ FILE: Tests/GDK/APIRunner.GDK/Kits/ATGTK/json/documentation.txt ================================================ https://nlohmann.github.io/json/ ================================================ FILE: Tests/GDK/APIRunner.GDK/Kits/ATGTK/json/json.hpp ================================================ #pragma once /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ | | |__ | | | | | | version 3.6.1 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . SPDX-License-Identifier: MIT Copyright (c) 2013-2019 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef INCLUDE_NLOHMANN_JSON_HPP_ #define INCLUDE_NLOHMANN_JSON_HPP_ #define NLOHMANN_JSON_VERSION_MAJOR 3 #define NLOHMANN_JSON_VERSION_MINOR 6 #define NLOHMANN_JSON_VERSION_PATCH 1 #include // all_of, find, for_each #include // assert #include // and, not, or #include // nullptr_t, ptrdiff_t, size_t #include // hash, less #include // initializer_list #include // istream, ostream #include // random_access_iterator_tag #include // unique_ptr #include // accumulate #include // string, stoi, to_string #include // declval, forward, move, pair, swap #include // vector // #include #include // #include #include // transform #include // array #include // and, not #include // forward_list #include // inserter, front_inserter, end #include // map #include // string #include // tuple, make_tuple #include // is_arithmetic, is_same, is_enum, underlying_type, is_convertible #include // unordered_map #include // pair, declval #include // valarray // #include #include // exception #include // runtime_error #include // to_string // #include #include // size_t namespace nlohmann { namespace detail { /// struct to capture the start position of the current token struct position_t { /// the total number of characters read std::size_t chars_read_total = 0; /// the number of characters read in the current line std::size_t chars_read_current_line = 0; /// the number of lines read std::size_t lines_read = 0; /// conversion to size_t to preserve SAX interface constexpr operator size_t() const { return chars_read_total; } }; } // namespace detail } // namespace nlohmann namespace nlohmann { namespace detail { //////////////// // exceptions // //////////////// /*! @brief general exception of the @ref basic_json class This class is an extension of `std::exception` objects with a member @a id for exception ids. It is used as the base class for all exceptions thrown by the @ref basic_json class. This class can hence be used as "wildcard" to catch exceptions. Subclasses: - @ref parse_error for exceptions indicating a parse error - @ref invalid_iterator for exceptions indicating errors with iterators - @ref type_error for exceptions indicating executing a member function with a wrong type - @ref out_of_range for exceptions indicating access out of the defined range - @ref other_error for exceptions indicating other library errors @internal @note To have nothrow-copy-constructible exceptions, we internally use `std::runtime_error` which can cope with arbitrary-length error messages. Intermediate strings are built with static functions and then passed to the actual constructor. @endinternal @liveexample{The following code shows how arbitrary library exceptions can be caught.,exception} @since version 3.0.0 */ class exception : public std::exception { public: /// returns the explanatory string const char* what() const noexcept override { return m.what(); } /// the id of the exception const int id; protected: exception(int id_, const char* what_arg) : id(id_), m(what_arg) {} static std::string name(const std::string& ename, int id_) { return "[json.exception." + ename + "." + std::to_string(id_) + "] "; } private: /// an exception object as storage for error messages std::runtime_error m; }; /*! @brief exception indicating a parse error This exception is thrown by the library when a parse error occurs. Parse errors can occur during the deserialization of JSON text, CBOR, MessagePack, as well as when using JSON Patch. Member @a byte holds the byte index of the last read character in the input file. Exceptions have ids 1xx. name / id | example message | description ------------------------------ | --------------- | ------------------------- json.exception.parse_error.101 | parse error at 2: unexpected end of input; expected string literal | This error indicates a syntax error while deserializing a JSON text. The error message describes that an unexpected token (character) was encountered, and the member @a byte indicates the error position. json.exception.parse_error.102 | parse error at 14: missing or wrong low surrogate | JSON uses the `\uxxxx` format to describe Unicode characters. Code points above above 0xFFFF are split into two `\uxxxx` entries ("surrogate pairs"). This error indicates that the surrogate pair is incomplete or contains an invalid code point. json.exception.parse_error.103 | parse error: code points above 0x10FFFF are invalid | Unicode supports code points up to 0x10FFFF. Code points above 0x10FFFF are invalid. json.exception.parse_error.104 | parse error: JSON patch must be an array of objects | [RFC 6902](https://tools.ietf.org/html/rfc6902) requires a JSON Patch document to be a JSON document that represents an array of objects. json.exception.parse_error.105 | parse error: operation must have string member 'op' | An operation of a JSON Patch document must contain exactly one "op" member, whose value indicates the operation to perform. Its value must be one of "add", "remove", "replace", "move", "copy", or "test"; other values are errors. json.exception.parse_error.106 | parse error: array index '01' must not begin with '0' | An array index in a JSON Pointer ([RFC 6901](https://tools.ietf.org/html/rfc6901)) may be `0` or any number without a leading `0`. json.exception.parse_error.107 | parse error: JSON pointer must be empty or begin with '/' - was: 'foo' | A JSON Pointer must be a Unicode string containing a sequence of zero or more reference tokens, each prefixed by a `/` character. json.exception.parse_error.108 | parse error: escape character '~' must be followed with '0' or '1' | In a JSON Pointer, only `~0` and `~1` are valid escape sequences. json.exception.parse_error.109 | parse error: array index 'one' is not a number | A JSON Pointer array index must be a number. json.exception.parse_error.110 | parse error at 1: cannot read 2 bytes from vector | When parsing CBOR or MessagePack, the byte vector ends before the complete value has been read. json.exception.parse_error.112 | parse error at 1: error reading CBOR; last byte: 0xF8 | Not all types of CBOR or MessagePack are supported. This exception occurs if an unsupported byte was read. json.exception.parse_error.113 | parse error at 2: expected a CBOR string; last byte: 0x98 | While parsing a map key, a value that is not a string has been read. json.exception.parse_error.114 | parse error: Unsupported BSON record type 0x0F | The parsing of the corresponding BSON record type is not implemented (yet). @note For an input with n bytes, 1 is the index of the first character and n+1 is the index of the terminating null byte or the end of file. This also holds true when reading a byte vector (CBOR or MessagePack). @liveexample{The following code shows how a `parse_error` exception can be caught.,parse_error} @sa - @ref exception for the base class of the library exceptions @sa - @ref invalid_iterator for exceptions indicating errors with iterators @sa - @ref type_error for exceptions indicating executing a member function with a wrong type @sa - @ref out_of_range for exceptions indicating access out of the defined range @sa - @ref other_error for exceptions indicating other library errors @since version 3.0.0 */ class parse_error : public exception { public: /*! @brief create a parse error exception @param[in] id_ the id of the exception @param[in] pos the position where the error occurred (or with chars_read_total=0 if the position cannot be determined) @param[in] what_arg the explanatory string @return parse_error object */ static parse_error create(int id_, const position_t& pos, const std::string& what_arg) { std::string w = exception::name("parse_error", id_) + "parse error" + position_string(pos) + ": " + what_arg; return parse_error(id_, pos.chars_read_total, w.c_str()); } static parse_error create(int id_, std::size_t byte_, const std::string& what_arg) { std::string w = exception::name("parse_error", id_) + "parse error" + (byte_ != 0 ? (" at byte " + std::to_string(byte_)) : "") + ": " + what_arg; return parse_error(id_, byte_, w.c_str()); } /*! @brief byte index of the parse error The byte index of the last read character in the input file. @note For an input with n bytes, 1 is the index of the first character and n+1 is the index of the terminating null byte or the end of file. This also holds true when reading a byte vector (CBOR or MessagePack). */ const std::size_t byte; private: parse_error(int id_, std::size_t byte_, const char* what_arg) : exception(id_, what_arg), byte(byte_) {} static std::string position_string(const position_t& pos) { return " at line " + std::to_string(pos.lines_read + 1) + ", column " + std::to_string(pos.chars_read_current_line); } }; /*! @brief exception indicating errors with iterators This exception is thrown if iterators passed to a library function do not match the expected semantics. Exceptions have ids 2xx. name / id | example message | description ----------------------------------- | --------------- | ------------------------- json.exception.invalid_iterator.201 | iterators are not compatible | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid. json.exception.invalid_iterator.202 | iterator does not fit current value | In an erase or insert function, the passed iterator @a pos does not belong to the JSON value for which the function was called. It hence does not define a valid position for the deletion/insertion. json.exception.invalid_iterator.203 | iterators do not fit current value | Either iterator passed to function @ref erase(IteratorType first, IteratorType last) does not belong to the JSON value from which values shall be erased. It hence does not define a valid range to delete values from. json.exception.invalid_iterator.204 | iterators out of range | When an iterator range for a primitive type (number, boolean, or string) is passed to a constructor or an erase function, this range has to be exactly (@ref begin(), @ref end()), because this is the only way the single stored value is expressed. All other ranges are invalid. json.exception.invalid_iterator.205 | iterator out of range | When an iterator for a primitive type (number, boolean, or string) is passed to an erase function, the iterator has to be the @ref begin() iterator, because it is the only way to address the stored value. All other iterators are invalid. json.exception.invalid_iterator.206 | cannot construct with iterators from null | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) belong to a JSON null value and hence to not define a valid range. json.exception.invalid_iterator.207 | cannot use key() for non-object iterators | The key() member function can only be used on iterators belonging to a JSON object, because other types do not have a concept of a key. json.exception.invalid_iterator.208 | cannot use operator[] for object iterators | The operator[] to specify a concrete offset cannot be used on iterators belonging to a JSON object, because JSON objects are unordered. json.exception.invalid_iterator.209 | cannot use offsets with object iterators | The offset operators (+, -, +=, -=) cannot be used on iterators belonging to a JSON object, because JSON objects are unordered. json.exception.invalid_iterator.210 | iterators do not fit | The iterator range passed to the insert function are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid. json.exception.invalid_iterator.211 | passed iterators may not belong to container | The iterator range passed to the insert function must not be a subrange of the container to insert to. json.exception.invalid_iterator.212 | cannot compare iterators of different containers | When two iterators are compared, they must belong to the same container. json.exception.invalid_iterator.213 | cannot compare order of object iterators | The order of object iterators cannot be compared, because JSON objects are unordered. json.exception.invalid_iterator.214 | cannot get value | Cannot get value for iterator: Either the iterator belongs to a null value or it is an iterator to a primitive type (number, boolean, or string), but the iterator is different to @ref begin(). @liveexample{The following code shows how an `invalid_iterator` exception can be caught.,invalid_iterator} @sa - @ref exception for the base class of the library exceptions @sa - @ref parse_error for exceptions indicating a parse error @sa - @ref type_error for exceptions indicating executing a member function with a wrong type @sa - @ref out_of_range for exceptions indicating access out of the defined range @sa - @ref other_error for exceptions indicating other library errors @since version 3.0.0 */ class invalid_iterator : public exception { public: static invalid_iterator create(int id_, const std::string& what_arg) { std::string w = exception::name("invalid_iterator", id_) + what_arg; return invalid_iterator(id_, w.c_str()); } private: invalid_iterator(int id_, const char* what_arg) : exception(id_, what_arg) {} }; /*! @brief exception indicating executing a member function with a wrong type This exception is thrown in case of a type error; that is, a library function is executed on a JSON value whose type does not match the expected semantics. Exceptions have ids 3xx. name / id | example message | description ----------------------------- | --------------- | ------------------------- json.exception.type_error.301 | cannot create object from initializer list | To create an object from an initializer list, the initializer list must consist only of a list of pairs whose first element is a string. When this constraint is violated, an array is created instead. json.exception.type_error.302 | type must be object, but is array | During implicit or explicit value conversion, the JSON type must be compatible to the target type. For instance, a JSON string can only be converted into string types, but not into numbers or boolean types. json.exception.type_error.303 | incompatible ReferenceType for get_ref, actual type is object | To retrieve a reference to a value stored in a @ref basic_json object with @ref get_ref, the type of the reference must match the value type. For instance, for a JSON array, the @a ReferenceType must be @ref array_t &. json.exception.type_error.304 | cannot use at() with string | The @ref at() member functions can only be executed for certain JSON types. json.exception.type_error.305 | cannot use operator[] with string | The @ref operator[] member functions can only be executed for certain JSON types. json.exception.type_error.306 | cannot use value() with string | The @ref value() member functions can only be executed for certain JSON types. json.exception.type_error.307 | cannot use erase() with string | The @ref erase() member functions can only be executed for certain JSON types. json.exception.type_error.308 | cannot use push_back() with string | The @ref push_back() and @ref operator+= member functions can only be executed for certain JSON types. json.exception.type_error.309 | cannot use insert() with | The @ref insert() member functions can only be executed for certain JSON types. json.exception.type_error.310 | cannot use swap() with number | The @ref swap() member functions can only be executed for certain JSON types. json.exception.type_error.311 | cannot use emplace_back() with string | The @ref emplace_back() member function can only be executed for certain JSON types. json.exception.type_error.312 | cannot use update() with string | The @ref update() member functions can only be executed for certain JSON types. json.exception.type_error.313 | invalid value to unflatten | The @ref unflatten function converts an object whose keys are JSON Pointers back into an arbitrary nested JSON value. The JSON Pointers must not overlap, because then the resulting value would not be well defined. json.exception.type_error.314 | only objects can be unflattened | The @ref unflatten function only works for an object whose keys are JSON Pointers. json.exception.type_error.315 | values in object must be primitive | The @ref unflatten function only works for an object whose keys are JSON Pointers and whose values are primitive. json.exception.type_error.316 | invalid UTF-8 byte at index 10: 0x7E | The @ref dump function only works with UTF-8 encoded strings; that is, if you assign a `std::string` to a JSON value, make sure it is UTF-8 encoded. | json.exception.type_error.317 | JSON value cannot be serialized to requested format | The dynamic type of the object cannot be represented in the requested serialization format (e.g. a raw `true` or `null` JSON object cannot be serialized to BSON) | @liveexample{The following code shows how a `type_error` exception can be caught.,type_error} @sa - @ref exception for the base class of the library exceptions @sa - @ref parse_error for exceptions indicating a parse error @sa - @ref invalid_iterator for exceptions indicating errors with iterators @sa - @ref out_of_range for exceptions indicating access out of the defined range @sa - @ref other_error for exceptions indicating other library errors @since version 3.0.0 */ class type_error : public exception { public: static type_error create(int id_, const std::string& what_arg) { std::string w = exception::name("type_error", id_) + what_arg; return type_error(id_, w.c_str()); } private: type_error(int id_, const char* what_arg) : exception(id_, what_arg) {} }; /*! @brief exception indicating access out of the defined range This exception is thrown in case a library function is called on an input parameter that exceeds the expected range, for instance in case of array indices or nonexisting object keys. Exceptions have ids 4xx. name / id | example message | description ------------------------------- | --------------- | ------------------------- json.exception.out_of_range.401 | array index 3 is out of range | The provided array index @a i is larger than @a size-1. json.exception.out_of_range.402 | array index '-' (3) is out of range | The special array index `-` in a JSON Pointer never describes a valid element of the array, but the index past the end. That is, it can only be used to add elements at this position, but not to read it. json.exception.out_of_range.403 | key 'foo' not found | The provided key was not found in the JSON object. json.exception.out_of_range.404 | unresolved reference token 'foo' | A reference token in a JSON Pointer could not be resolved. json.exception.out_of_range.405 | JSON pointer has no parent | The JSON Patch operations 'remove' and 'add' can not be applied to the root element of the JSON value. json.exception.out_of_range.406 | number overflow parsing '10E1000' | A parsed number could not be stored as without changing it to NaN or INF. json.exception.out_of_range.407 | number overflow serializing '9223372036854775808' | UBJSON and BSON only support integer numbers up to 9223372036854775807. | json.exception.out_of_range.408 | excessive array size: 8658170730974374167 | The size (following `#`) of an UBJSON array or object exceeds the maximal capacity. | json.exception.out_of_range.409 | BSON key cannot contain code point U+0000 (at byte 2) | Key identifiers to be serialized to BSON cannot contain code point U+0000, since the key is stored as zero-terminated c-string | @liveexample{The following code shows how an `out_of_range` exception can be caught.,out_of_range} @sa - @ref exception for the base class of the library exceptions @sa - @ref parse_error for exceptions indicating a parse error @sa - @ref invalid_iterator for exceptions indicating errors with iterators @sa - @ref type_error for exceptions indicating executing a member function with a wrong type @sa - @ref other_error for exceptions indicating other library errors @since version 3.0.0 */ class out_of_range : public exception { public: static out_of_range create(int id_, const std::string& what_arg) { std::string w = exception::name("out_of_range", id_) + what_arg; return out_of_range(id_, w.c_str()); } private: out_of_range(int id_, const char* what_arg) : exception(id_, what_arg) {} }; /*! @brief exception indicating other library errors This exception is thrown in case of errors that cannot be classified with the other exception types. Exceptions have ids 5xx. name / id | example message | description ------------------------------ | --------------- | ------------------------- json.exception.other_error.501 | unsuccessful: {"op":"test","path":"/baz", "value":"bar"} | A JSON Patch operation 'test' failed. The unsuccessful operation is also printed. @sa - @ref exception for the base class of the library exceptions @sa - @ref parse_error for exceptions indicating a parse error @sa - @ref invalid_iterator for exceptions indicating errors with iterators @sa - @ref type_error for exceptions indicating executing a member function with a wrong type @sa - @ref out_of_range for exceptions indicating access out of the defined range @liveexample{The following code shows how an `other_error` exception can be caught.,other_error} @since version 3.0.0 */ class other_error : public exception { public: static other_error create(int id_, const std::string& what_arg) { std::string w = exception::name("other_error", id_) + what_arg; return other_error(id_, w.c_str()); } private: other_error(int id_, const char* what_arg) : exception(id_, what_arg) {} }; } // namespace detail } // namespace nlohmann // #include #include // pair // This file contains all internal macro definitions // You MUST include macro_unscope.hpp at the end of json.hpp to undef all of them // exclude unsupported compilers #if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK) #if defined(__clang__) #if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400 #error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers" #endif #elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER)) #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40800 #error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers" #endif #endif #endif // C++ language standard detection #if (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 #define JSON_HAS_CPP_17 #define JSON_HAS_CPP_14 #elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) #define JSON_HAS_CPP_14 #endif // disable float-equal warnings on GCC/clang #if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wfloat-equal" #endif // disable documentation warnings on clang #if defined(__clang__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdocumentation" #endif // allow for portable deprecation warnings #if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) #define JSON_DEPRECATED __attribute__((deprecated)) #elif defined(_MSC_VER) #define JSON_DEPRECATED __declspec(deprecated) #else #define JSON_DEPRECATED #endif // allow for portable nodiscard warnings #if defined(__has_cpp_attribute) #if __has_cpp_attribute(nodiscard) #if defined(__clang__) && !defined(JSON_HAS_CPP_17) // issue #1535 #define JSON_NODISCARD #else #define JSON_NODISCARD [[nodiscard]] #endif #elif __has_cpp_attribute(gnu::warn_unused_result) #define JSON_NODISCARD [[gnu::warn_unused_result]] #else #define JSON_NODISCARD #endif #else #define JSON_NODISCARD #endif // allow to disable exceptions #if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION) #define JSON_THROW(exception) throw exception #define JSON_TRY try #define JSON_CATCH(exception) catch(exception) #define JSON_INTERNAL_CATCH(exception) catch(exception) #else #include #define JSON_THROW(exception) std::abort() #define JSON_TRY if(true) #define JSON_CATCH(exception) if(false) #define JSON_INTERNAL_CATCH(exception) if(false) #endif // override exception macros #if defined(JSON_THROW_USER) #undef JSON_THROW #define JSON_THROW JSON_THROW_USER #endif #if defined(JSON_TRY_USER) #undef JSON_TRY #define JSON_TRY JSON_TRY_USER #endif #if defined(JSON_CATCH_USER) #undef JSON_CATCH #define JSON_CATCH JSON_CATCH_USER #undef JSON_INTERNAL_CATCH #define JSON_INTERNAL_CATCH JSON_CATCH_USER #endif #if defined(JSON_INTERNAL_CATCH_USER) #undef JSON_INTERNAL_CATCH #define JSON_INTERNAL_CATCH JSON_INTERNAL_CATCH_USER #endif // manual branch prediction #if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) #define JSON_LIKELY(x) __builtin_expect(x, 1) #define JSON_UNLIKELY(x) __builtin_expect(x, 0) #else #define JSON_LIKELY(x) x #define JSON_UNLIKELY(x) x #endif /*! @brief macro to briefly define a mapping between an enum and JSON @def NLOHMANN_JSON_SERIALIZE_ENUM @since version 3.4.0 */ #define NLOHMANN_JSON_SERIALIZE_ENUM(ENUM_TYPE, ...) \ template \ inline void to_json(BasicJsonType& j, const ENUM_TYPE& e) \ { \ static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ static const std::pair m[] = __VA_ARGS__; \ auto it = std::find_if(std::begin(m), std::end(m), \ [e](const std::pair& ej_pair) -> bool \ { \ return ej_pair.first == e; \ }); \ j = ((it != std::end(m)) ? it : std::begin(m))->second; \ } \ template \ inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \ { \ static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ static const std::pair m[] = __VA_ARGS__; \ auto it = std::find_if(std::begin(m), std::end(m), \ [j](const std::pair& ej_pair) -> bool \ { \ return ej_pair.second == j; \ }); \ e = ((it != std::end(m)) ? it : std::begin(m))->first; \ } // Ugly macros to avoid uglier copy-paste when specializing basic_json. They // may be removed in the future once the class is split. #define NLOHMANN_BASIC_JSON_TPL_DECLARATION \ template class ObjectType, \ template class ArrayType, \ class StringType, class BooleanType, class NumberIntegerType, \ class NumberUnsignedType, class NumberFloatType, \ template class AllocatorType, \ template class JSONSerializer> #define NLOHMANN_BASIC_JSON_TPL \ basic_json // #include #include // not #include // size_t #include // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type namespace nlohmann { namespace detail { // alias templates to reduce boilerplate template using enable_if_t = typename std::enable_if::type; template using uncvref_t = typename std::remove_cv::type>::type; // implementation of C++14 index_sequence and affiliates // source: https://stackoverflow.com/a/32223343 template struct index_sequence { using type = index_sequence; using value_type = std::size_t; static constexpr std::size_t size() noexcept { return sizeof...(Ints); } }; template struct merge_and_renumber; template struct merge_and_renumber, index_sequence> : index_sequence < I1..., (sizeof...(I1) + I2)... > {}; template struct make_index_sequence : merge_and_renumber < typename make_index_sequence < N / 2 >::type, typename make_index_sequence < N - N / 2 >::type > {}; template<> struct make_index_sequence<0> : index_sequence<> {}; template<> struct make_index_sequence<1> : index_sequence<0> {}; template using index_sequence_for = make_index_sequence; // dispatch utility (taken from ranges-v3) template struct priority_tag : priority_tag < N - 1 > {}; template<> struct priority_tag<0> {}; // taken from ranges-v3 template struct static_const { static constexpr T value{}; }; template constexpr T static_const::value; } // namespace detail } // namespace nlohmann // #include #include // not #include // numeric_limits #include // false_type, is_constructible, is_integral, is_same, true_type #include // declval // #include #include // random_access_iterator_tag // #include namespace nlohmann { namespace detail { template struct make_void { using type = void; }; template using void_t = typename make_void::type; } // namespace detail } // namespace nlohmann // #include namespace nlohmann { namespace detail { template struct iterator_types {}; template struct iterator_types < It, void_t> { using difference_type = typename It::difference_type; using value_type = typename It::value_type; using pointer = typename It::pointer; using reference = typename It::reference; using iterator_category = typename It::iterator_category; }; // This is required as some compilers implement std::iterator_traits in a way that // doesn't work with SFINAE. See https://github.com/nlohmann/json/issues/1341. template struct iterator_traits { }; template struct iterator_traits < T, enable_if_t < !std::is_pointer::value >> : iterator_types { }; template struct iterator_traits::value>> { using iterator_category = std::random_access_iterator_tag; using value_type = T; using difference_type = ptrdiff_t; using pointer = T * ; using reference = T & ; }; } // namespace detail } // namespace nlohmann // #include // #include // #include #include // #include // http://en.cppreference.com/w/cpp/experimental/is_detected namespace nlohmann { namespace detail { struct nonesuch { nonesuch() = delete; ~nonesuch() = delete; nonesuch(nonesuch const&) = delete; nonesuch(nonesuch const&&) = delete; void operator=(nonesuch const&) = delete; void operator=(nonesuch&&) = delete; }; template class Op, class... Args> struct detector { using value_t = std::false_type; using type = Default; }; template class Op, class... Args> struct detector>, Op, Args...> { using value_t = std::true_type; using type = Op; }; template